From abb7f7f89452cdd44bbc5f34d377a9306684fc9b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:10:17 +0100 Subject: [PATCH 001/194] Added Basic Scan interface ref #11 --- src/models/IScan.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/models/IScan.ts diff --git a/src/models/IScan.ts b/src/models/IScan.ts new file mode 100644 index 0000000..6906c5c --- /dev/null +++ b/src/models/IScan.ts @@ -0,0 +1,19 @@ +/** + * Defines the scan interface. +*/ +export interface IScan { + /** + * Autogenerated unique id (primary key). + */ + id: number; + + /** + * The associated runner. + */ + runner: Runner; + + /** + * The scan's distance in meters. + */ + distance: number; +} From a2cf8d1f2c4a2087e2dd6c297bea1a46db4220f9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:16:50 +0100 Subject: [PATCH 002/194] Switched from implementing the "interfaces" as interface to abstract classes ref #11 This was done to take advantage of typeorm and class validator --- src/models/IScan.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/models/IScan.ts b/src/models/IScan.ts index 6906c5c..fdd6541 100644 --- a/src/models/IScan.ts +++ b/src/models/IScan.ts @@ -1,19 +1,35 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsPositive, +} from "class-validator"; + /** * Defines the scan interface. */ -export interface IScan { +export abstract class IScan { /** * Autogenerated unique id (primary key). */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() id: number; /** * The associated runner. */ + @Column() + @IsNotEmpty() runner: Runner; /** * The scan's distance in meters. */ + @Column() + @IsInt() + @IsPositive() distance: number; } From f350007ae5696a16ecb1994bf3b4be843781b91d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:20:43 +0100 Subject: [PATCH 003/194] Added participant abstract class ref #11 --- src/models/IParticipant.ts | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/models/IParticipant.ts diff --git a/src/models/IParticipant.ts b/src/models/IParticipant.ts new file mode 100644 index 0000000..440e5e2 --- /dev/null +++ b/src/models/IParticipant.ts @@ -0,0 +1,73 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsEmail, + IsInt, + IsNotEmpty, + IsOptional, + IsPhoneNumber, + IsString, +} from "class-validator"; + +/** + * Defines the participant interface. +*/ +export abstract class IParticipant { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The participant's first name. + */ + @Column() + @IsNotEmpty() + @IsString() + firstname: string; + + /** + * The participant's middle name. + * Optional + */ + @Column() + @IsOptional() + @IsString() + middlename?: string; + + /** + * The participant's last name. + */ + @Column() + @IsOptional() + @IsString() + lastname: string; + + /** + * The participant's address. + * Optional + */ + @Column() + @IsOptional() + address?: Location; + + /** + * The participant's phone number. + * Optional + */ + @Column() + @IsOptional() + @IsPhoneNumber("DE") + phone?: string; + + /** + * The participant's email address. + * Optional + */ + @Column() + @IsOptional() + @IsEmail() + email?: string; +} From f8e1bf715b57788c16c8f8565bc20696c26b1499 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:21:44 +0100 Subject: [PATCH 004/194] Changed nameing scheme for the abstract classes since we're not useing interfaces ref #11 --- src/models/{IParticipant.ts => Participant.ts} | 4 ++-- src/models/{IScan.ts => Scan.ts} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/models/{IParticipant.ts => Participant.ts} (96%) rename src/models/{IScan.ts => Scan.ts} (94%) diff --git a/src/models/IParticipant.ts b/src/models/Participant.ts similarity index 96% rename from src/models/IParticipant.ts rename to src/models/Participant.ts index 440e5e2..551aeec 100644 --- a/src/models/IParticipant.ts +++ b/src/models/Participant.ts @@ -11,7 +11,7 @@ import { /** * Defines the participant interface. */ -export abstract class IParticipant { +export abstract class Participant { /** * Autogenerated unique id (primary key). */ @@ -70,4 +70,4 @@ export abstract class IParticipant { @IsOptional() @IsEmail() email?: string; -} +} \ No newline at end of file diff --git a/src/models/IScan.ts b/src/models/Scan.ts similarity index 94% rename from src/models/IScan.ts rename to src/models/Scan.ts index fdd6541..dce9230 100644 --- a/src/models/IScan.ts +++ b/src/models/Scan.ts @@ -9,7 +9,7 @@ import { /** * Defines the scan interface. */ -export abstract class IScan { +export abstract class Scan { /** * Autogenerated unique id (primary key). */ From f999c416c4367ddbb99fe19e443c1ce4867f5053 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:27:18 +0100 Subject: [PATCH 005/194] Added Runnergroup abstract class ref #11 --- src/models/RunnerGroup.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/models/RunnerGroup.ts diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts new file mode 100644 index 0000000..d5223bb --- /dev/null +++ b/src/models/RunnerGroup.ts @@ -0,0 +1,38 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsEmail, + IsInt, + IsNotEmpty, + IsOptional, + IsPhoneNumber, + IsString, +} from "class-validator"; + +/** + * Defines the runnerGroup interface. +*/ +export abstract class RunnerGroup { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The group's first name. + */ + @Column() + @IsNotEmpty() + @IsString() + name: string; + + /** + * The participant's middle name. + * Optional + */ + @Column() + @IsOptional() + contact?: GroupContact; +} \ No newline at end of file From 72f80859a974086ba354b48058844d12def937dd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:27:48 +0100 Subject: [PATCH 006/194] Added todo's for relationships --- src/models/Participant.ts | 1 + src/models/RunnerGroup.ts | 1 + src/models/Scan.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/src/models/Participant.ts b/src/models/Participant.ts index 551aeec..0d4ebed 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -51,6 +51,7 @@ export abstract class Participant { */ @Column() @IsOptional() + //TODO: Relationship address?: Location; /** diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index d5223bb..e074b29 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -34,5 +34,6 @@ export abstract class RunnerGroup { */ @Column() @IsOptional() + //TODO: Relationship contact?: GroupContact; } \ No newline at end of file diff --git a/src/models/Scan.ts b/src/models/Scan.ts index dce9230..e1951db 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -23,6 +23,7 @@ export abstract class Scan { */ @Column() @IsNotEmpty() + //TODO: Relationship runner: Runner; /** From 57ba0c3051ac539eca96a9d32faa00a910332db3 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:31:05 +0100 Subject: [PATCH 007/194] Added the donation abstract/interface --- src/models/Donation.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/models/Donation.ts diff --git a/src/models/Donation.ts b/src/models/Donation.ts new file mode 100644 index 0000000..82897c8 --- /dev/null +++ b/src/models/Donation.ts @@ -0,0 +1,37 @@ +import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsPositive, +} from "class-validator"; +import { Participant } from "./Participant"; + +/** + * Defines the donation interface. +*/ +export abstract class Donation { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The donations's donor. + */ + @Column() + @IsNotEmpty() + //TODO: Relationship + donor: Participant; + + /** + * The donation's amount in cents (or whatever your currency's smallest unit is.). + */ + @Column() + @IsInt() + @IsPositive() + amount: number; +} \ No newline at end of file From 748fff5c323c63cdfe0c8e9260ca175d99194a6b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:32:21 +0100 Subject: [PATCH 008/194] Cleaned up imports and descriptions ref #11 --- src/models/Participant.ts | 2 +- src/models/RunnerGroup.ts | 6 ++---- src/models/Scan.ts | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/models/Participant.ts b/src/models/Participant.ts index 0d4ebed..ca8f2ec 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column } from "typeorm"; import { IsEmail, IsInt, diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index e074b29..e7d8ecf 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -1,10 +1,8 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column } from "typeorm"; import { - IsEmail, IsInt, IsNotEmpty, IsOptional, - IsPhoneNumber, IsString, } from "class-validator"; @@ -29,7 +27,7 @@ export abstract class RunnerGroup { name: string; /** - * The participant's middle name. + * The group's contact. * Optional */ @Column() diff --git a/src/models/Scan.ts b/src/models/Scan.ts index e1951db..9c056f2 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column } from "typeorm"; import { IsInt, IsNotEmpty, From 5a04e61d1cb1583e2e496fbfcb5ba78c5be6dea1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:38:28 +0100 Subject: [PATCH 009/194] Added the runner class ref #11 --- src/models/Runner.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/models/Runner.ts diff --git a/src/models/Runner.ts b/src/models/Runner.ts new file mode 100644 index 0000000..3c9cec2 --- /dev/null +++ b/src/models/Runner.ts @@ -0,0 +1,18 @@ +import { Entity, Column } from "typeorm"; +import { IsNotEmpty,} from "class-validator"; +import { Participant } from "./Participant"; +import { RunnerGroup } from "./RunnerGroup"; + +/** + * Defines a runner. +*/ +@Entity() +export class Track extends Participant { + /** + * The runner's associated group. + */ + @Column() + @IsNotEmpty() + //TODO:Relation + group: RunnerGroup; +} From 7ce8c375a2cf6aceab827d69aaa9b2835a71a5f6 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:39:03 +0100 Subject: [PATCH 010/194] Fixed copy-paste slip up ref #11 --- src/models/Runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/Runner.ts b/src/models/Runner.ts index 3c9cec2..a625333 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -7,7 +7,7 @@ import { RunnerGroup } from "./RunnerGroup"; * Defines a runner. */ @Entity() -export class Track extends Participant { +export class Runner extends Participant { /** * The runner's associated group. */ From b632c09924bd3f1f42bfc0d3ed820d349576b13a Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:41:56 +0100 Subject: [PATCH 011/194] Added donor ref #11 --- src/models/Donor.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/models/Donor.ts diff --git a/src/models/Donor.ts b/src/models/Donor.ts new file mode 100644 index 0000000..0ed60da --- /dev/null +++ b/src/models/Donor.ts @@ -0,0 +1,17 @@ +import { Entity, Column } from "typeorm"; +import { IsBoolean } from "class-validator"; +import { Participant } from "./Participant"; + +/** + * Defines a donor. +*/ +@Entity() +export class Donor extends Participant { + /** + * Does this donor need a receipt?. + * Default: True + */ + @Column() + @IsBoolean() + receiptNeeded = false; +} \ No newline at end of file From daea0568a8da9dd661779510ab328175d6f790f2 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:45:59 +0100 Subject: [PATCH 012/194] Amount no longer is a column by default --- src/models/Donation.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/models/Donation.ts b/src/models/Donation.ts index 82897c8..3ccdafc 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -29,9 +29,8 @@ 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. */ - @Column() @IsInt() - @IsPositive() amount: number; } \ No newline at end of file From a8d1ec6f9b3a3a4673945de9143b299cac1933f8 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:50:43 +0100 Subject: [PATCH 013/194] Marked amount as abstract ref #11 --- src/models/Donation.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/models/Donation.ts b/src/models/Donation.ts index 3ccdafc..87f4e80 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -31,6 +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. */ - @IsInt() - amount: number; + abstract amount: number; } \ No newline at end of file From 6c32a9ebe954cd272ee140d06ef2553c9091239e Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:51:30 +0100 Subject: [PATCH 014/194] Added distance Donation --- src/models/DistanceDonation.ts | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/models/DistanceDonation.ts diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts new file mode 100644 index 0000000..59e0f62 --- /dev/null +++ b/src/models/DistanceDonation.ts @@ -0,0 +1,42 @@ +import { Entity, Column } from "typeorm"; +import { IsInt, IsNotEmpty, IsPositive,} from "class-validator"; +import { Donation } from "./Donation"; +import { Runner } from "./Runner"; + +/** + * Defines a distance based donation. + * Here people donate a certain amout per kilometer +*/ +@Entity() +export class DistanceDonation extends Donation { + /** + * The runner associated. + */ + @Column() + @IsNotEmpty() + //TODO:Relation + runner: Runner; + + /** + * The amount the donor set to be donated per kilometer that the runner ran. + */ + @Column() + @IsInt() + @IsPositive() + amountPerDistance: number; + + /** + * The donation's amount in cents (or whatever your currency's smallest unit is.). + * The exact implementation may differ for each type of donation. + */ + @IsInt() + public get amount(): number { + let calculatedAmount = -1; + try { + calculatedAmount = this.amountPerDistance * this.runner.getDistance(); + } catch (error) { + throw error; + } + return calculatedAmount; + } +} From deae0bb84b02b2ddf18104b7e3181c05cf20ec35 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:53:43 +0100 Subject: [PATCH 015/194] Added finxed donations ref #11 --- src/models/FixedDonation.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/models/FixedDonation.ts diff --git a/src/models/FixedDonation.ts b/src/models/FixedDonation.ts new file mode 100644 index 0000000..e429ddf --- /dev/null +++ b/src/models/FixedDonation.ts @@ -0,0 +1,18 @@ +import { Entity, Column } from "typeorm"; +import { IsInt, IsPositive,} from "class-validator"; +import { Donation } from "./Donation"; + +/** + * Defines a fixed donation. +*/ +@Entity() +export class FixedDonation extends Donation { + + /** + * The donation's amount in cents (or whatever your currency's smallest unit is.). + */ + @Column() + @IsInt() + @IsPositive() + amount: number; +} \ No newline at end of file From 66f7a7928c60ff6def7da2c7238de297d03d67e1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 17:58:23 +0100 Subject: [PATCH 016/194] Added the runner org class ref #11 --- src/models/RunnerOrganisation.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/models/RunnerOrganisation.ts diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts new file mode 100644 index 0000000..346d75a --- /dev/null +++ b/src/models/RunnerOrganisation.ts @@ -0,0 +1,19 @@ +import { Entity, Column } from "typeorm"; +import { IsOptional,} from "class-validator"; +import { RunnerGroup } from "./RunnerGroup"; + +/** + * Defines a runner organisation (business or school for example). +*/ +@Entity() +export class RunnerOrganisation extends RunnerGroup { + + /** + * The organisations's address. + * Optional + */ + @Column() + @IsOptional() + //TODO: Relationship + address?: Location; +} \ No newline at end of file From ac40527fa2997450f2e0685485bc7d91ca3e9bde Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:01:32 +0100 Subject: [PATCH 017/194] Added the runnerteam class ref #11 --- src/models/RunnerTeam.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/models/RunnerTeam.ts diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts new file mode 100644 index 0000000..204bc01 --- /dev/null +++ b/src/models/RunnerTeam.ts @@ -0,0 +1,19 @@ +import { Entity, Column } from "typeorm"; +import { IsNotEmpty } from "class-validator"; +import { RunnerGroup } from "./RunnerGroup"; + +/** + * Defines a runner team (class or deparment for example). +*/ +@Entity() +export class RunnerTeam extends RunnerGroup { + + /** + * The team's parent group. + * Optional + */ + @Column() + @IsNotEmpty() + //TODO: Relationship + parentGroup?: RunnerGroup; +} \ No newline at end of file From 96d70d5048b89324266f33468ab3a3ea4c771604 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:07:39 +0100 Subject: [PATCH 018/194] Added group contact class ref #11 --- src/models/GroupContact.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/models/GroupContact.ts diff --git a/src/models/GroupContact.ts b/src/models/GroupContact.ts new file mode 100644 index 0000000..e243d00 --- /dev/null +++ b/src/models/GroupContact.ts @@ -0,0 +1,8 @@ +import { Entity } from "typeorm"; +import { Participant } from "./Participant"; + +/** + * Defines a group's contact. +*/ +@Entity() +export class GroupContact extends Participant{} \ No newline at end of file From fbbb5df64f6033c528c7e20141fd3208b103500f Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:16:17 +0100 Subject: [PATCH 019/194] Added runnerCard class ref #11 --- src/models/RunnerCard.ts | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/models/RunnerCard.ts diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts new file mode 100644 index 0000000..09b3746 --- /dev/null +++ b/src/models/RunnerCard.ts @@ -0,0 +1,49 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsBoolean, + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; +import { Runner } from "./Runner"; + +/** + * Defines a card that can be scanned via a scanner station. +*/ +@Entity() +export class RunnerCard { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The runner that is currently associated with this card. + */ + @Column() + @IsOptional() + //TODO: Relation + runner: Runner; + + /** + * The card's code. + * This has to be able to being converted to something barcode compatible. + * Probably gonna be autogenerated. + */ + @Column() + @IsString() + @IsNotEmpty() + //TODO: Generate this + code: string; + + /** + * Is the card enabled (for fraud reasons)? + */ + @Column() + @IsBoolean() + enabled = true; +} From f7beebce3f5dc39f6b5e8ae34a1010ebf25058e1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:39:33 +0100 Subject: [PATCH 020/194] Added scanstation class ref #11 --- src/models/ScanStation.ts | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/models/ScanStation.ts diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts new file mode 100644 index 0000000..2989be0 --- /dev/null +++ b/src/models/ScanStation.ts @@ -0,0 +1,54 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsBoolean, + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; +import { Track } from "./Track"; + +/** + * ScannerStations have the ability to create scans for specific tracks. +*/ +@Entity() +export class ScanStation { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The station's description. + */ + @Column() + @IsNotEmpty() + @IsString() + description: string; + + /** + * The track this station is associated with. + */ + @Column() + @IsNotEmpty() + //TODO: Relation + track: Track; + + /** + * The station's api key. + */ + @Column() + @IsNotEmpty() + @IsString() + key: string; + + /** + * Is the station enabled (for fraud reasons)? + */ + @Column() + @IsBoolean() + enabled = true; +} From 79eecbb329c1f7181558d8a2fc39b91179ec91cf Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:48:28 +0100 Subject: [PATCH 021/194] fixxed missing imports and commented out a non-implemented function call ref #11 --- src/models/DistanceDonation.ts | 2 +- src/models/RunnerGroup.ts | 1 + src/models/Scan.ts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts index 59e0f62..d30b093 100644 --- a/src/models/DistanceDonation.ts +++ b/src/models/DistanceDonation.ts @@ -33,7 +33,7 @@ export class DistanceDonation extends Donation { public get amount(): number { let calculatedAmount = -1; try { - calculatedAmount = this.amountPerDistance * this.runner.getDistance(); + //calculatedAmount = this.amountPerDistance * this.runner.getDistance(); } catch (error) { throw error; } diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index e7d8ecf..08ea35d 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -5,6 +5,7 @@ import { IsOptional, IsString, } from "class-validator"; +import { GroupContact } from "./GroupContact"; /** * Defines the runnerGroup interface. diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 9c056f2..d59e0f5 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -5,6 +5,7 @@ import { IsOptional, IsPositive, } from "class-validator"; +import { Runner } from "./Runner"; /** * Defines the scan interface. From 084e2d99303ebf02f69782e647ce964649156d07 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:50:06 +0100 Subject: [PATCH 022/194] Renamed property, so it fits with the rest of the nameing ref #11 --- src/models/Track.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/Track.ts b/src/models/Track.ts index ea09063..d5aeaa5 100644 --- a/src/models/Track.ts +++ b/src/models/Track.ts @@ -29,10 +29,10 @@ export class Track { name: string; /** - * The track's length in meters. + * The track's length/distance in meters. */ @Column() @IsInt() @IsPositive() - length: number; + distance: number; } From df3715d8d67ea2506d541fe2418cd1a5bff77e8f Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:57:34 +0100 Subject: [PATCH 023/194] Changed the distance to be an abstract ref #11 --- src/models/Scan.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/models/Scan.ts b/src/models/Scan.ts index d59e0f5..3991b7a 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -30,8 +30,7 @@ export abstract class Scan { /** * The scan's distance in meters. */ - @Column() @IsInt() @IsPositive() - distance: number; + abstract distance: number; } From 8b2d6840a861d44132740fa65128d45400ebd449 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 18:57:44 +0100 Subject: [PATCH 024/194] Added the track scan class ref #11 --- src/models/TrackScan.ts | 83 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/models/TrackScan.ts diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts new file mode 100644 index 0000000..c7bc0b9 --- /dev/null +++ b/src/models/TrackScan.ts @@ -0,0 +1,83 @@ +import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsBoolean, + IsDateString, + IsInt, + IsNotEmpty, + IsOptional, + IsPositive, +} from "class-validator"; +import { Scan } from "./Scan"; +import { Runner } from "./Runner"; +import { Track } from "./Track"; +import { RunnerCard } from "./RunnerCard"; +import { ScanStation } from "./ScanStation"; + +/** + * Defines the scan interface. +*/ +export class TrackScan extends Scan { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The associated runner. + */ + @Column() + @IsNotEmpty() + //TODO: Relationship + runner: Runner; + + /** + * The associated track. + */ + @Column() + @IsNotEmpty() + //TODO: Relationship + track: Track; + + /** + * The associated card. + */ + @Column() + @IsNotEmpty() + //TODO: Relationship + card: RunnerCard; + + /** + * The scanning station. + */ + @Column() + @IsNotEmpty() + //TODO: Relationship + station: ScanStation; + + /** + * The scan's distance in meters. + */ + @IsInt() + @IsPositive() + public get distance(): number { + return this.track.distance; + } + + /** + * The scan's creation timestamp. + */ + @Column() + @IsDateString() + @IsNotEmpty() + timestamp: string; + + /** + * Is the scan valid (for fraud reasons). + */ + @Column() + @IsBoolean() + valid = true; +} From 2bd0cbadbed6b04136d7fc3c6f0f5e8ef35d982d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:03:41 +0100 Subject: [PATCH 025/194] Added the address class ref #11 --- src/models/Address.ts | 73 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/models/Address.ts diff --git a/src/models/Address.ts b/src/models/Address.ts new file mode 100644 index 0000000..df8e2ca --- /dev/null +++ b/src/models/Address.ts @@ -0,0 +1,73 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsPostalCode, + IsString, +} from "class-validator"; + +/** + * Defines a address (to be used for contact information). +*/ +@Entity() +export class Address { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The address's description. + */ + @Column() + @IsString() + @IsOptional() + description?: string; + + /** + * The address's first line. + * Containing the street and house number. + */ + @Column() + @IsString() + @IsNotEmpty() + address1: string; + + /** + * The address's second line. + * Containing optional information. + */ + @Column() + @IsString() + @IsOptional() + address2?: string; + + /** + * The address's postal code. + */ + @Column() + @IsString() + @IsNotEmpty() + @IsPostalCode("DE") + postalcode: string; + + /** + * The address's city. + */ + @Column() + @IsString() + @IsNotEmpty() + city: string; + + /** + * The address's country. + */ + @Column() + @IsString() + @IsNotEmpty() + country: string; +} From dca9aef25842b6127e258475039d6c2e1bad99f7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:06:56 +0100 Subject: [PATCH 026/194] Other classed are now using the new Address class rather than the old location placeholder ref #11 --- src/models/Participant.ts | 3 ++- src/models/RunnerOrganisation.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/models/Participant.ts b/src/models/Participant.ts index ca8f2ec..2b356a3 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -7,6 +7,7 @@ import { IsPhoneNumber, IsString, } from "class-validator"; +import { Address } from "./Address"; /** * Defines the participant interface. @@ -52,7 +53,7 @@ export abstract class Participant { @Column() @IsOptional() //TODO: Relationship - address?: Location; + address?: Address; /** * The participant's phone number. diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index 346d75a..3166964 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -1,6 +1,7 @@ import { Entity, Column } from "typeorm"; import { IsOptional,} from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; +import { Address } from "./Address"; /** * Defines a runner organisation (business or school for example). @@ -15,5 +16,5 @@ export class RunnerOrganisation extends RunnerGroup { @Column() @IsOptional() //TODO: Relationship - address?: Location; + address?: Address; } \ No newline at end of file From 1c43442300b9287ed7148a0847bf1db7805e7f0b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:10:52 +0100 Subject: [PATCH 027/194] Relations for distanceDonation ref #11 --- src/models/DistanceDonation.ts | 6 +++--- src/models/Runner.ts | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts index d30b093..915362b 100644 --- a/src/models/DistanceDonation.ts +++ b/src/models/DistanceDonation.ts @@ -1,4 +1,4 @@ -import { Entity, Column } from "typeorm"; +import { Entity, Column, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, IsPositive,} from "class-validator"; import { Donation } from "./Donation"; import { Runner } from "./Runner"; @@ -14,7 +14,7 @@ export class DistanceDonation extends Donation { */ @Column() @IsNotEmpty() - //TODO:Relation + @ManyToOne(() => Runner, runner => runner.distanceDonations) runner: Runner; /** @@ -33,7 +33,7 @@ export class DistanceDonation extends Donation { public get amount(): number { let calculatedAmount = -1; try { - //calculatedAmount = this.amountPerDistance * this.runner.getDistance(); + calculatedAmount = this.amountPerDistance * this.runner.getDistance(); } catch (error) { throw error; } diff --git a/src/models/Runner.ts b/src/models/Runner.ts index a625333..21af21a 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -1,7 +1,8 @@ -import { Entity, Column } from "typeorm"; +import { Entity, Column, OneToMany } from "typeorm"; import { IsNotEmpty,} from "class-validator"; import { Participant } from "./Participant"; import { RunnerGroup } from "./RunnerGroup"; +import { DistanceDonation } from "./DistanceDonation"; /** * Defines a runner. @@ -15,4 +16,7 @@ export class Runner extends Participant { @IsNotEmpty() //TODO:Relation group: RunnerGroup; + + @OneToMany(() => DistanceDonation, distanceDonation => distanceDonation.runner) + distanceDonations: DistanceDonation[]; } From 2b693917b0d003165bed24d97d9218f5dc613d18 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:13:56 +0100 Subject: [PATCH 028/194] Added relationships for donation ref #11 --- src/models/Donation.ts | 4 ++-- src/models/Participant.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/models/Donation.ts b/src/models/Donation.ts index 87f4e80..f7ea19c 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, @@ -24,7 +24,7 @@ export abstract class Donation { */ @Column() @IsNotEmpty() - //TODO: Relationship + @ManyToOne(() => Participant, donor => donor.donations) donor: Participant; /** diff --git a/src/models/Participant.ts b/src/models/Participant.ts index 2b356a3..1a8c245 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { IsEmail, IsInt, @@ -8,6 +8,7 @@ import { IsString, } from "class-validator"; import { Address } from "./Address"; +import { Donation } from "./Donation"; /** * Defines the participant interface. @@ -72,4 +73,10 @@ export abstract class Participant { @IsOptional() @IsEmail() email?: string; + + /** + * Used to link the participant as the donor of a donation. + */ + @OneToMany(() => Donation, donation => donation.donor) + donations: Donation[]; } \ No newline at end of file From 40752761302a043e8c974fe98bc57a865eedcb55 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:17:53 +0100 Subject: [PATCH 029/194] Added relations for participants ref #11 --- src/models/Address.ts | 9 ++++++++- src/models/Participant.ts | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/models/Address.ts b/src/models/Address.ts index df8e2ca..723bac9 100644 --- a/src/models/Address.ts +++ b/src/models/Address.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { IsInt, IsNotEmpty, @@ -6,6 +6,7 @@ import { IsPostalCode, IsString, } from "class-validator"; +import { Participant } from "./Participant"; /** * Defines a address (to be used for contact information). @@ -70,4 +71,10 @@ export class Address { @IsString() @IsNotEmpty() country: string; + + /** + * Used to link the address to participants. + */ + @OneToMany(() => Participant, participant => participant.address) + participants: Participant[]; } diff --git a/src/models/Participant.ts b/src/models/Participant.ts index 1a8c245..e70e14e 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from "typeorm"; import { IsEmail, IsInt, @@ -53,7 +53,7 @@ export abstract class Participant { */ @Column() @IsOptional() - //TODO: Relationship + @ManyToOne(() => Address, address => address.participants) address?: Address; /** From a6222a80251168153dd82b69a9eb65be87aaf3a8 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:20:27 +0100 Subject: [PATCH 030/194] Added relations for runners ref #11 --- src/models/Runner.ts | 7 +++++-- src/models/RunnerGroup.ts | 9 ++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/models/Runner.ts b/src/models/Runner.ts index 21af21a..49ce643 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -1,4 +1,4 @@ -import { Entity, Column, OneToMany } from "typeorm"; +import { Entity, Column, OneToMany, ManyToOne } from "typeorm"; import { IsNotEmpty,} from "class-validator"; import { Participant } from "./Participant"; import { RunnerGroup } from "./RunnerGroup"; @@ -14,9 +14,12 @@ export class Runner extends Participant { */ @Column() @IsNotEmpty() - //TODO:Relation + @ManyToOne(() => RunnerGroup, group => group.runners) group: RunnerGroup; + /** + * Used to link runners to donations. + */ @OneToMany(() => DistanceDonation, distanceDonation => distanceDonation.runner) distanceDonations: DistanceDonation[]; } diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index 08ea35d..d619e2f 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { IsInt, IsNotEmpty, @@ -6,6 +6,7 @@ import { IsString, } from "class-validator"; import { GroupContact } from "./GroupContact"; +import { Runner } from "./Runner"; /** * Defines the runnerGroup interface. @@ -35,4 +36,10 @@ export abstract class RunnerGroup { @IsOptional() //TODO: Relationship contact?: GroupContact; + + /** + * Used to link runners to a runner group. + */ + @OneToMany(() => Runner, runner => runner.group) + runners: Runner[]; } \ No newline at end of file From 029e4beaf545b2dacaeb272da7f0776cfc28b82d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:27:36 +0100 Subject: [PATCH 031/194] Added relations for runner cards ref #11 --- src/models/Runner.ts | 7 +++++++ src/models/RunnerCard.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/models/Runner.ts b/src/models/Runner.ts index 49ce643..3db90fc 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -3,6 +3,7 @@ import { IsNotEmpty,} from "class-validator"; import { Participant } from "./Participant"; import { RunnerGroup } from "./RunnerGroup"; import { DistanceDonation } from "./DistanceDonation"; +import { RunnerCard } from "./RunnerCard"; /** * Defines a runner. @@ -22,4 +23,10 @@ export class Runner extends Participant { */ @OneToMany(() => DistanceDonation, distanceDonation => distanceDonation.runner) distanceDonations: DistanceDonation[]; + + /** + * Used to link runners to cards. + */ + @OneToMany(() => RunnerCard, card => card.runner) + cards: RunnerCard[]; } diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index 09b3746..151f58c 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { IsBoolean, IsInt, @@ -26,7 +26,7 @@ export class RunnerCard { */ @Column() @IsOptional() - //TODO: Relation + @ManyToOne(() => Runner, runner => runner.cards) runner: Runner; /** From f28b08ed654309305960f8f5a882b2f6d492bf1d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:44:51 +0100 Subject: [PATCH 032/194] Added relations to RunnerGroup ref #11 --- src/models/GroupContact.ts | 11 +++++++++-- src/models/RunnerGroup.ts | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/models/GroupContact.ts b/src/models/GroupContact.ts index e243d00..3ed4e63 100644 --- a/src/models/GroupContact.ts +++ b/src/models/GroupContact.ts @@ -1,8 +1,15 @@ -import { Entity } from "typeorm"; +import { Entity, OneToMany } from "typeorm"; import { Participant } from "./Participant"; +import { RunnerGroup } from "./RunnerGroup"; /** * Defines a group's contact. */ @Entity() -export class GroupContact extends Participant{} \ No newline at end of file +export class GroupContact extends Participant{ + /** + * Used to link runners to donations. + */ + @OneToMany(() => RunnerGroup, group => group.contact) + groups: RunnerGroup[]; +} \ No newline at end of file diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index d619e2f..c35ad48 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, @@ -34,7 +34,7 @@ export abstract class RunnerGroup { */ @Column() @IsOptional() - //TODO: Relationship + @ManyToOne(() => GroupContact, contact => contact.groups) contact?: GroupContact; /** From 7ac46a7cc75618f5c01c46d3baee83c30d124124 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:48:20 +0100 Subject: [PATCH 033/194] Added relations to RunnerOrganisation --- src/models/Address.ts | 7 +++++++ src/models/RunnerOrganisation.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/models/Address.ts b/src/models/Address.ts index 723bac9..59e6570 100644 --- a/src/models/Address.ts +++ b/src/models/Address.ts @@ -7,6 +7,7 @@ import { IsString, } from "class-validator"; import { Participant } from "./Participant"; +import { RunnerOrganisation } from "./RunnerOrganisation"; /** * Defines a address (to be used for contact information). @@ -77,4 +78,10 @@ export class Address { */ @OneToMany(() => Participant, participant => participant.address) participants: Participant[]; + + /** + * Used to link the address to runner groups. + */ + @OneToMany(() => RunnerOrganisation, group => group.address) + groups: RunnerOrganisation[]; } diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index 3166964..d3e4ea8 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -1,4 +1,4 @@ -import { Entity, Column } from "typeorm"; +import { Entity, Column, ManyToOne } from "typeorm"; import { IsOptional,} from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; import { Address } from "./Address"; @@ -15,6 +15,6 @@ export class RunnerOrganisation extends RunnerGroup { */ @Column() @IsOptional() - //TODO: Relationship + @ManyToOne(() => Address, address => address.groups) address?: Address; } \ No newline at end of file From 0d9d72c223782310a86a20ef483d695b8845dbdd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 1 Dec 2020 19:51:16 +0100 Subject: [PATCH 034/194] Added relations for RunnerTeams ref #11 --- src/models/RunnerGroup.ts | 7 +++++++ src/models/RunnerTeam.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index c35ad48..652b861 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -7,6 +7,7 @@ import { } from "class-validator"; import { GroupContact } from "./GroupContact"; import { Runner } from "./Runner"; +import { RunnerTeam } from "./RunnerTeam"; /** * Defines the runnerGroup interface. @@ -42,4 +43,10 @@ export abstract class RunnerGroup { */ @OneToMany(() => Runner, runner => runner.group) runners: Runner[]; + + /** + * Used to link teams to runner groups. + */ + @OneToMany(() => RunnerTeam, team => team.parentGroup) + teams: RunnerTeam[]; } \ No newline at end of file diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts index 204bc01..1799e30 100644 --- a/src/models/RunnerTeam.ts +++ b/src/models/RunnerTeam.ts @@ -1,4 +1,4 @@ -import { Entity, Column } from "typeorm"; +import { Entity, Column, ManyToOne } from "typeorm"; import { IsNotEmpty } from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; @@ -14,6 +14,6 @@ export class RunnerTeam extends RunnerGroup { */ @Column() @IsNotEmpty() - //TODO: Relationship + @ManyToOne(() => RunnerGroup, group => group.teams) parentGroup?: RunnerGroup; } \ No newline at end of file From 8e2eac9dc07d99da7c150e393164e96fafcdc632 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:32:11 +0100 Subject: [PATCH 035/194] Added relations for Scans ref #11 --- src/models/Runner.ts | 7 +++++++ src/models/Scan.ts | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/models/Runner.ts b/src/models/Runner.ts index 3db90fc..dc2cbe2 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -4,6 +4,7 @@ import { Participant } from "./Participant"; import { RunnerGroup } from "./RunnerGroup"; import { DistanceDonation } from "./DistanceDonation"; import { RunnerCard } from "./RunnerCard"; +import { Scan } from "./Scan"; /** * Defines a runner. @@ -29,4 +30,10 @@ export class Runner extends Participant { */ @OneToMany(() => RunnerCard, card => card.runner) cards: RunnerCard[]; + + /** + * Used to link runners to a scans + */ + @OneToMany(() => Scan, scan => scan.runner) + scans: Scan[]; } diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 3991b7a..51d71c3 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, @@ -24,7 +24,7 @@ export abstract class Scan { */ @Column() @IsNotEmpty() - //TODO: Relationship + @ManyToOne(() => Runner, runner => runner.scans) runner: Runner; /** From f32291d7142ed262680e627832594984b901c7e4 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:34:04 +0100 Subject: [PATCH 036/194] Added scan station relationship ref #11 --- src/models/ScanStation.ts | 4 ++-- src/models/Track.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index 2989be0..cd317af 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { IsBoolean, IsInt, @@ -34,7 +34,7 @@ export class ScanStation { */ @Column() @IsNotEmpty() - //TODO: Relation + @ManyToOne(() => Track, track => track.stations) track: Track; /** diff --git a/src/models/Track.ts b/src/models/Track.ts index d5aeaa5..e8689a4 100644 --- a/src/models/Track.ts +++ b/src/models/Track.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { IsInt, IsNotEmpty, @@ -6,6 +6,7 @@ import { IsPositive, IsString, } from "class-validator"; +import { ScanStation } from "./ScanStation"; /** * Defines a track of given length. @@ -35,4 +36,10 @@ export class Track { @IsInt() @IsPositive() distance: number; + + /** + * Used to link scan stations to track. + */ + @OneToMany(() => ScanStation, station => station.track) + stations: ScanStation[]; } From 4d593eb840f51d33746479a26787c30f5a3983f5 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:36:11 +0100 Subject: [PATCH 037/194] Removed relation that was already implemented in the super ref #11 --- src/models/TrackScan.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index c7bc0b9..f6ffb81 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { IsBoolean, IsDateString, @@ -25,14 +25,6 @@ export class TrackScan extends Scan { @IsInt() id: number; - /** - * The associated runner. - */ - @Column() - @IsNotEmpty() - //TODO: Relationship - runner: Runner; - /** * The associated track. */ From c1242b2a2aa0cc2bb07caf845a204052ac3934a7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:40:25 +0100 Subject: [PATCH 038/194] Added TrackScan relationships ref #11 --- src/models/RunnerCard.ts | 9 ++++++++- src/models/ScanStation.ts | 9 ++++++++- src/models/Track.ts | 7 +++++++ src/models/TrackScan.ts | 6 +++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index 151f58c..b5a3e5a 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; import { IsBoolean, IsInt, @@ -7,6 +7,7 @@ import { IsString, } from "class-validator"; import { Runner } from "./Runner"; +import { TrackScan } from "./TrackScan"; /** * Defines a card that can be scanned via a scanner station. @@ -46,4 +47,10 @@ export class RunnerCard { @Column() @IsBoolean() enabled = true; + + /** + * Used to link cards to a track scans. + */ + @OneToMany(() => TrackScan, scan => scan.track) + scans: TrackScan[]; } diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index cd317af..5950ad0 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; import { IsBoolean, IsInt, @@ -7,6 +7,7 @@ import { IsString, } from "class-validator"; import { Track } from "./Track"; +import { TrackScan } from "./TrackScan"; /** * ScannerStations have the ability to create scans for specific tracks. @@ -51,4 +52,10 @@ export class ScanStation { @Column() @IsBoolean() enabled = true; + + /** + * Used to link track scans to a scan station. + */ + @OneToMany(() => TrackScan, scan => scan.track) + scans: TrackScan[]; } diff --git a/src/models/Track.ts b/src/models/Track.ts index e8689a4..eda6def 100644 --- a/src/models/Track.ts +++ b/src/models/Track.ts @@ -7,6 +7,7 @@ import { IsString, } from "class-validator"; import { ScanStation } from "./ScanStation"; +import { TrackScan } from "./TrackScan"; /** * Defines a track of given length. @@ -42,4 +43,10 @@ export class Track { */ @OneToMany(() => ScanStation, station => station.track) stations: ScanStation[]; + + /** + * Used to link track scans to a track. + */ + @OneToMany(() => TrackScan, scan => scan.track) + scans: TrackScan[]; } diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index f6ffb81..5eeb1bf 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -30,7 +30,7 @@ export class TrackScan extends Scan { */ @Column() @IsNotEmpty() - //TODO: Relationship + @ManyToOne(() => Track, track => track.scans) track: Track; /** @@ -38,7 +38,7 @@ export class TrackScan extends Scan { */ @Column() @IsNotEmpty() - //TODO: Relationship + @ManyToOne(() => RunnerCard, card => card.scans) card: RunnerCard; /** @@ -46,7 +46,7 @@ export class TrackScan extends Scan { */ @Column() @IsNotEmpty() - //TODO: Relationship + @ManyToOne(() => ScanStation, station => station.scans) station: ScanStation; /** From c82cc9a67060268518b33b4e6ee383292c529998 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:44:56 +0100 Subject: [PATCH 039/194] Moved attribute to super ref #11 --- src/models/Scan.ts | 7 +++++++ src/models/TrackScan.ts | 15 --------------- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 51d71c3..8fb7215 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -33,4 +33,11 @@ export abstract class Scan { @IsInt() @IsPositive() abstract distance: number; + + /** + * Is the scan valid (for fraud reasons). + */ + @Column() + @Boolean() + valid = true; } diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index 5eeb1bf..3691cf1 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -17,14 +17,6 @@ import { ScanStation } from "./ScanStation"; * Defines the scan interface. */ export class TrackScan extends Scan { - /** - * Autogenerated unique id (primary key). - */ - @PrimaryGeneratedColumn() - @IsOptional() - @IsInt() - id: number; - /** * The associated track. */ @@ -65,11 +57,4 @@ export class TrackScan extends Scan { @IsDateString() @IsNotEmpty() timestamp: string; - - /** - * Is the scan valid (for fraud reasons). - */ - @Column() - @IsBoolean() - valid = true; } From abf7aaeda3a39aecac98756c19b275405d869cb5 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:46:43 +0100 Subject: [PATCH 040/194] Fixed import ref #11 --- src/models/Scan.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 8fb7215..38c18c9 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -1,5 +1,6 @@ import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; import { + IsBoolean, IsInt, IsNotEmpty, IsOptional, @@ -38,6 +39,6 @@ export abstract class Scan { * Is the scan valid (for fraud reasons). */ @Column() - @Boolean() + @IsBoolean() valid = true; } From 84dd1fe4a5871e1bf3d5510bb92d826d519dd6ad Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:46:53 +0100 Subject: [PATCH 041/194] Added missing getter --- src/models/Runner.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/models/Runner.ts b/src/models/Runner.ts index dc2cbe2..bb0047a 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -1,5 +1,5 @@ import { Entity, Column, OneToMany, ManyToOne } from "typeorm"; -import { IsNotEmpty,} from "class-validator"; +import { IsInt, IsNotEmpty,} from "class-validator"; import { Participant } from "./Participant"; import { RunnerGroup } from "./RunnerGroup"; import { DistanceDonation } from "./DistanceDonation"; @@ -36,4 +36,10 @@ export class Runner extends Participant { */ @OneToMany(() => Scan, scan => scan.runner) scans: Scan[]; + + @IsInt() + public get distance() : number { + return this.scans.filter(scan => scan.valid === true).reduce((sum, current) => sum + current.distance, 0); + } + } From d0a1ea3292cf7c033d24a39ba6fdccde3c0f4022 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 15:49:19 +0100 Subject: [PATCH 042/194] Renamed getter ref #11 --- src/models/DistanceDonation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts index 915362b..55236d1 100644 --- a/src/models/DistanceDonation.ts +++ b/src/models/DistanceDonation.ts @@ -33,7 +33,7 @@ export class DistanceDonation extends Donation { public get amount(): number { let calculatedAmount = -1; try { - calculatedAmount = this.amountPerDistance * this.runner.getDistance(); + calculatedAmount = this.amountPerDistance * this.runner.distance; } catch (error) { throw error; } From dd5f4488be9cf5df6fae740f60fb5597382ca984 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 16:00:09 +0100 Subject: [PATCH 043/194] Cleaned up relations ref #11 --- src/models/DistanceDonation.ts | 1 - src/models/Donation.ts | 1 - src/models/Participant.ts | 1 - src/models/Runner.ts | 1 - src/models/RunnerCard.ts | 1 - src/models/RunnerGroup.ts | 1 - src/models/RunnerOrganisation.ts | 1 - src/models/RunnerTeam.ts | 1 - src/models/Scan.ts | 1 - src/models/ScanStation.ts | 1 - src/models/TrackScan.ts | 3 --- 11 files changed, 13 deletions(-) diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts index 55236d1..878f8cb 100644 --- a/src/models/DistanceDonation.ts +++ b/src/models/DistanceDonation.ts @@ -12,7 +12,6 @@ export class DistanceDonation extends Donation { /** * The runner associated. */ - @Column() @IsNotEmpty() @ManyToOne(() => Runner, runner => runner.distanceDonations) runner: Runner; diff --git a/src/models/Donation.ts b/src/models/Donation.ts index f7ea19c..3d59202 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -22,7 +22,6 @@ export abstract class Donation { /** * The donations's donor. */ - @Column() @IsNotEmpty() @ManyToOne(() => Participant, donor => donor.donations) donor: Participant; diff --git a/src/models/Participant.ts b/src/models/Participant.ts index e70e14e..0465e01 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -51,7 +51,6 @@ export abstract class Participant { * The participant's address. * Optional */ - @Column() @IsOptional() @ManyToOne(() => Address, address => address.participants) address?: Address; diff --git a/src/models/Runner.ts b/src/models/Runner.ts index bb0047a..3a8532a 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -14,7 +14,6 @@ export class Runner extends Participant { /** * The runner's associated group. */ - @Column() @IsNotEmpty() @ManyToOne(() => RunnerGroup, group => group.runners) group: RunnerGroup; diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index b5a3e5a..e751506 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -25,7 +25,6 @@ export class RunnerCard { /** * The runner that is currently associated with this card. */ - @Column() @IsOptional() @ManyToOne(() => Runner, runner => runner.cards) runner: Runner; diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index 652b861..8f4c5ea 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -33,7 +33,6 @@ export abstract class RunnerGroup { * The group's contact. * Optional */ - @Column() @IsOptional() @ManyToOne(() => GroupContact, contact => contact.groups) contact?: GroupContact; diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index d3e4ea8..22fe8a5 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -13,7 +13,6 @@ export class RunnerOrganisation extends RunnerGroup { * The organisations's address. * Optional */ - @Column() @IsOptional() @ManyToOne(() => Address, address => address.groups) address?: Address; diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts index 1799e30..e716e27 100644 --- a/src/models/RunnerTeam.ts +++ b/src/models/RunnerTeam.ts @@ -12,7 +12,6 @@ export class RunnerTeam extends RunnerGroup { * The team's parent group. * Optional */ - @Column() @IsNotEmpty() @ManyToOne(() => RunnerGroup, group => group.teams) parentGroup?: RunnerGroup; diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 38c18c9..6e3f654 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -23,7 +23,6 @@ export abstract class Scan { /** * The associated runner. */ - @Column() @IsNotEmpty() @ManyToOne(() => Runner, runner => runner.scans) runner: Runner; diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index 5950ad0..c89a3a4 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -33,7 +33,6 @@ export class ScanStation { /** * The track this station is associated with. */ - @Column() @IsNotEmpty() @ManyToOne(() => Track, track => track.stations) track: Track; diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index 3691cf1..0f08d2a 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -20,7 +20,6 @@ export class TrackScan extends Scan { /** * The associated track. */ - @Column() @IsNotEmpty() @ManyToOne(() => Track, track => track.scans) track: Track; @@ -28,7 +27,6 @@ export class TrackScan extends Scan { /** * The associated card. */ - @Column() @IsNotEmpty() @ManyToOne(() => RunnerCard, card => card.scans) card: RunnerCard; @@ -36,7 +34,6 @@ export class TrackScan extends Scan { /** * The scanning station. */ - @Column() @IsNotEmpty() @ManyToOne(() => ScanStation, station => station.scans) station: ScanStation; From 5bf978d32dc8e9d49d9fcff0adeb75c8ee50ad03 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 16:07:18 +0100 Subject: [PATCH 044/194] Turned the abstracts into entities ref #11 --- src/models/Donation.ts | 3 ++- src/models/Participant.ts | 3 ++- src/models/RunnerGroup.ts | 3 ++- src/models/Scan.ts | 3 ++- src/models/TrackScan.ts | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/models/Donation.ts b/src/models/Donation.ts index 3d59202..7143c99 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from "typeorm"; import { IsInt, IsNotEmpty, @@ -10,6 +10,7 @@ import { Participant } from "./Participant"; /** * Defines the donation interface. */ +@Entity() export abstract class Donation { /** * Autogenerated unique id (primary key). diff --git a/src/models/Participant.ts b/src/models/Participant.ts index 0465e01..2f4327b 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm"; import { IsEmail, IsInt, @@ -13,6 +13,7 @@ import { Donation } from "./Donation"; /** * Defines the participant interface. */ +@Entity() export abstract class Participant { /** * Autogenerated unique id (primary key). diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index 8f4c5ea..52f478f 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm"; import { IsInt, IsNotEmpty, @@ -12,6 +12,7 @@ import { RunnerTeam } from "./RunnerTeam"; /** * Defines the runnerGroup interface. */ +@Entity() export abstract class RunnerGroup { /** * Autogenerated unique id (primary key). diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 6e3f654..6c2dccc 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from "typeorm"; import { IsBoolean, IsInt, @@ -11,6 +11,7 @@ import { Runner } from "./Runner"; /** * Defines the scan interface. */ +@Entity() export abstract class Scan { /** * Autogenerated unique id (primary key). diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index 0f08d2a..ce35ee8 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from "typeorm"; import { IsBoolean, IsDateString, @@ -16,6 +16,7 @@ import { ScanStation } from "./ScanStation"; /** * Defines the scan interface. */ +@Entity() export class TrackScan extends Scan { /** * The associated track. From ac0ce799f950dac194192177329d07c802feb457 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 17:40:03 +0100 Subject: [PATCH 045/194] Fixed the cirvular import BS ref #11 --- src/models/Donor.ts | 2 +- src/models/GroupContact.ts | 80 ++++++++++++++++++++++++++++++-- src/models/RunnerCard.ts | 2 +- src/models/RunnerGroup.ts | 6 --- src/models/RunnerOrganisation.ts | 9 +++- src/models/RunnerTeam.ts | 5 +- src/models/Scan.ts | 2 +- src/models/ScanStation.ts | 2 +- 8 files changed, 92 insertions(+), 16 deletions(-) diff --git a/src/models/Donor.ts b/src/models/Donor.ts index 0ed60da..db80082 100644 --- a/src/models/Donor.ts +++ b/src/models/Donor.ts @@ -13,5 +13,5 @@ export class Donor extends Participant { */ @Column() @IsBoolean() - receiptNeeded = false; + receiptNeeded: boolean; } \ No newline at end of file diff --git a/src/models/GroupContact.ts b/src/models/GroupContact.ts index 3ed4e63..58a07a7 100644 --- a/src/models/GroupContact.ts +++ b/src/models/GroupContact.ts @@ -1,12 +1,86 @@ -import { Entity, OneToMany } from "typeorm"; -import { Participant } from "./Participant"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm"; +import { + IsEmail, + IsInt, + IsNotEmpty, + IsOptional, + IsPhoneNumber, + IsString, +} from "class-validator"; +import { Address } from "./Address"; +import { Donation } from "./Donation"; import { RunnerGroup } from "./RunnerGroup"; /** * Defines a group's contact. */ @Entity() -export class GroupContact extends Participant{ +export class GroupContact { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The contact's first name. + */ + @Column() + @IsNotEmpty() + @IsString() + firstname: string; + + /** + * The contact's middle name. + * Optional + */ + @Column() + @IsOptional() + @IsString() + middlename?: string; + + /** + * The contact's last name. + */ + @Column() + @IsOptional() + @IsString() + lastname: string; + + /** + * The contact's address. + * Optional + */ + @IsOptional() + @ManyToOne(() => Address, address => address.participants) + address?: Address; + + /** + * The contact's phone number. + * Optional + */ + @Column() + @IsOptional() + @IsPhoneNumber("DE") + phone?: string; + + /** + * The contact's email address. + * Optional + */ + @Column() + @IsOptional() + @IsEmail() + email?: string; + + /** + * Used to link the contact as the donor of a donation. + */ + @OneToMany(() => Donation, donation => donation.donor) + donations: Donation[]; + /** * Used to link runners to donations. */ diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index e751506..2796f4c 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -45,7 +45,7 @@ export class RunnerCard { */ @Column() @IsBoolean() - enabled = true; + enabled: boolean; /** * Used to link cards to a track scans. diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index 52f478f..a7c199b 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -43,10 +43,4 @@ export abstract class RunnerGroup { */ @OneToMany(() => Runner, runner => runner.group) runners: Runner[]; - - /** - * Used to link teams to runner groups. - */ - @OneToMany(() => RunnerTeam, team => team.parentGroup) - teams: RunnerTeam[]; } \ No newline at end of file diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index 22fe8a5..958b363 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -1,7 +1,8 @@ -import { Entity, Column, ManyToOne } from "typeorm"; +import { Entity, Column, ManyToOne, OneToMany } from "typeorm"; import { IsOptional,} from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; import { Address } from "./Address"; +import { RunnerTeam } from "./RunnerTeam"; /** * Defines a runner organisation (business or school for example). @@ -16,4 +17,10 @@ export class RunnerOrganisation extends RunnerGroup { @IsOptional() @ManyToOne(() => Address, address => address.groups) address?: Address; + + /** + * Used to link teams to runner groups. + */ + @OneToMany(() => RunnerTeam, team => team.parentGroup) + teams: RunnerTeam[]; } \ No newline at end of file diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts index e716e27..f12c5eb 100644 --- a/src/models/RunnerTeam.ts +++ b/src/models/RunnerTeam.ts @@ -1,6 +1,7 @@ import { Entity, Column, ManyToOne } from "typeorm"; import { IsNotEmpty } from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; +import { RunnerOrganisation } from "./RunnerOrganisation"; /** * Defines a runner team (class or deparment for example). @@ -13,6 +14,6 @@ export class RunnerTeam extends RunnerGroup { * Optional */ @IsNotEmpty() - @ManyToOne(() => RunnerGroup, group => group.teams) - parentGroup?: RunnerGroup; + @ManyToOne(() => RunnerOrganisation, org => org.teams) + parentGroup?: RunnerOrganisation; } \ No newline at end of file diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 6c2dccc..db41242 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -40,5 +40,5 @@ export abstract class Scan { */ @Column() @IsBoolean() - valid = true; + valid: boolean; } diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index c89a3a4..4cd0246 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -50,7 +50,7 @@ export class ScanStation { */ @Column() @IsBoolean() - enabled = true; + enabled: boolean; /** * Used to link track scans to a scan station. From 932e782a14454a05aa59f9b76c7c9638aa440385 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 18:05:18 +0100 Subject: [PATCH 046/194] Added defaults back in --- src/models/Donor.ts | 4 ++-- src/models/RunnerCard.ts | 3 ++- src/models/Scan.ts | 3 ++- src/models/ScanStation.ts | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/models/Donor.ts b/src/models/Donor.ts index db80082..5d0af07 100644 --- a/src/models/Donor.ts +++ b/src/models/Donor.ts @@ -9,9 +9,9 @@ import { Participant } from "./Participant"; export class Donor extends Participant { /** * Does this donor need a receipt?. - * Default: True + * Default: false */ @Column() @IsBoolean() - receiptNeeded: boolean; + receiptNeeded: boolean = false; } \ No newline at end of file diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index 2796f4c..f978d0c 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -42,10 +42,11 @@ export class RunnerCard { /** * Is the card enabled (for fraud reasons)? + * Default: true */ @Column() @IsBoolean() - enabled: boolean; + enabled: boolean = true; /** * Used to link cards to a track scans. diff --git a/src/models/Scan.ts b/src/models/Scan.ts index db41242..055e94b 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -37,8 +37,9 @@ export abstract class Scan { /** * Is the scan valid (for fraud reasons). + * Default: true */ @Column() @IsBoolean() - valid: boolean; + valid: boolean = true; } diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index 4cd0246..4415ebc 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -47,10 +47,11 @@ export class ScanStation { /** * Is the station enabled (for fraud reasons)? + * Default: true */ @Column() @IsBoolean() - enabled: boolean; + enabled: boolean = true; /** * Used to link track scans to a scan station. From 48e28e7b7a19e1ad66ba12461826676bd9074a58 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 18:07:33 +0100 Subject: [PATCH 047/194] User + UserGroup --- src/models/User.ts | 100 ++++++++++++++++++++++++++++++++++++++++ src/models/UserGroup.ts | 44 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 src/models/User.ts create mode 100644 src/models/UserGroup.ts diff --git a/src/models/User.ts b/src/models/User.ts new file mode 100644 index 0000000..e575a79 --- /dev/null +++ b/src/models/User.ts @@ -0,0 +1,100 @@ +import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique } from "typeorm"; +import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, isUUID, } from "class-validator"; +import { UserGroup } from './UserGroup'; + +/** + * Defines a admin user. +*/ +@Entity() +export class User { + /** + * autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * autogenerated uuid + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + @Generated("uuid") + uuid: string; + + /** + * user email + */ + @IsEmail() + email: string; + + /** + * username + */ + @IsString() + username: string; + + /** + * firstname + */ + @IsString() + @IsNotEmpty() + firstname: string; + + /** + * middlename + */ + @IsString() + @IsOptional() + middlename: string; + + /** + * lastname + */ + @IsString() + @IsNotEmpty() + lastname: string; + + /** + * password + */ + @IsString() + @IsNotEmpty() + password: string; + + /** + * userpermissions + */ + // TODO: UserPermission implementation + // @OneToMany(() => UserPermission,userpermission=>) + // userpermissions: UserPermission[]; + + /** + * groups + */ + // TODO: UserGroup implementation + // @OneToMany(() => UserGroup, usergroup => usergroup.) + @IsOptional() + groups: UserGroup[]; + + /** + * is user enabled? + */ + @IsBoolean() + enabled: boolean; + + /** + * jwt refresh count + */ + @IsInt() + @Column({ default: 1 }) + refreshTokenCount: number; + + /** + * profilepic + */ + @IsString() + profilepic: string; +} diff --git a/src/models/UserGroup.ts b/src/models/UserGroup.ts new file mode 100644 index 0000000..61a434e --- /dev/null +++ b/src/models/UserGroup.ts @@ -0,0 +1,44 @@ +import { PrimaryGeneratedColumn, Column, OneToMany, Entity } from "typeorm"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; +import { User } from "./User"; + +/** + * Defines the UserGroup interface. +*/ +@Entity() +export abstract class UserGroup { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The group's name + */ + @Column() + @IsNotEmpty() + @IsString() + name: string; + + /** + * The group's description + */ + @Column() + @IsOptional() + @IsString() + description: string; + + /** + * Used to link users to a user group. + */ + // TODO: + // grouppermissions: GroupPermissions[]; +} \ No newline at end of file From d47983a032c5bb4d4b2958cc013df0b92ece2844 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 18:11:23 +0100 Subject: [PATCH 048/194] =?UTF-8?q?=F0=9F=9A=A7=20User=20class=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/User.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/User.ts b/src/models/User.ts index e575a79..fcc897e 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -18,7 +18,6 @@ export class User { /** * autogenerated uuid */ - @PrimaryGeneratedColumn() @IsOptional() @IsInt() @Generated("uuid") From 4a9fd57356132b2fce539724c2faecdc6f160e7f Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 18:20:53 +0100 Subject: [PATCH 049/194] Fixed user<-> Group relationship --- src/models/User.ts | 7 +++---- src/models/UserGroup.ts | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/models/User.ts b/src/models/User.ts index fcc897e..e068b57 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,4 +1,4 @@ -import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique } from "typeorm"; +import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, isUUID, } from "class-validator"; import { UserGroup } from './UserGroup'; @@ -73,9 +73,8 @@ export class User { /** * groups */ - // TODO: UserGroup implementation - // @OneToMany(() => UserGroup, usergroup => usergroup.) - @IsOptional() + @ManyToMany(() => UserGroup) + @JoinTable() groups: UserGroup[]; /** diff --git a/src/models/UserGroup.ts b/src/models/UserGroup.ts index 61a434e..96b1633 100644 --- a/src/models/UserGroup.ts +++ b/src/models/UserGroup.ts @@ -37,7 +37,7 @@ export abstract class UserGroup { description: string; /** - * Used to link users to a user group. + * TODO: Something about permission stuff */ // TODO: // grouppermissions: GroupPermissions[]; From 82ca8f48dc8cd5438ba1eeab7b222b1df60355df Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 18:27:00 +0100 Subject: [PATCH 050/194] =?UTF-8?q?=F0=9F=9A=A7=20UserAction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/UserAction.ts | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/models/UserAction.ts diff --git a/src/models/UserAction.ts b/src/models/UserAction.ts new file mode 100644 index 0000000..7f6dc43 --- /dev/null +++ b/src/models/UserAction.ts @@ -0,0 +1,48 @@ +import { PrimaryGeneratedColumn, Column, OneToMany, Entity } from "typeorm"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; + +/** + * Defines the UserAction interface. +*/ +@Entity() +export class UserAction { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + // TODO: + // user: relation + + /** + * The actions's target (e.g. Track#2) + */ + @Column() + @IsNotEmpty() + @IsString() + target: string; + + /** + * The actions's action (e.g. UPDATE) + */ + @Column() + @IsNotEmpty() + @IsString() + action: string; + + /** + * The description of change (before-> after; e.g. distance:15->17) + */ + @Column() + @IsOptional() + @IsString() + changed: string; +} \ No newline at end of file From 1d5726492286f21adcc6064c62159de8d134538d Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 18:27:06 +0100 Subject: [PATCH 051/194] =?UTF-8?q?=F0=9F=9A=A7=20Permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/Permission.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/models/Permission.ts diff --git a/src/models/Permission.ts b/src/models/Permission.ts new file mode 100644 index 0000000..4dfeff2 --- /dev/null +++ b/src/models/Permission.ts @@ -0,0 +1,37 @@ +import { PrimaryGeneratedColumn, Column, OneToMany, Entity } from "typeorm"; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsString, +} from "class-validator"; + +/** + * Defines the UserGroup interface. +*/ +@Entity() +export abstract class UserGroup { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * The target + */ + @Column() + @IsNotEmpty() + @IsString() + target: string; + + /** + * The action type + */ + @Column() + @IsNotEmpty() + @IsString() + action: string; +} \ No newline at end of file From e4d5afbebe7179c926a0625fd11d17f5285e55a8 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 18:28:48 +0100 Subject: [PATCH 052/194] =?UTF-8?q?=F0=9F=9A=A7=20Permission?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/Permission.ts | 4 ++-- src/models/User.ts | 9 +++++---- src/models/UserGroup.ts | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/models/Permission.ts b/src/models/Permission.ts index 4dfeff2..e5d3d3c 100644 --- a/src/models/Permission.ts +++ b/src/models/Permission.ts @@ -7,10 +7,10 @@ import { } from "class-validator"; /** - * Defines the UserGroup interface. + * Defines the Permission interface. */ @Entity() -export abstract class UserGroup { +export abstract class Permission { /** * Autogenerated unique id (primary key). */ diff --git a/src/models/User.ts b/src/models/User.ts index e068b57..b59ad3f 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,6 +1,7 @@ import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, isUUID, } from "class-validator"; import { UserGroup } from './UserGroup'; +import { Permission } from './Permission'; /** * Defines a admin user. @@ -64,11 +65,11 @@ export class User { password: string; /** - * userpermissions + * permissions */ - // TODO: UserPermission implementation - // @OneToMany(() => UserPermission,userpermission=>) - // userpermissions: UserPermission[]; + // TODO: Permission implementation + // @OneToMany(() => Permission,userpermission=>) + // permissions: Permission[]; /** * groups diff --git a/src/models/UserGroup.ts b/src/models/UserGroup.ts index 96b1633..90f6378 100644 --- a/src/models/UserGroup.ts +++ b/src/models/UserGroup.ts @@ -5,7 +5,7 @@ import { IsOptional, IsString, } from "class-validator"; -import { User } from "./User"; +import { Permission } from "./Permission"; /** * Defines the UserGroup interface. @@ -40,5 +40,5 @@ export abstract class UserGroup { * TODO: Something about permission stuff */ // TODO: - // grouppermissions: GroupPermissions[]; + // grouppermissions: Permission[]; } \ No newline at end of file From a78bbb1de5765f6339440663bb884367b6d3bfdf Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 18:46:36 +0100 Subject: [PATCH 053/194] =?UTF-8?q?=F0=9F=9A=A7=20User=20+=20Permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/Permission.ts | 17 +++++++++++++++-- src/models/User.ts | 16 +++++++++++++--- src/models/UserGroup.ts | 14 +++++++------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/models/Permission.ts b/src/models/Permission.ts index e5d3d3c..e9b049b 100644 --- a/src/models/Permission.ts +++ b/src/models/Permission.ts @@ -1,11 +1,12 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, IsString, } from "class-validator"; - +import { User } from './User'; +import { UserGroup } from './UserGroup'; /** * Defines the Permission interface. */ @@ -19,6 +20,18 @@ export abstract class Permission { @IsInt() id: number; + /** + * users + */ + @OneToMany(() => User, user => user.permissions) + users: User[] + + /** + * groups + */ + @OneToMany(() => UserGroup, group => group.permissions) + groups: UserGroup[] + /** * The target */ diff --git a/src/models/User.ts b/src/models/User.ts index b59ad3f..d946500 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -67,9 +67,8 @@ export class User { /** * permissions */ - // TODO: Permission implementation - // @OneToMany(() => Permission,userpermission=>) - // permissions: Permission[]; + @ManyToOne(() => Permission, permission => permission.users) + permissions: Permission[]; /** * groups @@ -96,4 +95,15 @@ export class User { */ @IsString() profilepic: string; + + /** + * calculate all permissions + */ + public get calc_permissions(): Permission[] { + let final_permissions = this.groups.forEach((permission) => { + console.log(permission); + }) + // TODO: add user permissions on top of group permissions + return + return [] + } } diff --git a/src/models/UserGroup.ts b/src/models/UserGroup.ts index 90f6378..5a19713 100644 --- a/src/models/UserGroup.ts +++ b/src/models/UserGroup.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, @@ -20,6 +20,12 @@ export abstract class UserGroup { @IsInt() id: number; + /** + * permissions + */ + @ManyToOne(() => Permission, permission => permission.groups) + permissions: Permission[]; + /** * The group's name */ @@ -35,10 +41,4 @@ export abstract class UserGroup { @IsOptional() @IsString() description: string; - - /** - * TODO: Something about permission stuff - */ - // TODO: - // grouppermissions: Permission[]; } \ No newline at end of file From 701207e100c7b7b71a4791a7594bd346a4618066 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 18:46:40 +0100 Subject: [PATCH 054/194] Created basic runner controller ref #13 --- src/controllers/RunnerController.ts | 91 +++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/controllers/RunnerController.ts diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts new file mode 100644 index 0000000..89e5e8b --- /dev/null +++ b/src/controllers/RunnerController.ts @@ -0,0 +1,91 @@ +import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError, Authorized } from 'routing-controllers'; +import { getConnectionManager, Repository } from 'typeorm'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { Runner } from '../models/Runner'; +import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; +import {RunnerIdsNotMatchingError, RunnerNotFoundError} from "../errors/RunnerErrors"; + +class CreateRunner { + @IsString() + @IsNotEmpty() + name: string; + + @IsInt() + @IsPositive() + length: number; +} + +@JsonController('/runners') +@Authorized("RUNNERS:read") +export class RunnerController { + private runnerRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.runnerRepository = getConnectionManager().get().getRepository(Runner); + } + + @Get() + @ResponseSchema(Runner, { isArray: true }) + @OpenAPI({ description: "Lists all runners." }) + getAll() { + return this.runnerRepository.find(); + } + + @Get('/:id') + @ResponseSchema(Runner) + @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 }); + } + + @Post() + @ResponseSchema(Runner) + @OpenAPI({ description: "Create a new runner object (id will be generated automagicly)." }) + post( + @Body({ validate: true }) + runner: CreateRunner + ) { + return this.runnerRepository.save(runner); + } + + @Put('/:id') + @ResponseSchema(Runner) + @ResponseSchema(RunnerNotFoundError, {statusCode: 404}) + @ResponseSchema(RunnerIdsNotMatchingError, {statusCode: 406}) + @OpenAPI({description: "Update a runner object (id can't be changed)."}) + async put(@Param('id') id: number, @EntityFromBody() runner: Runner) { + let oldRunner = await this.runnerRepository.findOne({ id: id }); + + if (!oldRunner) { + throw new RunnerNotFoundError(); + } + + if(oldRunner.id != runner.id){ + throw new RunnerIdsNotMatchingError(); + } + + await this.runnerRepository.update(oldRunner, runner); + return runner; + } + + @Delete('/:id') + @ResponseSchema(Runner) + @ResponseSchema(RunnerNotFoundError, {statusCode: 404}) + @OpenAPI({description: "Delete a specified runner (if it exists)."}) + async remove(@Param('id') id: number) { + let runner = await this.runnerRepository.findOne({ id: id }); + + if (!runner) { + throw new RunnerNotFoundError(); + } + + await this.runnerRepository.delete(runner); + return runner; + } +} From 980ac646889690ae773de75b984dc9eec4e67bb1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 18:50:49 +0100 Subject: [PATCH 055/194] Added basic runner related errors --- src/errors/RunnerErrors.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/errors/RunnerErrors.ts diff --git a/src/errors/RunnerErrors.ts b/src/errors/RunnerErrors.ts new file mode 100644 index 0000000..c725c03 --- /dev/null +++ b/src/errors/RunnerErrors.ts @@ -0,0 +1,27 @@ +import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError } from 'routing-controllers'; +import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; + +/** + * Error to throw when a runner couldn't be found. + * Implemented this ways to work with the json-schema conversion for openapi. + */ +export class RunnerNotFoundError extends NotFoundError { + @IsString() + name = "RunnerNotFoundError" + + @IsString() + message = "Runner not found!" +} + +/** + * Error to throw when two runners' 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. + */ +export class RunnerIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "RunnerIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n And if you wanted to change a runner's id: This isn't allowed" +} \ No newline at end of file From 7bbf769bddaa92ec263d06adb5ccd3199feb8bc4 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 19:05:58 +0100 Subject: [PATCH 056/194] Added basic creation class --- src/controllers/RunnerController.ts | 80 +++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 89e5e8b..5b38bf6 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -1,23 +1,69 @@ -import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError, Authorized } from 'routing-controllers'; +import { + JsonController, + Param, + Body, + Get, + Post, + Put, + Delete, + NotFoundError, + OnUndefined, + NotAcceptableError, + Authorized +} from 'routing-controllers'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { Runner } from '../models/Runner'; -import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; -import {RunnerIdsNotMatchingError, RunnerNotFoundError} from "../errors/RunnerErrors"; +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsPositive, IsString } from 'class-validator'; +import { RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; class CreateRunner { + /** + * The runners's first name. + */ + @IsNotEmpty() + @IsString() + firstname: string; + + /** + * The runners's middle name. + * Optional + */ + @IsOptional() + @IsString() + middlename?: string; + + /** + * The runers's last name. + */ @IsString() @IsNotEmpty() - name: string; + lastname: string; + + /** + * The runner's phone number. + * Optional + */ + @IsOptional() + @IsPhoneNumber('DE') + phone?: string; + + /** + * The runner's email address. + * Optional + */ + @IsOptional() + @IsEmail() + email?: string; @IsInt() - @IsPositive() - length: number; + @IsOptional() + groupId?: number; } @JsonController('/runners') -@Authorized("RUNNERS:read") +//@Authorized('RUNNERS:read') export class RunnerController { private runnerRepository: Repository; @@ -30,23 +76,23 @@ export class RunnerController { @Get() @ResponseSchema(Runner, { isArray: true }) - @OpenAPI({ description: "Lists all runners." }) + @OpenAPI({ description: 'Lists all runners.' }) getAll() { return this.runnerRepository.find(); } @Get('/:id') @ResponseSchema(Runner) - @ResponseSchema(RunnerNotFoundError, {statusCode: 404}) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) - @OpenAPI({ description: "Returns a runner of a specified id (if it exists)" }) + @OpenAPI({ description: 'Returns a runner of a specified id (if it exists)' }) getOne(@Param('id') id: number) { return this.runnerRepository.findOne({ id: id }); } @Post() @ResponseSchema(Runner) - @OpenAPI({ description: "Create a new runner object (id will be generated automagicly)." }) + @OpenAPI({ description: 'Create a new runner object (id will be generated automagicly).' }) post( @Body({ validate: true }) runner: CreateRunner @@ -56,9 +102,9 @@ export class RunnerController { @Put('/:id') @ResponseSchema(Runner) - @ResponseSchema(RunnerNotFoundError, {statusCode: 404}) - @ResponseSchema(RunnerIdsNotMatchingError, {statusCode: 406}) - @OpenAPI({description: "Update a runner object (id can't be changed)."}) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a runner object (id can't be changed)." }) async put(@Param('id') id: number, @EntityFromBody() runner: Runner) { let oldRunner = await this.runnerRepository.findOne({ id: id }); @@ -66,7 +112,7 @@ export class RunnerController { throw new RunnerNotFoundError(); } - if(oldRunner.id != runner.id){ + if (oldRunner.id != runner.id) { throw new RunnerIdsNotMatchingError(); } @@ -76,8 +122,8 @@ export class RunnerController { @Delete('/:id') @ResponseSchema(Runner) - @ResponseSchema(RunnerNotFoundError, {statusCode: 404}) - @OpenAPI({description: "Delete a specified runner (if it exists)."}) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) async remove(@Param('id') id: number) { let runner = await this.runnerRepository.findOne({ id: id }); From aa565c6b344f62521b9b53575ffce36e8b77af74 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 19:14:45 +0100 Subject: [PATCH 057/194] Updated a bunch of optional collumns to be nullable ref #11 #13 --- src/models/Address.ts | 4 ++-- src/models/GroupContact.ts | 9 +++++---- src/models/Participant.ts | 10 +++++----- src/models/ScanStation.ts | 6 +++--- src/models/Track.ts | 2 +- src/models/User.ts | 8 ++++---- src/models/UserAction.ts | 2 +- src/models/UserGroup.ts | 4 ++-- 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/models/Address.ts b/src/models/Address.ts index 59e6570..ccc2c70 100644 --- a/src/models/Address.ts +++ b/src/models/Address.ts @@ -25,7 +25,7 @@ export class Address { /** * The address's description. */ - @Column() + @Column({nullable: true}) @IsString() @IsOptional() description?: string; @@ -43,7 +43,7 @@ export class Address { * The address's second line. * Containing optional information. */ - @Column() + @Column({nullable: true}) @IsString() @IsOptional() address2?: string; diff --git a/src/models/GroupContact.ts b/src/models/GroupContact.ts index 58a07a7..745174f 100644 --- a/src/models/GroupContact.ts +++ b/src/models/GroupContact.ts @@ -5,6 +5,7 @@ import { IsNotEmpty, IsOptional, IsPhoneNumber, + IsPositive, IsString, } from "class-validator"; import { Address } from "./Address"; @@ -36,7 +37,7 @@ export class GroupContact { * The contact's middle name. * Optional */ - @Column() + @Column({nullable: true}) @IsOptional() @IsString() middlename?: string; @@ -45,7 +46,7 @@ export class GroupContact { * The contact's last name. */ @Column() - @IsOptional() + @IsNotEmpty() @IsString() lastname: string; @@ -61,7 +62,7 @@ export class GroupContact { * The contact's phone number. * Optional */ - @Column() + @Column({nullable: true}) @IsOptional() @IsPhoneNumber("DE") phone?: string; @@ -70,7 +71,7 @@ export class GroupContact { * The contact's email address. * Optional */ - @Column() + @Column({nullable: true}) @IsOptional() @IsEmail() email?: string; diff --git a/src/models/Participant.ts b/src/models/Participant.ts index 2f4327b..bcbf5ca 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -5,6 +5,7 @@ import { IsNotEmpty, IsOptional, IsPhoneNumber, + IsPositive, IsString, } from "class-validator"; import { Address } from "./Address"; @@ -35,7 +36,7 @@ export abstract class Participant { * The participant's middle name. * Optional */ - @Column() + @Column({nullable: true}) @IsOptional() @IsString() middlename?: string; @@ -44,7 +45,7 @@ export abstract class Participant { * The participant's last name. */ @Column() - @IsOptional() + @IsNotEmpty() @IsString() lastname: string; @@ -52,7 +53,6 @@ export abstract class Participant { * The participant's address. * Optional */ - @IsOptional() @ManyToOne(() => Address, address => address.participants) address?: Address; @@ -60,7 +60,7 @@ export abstract class Participant { * The participant's phone number. * Optional */ - @Column() + @Column({nullable: true}) @IsOptional() @IsPhoneNumber("DE") phone?: string; @@ -69,7 +69,7 @@ export abstract class Participant { * The participant's email address. * Optional */ - @Column() + @Column({nullable: true}) @IsOptional() @IsEmail() email?: string; diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index 4415ebc..fd30a34 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -25,10 +25,10 @@ export class ScanStation { /** * The station's description. */ - @Column() - @IsNotEmpty() + @Column({nullable: true}) + @IsOptional() @IsString() - description: string; + description?: string; /** * The track this station is associated with. diff --git a/src/models/Track.ts b/src/models/Track.ts index eda6def..ce4693c 100644 --- a/src/models/Track.ts +++ b/src/models/Track.ts @@ -20,7 +20,7 @@ export class Track { @PrimaryGeneratedColumn() @IsOptional() @IsInt() - id: number; + id: number;; /** * The track's name. diff --git a/src/models/User.ts b/src/models/User.ts index d946500..fce74e8 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -11,10 +11,10 @@ export class User { /** * autogenerated unique id (primary key). */ - @PrimaryGeneratedColumn() - @IsOptional() - @IsInt() - id: number; + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; /** * autogenerated uuid diff --git a/src/models/UserAction.ts b/src/models/UserAction.ts index 7f6dc43..ea794a5 100644 --- a/src/models/UserAction.ts +++ b/src/models/UserAction.ts @@ -41,7 +41,7 @@ export class UserAction { /** * The description of change (before-> after; e.g. distance:15->17) */ - @Column() + @Column({nullable: true}) @IsOptional() @IsString() changed: string; diff --git a/src/models/UserGroup.ts b/src/models/UserGroup.ts index 5a19713..ae014e2 100644 --- a/src/models/UserGroup.ts +++ b/src/models/UserGroup.ts @@ -37,8 +37,8 @@ export abstract class UserGroup { /** * The group's description */ - @Column() + @Column({nullable: true}) @IsOptional() @IsString() - description: string; + description?: string; } \ No newline at end of file From f50e7f0b3ac49b6f728099e9ed1f1ccb0d9e78ff Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 19:29:40 +0100 Subject: [PATCH 058/194] =?UTF-8?q?=F0=9F=9A=A7=20UserAction=20relation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/User.ts | 15 +++++++++++---- src/models/UserAction.ts | 12 ++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/models/User.ts b/src/models/User.ts index fce74e8..b039d20 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -2,6 +2,7 @@ import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, isUUID, } from "class-validator"; import { UserGroup } from './UserGroup'; import { Permission } from './Permission'; +import { UserAction } from './UserAction'; /** * Defines a admin user. @@ -11,10 +12,10 @@ export class User { /** * autogenerated unique id (primary key). */ - @PrimaryGeneratedColumn() - @IsOptional() - @IsInt() - id: number; + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; /** * autogenerated uuid @@ -96,6 +97,12 @@ export class User { @IsString() profilepic: string; + /** + * actions + */ + @OneToMany(() => UserAction, action => action.user) + actions: UserAction + /** * calculate all permissions */ diff --git a/src/models/UserAction.ts b/src/models/UserAction.ts index ea794a5..c9caf60 100644 --- a/src/models/UserAction.ts +++ b/src/models/UserAction.ts @@ -1,10 +1,11 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, IsString, } from "class-validator"; +import { User } from './User'; /** * Defines the UserAction interface. @@ -19,8 +20,11 @@ export class UserAction { @IsInt() id: number; - // TODO: - // user: relation + /** + * user + */ + @ManyToOne(() => User, user => user.actions) + user: User /** * The actions's target (e.g. Track#2) @@ -41,7 +45,7 @@ export class UserAction { /** * The description of change (before-> after; e.g. distance:15->17) */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsString() changed: string; From 1cf35f016b0aeb0f1224648b301044b2ea76dc60 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 2 Dec 2020 19:34:43 +0100 Subject: [PATCH 059/194] =?UTF-8?q?=F0=9F=9A=A7=20Permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/User.ts | 67 +++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/src/models/User.ts b/src/models/User.ts index b039d20..5f153b7 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -10,90 +10,90 @@ import { UserAction } from './UserAction'; @Entity() export class User { /** - * autogenerated unique id (primary key). - */ + * autogenerated unique id (primary key). + */ @PrimaryGeneratedColumn() @IsOptional() @IsInt() id: number; /** - * autogenerated uuid - */ + * autogenerated uuid + */ @IsOptional() @IsInt() @Generated("uuid") uuid: string; /** - * user email - */ + * user email + */ @IsEmail() email: string; /** - * username - */ + * username + */ @IsString() username: string; /** - * firstname - */ + * firstname + */ @IsString() @IsNotEmpty() firstname: string; /** - * middlename - */ + * middlename + */ @IsString() @IsOptional() middlename: string; /** - * lastname - */ + * lastname + */ @IsString() @IsNotEmpty() lastname: string; /** - * password - */ + * password + */ @IsString() @IsNotEmpty() password: string; /** - * permissions - */ + * permissions + */ @ManyToOne(() => Permission, permission => permission.users) permissions: Permission[]; /** - * groups - */ + * groups + */ @ManyToMany(() => UserGroup) @JoinTable() groups: UserGroup[]; /** - * is user enabled? - */ + * is user enabled? + */ @IsBoolean() enabled: boolean; /** - * jwt refresh count - */ + * jwt refresh count + */ @IsInt() @Column({ default: 1 }) refreshTokenCount: number; /** - * profilepic - */ + * profilepic + */ @IsString() profilepic: string; @@ -107,10 +107,17 @@ export class User { * calculate all permissions */ public get calc_permissions(): Permission[] { - let final_permissions = this.groups.forEach((permission) => { - console.log(permission); + let final_permissions = [] + this.groups.forEach((permission) => { + if (!final_permissions.includes(permission)) { + final_permissions.push(permission) + } }) - // TODO: add user permissions on top of group permissions + return - return [] + this.permissions.forEach((permission) => { + if (!final_permissions.includes(permission)) { + final_permissions.push(permission) + } + }) + return final_permissions } } From cb5d5e546d0232e771495a3c5d140ac9531b7743 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 19:41:03 +0100 Subject: [PATCH 060/194] Added more runner errors ref #13 --- src/errors/RunnerErrors.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/errors/RunnerErrors.ts b/src/errors/RunnerErrors.ts index c725c03..0be6419 100644 --- a/src/errors/RunnerErrors.ts +++ b/src/errors/RunnerErrors.ts @@ -24,4 +24,29 @@ export class RunnerIdsNotMatchingError 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" +} + +export class RunnerOnlyOneGroupAllowedError extends NotAcceptableError { + @IsString() + name = "RunnerOnlyOneGroupAllowedError" + + @IsString() + message = "Runner's can only be part of one group (team or organisiation)! \n You provided an id for both." +} + +export class RunnerGroupNeededError extends NotAcceptableError { + @IsString() + name = "RunnerGroupNeededError" + + @IsString() + message = "Runner's need to be part of one group (team or organisiation)! \n You provided neither." +} + + +export class RunnerGroupNotFoundError extends NotFoundError { + @IsString() + name = "RunnerGroupNotFoundError" + + @IsString() + message = "The group you provided couldn't be located in the system. \n Please check your request." } \ No newline at end of file From 3ade01def931fbb7feb1a1fe8b8b8e29a22b0a58 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 19:41:47 +0100 Subject: [PATCH 061/194] temp commit: added first part of create runner ref #13 --- src/controllers/RunnerController.ts | 40 ++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 5b38bf6..ea5a70d 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -16,7 +16,10 @@ import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { Runner } from '../models/Runner'; import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsPositive, IsString } from 'class-validator'; -import { RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; +import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; +import { RunnerGroup } from '../models/RunnerGroup'; +import { RunnerOrganisation } from '../models/RunnerOrganisation'; +import { RunnerTeam } from '../models/RunnerTeam'; class CreateRunner { /** @@ -59,7 +62,11 @@ class CreateRunner { @IsInt() @IsOptional() - groupId?: number; + teamId?: number; + + @IsInt() + @IsOptional() + orgId?: number; } @JsonController('/runners') @@ -93,10 +100,35 @@ export class RunnerController { @Post() @ResponseSchema(Runner) @OpenAPI({ description: 'Create a new runner object (id will be generated automagicly).' }) - post( + async post( @Body({ validate: true }) - runner: CreateRunner + createRunner: CreateRunner ) { + let group: RunnerGroup; + + if(createRunner.teamId && createRunner.orgId){ + throw new RunnerOnlyOneGroupAllowedError(); + } + if(!createRunner.teamId && !createRunner.orgId){ + throw new RunnerGroupNeededError(); + } + + if(createRunner.teamId){ + group = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: createRunner.teamId }); + } + if(createRunner.orgId){ + group = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: createRunner.orgId }); + } + if(!group){ + throw new RunnerGroupNotFoundError(); + } + + delete createRunner.teamId; + delete createRunner.orgId; + let runner = createRunner; + runner.group=group; + console.log(runner) + return this.runnerRepository.save(runner); } From 4c80ab1516d11ec069fd79e06cdf050712647090 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 2 Dec 2020 19:45:02 +0100 Subject: [PATCH 062/194] Updated relationships to be nullable ref #11 #13 --- src/models/Address.ts | 4 ++-- src/models/DistanceDonation.ts | 2 +- src/models/Donation.ts | 2 +- src/models/GroupContact.ts | 6 +++--- src/models/Participant.ts | 4 ++-- src/models/Permission.ts | 4 ++-- src/models/Runner.ts | 8 ++++---- src/models/RunnerCard.ts | 4 ++-- src/models/RunnerGroup.ts | 4 ++-- src/models/RunnerOrganisation.ts | 4 ++-- src/models/RunnerTeam.ts | 2 +- src/models/Scan.ts | 2 +- src/models/ScanStation.ts | 4 ++-- src/models/Track.ts | 4 ++-- src/models/TrackScan.ts | 6 +++--- src/models/User.ts | 2 +- src/models/UserGroup.ts | 2 +- 17 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/models/Address.ts b/src/models/Address.ts index ccc2c70..735ecd3 100644 --- a/src/models/Address.ts +++ b/src/models/Address.ts @@ -76,12 +76,12 @@ export class Address { /** * Used to link the address to participants. */ - @OneToMany(() => Participant, participant => participant.address) + @OneToMany(() => Participant, participant => participant.address, { nullable: true }) participants: Participant[]; /** * Used to link the address to runner groups. */ - @OneToMany(() => RunnerOrganisation, group => group.address) + @OneToMany(() => RunnerOrganisation, group => group.address, { nullable: true }) groups: RunnerOrganisation[]; } diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts index 878f8cb..79bf0b8 100644 --- a/src/models/DistanceDonation.ts +++ b/src/models/DistanceDonation.ts @@ -13,7 +13,7 @@ export class DistanceDonation extends Donation { * The runner associated. */ @IsNotEmpty() - @ManyToOne(() => Runner, runner => runner.distanceDonations) + @ManyToOne(() => Runner, runner => runner.distanceDonations, { nullable: true }) runner: Runner; /** diff --git a/src/models/Donation.ts b/src/models/Donation.ts index 7143c99..b94e182 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -24,7 +24,7 @@ export abstract class Donation { * The donations's donor. */ @IsNotEmpty() - @ManyToOne(() => Participant, donor => donor.donations) + @ManyToOne(() => Participant, donor => donor.donations, { nullable: true }) donor: Participant; /** diff --git a/src/models/GroupContact.ts b/src/models/GroupContact.ts index 745174f..5d12066 100644 --- a/src/models/GroupContact.ts +++ b/src/models/GroupContact.ts @@ -55,7 +55,7 @@ export class GroupContact { * Optional */ @IsOptional() - @ManyToOne(() => Address, address => address.participants) + @ManyToOne(() => Address, address => address.participants, { nullable: true }) address?: Address; /** @@ -79,12 +79,12 @@ export class GroupContact { /** * Used to link the contact as the donor of a donation. */ - @OneToMany(() => Donation, donation => donation.donor) + @OneToMany(() => Donation, donation => donation.donor, { nullable: true }) donations: Donation[]; /** * Used to link runners to donations. */ - @OneToMany(() => RunnerGroup, group => group.contact) + @OneToMany(() => RunnerGroup, group => group.contact, { nullable: true }) groups: RunnerGroup[]; } \ No newline at end of file diff --git a/src/models/Participant.ts b/src/models/Participant.ts index bcbf5ca..e80d348 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -53,7 +53,7 @@ export abstract class Participant { * The participant's address. * Optional */ - @ManyToOne(() => Address, address => address.participants) + @ManyToOne(() => Address, address => address.participants, { nullable: true }) address?: Address; /** @@ -77,6 +77,6 @@ export abstract class Participant { /** * Used to link the participant as the donor of a donation. */ - @OneToMany(() => Donation, donation => donation.donor) + @OneToMany(() => Donation, donation => donation.donor, { nullable: true }) donations: Donation[]; } \ No newline at end of file diff --git a/src/models/Permission.ts b/src/models/Permission.ts index e9b049b..751efe3 100644 --- a/src/models/Permission.ts +++ b/src/models/Permission.ts @@ -23,13 +23,13 @@ export abstract class Permission { /** * users */ - @OneToMany(() => User, user => user.permissions) + @OneToMany(() => User, user => user.permissions, { nullable: true }) users: User[] /** * groups */ - @OneToMany(() => UserGroup, group => group.permissions) + @OneToMany(() => UserGroup, group => group.permissions, { nullable: true }) groups: UserGroup[] /** diff --git a/src/models/Runner.ts b/src/models/Runner.ts index 3a8532a..7423c22 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -15,25 +15,25 @@ export class Runner extends Participant { * The runner's associated group. */ @IsNotEmpty() - @ManyToOne(() => RunnerGroup, group => group.runners) + @ManyToOne(() => RunnerGroup, group => group.runners, { nullable: true }) group: RunnerGroup; /** * Used to link runners to donations. */ - @OneToMany(() => DistanceDonation, distanceDonation => distanceDonation.runner) + @OneToMany(() => DistanceDonation, distanceDonation => distanceDonation.runner, { nullable: true }) distanceDonations: DistanceDonation[]; /** * Used to link runners to cards. */ - @OneToMany(() => RunnerCard, card => card.runner) + @OneToMany(() => RunnerCard, card => card.runner, { nullable: true }) cards: RunnerCard[]; /** * Used to link runners to a scans */ - @OneToMany(() => Scan, scan => scan.runner) + @OneToMany(() => Scan, scan => scan.runner, { nullable: true }) scans: Scan[]; @IsInt() diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index f978d0c..8b0e05b 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -26,7 +26,7 @@ export class RunnerCard { * The runner that is currently associated with this card. */ @IsOptional() - @ManyToOne(() => Runner, runner => runner.cards) + @ManyToOne(() => Runner, runner => runner.cards, { nullable: true }) runner: Runner; /** @@ -51,6 +51,6 @@ export class RunnerCard { /** * Used to link cards to a track scans. */ - @OneToMany(() => TrackScan, scan => scan.track) + @OneToMany(() => TrackScan, scan => scan.track, { nullable: true }) scans: TrackScan[]; } diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index a7c199b..2cd3694 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -35,12 +35,12 @@ export abstract class RunnerGroup { * Optional */ @IsOptional() - @ManyToOne(() => GroupContact, contact => contact.groups) + @ManyToOne(() => GroupContact, contact => contact.groups, { nullable: true }) contact?: GroupContact; /** * Used to link runners to a runner group. */ - @OneToMany(() => Runner, runner => runner.group) + @OneToMany(() => Runner, runner => runner.group, { nullable: true }) runners: Runner[]; } \ No newline at end of file diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index 958b363..4245fa0 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -15,12 +15,12 @@ export class RunnerOrganisation extends RunnerGroup { * Optional */ @IsOptional() - @ManyToOne(() => Address, address => address.groups) + @ManyToOne(() => Address, address => address.groups, { nullable: true }) address?: Address; /** * Used to link teams to runner groups. */ - @OneToMany(() => RunnerTeam, team => team.parentGroup) + @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) teams: RunnerTeam[]; } \ No newline at end of file diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts index f12c5eb..5adc60d 100644 --- a/src/models/RunnerTeam.ts +++ b/src/models/RunnerTeam.ts @@ -14,6 +14,6 @@ export class RunnerTeam extends RunnerGroup { * Optional */ @IsNotEmpty() - @ManyToOne(() => RunnerOrganisation, org => org.teams) + @ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: true }) parentGroup?: RunnerOrganisation; } \ No newline at end of file diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 055e94b..3fd64ed 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -25,7 +25,7 @@ export abstract class Scan { * The associated runner. */ @IsNotEmpty() - @ManyToOne(() => Runner, runner => runner.scans) + @ManyToOne(() => Runner, runner => runner.scans, { nullable: true }) runner: Runner; /** diff --git a/src/models/ScanStation.ts b/src/models/ScanStation.ts index fd30a34..c529b25 100644 --- a/src/models/ScanStation.ts +++ b/src/models/ScanStation.ts @@ -34,7 +34,7 @@ export class ScanStation { * The track this station is associated with. */ @IsNotEmpty() - @ManyToOne(() => Track, track => track.stations) + @ManyToOne(() => Track, track => track.stations, { nullable: true }) track: Track; /** @@ -56,6 +56,6 @@ export class ScanStation { /** * Used to link track scans to a scan station. */ - @OneToMany(() => TrackScan, scan => scan.track) + @OneToMany(() => TrackScan, scan => scan.track, { nullable: true }) scans: TrackScan[]; } diff --git a/src/models/Track.ts b/src/models/Track.ts index ce4693c..ad6f476 100644 --- a/src/models/Track.ts +++ b/src/models/Track.ts @@ -41,12 +41,12 @@ export class Track { /** * Used to link scan stations to track. */ - @OneToMany(() => ScanStation, station => station.track) + @OneToMany(() => ScanStation, station => station.track, { nullable: true }) stations: ScanStation[]; /** * Used to link track scans to a track. */ - @OneToMany(() => TrackScan, scan => scan.track) + @OneToMany(() => TrackScan, scan => scan.track, { nullable: true }) scans: TrackScan[]; } diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index ce35ee8..ae0467c 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -22,21 +22,21 @@ export class TrackScan extends Scan { * The associated track. */ @IsNotEmpty() - @ManyToOne(() => Track, track => track.scans) + @ManyToOne(() => Track, track => track.scans, { nullable: true }) track: Track; /** * The associated card. */ @IsNotEmpty() - @ManyToOne(() => RunnerCard, card => card.scans) + @ManyToOne(() => RunnerCard, card => card.scans, { nullable: true }) card: RunnerCard; /** * The scanning station. */ @IsNotEmpty() - @ManyToOne(() => ScanStation, station => station.scans) + @ManyToOne(() => ScanStation, station => station.scans, { nullable: true }) station: ScanStation; /** diff --git a/src/models/User.ts b/src/models/User.ts index fce74e8..3f9a1db 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -67,7 +67,7 @@ export class User { /** * permissions */ - @ManyToOne(() => Permission, permission => permission.users) + @ManyToOne(() => Permission, permission => permission.users, { nullable: true }) permissions: Permission[]; /** diff --git a/src/models/UserGroup.ts b/src/models/UserGroup.ts index ae014e2..c3b2f0c 100644 --- a/src/models/UserGroup.ts +++ b/src/models/UserGroup.ts @@ -23,7 +23,7 @@ export abstract class UserGroup { /** * permissions */ - @ManyToOne(() => Permission, permission => permission.groups) + @ManyToOne(() => Permission, permission => permission.groups, { nullable: true }) permissions: Permission[]; /** From 684e7c4ddbb10e88ead1d33e93f97fc7dcb0db25 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 17:12:27 +0100 Subject: [PATCH 063/194] =?UTF-8?q?=E2=9A=99=20settings=20-=20standard=20i?= =?UTF-8?q?mports=20+=20quote=20formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 812953b..bc63ed0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,5 +8,9 @@ "prettier.enable": false, "[typescript]": { "editor.defaultFormatter": "vscode.typescript-language-features" - } + }, + "javascript.preferences.quoteStyle": "single", + "javascript.preferences.importModuleSpecifierEnding": "minimal", + "typescript.preferences.importModuleSpecifierEnding": "minimal", + "typescript.preferences.includePackageJsonAutoImports": "on" } \ No newline at end of file From 33d159dbcf49b6dd0e87e85d0a98042e7d5f34a8 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 17:26:54 +0100 Subject: [PATCH 064/194] =?UTF-8?q?=F0=9F=9A=A7=20RunnerCard=20EAN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/RunnerCard.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts index 8b0e05b..0ba0428 100644 --- a/src/models/RunnerCard.ts +++ b/src/models/RunnerCard.ts @@ -1,6 +1,7 @@ import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; import { IsBoolean, + IsEAN, IsInt, IsNotEmpty, IsOptional, @@ -32,12 +33,12 @@ export class RunnerCard { /** * The card's code. * This has to be able to being converted to something barcode compatible. - * Probably gonna be autogenerated. + * could theoretically be autogenerated */ @Column() + @IsEAN() @IsString() @IsNotEmpty() - //TODO: Generate this code: string; /** From da4597fa62dd85b0496aaa6d9407a263b5291912 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 17:43:24 +0100 Subject: [PATCH 065/194] Moded track controller related models to a new file --- src/controllers/TrackController.ts | 31 +++++++++++------------------- src/models/CreateTrack.ts | 21 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) create mode 100644 src/models/CreateTrack.ts diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index d0b3a0c..a448922 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -4,20 +4,11 @@ import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { Track } from '../models/Track'; import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; -import {TrackIdsNotMatchingError, TrackNotFoundError} from "../errors/TrackErrors"; - -class CreateTrack { - @IsString() - @IsNotEmpty() - name: string; - - @IsInt() - @IsPositive() - length: number; -} +import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; +import { CreateTrack } from '../models/CreateTrack'; @JsonController('/tracks') -@Authorized("TRACKS:read") +//@Authorized("TRACKS:read") export class TrackController { private trackRepository: Repository; @@ -37,7 +28,7 @@ export class TrackController { @Get('/:id') @ResponseSchema(Track) - @ResponseSchema(TrackNotFoundError, {statusCode: 404}) + @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) @OnUndefined(TrackNotFoundError) @OpenAPI({ description: "Returns a track of a specified id (if it exists)" }) getOne(@Param('id') id: number) { @@ -51,14 +42,14 @@ export class TrackController { @Body({ validate: true }) track: CreateTrack ) { - return this.trackRepository.save(track); + return this.trackRepository.save(track.getTrack()); } @Put('/:id') @ResponseSchema(Track) - @ResponseSchema(TrackNotFoundError, {statusCode: 404}) - @ResponseSchema(TrackIdsNotMatchingError, {statusCode: 406}) - @OpenAPI({description: "Update a track object (id can't be changed)."}) + @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) + @ResponseSchema(TrackIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a track object (id can't be changed)." }) async put(@Param('id') id: number, @EntityFromBody() track: Track) { let oldTrack = await this.trackRepository.findOne({ id: id }); @@ -66,7 +57,7 @@ export class TrackController { throw new TrackNotFoundError(); } - if(oldTrack.id != track.id){ + if (oldTrack.id != track.id) { throw new TrackIdsNotMatchingError(); } @@ -76,8 +67,8 @@ export class TrackController { @Delete('/:id') @ResponseSchema(Track) - @ResponseSchema(TrackNotFoundError, {statusCode: 404}) - @OpenAPI({description: "Delete a specified track (if it exists)."}) + @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: "Delete a specified track (if it exists)." }) async remove(@Param('id') id: number) { let track = await this.trackRepository.findOne({ id: id }); diff --git a/src/models/CreateTrack.ts b/src/models/CreateTrack.ts new file mode 100644 index 0000000..025d8a9 --- /dev/null +++ b/src/models/CreateTrack.ts @@ -0,0 +1,21 @@ +import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; +import { Track } from './Track'; + +export class CreateTrack { + @IsString() + @IsNotEmpty() + name: string; + + @IsInt() + @IsPositive() + distance: number; + + public getTrack(): Track { + let newTrack: Track = new Track(); + + newTrack.name = this.name; + newTrack.distance = this.distance; + + return newTrack; + } +} \ No newline at end of file From 6d81fc13095b2d96b6cd864f952242749567bbab Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 17:46:32 +0100 Subject: [PATCH 066/194] Added Comments. ref #11 #13 --- src/controllers/TrackController.ts | 2 +- src/models/CreateTrack.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index a448922..7b22cc2 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -42,7 +42,7 @@ export class TrackController { @Body({ validate: true }) track: CreateTrack ) { - return this.trackRepository.save(track.getTrack()); + return this.trackRepository.save(track.toTrack()); } @Put('/:id') diff --git a/src/models/CreateTrack.ts b/src/models/CreateTrack.ts index 025d8a9..015a58f 100644 --- a/src/models/CreateTrack.ts +++ b/src/models/CreateTrack.ts @@ -2,15 +2,24 @@ import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; import { Track } from './Track'; export class CreateTrack { + /** + * The track's name. + */ @IsString() @IsNotEmpty() name: string; + /** + * The track's distance in meters (must be greater 0). + */ @IsInt() @IsPositive() distance: number; - public getTrack(): Track { + /** + * Converts a Track object based on this. + */ + public toTrack(): Track { let newTrack: Track = new Track(); newTrack.name = this.name; From 9395813f5aafb282a50b8e2313a3022737e7decc Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 17:55:27 +0100 Subject: [PATCH 067/194] =?UTF-8?q?=F0=9F=9A=A7=20Scan.ts=20-=20secondsSin?= =?UTF-8?q?ceLastScan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/Scan.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 3fd64ed..5e8cbf9 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -35,11 +35,19 @@ export abstract class Scan { @IsPositive() abstract distance: number; - /** - * Is the scan valid (for fraud reasons). - * Default: true - */ + /** + * Is the scan valid (for fraud reasons). + * Default: true + */ @Column() @IsBoolean() valid: boolean = true; + + /** + * seconds since last scan + */ + @IsInt() + @IsOptional() + secondsSinceLastScan: number; + } From 9e3ee433d2571110004d797381a872348075a317 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 18:03:43 +0100 Subject: [PATCH 068/194] Moved Create Runner to it's own file --- src/controllers/RunnerController.ts | 105 ++++------------------------ src/models/CreateRunner.ts | 56 +++++++++++++++ 2 files changed, 69 insertions(+), 92 deletions(-) create mode 100644 src/models/CreateRunner.ts diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index ea5a70d..29685a6 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -1,73 +1,11 @@ -import { - JsonController, - Param, - Body, - Get, - Post, - Put, - Delete, - NotFoundError, - OnUndefined, - NotAcceptableError, - Authorized -} from 'routing-controllers'; +import { JsonController, Param, Body, Get, Post, Put, Delete, OnUndefined } from 'routing-controllers'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { Runner } from '../models/Runner'; -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsPositive, IsString } from 'class-validator'; import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; -import { RunnerGroup } from '../models/RunnerGroup'; -import { RunnerOrganisation } from '../models/RunnerOrganisation'; -import { RunnerTeam } from '../models/RunnerTeam'; +import { CreateRunner } from '../models/CreateRunner'; -class CreateRunner { - /** - * The runners's first name. - */ - @IsNotEmpty() - @IsString() - firstname: string; - - /** - * The runners's middle name. - * Optional - */ - @IsOptional() - @IsString() - middlename?: string; - - /** - * The runers's last name. - */ - @IsString() - @IsNotEmpty() - lastname: string; - - /** - * The runner's phone number. - * Optional - */ - @IsOptional() - @IsPhoneNumber('DE') - phone?: string; - - /** - * The runner's email address. - * Optional - */ - @IsOptional() - @IsEmail() - email?: string; - - @IsInt() - @IsOptional() - teamId?: number; - - @IsInt() - @IsOptional() - orgId?: number; -} @JsonController('/runners') //@Authorized('RUNNERS:read') @@ -99,37 +37,20 @@ export class RunnerController { @Post() @ResponseSchema(Runner) + @ResponseSchema(RunnerOnlyOneGroupAllowedError) + @ResponseSchema(RunnerGroupNeededError) + @ResponseSchema(RunnerGroupNotFoundError) @OpenAPI({ description: 'Create a new runner object (id will be generated automagicly).' }) - async post( - @Body({ validate: true }) - createRunner: CreateRunner - ) { - let group: RunnerGroup; - - if(createRunner.teamId && createRunner.orgId){ - throw new RunnerOnlyOneGroupAllowedError(); - } - if(!createRunner.teamId && !createRunner.orgId){ - throw new RunnerGroupNeededError(); + async post(@Body({ validate: true }) createRunner: CreateRunner) { + let runner; + try { + runner = await createRunner.toRunner(); + } catch (error) { + return error; } - if(createRunner.teamId){ - group = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: createRunner.teamId }); - } - if(createRunner.orgId){ - group = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: createRunner.orgId }); - } - if(!group){ - throw new RunnerGroupNotFoundError(); - } - - delete createRunner.teamId; - delete createRunner.orgId; - let runner = createRunner; - runner.group=group; - console.log(runner) - - return this.runnerRepository.save(runner); + return runner; + //return this.runnerRepository.save(runner); } @Put('/:id') diff --git a/src/models/CreateRunner.ts b/src/models/CreateRunner.ts new file mode 100644 index 0000000..b35be10 --- /dev/null +++ b/src/models/CreateRunner.ts @@ -0,0 +1,56 @@ +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsPositive, IsString } from 'class-validator'; +import { Runner } from '../models/Runner'; +import { getConnectionManager, Repository } from 'typeorm'; +import { group } from 'console'; +import { RunnerOnlyOneGroupAllowedError, RunnerGroupNeededError, RunnerGroupNotFoundError } from '../errors/RunnerErrors'; +import { RunnerOrganisation } from './RunnerOrganisation'; +import { RunnerTeam } from './RunnerTeam'; +import { RunnerGroup } from './RunnerGroup'; +import { Address } from 'cluster'; + +export class CreateRunner { + @IsString() + firstname: string; + @IsString() + middlename?: string; + @IsString() + lastname: string; + @IsString() + phone?: string; + @IsString() + email?: string; + @IsInt() + teamId?: number + @IsInt() + orgId?: number + + public async toRunner(): Promise { + let newRunner: Runner = new Runner(); + + if (this.teamId !== undefined && this.orgId !== undefined) { + throw new RunnerOnlyOneGroupAllowedError(); + } + if (this.teamId === undefined && this.orgId === undefined) { + throw new RunnerGroupNeededError(); + } + + if (this.teamId) { + newRunner.group = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: this.teamId }); + } + if (this.orgId) { + newRunner.group = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.orgId }); + } + if (!newRunner.group) { + throw new RunnerGroupNotFoundError(); + } + + newRunner.firstname = this.firstname; + newRunner.middlename = this.middlename; + newRunner.lastname = this.lastname; + newRunner.phone = this.phone; + newRunner.email = this.email; + + console.log(newRunner) + return newRunner; + } +} \ No newline at end of file From 098699e09a4b2c1399c471149a3379645710274a Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:07:49 +0100 Subject: [PATCH 069/194] =?UTF-8?q?=F0=9F=90=9E=20CreateRunner=20-=20optio?= =?UTF-8?q?nal=20orgId=20&=20teamId?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/CreateRunner.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/models/CreateRunner.ts b/src/models/CreateRunner.ts index b35be10..9eb15eb 100644 --- a/src/models/CreateRunner.ts +++ b/src/models/CreateRunner.ts @@ -20,8 +20,10 @@ export class CreateRunner { @IsString() email?: string; @IsInt() + @IsOptional() teamId?: number @IsInt() + @IsOptional() orgId?: number public async toRunner(): Promise { From 8d1dd78194be42c837934f0804287624ff7079b9 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:25:52 +0100 Subject: [PATCH 070/194] =?UTF-8?q?=F0=9F=9A=A7=20User.ts=20-=20optional?= =?UTF-8?q?=20phone=20number?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/User.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/models/User.ts b/src/models/User.ts index 4ba57f8..94d770e 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,5 +1,5 @@ import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; -import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, isUUID, } from "class-validator"; +import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, isUUID, } from "class-validator"; import { UserGroup } from './UserGroup'; import { Permission } from './Permission'; import { UserAction } from './UserAction'; @@ -31,6 +31,13 @@ export class User { @IsEmail() email: string; + /** + * user phone + */ + @IsPhoneNumber("ZZ") + @IsOptional() + phone: string; + /** * username */ From d2c826c7c9a8b79ff5ab6268865a26791b36c2c2 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:33:20 +0100 Subject: [PATCH 071/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateUser=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/CreateUser.ts | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/models/CreateUser.ts diff --git a/src/models/CreateUser.ts b/src/models/CreateUser.ts new file mode 100644 index 0000000..23cd934 --- /dev/null +++ b/src/models/CreateUser.ts @@ -0,0 +1,53 @@ +import { IsInt, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { User } from '../models/User'; +import { getConnectionManager } from 'typeorm'; +import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../errors/CreateUserErrors'; +import { UserGroup } from './UserGroup'; + +export class CreateUser { + @IsString() + firstname: string; + @IsString() + middlename?: string; + @IsOptional() + @IsString() + username?: string; + @IsPhoneNumber("ZZ") + @IsOptional() + phone?: string; + @IsString() + password: string; + @IsString() + lastname: string; + @IsString() + email?: string; + @IsInt() + @IsOptional() + groupId?: number[] | number + + public async toUser(): Promise { + let newUser: User = new User(); + + if (this.email === undefined && this.username === undefined) { + throw new UsernameOrEmailNeededError(); + } + + if (this.groupId) { + // TODO: link user groups + // newUser.groups = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: this.teamId }); + } else { + throw new UserGroupNotFoundError(); + } + + newUser.email = this.email + newUser.username = this.username + newUser.firstname = this.firstname + newUser.middlename = this.middlename + newUser.lastname = this.lastname + // TODO: hash password here or in controller/ in User model via setter? + newUser.password = this.password + + console.log(newUser) + return newUser; + } +} \ No newline at end of file From 983fa41cba3a4d537ab742a149b86d6c6a87f5bf Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:33:30 +0100 Subject: [PATCH 072/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateUserErrors=20model?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/errors/CreateUserErrors.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/errors/CreateUserErrors.ts diff --git a/src/errors/CreateUserErrors.ts b/src/errors/CreateUserErrors.ts new file mode 100644 index 0000000..3b28735 --- /dev/null +++ b/src/errors/CreateUserErrors.ts @@ -0,0 +1,24 @@ +import { NotFoundError } from 'routing-controllers'; +import { IsString } from 'class-validator'; + +/** + * Error to throw when a usergroup couldn't be found. + */ +export class UserGroupNotFoundError extends NotFoundError { + @IsString() + name = "UserGroupNotFoundError" + + @IsString() + message = "User Group not found!" +} + +/** + * Error to throw when no username or email is set + */ +export class UsernameOrEmailNeededError extends NotFoundError { + @IsString() + name = "UsernameOrEmailNeededError" + + @IsString() + message = "no username or email is set!" +} From 084691c294ff3bf4d09ef634ce2d0d81ef93c50d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 18:46:53 +0100 Subject: [PATCH 073/194] Now returning the saved runner ref #13 --- src/controllers/RunnerController.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 29685a6..da80b31 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -49,8 +49,7 @@ export class RunnerController { return error; } - return runner; - //return this.runnerRepository.save(runner); + return this.runnerRepository.save(runner); } @Put('/:id') From ee35da7342de601650052eae13bc773831ea9ad7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 18:48:24 +0100 Subject: [PATCH 074/194] Fixxed dockerfile --- Dockerfile | 2 +- docker-compose.yml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index da54cd5..089c4de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM node:alpine WORKDIR /app COPY ./package.json ./ -RUN npm i +RUN yarn COPY ./ ./ ENTRYPOINT [ "yarn","dev" ] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1efd5ba..7ca0bb8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,9 +12,12 @@ services: DB_USER: lfk DB_PASSWORD: changeme DB_NAME: lfk + NODE_ENV: production backend_db: image: postgres:11-alpine environment: POSTGRES_DB: lfk POSTGRES_PASSWORD: changeme - POSTGRES_USER: lfk \ No newline at end of file + POSTGRES_USER: lfk + ports: + - 5432:5432 \ No newline at end of file From 8ef6f933a745214ba02a12cf778eb36bc04a0891 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:48:35 +0100 Subject: [PATCH 075/194] =?UTF-8?q?=E2=9A=99=20tsconfig=20-=20includes=20+?= =?UTF-8?q?=20excludes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tsconfig.json b/tsconfig.json index 0f2192a..3f35579 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,4 +9,11 @@ "experimentalDecorators": true, "emitDecoratorMetadata": true } + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "**/*.spec.ts" + ] } \ No newline at end of file From ffc31506e36b9c0b22cc7cc66e6ab5f06c4a64cf Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:48:51 +0100 Subject: [PATCH 076/194] =?UTF-8?q?=E2=9A=99tsconfig=20-=20no=20sourcemaps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 3f35579..573aa28 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,8 +7,9 @@ "esModuleInterop": true, "strict": false, "experimentalDecorators": true, - "emitDecoratorMetadata": true - } + "emitDecoratorMetadata": true, + "sourceMap": false + }, "include": [ "src/**/*" ], From 9051b7565cf3506ddecfdc0abc05e7266be450dd Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 18:49:21 +0100 Subject: [PATCH 077/194] =?UTF-8?q?=E2=9A=99target:=20es2017=20=E2=96=B6?= =?UTF-8?q?=20ES2020?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 573aa28..dfd4d15 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2017", + "target": "ES2020", "module": "commonjs", "rootDir": "./src", "outDir": "./build", From a35e6d0a9f19dc7dbde3c867b840276416eac9f6 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 19:20:53 +0100 Subject: [PATCH 078/194] Added basics for runnerorg controller ref #13 --- .../RunnerOrganisationController.ts | 87 +++++++++++++++++++ src/errors/RunnerOrganisationErrors.ts | 27 ++++++ src/models/CreateRunnerOrganisation.ts | 15 ++++ 3 files changed, 129 insertions(+) create mode 100644 src/controllers/RunnerOrganisationController.ts create mode 100644 src/errors/RunnerOrganisationErrors.ts create mode 100644 src/models/CreateRunnerOrganisation.ts diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts new file mode 100644 index 0000000..6297073 --- /dev/null +++ b/src/controllers/RunnerOrganisationController.ts @@ -0,0 +1,87 @@ +import { JsonController, Param, Body, Get, Post, Put, Delete, OnUndefined } from 'routing-controllers'; +import { getConnectionManager, Repository } from 'typeorm'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { RunnerOrganisation } from '../models/RunnerOrganisation'; +import { RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; +import { CreateRunnerOrganisation } from '../models/CreateRunnerOrganisation'; +import { RunnerGroup } from '../models/RunnerGroup'; + + +@JsonController('/organisations') +//@Authorized('RUNNERS:read') +export class RunnerOrganisationController { + private runnerOrganisationRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.runnerOrganisationRepository = getConnectionManager().get().getRepository(RunnerGroup); + } + + @Get() + @ResponseSchema(RunnerOrganisation, { isArray: true }) + @OpenAPI({ description: 'Lists all runnerOrganisations.' }) + getAll() { + return this.runnerOrganisationRepository.find(); + } + + @Get('/:id') + @ResponseSchema(RunnerOrganisation) + @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 }); + } + + @Post() + @ResponseSchema(RunnerOrganisation) + @OpenAPI({ description: 'Create a new runnerOrganisation object (id will be generated automagicly).' }) + async post(@Body({ validate: true }) createRunnerOrganisation: CreateRunnerOrganisation) { + let runnerOrganisation; + try { + runnerOrganisation = await createRunnerOrganisation.toRunnerOrganisation(); + } catch (error) { + return error; + } + + return this.runnerOrganisationRepository.save(runnerOrganisation); + } + + @Put('/:id') + @ResponseSchema(RunnerOrganisation) + @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) + @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 }); + + if (!oldRunnerOrganisation) { + throw new RunnerOrganisationNotFoundError(); + } + + if (oldRunnerOrganisation.id != runnerOrganisation.id) { + throw new RunnerOrganisationIdsNotMatchingError(); + } + + await this.runnerOrganisationRepository.update(oldRunnerOrganisation, runnerOrganisation); + return runnerOrganisation; + } + + @Delete('/:id') + @ResponseSchema(RunnerOrganisation) + @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) + async remove(@Param('id') id: number) { + let runnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }); + + if (!runnerOrganisation) { + throw new RunnerOrganisationNotFoundError(); + } + + await this.runnerOrganisationRepository.delete(runnerOrganisation); + return runnerOrganisation; + } +} diff --git a/src/errors/RunnerOrganisationErrors.ts b/src/errors/RunnerOrganisationErrors.ts new file mode 100644 index 0000000..f846ffa --- /dev/null +++ b/src/errors/RunnerOrganisationErrors.ts @@ -0,0 +1,27 @@ +import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnUndefined, NotAcceptableError } from 'routing-controllers'; +import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; + +/** + * Error to throw when a runner couldn't be found. + * Implemented this ways to work with the json-schema conversion for openapi. + */ +export class RunnerOrganisationNotFoundError extends NotFoundError { + @IsString() + name = "RunnerOrganisationNotFoundError" + + @IsString() + message = "RunnerOrganisation not found!" +} + +/** + * Error to throw when two runners' 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. + */ +export class RunnerOrganisationIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "RunnerOrganisationIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n And if you wanted to change a runner's id: This isn't allowed" +} \ No newline at end of file diff --git a/src/models/CreateRunnerOrganisation.ts b/src/models/CreateRunnerOrganisation.ts new file mode 100644 index 0000000..b7e5c71 --- /dev/null +++ b/src/models/CreateRunnerOrganisation.ts @@ -0,0 +1,15 @@ +import { IsString } from 'class-validator'; +import { RunnerOrganisation } from './RunnerOrganisation'; + +export class CreateRunnerOrganisation { + @IsString() + name: string; + + public async toRunnerOrganisation(): Promise { + let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); + + newRunnerOrganisation.name = this.name; + + return newRunnerOrganisation; + } +} \ No newline at end of file From af75d6cfec13131f2980cc83866eb3d6b024b9e0 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 19:22:32 +0100 Subject: [PATCH 079/194] Formatting --- src/models/RunnerOrganisation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index 4245fa0..f266599 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -1,5 +1,5 @@ import { Entity, Column, ManyToOne, OneToMany } from "typeorm"; -import { IsOptional,} from "class-validator"; +import { IsOptional, } from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; import { Address } from "./Address"; import { RunnerTeam } from "./RunnerTeam"; @@ -18,9 +18,9 @@ export class RunnerOrganisation extends RunnerGroup { @ManyToOne(() => Address, address => address.groups, { nullable: true }) address?: Address; - /** - * Used to link teams to runner groups. - */ + /** + * Used to link teams to runner groups. + */ @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) teams: RunnerTeam[]; } \ No newline at end of file From ec2ff981b217e460e4547b57a1ad069059d13b7d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 19:25:02 +0100 Subject: [PATCH 080/194] Now creating runner orgs again ref #13 --- src/controllers/RunnerOrganisationController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 6297073..1575ef0 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -11,13 +11,13 @@ import { RunnerGroup } from '../models/RunnerGroup'; @JsonController('/organisations') //@Authorized('RUNNERS:read') export class RunnerOrganisationController { - private runnerOrganisationRepository: Repository; + private runnerOrganisationRepository: Repository; /** * Gets the repository of this controller's model/entity. */ constructor() { - this.runnerOrganisationRepository = getConnectionManager().get().getRepository(RunnerGroup); + this.runnerOrganisationRepository = getConnectionManager().get().getRepository(RunnerOrganisation); } @Get() From 32c4270dff0c3e1a9703e72b8b180a7289d413d4 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:19:58 +0100 Subject: [PATCH 081/194] Attention: Broken --- src/models/RunnerGroup.ts | 3 ++- src/models/RunnerTeam.ts | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts index 2cd3694..8142754 100644 --- a/src/models/RunnerGroup.ts +++ b/src/models/RunnerGroup.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsInt, IsNotEmpty, @@ -13,6 +13,7 @@ import { RunnerTeam } from "./RunnerTeam"; * Defines the runnerGroup interface. */ @Entity() +@TableInheritance({ column: { name: "type", type: "varchar" } }) export abstract class RunnerGroup { /** * Autogenerated unique id (primary key). diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts index 5adc60d..2a474ad 100644 --- a/src/models/RunnerTeam.ts +++ b/src/models/RunnerTeam.ts @@ -1,12 +1,12 @@ -import { Entity, Column, ManyToOne } from "typeorm"; -import { IsNotEmpty } from "class-validator"; +import { Entity, Column, ManyToOne, ChildEntity } from "typeorm"; +import { IsOptional } from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; import { RunnerOrganisation } from "./RunnerOrganisation"; /** * Defines a runner team (class or deparment for example). */ -@Entity() +@ChildEntity() export class RunnerTeam extends RunnerGroup { /** From 5bbf522362a8985e93b16884369f5859ab72d32a Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:23:56 +0100 Subject: [PATCH 082/194] Fixed missing child declaration --- src/models/RunnerOrganisation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/RunnerOrganisation.ts b/src/models/RunnerOrganisation.ts index f266599..b0a7220 100644 --- a/src/models/RunnerOrganisation.ts +++ b/src/models/RunnerOrganisation.ts @@ -1,4 +1,4 @@ -import { Entity, Column, ManyToOne, OneToMany } from "typeorm"; +import { Entity, Column, ManyToOne, OneToMany, ChildEntity } from "typeorm"; import { IsOptional, } from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; import { Address } from "./Address"; @@ -7,7 +7,7 @@ import { RunnerTeam } from "./RunnerTeam"; /** * Defines a runner organisation (business or school for example). */ -@Entity() +@ChildEntity() export class RunnerOrganisation extends RunnerGroup { /** From e3133e0d5eb70daf858a6b807970838db8fd0ef9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:24:43 +0100 Subject: [PATCH 083/194] Added missing import --- src/models/RunnerTeam.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/RunnerTeam.ts b/src/models/RunnerTeam.ts index 2a474ad..a9fb2f0 100644 --- a/src/models/RunnerTeam.ts +++ b/src/models/RunnerTeam.ts @@ -1,5 +1,5 @@ import { Entity, Column, ManyToOne, ChildEntity } from "typeorm"; -import { IsOptional } from "class-validator"; +import { IsNotEmpty } from "class-validator"; import { RunnerGroup } from "./RunnerGroup"; import { RunnerOrganisation } from "./RunnerOrganisation"; From a8956223c2c70b810b4ec9dea09900b30b963a24 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:28:07 +0100 Subject: [PATCH 084/194] Emergency fix: Switched to table inheritances ref #11 #13 --- src/models/DistanceDonation.ts | 6 +++--- src/models/Donation.ts | 3 ++- src/models/Donor.ts | 4 ++-- src/models/FixedDonation.ts | 6 +++--- src/models/Participant.ts | 9 +++++---- src/models/Runner.ts | 14 +++++++------- src/models/Scan.ts | 11 ++++++----- src/models/TrackScan.ts | 4 ++-- 8 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts index 79bf0b8..b54c642 100644 --- a/src/models/DistanceDonation.ts +++ b/src/models/DistanceDonation.ts @@ -1,5 +1,5 @@ -import { Entity, Column, ManyToOne } from "typeorm"; -import { IsInt, IsNotEmpty, IsPositive,} from "class-validator"; +import { Entity, Column, ManyToOne, ChildEntity } from "typeorm"; +import { IsInt, IsNotEmpty, IsPositive, } from "class-validator"; import { Donation } from "./Donation"; import { Runner } from "./Runner"; @@ -7,7 +7,7 @@ import { Runner } from "./Runner"; * Defines a distance based donation. * Here people donate a certain amout per kilometer */ -@Entity() +@ChildEntity() export class DistanceDonation extends Donation { /** * The runner associated. diff --git a/src/models/Donation.ts b/src/models/Donation.ts index b94e182..c4ab699 100644 --- a/src/models/Donation.ts +++ b/src/models/Donation.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsInt, IsNotEmpty, @@ -11,6 +11,7 @@ import { Participant } from "./Participant"; * Defines the donation interface. */ @Entity() +@TableInheritance({ column: { name: "type", type: "varchar" } }) export abstract class Donation { /** * Autogenerated unique id (primary key). diff --git a/src/models/Donor.ts b/src/models/Donor.ts index 5d0af07..ca65afd 100644 --- a/src/models/Donor.ts +++ b/src/models/Donor.ts @@ -1,11 +1,11 @@ -import { Entity, Column } from "typeorm"; +import { Entity, Column, ChildEntity } from "typeorm"; import { IsBoolean } from "class-validator"; import { Participant } from "./Participant"; /** * Defines a donor. */ -@Entity() +@ChildEntity() export class Donor extends Participant { /** * Does this donor need a receipt?. diff --git a/src/models/FixedDonation.ts b/src/models/FixedDonation.ts index e429ddf..dd05c40 100644 --- a/src/models/FixedDonation.ts +++ b/src/models/FixedDonation.ts @@ -1,11 +1,11 @@ -import { Entity, Column } from "typeorm"; -import { IsInt, IsPositive,} from "class-validator"; +import { Entity, Column, ChildEntity } from "typeorm"; +import { IsInt, IsPositive, } from "class-validator"; import { Donation } from "./Donation"; /** * Defines a fixed donation. */ -@Entity() +@ChildEntity() export class FixedDonation extends Donation { /** diff --git a/src/models/Participant.ts b/src/models/Participant.ts index e80d348..53562a3 100644 --- a/src/models/Participant.ts +++ b/src/models/Participant.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsEmail, IsInt, @@ -15,6 +15,7 @@ import { Donation } from "./Donation"; * Defines the participant interface. */ @Entity() +@TableInheritance({ column: { name: "type", type: "varchar" } }) export abstract class Participant { /** * Autogenerated unique id (primary key). @@ -36,7 +37,7 @@ export abstract class Participant { * The participant's middle name. * Optional */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsString() middlename?: string; @@ -60,7 +61,7 @@ export abstract class Participant { * The participant's phone number. * Optional */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsPhoneNumber("DE") phone?: string; @@ -69,7 +70,7 @@ export abstract class Participant { * The participant's email address. * Optional */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsEmail() email?: string; diff --git a/src/models/Runner.ts b/src/models/Runner.ts index 7423c22..e4d3ff7 100644 --- a/src/models/Runner.ts +++ b/src/models/Runner.ts @@ -1,5 +1,5 @@ -import { Entity, Column, OneToMany, ManyToOne } from "typeorm"; -import { IsInt, IsNotEmpty,} from "class-validator"; +import { Entity, Column, OneToMany, ManyToOne, ChildEntity } from "typeorm"; +import { IsInt, IsNotEmpty, } from "class-validator"; import { Participant } from "./Participant"; import { RunnerGroup } from "./RunnerGroup"; import { DistanceDonation } from "./DistanceDonation"; @@ -9,7 +9,7 @@ import { Scan } from "./Scan"; /** * Defines a runner. */ -@Entity() +@ChildEntity() export class Runner extends Participant { /** * The runner's associated group. @@ -17,7 +17,7 @@ export class Runner extends Participant { @IsNotEmpty() @ManyToOne(() => RunnerGroup, group => group.runners, { nullable: true }) group: RunnerGroup; - + /** * Used to link runners to donations. */ @@ -36,9 +36,9 @@ export class Runner extends Participant { @OneToMany(() => Scan, scan => scan.runner, { nullable: true }) scans: Scan[]; - @IsInt() - public get distance() : number { + @IsInt() + public get distance(): number { return this.scans.filter(scan => scan.valid === true).reduce((sum, current) => sum + current.distance, 0); } - + } diff --git a/src/models/Scan.ts b/src/models/Scan.ts index 3fd64ed..9911fec 100644 --- a/src/models/Scan.ts +++ b/src/models/Scan.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsBoolean, IsInt, @@ -12,6 +12,7 @@ import { Runner } from "./Runner"; * Defines the scan interface. */ @Entity() +@TableInheritance({ column: { name: "type", type: "varchar" } }) export abstract class Scan { /** * Autogenerated unique id (primary key). @@ -35,10 +36,10 @@ export abstract class Scan { @IsPositive() abstract distance: number; - /** - * Is the scan valid (for fraud reasons). - * Default: true - */ + /** + * Is the scan valid (for fraud reasons). + * Default: true + */ @Column() @IsBoolean() valid: boolean = true; diff --git a/src/models/TrackScan.ts b/src/models/TrackScan.ts index ae0467c..f7bee32 100644 --- a/src/models/TrackScan.ts +++ b/src/models/TrackScan.ts @@ -1,4 +1,4 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from "typeorm"; +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, ChildEntity } from "typeorm"; import { IsBoolean, IsDateString, @@ -16,7 +16,7 @@ import { ScanStation } from "./ScanStation"; /** * Defines the scan interface. */ -@Entity() +@ChildEntity() export class TrackScan extends Scan { /** * The associated track. From e8727ca922475f2a7f32ee0785625a3612c21c0e Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:38:47 +0100 Subject: [PATCH 085/194] Moved to a "cleaner" directory structure ref #11 --- ormconfig.ts | 2 +- src/controllers/RunnerController.ts | 4 +- .../RunnerOrganisationController.ts | 6 +- src/controllers/TrackController.ts | 4 +- src/models/{ => creation}/CreateRunner.ts | 10 +- .../CreateRunnerOrganisation.ts | 2 +- src/models/{ => creation}/CreateTrack.ts | 2 +- src/models/{ => entities}/Address.ts | 0 src/models/{ => entities}/DistanceDonation.ts | 0 src/models/{ => entities}/Donation.ts | 0 src/models/{ => entities}/Donor.ts | 0 src/models/{ => entities}/FixedDonation.ts | 0 src/models/{ => entities}/GroupContact.ts | 0 src/models/{ => entities}/Participant.ts | 0 src/models/{ => entities}/Permission.ts | 0 src/models/{ => entities}/Runner.ts | 0 src/models/{ => entities}/RunnerCard.ts | 0 src/models/{ => entities}/RunnerGroup.ts | 0 .../{ => entities}/RunnerOrganisation.ts | 0 src/models/{ => entities}/RunnerTeam.ts | 0 src/models/{ => entities}/Scan.ts | 0 src/models/{ => entities}/ScanStation.ts | 0 src/models/{ => entities}/Track.ts | 0 src/models/{ => entities}/TrackScan.ts | 0 src/models/{ => entities}/User.ts | 260 +++++++++--------- src/models/{ => entities}/UserAction.ts | 0 src/models/{ => entities}/UserGroup.ts | 0 27 files changed, 145 insertions(+), 145 deletions(-) rename src/models/{ => creation}/CreateRunner.ts (85%) rename src/models/{ => creation}/CreateRunnerOrganisation.ts (84%) rename src/models/{ => creation}/CreateTrack.ts (93%) rename src/models/{ => entities}/Address.ts (100%) rename src/models/{ => entities}/DistanceDonation.ts (100%) rename src/models/{ => entities}/Donation.ts (100%) rename src/models/{ => entities}/Donor.ts (100%) rename src/models/{ => entities}/FixedDonation.ts (100%) rename src/models/{ => entities}/GroupContact.ts (100%) rename src/models/{ => entities}/Participant.ts (100%) rename src/models/{ => entities}/Permission.ts (100%) rename src/models/{ => entities}/Runner.ts (100%) rename src/models/{ => entities}/RunnerCard.ts (100%) rename src/models/{ => entities}/RunnerGroup.ts (100%) rename src/models/{ => entities}/RunnerOrganisation.ts (100%) rename src/models/{ => entities}/RunnerTeam.ts (100%) rename src/models/{ => entities}/Scan.ts (100%) rename src/models/{ => entities}/ScanStation.ts (100%) rename src/models/{ => entities}/Track.ts (100%) rename src/models/{ => entities}/TrackScan.ts (100%) rename src/models/{ => entities}/User.ts (94%) rename src/models/{ => entities}/UserAction.ts (100%) rename src/models/{ => entities}/UserGroup.ts (100%) diff --git a/ormconfig.ts b/ormconfig.ts index b3b99c3..852ff37 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -8,5 +8,5 @@ export default { username: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, - entities: ["src/models/*.ts"] + entities: ["src/models/entities/*.ts"] }; diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index da80b31..798d3c6 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -2,9 +2,9 @@ import { JsonController, Param, Body, Get, Post, Put, Delete, OnUndefined } from import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { Runner } from '../models/Runner'; +import { Runner } from '../models/entities/Runner'; import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; -import { CreateRunner } from '../models/CreateRunner'; +import { CreateRunner } from '../models/creation/CreateRunner'; @JsonController('/runners') diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 1575ef0..8033c9b 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -2,10 +2,10 @@ import { JsonController, Param, Body, Get, Post, Put, Delete, OnUndefined } from import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { RunnerOrganisation } from '../models/RunnerOrganisation'; +import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; import { RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; -import { CreateRunnerOrganisation } from '../models/CreateRunnerOrganisation'; -import { RunnerGroup } from '../models/RunnerGroup'; +import { CreateRunnerOrganisation } from '../models/creation/CreateRunnerOrganisation'; +import { RunnerGroup } from '../models/entities/RunnerGroup'; @JsonController('/organisations') diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index 7b22cc2..869f1be 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -2,10 +2,10 @@ import { JsonController, Param, Body, Get, Post, Put, Delete, NotFoundError, OnU import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { Track } from '../models/Track'; +import { Track } from '../models/entities/Track'; import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; -import { CreateTrack } from '../models/CreateTrack'; +import { CreateTrack } from '../models/creation/CreateTrack'; @JsonController('/tracks') //@Authorized("TRACKS:read") diff --git a/src/models/CreateRunner.ts b/src/models/creation/CreateRunner.ts similarity index 85% rename from src/models/CreateRunner.ts rename to src/models/creation/CreateRunner.ts index 9eb15eb..178c5fb 100644 --- a/src/models/CreateRunner.ts +++ b/src/models/creation/CreateRunner.ts @@ -1,11 +1,11 @@ import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsPositive, IsString } from 'class-validator'; -import { Runner } from '../models/Runner'; +import { Runner } from '../entities/Runner'; import { getConnectionManager, Repository } from 'typeorm'; import { group } from 'console'; -import { RunnerOnlyOneGroupAllowedError, RunnerGroupNeededError, RunnerGroupNotFoundError } from '../errors/RunnerErrors'; -import { RunnerOrganisation } from './RunnerOrganisation'; -import { RunnerTeam } from './RunnerTeam'; -import { RunnerGroup } from './RunnerGroup'; +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 { diff --git a/src/models/CreateRunnerOrganisation.ts b/src/models/creation/CreateRunnerOrganisation.ts similarity index 84% rename from src/models/CreateRunnerOrganisation.ts rename to src/models/creation/CreateRunnerOrganisation.ts index b7e5c71..f054e5a 100644 --- a/src/models/CreateRunnerOrganisation.ts +++ b/src/models/creation/CreateRunnerOrganisation.ts @@ -1,5 +1,5 @@ import { IsString } from 'class-validator'; -import { RunnerOrganisation } from './RunnerOrganisation'; +import { RunnerOrganisation } from '../entities/RunnerOrganisation'; export class CreateRunnerOrganisation { @IsString() diff --git a/src/models/CreateTrack.ts b/src/models/creation/CreateTrack.ts similarity index 93% rename from src/models/CreateTrack.ts rename to src/models/creation/CreateTrack.ts index 015a58f..42bf384 100644 --- a/src/models/CreateTrack.ts +++ b/src/models/creation/CreateTrack.ts @@ -1,5 +1,5 @@ import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; -import { Track } from './Track'; +import { Track } from '../entities/Track'; export class CreateTrack { /** diff --git a/src/models/Address.ts b/src/models/entities/Address.ts similarity index 100% rename from src/models/Address.ts rename to src/models/entities/Address.ts diff --git a/src/models/DistanceDonation.ts b/src/models/entities/DistanceDonation.ts similarity index 100% rename from src/models/DistanceDonation.ts rename to src/models/entities/DistanceDonation.ts diff --git a/src/models/Donation.ts b/src/models/entities/Donation.ts similarity index 100% rename from src/models/Donation.ts rename to src/models/entities/Donation.ts diff --git a/src/models/Donor.ts b/src/models/entities/Donor.ts similarity index 100% rename from src/models/Donor.ts rename to src/models/entities/Donor.ts diff --git a/src/models/FixedDonation.ts b/src/models/entities/FixedDonation.ts similarity index 100% rename from src/models/FixedDonation.ts rename to src/models/entities/FixedDonation.ts diff --git a/src/models/GroupContact.ts b/src/models/entities/GroupContact.ts similarity index 100% rename from src/models/GroupContact.ts rename to src/models/entities/GroupContact.ts diff --git a/src/models/Participant.ts b/src/models/entities/Participant.ts similarity index 100% rename from src/models/Participant.ts rename to src/models/entities/Participant.ts diff --git a/src/models/Permission.ts b/src/models/entities/Permission.ts similarity index 100% rename from src/models/Permission.ts rename to src/models/entities/Permission.ts diff --git a/src/models/Runner.ts b/src/models/entities/Runner.ts similarity index 100% rename from src/models/Runner.ts rename to src/models/entities/Runner.ts diff --git a/src/models/RunnerCard.ts b/src/models/entities/RunnerCard.ts similarity index 100% rename from src/models/RunnerCard.ts rename to src/models/entities/RunnerCard.ts diff --git a/src/models/RunnerGroup.ts b/src/models/entities/RunnerGroup.ts similarity index 100% rename from src/models/RunnerGroup.ts rename to src/models/entities/RunnerGroup.ts diff --git a/src/models/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts similarity index 100% rename from src/models/RunnerOrganisation.ts rename to src/models/entities/RunnerOrganisation.ts diff --git a/src/models/RunnerTeam.ts b/src/models/entities/RunnerTeam.ts similarity index 100% rename from src/models/RunnerTeam.ts rename to src/models/entities/RunnerTeam.ts diff --git a/src/models/Scan.ts b/src/models/entities/Scan.ts similarity index 100% rename from src/models/Scan.ts rename to src/models/entities/Scan.ts diff --git a/src/models/ScanStation.ts b/src/models/entities/ScanStation.ts similarity index 100% rename from src/models/ScanStation.ts rename to src/models/entities/ScanStation.ts diff --git a/src/models/Track.ts b/src/models/entities/Track.ts similarity index 100% rename from src/models/Track.ts rename to src/models/entities/Track.ts diff --git a/src/models/TrackScan.ts b/src/models/entities/TrackScan.ts similarity index 100% rename from src/models/TrackScan.ts rename to src/models/entities/TrackScan.ts diff --git a/src/models/User.ts b/src/models/entities/User.ts similarity index 94% rename from src/models/User.ts rename to src/models/entities/User.ts index 94d770e..d5c4bfa 100644 --- a/src/models/User.ts +++ b/src/models/entities/User.ts @@ -1,130 +1,130 @@ -import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; -import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, isUUID, } from "class-validator"; -import { UserGroup } from './UserGroup'; -import { Permission } from './Permission'; -import { UserAction } from './UserAction'; - -/** - * Defines a admin user. -*/ -@Entity() -export class User { - /** - * autogenerated unique id (primary key). - */ - @PrimaryGeneratedColumn() - @IsOptional() - @IsInt() - id: number; - - /** - * autogenerated uuid - */ - @IsOptional() - @IsInt() - @Generated("uuid") - uuid: string; - - /** - * user email - */ - @IsEmail() - email: string; - - /** - * user phone - */ - @IsPhoneNumber("ZZ") - @IsOptional() - phone: string; - - /** - * username - */ - @IsString() - username: string; - - /** - * firstname - */ - @IsString() - @IsNotEmpty() - firstname: string; - - /** - * middlename - */ - @IsString() - @IsOptional() - middlename: string; - - /** - * lastname - */ - @IsString() - @IsNotEmpty() - lastname: string; - - /** - * password - */ - @IsString() - @IsNotEmpty() - password: string; - - /** - * permissions - */ - @ManyToOne(() => Permission, permission => permission.users, { nullable: true }) - permissions: Permission[]; - - /** - * groups - */ - @ManyToMany(() => UserGroup) - @JoinTable() - groups: UserGroup[]; - - /** - * is user enabled? - */ - @IsBoolean() - enabled: boolean; - - /** - * jwt refresh count - */ - @IsInt() - @Column({ default: 1 }) - refreshTokenCount: number; - - /** - * profilepic - */ - @IsString() - profilepic: string; - - /** - * actions - */ - @OneToMany(() => UserAction, action => action.user) - actions: UserAction - - /** - * calculate all permissions - */ - public get calc_permissions(): Permission[] { - let final_permissions = [] - this.groups.forEach((permission) => { - if (!final_permissions.includes(permission)) { - final_permissions.push(permission) - } - }) - this.permissions.forEach((permission) => { - if (!final_permissions.includes(permission)) { - final_permissions.push(permission) - } - }) - return final_permissions - } -} +import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; +import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, isUUID, } from "class-validator"; +import { UserGroup } from './UserGroup'; +import { Permission } from './Permission'; +import { UserAction } from './UserAction'; + +/** + * Defines a admin user. +*/ +@Entity() +export class User { + /** + * autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsOptional() + @IsInt() + id: number; + + /** + * autogenerated uuid + */ + @IsOptional() + @IsInt() + @Generated("uuid") + uuid: string; + + /** + * user email + */ + @IsEmail() + email: string; + + /** + * user phone + */ + @IsPhoneNumber("ZZ") + @IsOptional() + phone: string; + + /** + * username + */ + @IsString() + username: string; + + /** + * firstname + */ + @IsString() + @IsNotEmpty() + firstname: string; + + /** + * middlename + */ + @IsString() + @IsOptional() + middlename: string; + + /** + * lastname + */ + @IsString() + @IsNotEmpty() + lastname: string; + + /** + * password + */ + @IsString() + @IsNotEmpty() + password: string; + + /** + * permissions + */ + @ManyToOne(() => Permission, permission => permission.users, { nullable: true }) + permissions: Permission[]; + + /** + * groups + */ + @ManyToMany(() => UserGroup) + @JoinTable() + groups: UserGroup[]; + + /** + * is user enabled? + */ + @IsBoolean() + enabled: boolean; + + /** + * jwt refresh count + */ + @IsInt() + @Column({ default: 1 }) + refreshTokenCount: number; + + /** + * profilepic + */ + @IsString() + profilepic: string; + + /** + * actions + */ + @OneToMany(() => UserAction, action => action.user) + actions: UserAction + + /** + * calculate all permissions + */ + public get calc_permissions(): Permission[] { + let final_permissions = [] + this.groups.forEach((permission) => { + if (!final_permissions.includes(permission)) { + final_permissions.push(permission) + } + }) + this.permissions.forEach((permission) => { + if (!final_permissions.includes(permission)) { + final_permissions.push(permission) + } + }) + return final_permissions + } +} diff --git a/src/models/UserAction.ts b/src/models/entities/UserAction.ts similarity index 100% rename from src/models/UserAction.ts rename to src/models/entities/UserAction.ts diff --git a/src/models/UserGroup.ts b/src/models/entities/UserGroup.ts similarity index 100% rename from src/models/UserGroup.ts rename to src/models/entities/UserGroup.ts From 92bafb0cf457b50a19f0f965c3a7e241974e750d Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 3 Dec 2020 20:40:05 +0100 Subject: [PATCH 086/194] =?UTF-8?q?=E2=9A=99=20vscode=20setting=20-=20impo?= =?UTF-8?q?rt=20organize=20+=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index bc63ed0..3fb3d30 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,11 @@ }, "prettier.enable": false, "[typescript]": { - "editor.defaultFormatter": "vscode.typescript-language-features" + "editor.defaultFormatter": "vscode.typescript-language-features", + "editor.codeActionsOnSave": { + "source.organizeImports": true, + "source.fixAll": true + } }, "javascript.preferences.quoteStyle": "single", "javascript.preferences.importModuleSpecifierEnding": "minimal", From 330cbd5f57a9f16cbf277ea57fadefcb6d24e0fa Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Thu, 3 Dec 2020 20:49:55 +0100 Subject: [PATCH 087/194] 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 f1629440feae3a49ab17ec7d29b709ff392d6988 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:04:33 +0100 Subject: [PATCH 088/194] =?UTF-8?q?=F0=9F=9A=A7=20better=20uuid=20+=20star?= =?UTF-8?q?ting=20hashing=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- package.json | 7 +++++-- src/models/CreateUser.ts | 14 ++++++++++---- src/models/User.ts | 10 ++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 335f5ce..afc485d 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ ], "license": "CC-BY-NC-SA-4.0", "dependencies": { + "argon2": "^0.27.0", "body-parser": "^1.19.0", "class-transformer": "^0.3.1", "class-validator": "^0.12.2", @@ -39,7 +40,8 @@ "routing-controllers-openapi": "^2.1.0", "swagger-ui-express": "^4.1.5", "typeorm": "^0.2.29", - "typeorm-routing-controllers-extensions": "^0.2.0" + "typeorm-routing-controllers-extensions": "^0.2.0", + "uuid": "^8.3.1" }, "devDependencies": { "@types/cors": "^2.8.8", @@ -49,6 +51,7 @@ "@types/multer": "^1.4.4", "@types/node": "^14.14.9", "@types/swagger-ui-express": "^4.1.2", + "@types/uuid": "^8.3.0", "dotenv-safe": "^8.2.0", "nodemon": "^2.0.6", "sqlite3": "^5.0.0", @@ -61,4 +64,4 @@ "build": "tsc", "docs": "typedoc --out docs src" } -} +} \ No newline at end of file diff --git a/src/models/CreateUser.ts b/src/models/CreateUser.ts index 23cd934..9b57318 100644 --- a/src/models/CreateUser.ts +++ b/src/models/CreateUser.ts @@ -1,8 +1,8 @@ -import { IsInt, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { User } from '../models/User'; -import { getConnectionManager } from 'typeorm'; +import * as argon2 from "argon2"; +import { IsInt, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import * as uuid from 'uuid'; import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../errors/CreateUserErrors'; -import { UserGroup } from './UserGroup'; +import { User } from '../models/User'; export class CreateUser { @IsString() @@ -24,6 +24,8 @@ export class CreateUser { @IsInt() @IsOptional() groupId?: number[] | number + @IsUUID("5") + uuid: string; public async toUser(): Promise { let newUser: User = new User(); @@ -39,12 +41,16 @@ export class CreateUser { throw new UserGroupNotFoundError(); } + const new_uuid = uuid.v4() + newUser.email = this.email newUser.username = this.username newUser.firstname = this.firstname newUser.middlename = this.middlename newUser.lastname = this.lastname + newUser.uuid = new_uuid // TODO: hash password here or in controller/ in User model via setter? + this.password = await argon2.hash(this.password); newUser.password = this.password console.log(newUser) diff --git a/src/models/User.ts b/src/models/User.ts index 94d770e..16f8653 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,8 +1,8 @@ -import { Entity, Column, OneToMany, ManyToOne, PrimaryGeneratedColumn, Generated, Unique, JoinTable, ManyToMany } from "typeorm"; -import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, isUUID, } from "class-validator"; -import { UserGroup } from './UserGroup'; +import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUUID } from "class-validator"; +import { Column, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Permission } from './Permission'; import { UserAction } from './UserAction'; +import { UserGroup } from './UserGroup'; /** * Defines a admin user. @@ -20,9 +20,7 @@ export class User { /** * autogenerated uuid */ - @IsOptional() - @IsInt() - @Generated("uuid") + @IsUUID("5") uuid: string; /** From 5e0fcf1f4ab8ac7929b69cbbf117bbd49edc1407 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:05:13 +0100 Subject: [PATCH 089/194] =?UTF-8?q?=E2=9A=99=F0=9F=90=9E=20VSCode=20format?= =?UTF-8?q?ting=20broke=20code=20by=20removing=20all=20unused?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3fb3d30..fd68d0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,7 +10,7 @@ "editor.defaultFormatter": "vscode.typescript-language-features", "editor.codeActionsOnSave": { "source.organizeImports": true, - "source.fixAll": true + // "source.fixAll": true } }, "javascript.preferences.quoteStyle": "single", From 0c6528bdc5eb314399bc722087e3b907f0f59ff8 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 17:06:37 +0100 Subject: [PATCH 090/194] 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 ae24c3394b4243065119535d24252a66cce2cdeb Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:10:00 +0100 Subject: [PATCH 091/194] =?UTF-8?q?=F0=9F=93=8F=20fit=20to=20new=20structu?= =?UTF-8?q?re?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/{ => creation}/CreateUser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/models/{ => creation}/CreateUser.ts (95%) diff --git a/src/models/CreateUser.ts b/src/models/creation/CreateUser.ts similarity index 95% rename from src/models/CreateUser.ts rename to src/models/creation/CreateUser.ts index 9b57318..92f6e22 100644 --- a/src/models/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -1,8 +1,8 @@ import * as argon2 from "argon2"; import { IsInt, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; import * as uuid from 'uuid'; -import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../errors/CreateUserErrors'; -import { User } from '../models/User'; +import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/CreateUserErrors'; +import { User } from '../entities/User'; export class CreateUser { @IsString() From ce2c38e1882882da13e631fdde62a79b879d13b6 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:25:15 +0100 Subject: [PATCH 092/194] =?UTF-8?q?=F0=9F=94=92argon2=20password=20hashing?= =?UTF-8?q?=20w/=20salt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/creation/CreateUser.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 92f6e22..28dadc1 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -49,9 +49,7 @@ export class CreateUser { newUser.middlename = this.middlename newUser.lastname = this.lastname newUser.uuid = new_uuid - // TODO: hash password here or in controller/ in User model via setter? - this.password = await argon2.hash(this.password); - newUser.password = this.password + newUser.password = await argon2.hash(this.password + new_uuid); console.log(newUser) return newUser; From ca917b057767c341f7d0e2f2d62603df9d291a3b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 17:50:10 +0100 Subject: [PATCH 093/194] 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 094/194] 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 a0e6424d482c54d77a887d8b91c81a6f39b51f59 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:53:28 +0100 Subject: [PATCH 095/194] =?UTF-8?q?=F0=9F=9A=A7=20better/=20more=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/errors/CreateUserErrors.ts | 24 ----------------- src/errors/UserErrors.ts | 47 ++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 24 deletions(-) delete mode 100644 src/errors/CreateUserErrors.ts create mode 100644 src/errors/UserErrors.ts diff --git a/src/errors/CreateUserErrors.ts b/src/errors/CreateUserErrors.ts deleted file mode 100644 index 3b28735..0000000 --- a/src/errors/CreateUserErrors.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NotFoundError } from 'routing-controllers'; -import { IsString } from 'class-validator'; - -/** - * Error to throw when a usergroup couldn't be found. - */ -export class UserGroupNotFoundError extends NotFoundError { - @IsString() - name = "UserGroupNotFoundError" - - @IsString() - message = "User Group not found!" -} - -/** - * Error to throw when no username or email is set - */ -export class UsernameOrEmailNeededError extends NotFoundError { - @IsString() - name = "UsernameOrEmailNeededError" - - @IsString() - message = "no username or email is set!" -} diff --git a/src/errors/UserErrors.ts b/src/errors/UserErrors.ts new file mode 100644 index 0000000..4c9e98b --- /dev/null +++ b/src/errors/UserErrors.ts @@ -0,0 +1,47 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when a usergroup couldn't be found. + */ +export class UserGroupNotFoundError extends NotFoundError { + @IsString() + name = "UserGroupNotFoundError" + + @IsString() + message = "User Group not found!" +} + +/** + * Error to throw when no username or email is set + */ +export class UsernameOrEmailNeededError extends NotFoundError { + @IsString() + name = "UsernameOrEmailNeededError" + + @IsString() + message = "no username or email is set!" +} + +/** + * Error to throw when a user couldn't be found. + */ +export class UserNotFoundError extends NotFoundError { + @IsString() + name = "UserNotFoundError" + + @IsString() + message = "User not found!" +} + +/** + * Error to throw when two users' ids don't match. + * Usually occurs when a user tries to change a user's id. + */ +export class UserIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "UserIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n And if you wanted to change a user's id: This isn't allowed" +} \ No newline at end of file From 091b455460dfe79276305c533170f5d153d78b73 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:53:49 +0100 Subject: [PATCH 096/194] =?UTF-8?q?=F0=9F=9A=A7=20move=20to=20uuidV4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/creation/CreateUser.ts | 2 +- src/models/entities/User.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 28dadc1..14f0f77 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -24,7 +24,7 @@ export class CreateUser { @IsInt() @IsOptional() groupId?: number[] | number - @IsUUID("5") + @IsUUID("4") uuid: string; public async toUser(): Promise { diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 16f8653..6fd3321 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -18,9 +18,9 @@ export class User { id: number; /** - * autogenerated uuid + * uuid */ - @IsUUID("5") + @IsUUID("4") uuid: string; /** From 1efca4733617806363e51d013590c885b9b13405 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:55:51 +0100 Subject: [PATCH 097/194] =?UTF-8?q?=F0=9F=9A=A7=20reference=20new=20Errors?= =?UTF-8?q?=20from=20CreateUser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/creation/CreateUser.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 14f0f77..102919a 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -1,8 +1,10 @@ import * as argon2 from "argon2"; -import { IsInt, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import { IsEmail, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; -import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/CreateUserErrors'; +import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { User } from '../entities/User'; +import { UserGroup } from '../entities/UserGroup'; export class CreateUser { @IsString() @@ -19,9 +21,11 @@ export class CreateUser { password: string; @IsString() lastname: string; + @IsEmail() @IsString() email?: string; - @IsInt() + // @IsArray() + // @IsInt() @IsOptional() groupId?: number[] | number @IsUUID("4") @@ -36,9 +40,13 @@ export class CreateUser { if (this.groupId) { // TODO: link user groups - // newUser.groups = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: this.teamId }); + if (Array.isArray(this.groupId)) { + + } else { + newUser.groups = await getConnectionManager().get().getRepository(UserGroup).find({ id: this.groupId }); + } } else { - throw new UserGroupNotFoundError(); + // throw new UserGroupNotFoundError(); } const new_uuid = uuid.v4() From d556e9ba190222c615b2b7567bd30fece0c08c5c Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 17:56:09 +0100 Subject: [PATCH 098/194] =?UTF-8?q?=F0=9F=9A=A7=20UserController?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/controllers/UserController.ts | 86 +++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/controllers/UserController.ts diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts new file mode 100644 index 0000000..dbc4d3a --- /dev/null +++ b/src/controllers/UserController.ts @@ -0,0 +1,86 @@ +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 { UserGroupNotFoundError, UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; +import { CreateUser } from '../models/creation/CreateUser'; +import { User } from '../models/entities/User'; + + +@JsonController('/users') +export class UserController { + private userRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.userRepository = getConnectionManager().get().getRepository(User); + } + + @Get() + @ResponseSchema(User, { isArray: true }) + @OpenAPI({ description: 'Lists all users.' }) + getAll() { + return this.userRepository.find(); + } + + @Get('/:id') + @ResponseSchema(User) + @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @OnUndefined(UserNotFoundError) + @OpenAPI({ description: 'Returns a user of a specified id (if it exists)' }) + getOne(@Param('id') id: number) { + return this.userRepository.findOne({ id: id }); + } + + @Post() + @ResponseSchema(User) + @ResponseSchema(UserGroupNotFoundError) + @OpenAPI({ description: 'Create a new user object (id will be generated automagicly).' }) + async post(@Body({ validate: true }) createUser: CreateUser) { + let user; + try { + user = await createUser.toUser(); + } catch (error) { + return error; + } + + return this.userRepository.save(user); + } + + @Put('/:id') + @ResponseSchema(User) + @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @ResponseSchema(UserIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a user object (id can't be changed)." }) + async put(@Param('id') id: number, @EntityFromBody() user: User) { + let oldUser = await this.userRepository.findOne({ id: id }); + + if (!oldUser) { + throw new UserNotFoundError(); + } + + if (oldUser.id != user.id) { + throw new UserIdsNotMatchingError(); + } + + await this.userRepository.update(oldUser, user); + return user; + } + + @Delete('/:id') + @ResponseSchema(User) + @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) + async remove(@Param('id') id: number) { + let runner = await this.userRepository.findOne({ id: id }); + + if (!runner) { + throw new UserNotFoundError(); + } + + await this.userRepository.delete(runner); + return runner; + } +} From 5b7f3ae12f3f8749aa637d18763cf5f5df8001eb Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 18:02:28 +0100 Subject: [PATCH 099/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateUser=20group=20sea?= =?UTF-8?q?rch=20+=20adding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/creation/CreateUser.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 102919a..9595039 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -2,7 +2,7 @@ import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; -import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { User } from '../entities/User'; import { UserGroup } from '../entities/UserGroup'; @@ -24,8 +24,6 @@ export class CreateUser { @IsEmail() @IsString() email?: string; - // @IsArray() - // @IsInt() @IsOptional() groupId?: number[] | number @IsUUID("4") @@ -39,14 +37,20 @@ export class CreateUser { } if (this.groupId) { - // TODO: link user groups if (Array.isArray(this.groupId)) { - + let found_groups = [] + this.groupId.forEach(async (g) => { + const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + if (foundGroup) { + found_groups.push(foundGroup) + } else { + throw new UserGroupNotFoundError(); + } + }); + newUser.groups = found_groups } else { newUser.groups = await getConnectionManager().get().getRepository(UserGroup).find({ id: this.groupId }); } - } else { - // throw new UserGroupNotFoundError(); } const new_uuid = uuid.v4() From 795599fd388150a07c959cb3fbed6f676d3db790 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 18:15:38 +0100 Subject: [PATCH 100/194] 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 101/194] 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 102/194] 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 3275b5fd8008b0c10601c33844b6d35ab39d4972 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 18:34:14 +0100 Subject: [PATCH 103/194] =?UTF-8?q?=F0=9F=9A=A7=20UserGroups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/controllers/UserGroupController.ts | 86 ++++++++++++++++++++++++++ src/errors/UserGroupErrors.ts | 36 +++++++++++ src/models/creation/CreateUserGroup.ts | 28 +++++++++ src/models/entities/UserGroup.ts | 8 +-- 4 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 src/controllers/UserGroupController.ts create mode 100644 src/errors/UserGroupErrors.ts create mode 100644 src/models/creation/CreateUserGroup.ts diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts new file mode 100644 index 0000000..edb8658 --- /dev/null +++ b/src/controllers/UserGroupController.ts @@ -0,0 +1,86 @@ +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 { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; +import { CreateUserGroup } from '../models/creation/CreateUserGroup'; +import { UserGroup } from '../models/entities/UserGroup'; + + +@JsonController('/usergroups') +export class UserController { + private userGroupsRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.userGroupsRepository = getConnectionManager().get().getRepository(UserGroup); + } + + @Get() + @ResponseSchema(UserGroup, { isArray: true }) + @OpenAPI({ description: 'Lists all usergroups.' }) + getAll() { + return this.userGroupsRepository.find(); + } + + @Get('/:id') + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @OnUndefined(UserGroupNotFoundError) + @OpenAPI({ description: 'Returns a usergroup of a specified id (if it exists)' }) + getOne(@Param('id') id: number) { + return this.userGroupsRepository.findOne({ id: id }); + } + + @Post() + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError) + @OpenAPI({ description: 'Create a new usergroup object (id will be generated automagicly).' }) + async post(@Body({ validate: true }) createUserGroup: CreateUserGroup) { + let userGroup; + try { + userGroup = await createUserGroup.toUserGroup(); + } catch (error) { + return error; + } + + return this.userGroupsRepository.save(userGroup); + } + + @Put('/:id') + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(UserGroupIdsNotMatchingError, { statusCode: 406 }) + @OpenAPI({ description: "Update a usergroup object (id can't be changed)." }) + async put(@Param('id') id: number, @EntityFromBody() userGroup: UserGroup) { + let oldUserGroup = await this.userGroupsRepository.findOne({ id: id }); + + if (!oldUserGroup) { + throw new UserGroupNotFoundError() + } + + if (oldUserGroup.id != userGroup.id) { + throw new UserGroupIdsNotMatchingError(); + } + + await this.userGroupsRepository.update(oldUserGroup, userGroup); + return userGroup; + } + + @Delete('/:id') + @ResponseSchema(UserGroup) + @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Delete a specified usergroup (if it exists).' }) + async remove(@Param('id') id: number) { + let userGroup = await this.userGroupsRepository.findOne({ id: id }); + + if (!userGroup) { + throw new UserGroupNotFoundError(); + } + + await this.userGroupsRepository.delete(userGroup); + return userGroup; + } +} diff --git a/src/errors/UserGroupErrors.ts b/src/errors/UserGroupErrors.ts new file mode 100644 index 0000000..15495b9 --- /dev/null +++ b/src/errors/UserGroupErrors.ts @@ -0,0 +1,36 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when no groupname is set + */ +export class GroupNameNeededError extends NotFoundError { + @IsString() + name = "GroupNameNeededError" + + @IsString() + message = "no groupname is set!" +} + +/** + * Error to throw when a usergroup couldn't be found. + */ +export class UserGroupNotFoundError extends NotFoundError { + @IsString() + name = "UserGroupNotFoundError" + + @IsString() + message = "User Group not found!" +} + +/** + * Error to throw when two usergroups' ids don't match. + * Usually occurs when a user tries to change a usergroups's id. + */ +export class UserGroupIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "UserGroupIdsNotMatchingError" + + @IsString() + message = "The id's don't match!! \n If you wanted to change a usergroup's id: This isn't allowed" +} \ No newline at end of file diff --git a/src/models/creation/CreateUserGroup.ts b/src/models/creation/CreateUserGroup.ts new file mode 100644 index 0000000..d55618c --- /dev/null +++ b/src/models/creation/CreateUserGroup.ts @@ -0,0 +1,28 @@ +import { IsOptional, IsString } from 'class-validator'; +import { GroupNameNeededError } from '../../errors/UserGroupErrors'; +import { UserGroup } from '../entities/UserGroup'; + +export class CreateUserGroup { + @IsOptional() + @IsString() + name: string; + @IsOptional() + @IsString() + description?: string; + + public async toUserGroup(): Promise { + let newUserGroup: UserGroup = new UserGroup(); + + if (this.name === undefined) { + throw new GroupNameNeededError(); + } + + newUserGroup.name = this.name + if (this.description) { + newUserGroup.description = this.description + } + + console.log(newUserGroup) + return newUserGroup; + } +} \ No newline at end of file diff --git a/src/models/entities/UserGroup.ts b/src/models/entities/UserGroup.ts index c3b2f0c..2156d14 100644 --- a/src/models/entities/UserGroup.ts +++ b/src/models/entities/UserGroup.ts @@ -1,17 +1,17 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, - IsString, + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; import { Permission } from "./Permission"; /** * Defines the UserGroup interface. */ @Entity() -export abstract class UserGroup { +export class UserGroup { /** * Autogenerated unique id (primary key). */ @@ -37,7 +37,7 @@ export abstract class UserGroup { /** * The group's description */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsString() description?: string; From b101682e3cd23ea501ca8c6036084c0d4aef2644 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 18:34:24 +0100 Subject: [PATCH 104/194] CreateUser ref #14 --- src/models/creation/CreateUser.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 9595039..b024e4c 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -37,20 +37,19 @@ export class CreateUser { } if (this.groupId) { - if (Array.isArray(this.groupId)) { - let found_groups = [] - this.groupId.forEach(async (g) => { - const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); - if (foundGroup) { - found_groups.push(foundGroup) - } else { - throw new UserGroupNotFoundError(); - } - }); - newUser.groups = found_groups - } else { - newUser.groups = await getConnectionManager().get().getRepository(UserGroup).find({ id: this.groupId }); + if (!Array.isArray(this.groupId)) { + this.groupId = [this.groupId] } + let found_groups = [] + this.groupId.forEach(async (g) => { + const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + if (foundGroup) { + found_groups.push(foundGroup) + } else { + throw new UserGroupNotFoundError(); + } + }); + newUser.groups = found_groups } const new_uuid = uuid.v4() From a3b79ef21df1ce96f0b301097cc0122eef06bfbd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 18:36:46 +0100 Subject: [PATCH 105/194] Fix ref #14 --- src/controllers/UserGroupController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index edb8658..fb158ed 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -8,7 +8,7 @@ import { UserGroup } from '../models/entities/UserGroup'; @JsonController('/usergroups') -export class UserController { +export class UserGroupController { private userGroupsRepository: Repository; /** From 8beb658bccb754b73ff7cb39d9d1e18e33cde0dd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 19:01:36 +0100 Subject: [PATCH 106/194] 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 451d0c92dddd4e56626eca8c8b9e6728b6427a23 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 19:02:07 +0100 Subject: [PATCH 107/194] trying to fix UserGroupNotFoundError (false/not triggering) ref #14 --- src/models/creation/CreateUser.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index b024e4c..1182519 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -42,14 +42,16 @@ export class CreateUser { } let found_groups = [] this.groupId.forEach(async (g) => { - const foundGroup = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); - if (foundGroup) { - found_groups.push(foundGroup) - } else { - throw new UserGroupNotFoundError(); - } + const foundGroup = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: g }); + console.log(foundGroup); + found_groups.push(foundGroup) }); - newUser.groups = found_groups + console.log(found_groups); + if (found_groups.includes(undefined) || found_groups.includes(null)) { + throw new UserGroupNotFoundError(); + } else { + newUser.groups = found_groups + } } const new_uuid = uuid.v4() From d4753a02d4fa04beaf0794f1b15debb38c7827b9 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 19:31:10 +0100 Subject: [PATCH 108/194] =?UTF-8?q?=F0=9F=90=9E=20fixed=20UserGroupNotFoun?= =?UTF-8?q?dError=20throwing=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #14 --- src/models/creation/CreateUser.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 1182519..5660227 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -40,17 +40,23 @@ export class CreateUser { if (!Array.isArray(this.groupId)) { this.groupId = [this.groupId] } - let found_groups = [] - this.groupId.forEach(async (g) => { - const foundGroup = await getConnectionManager().get().getRepository(UserGroup).findOne({ id: g }); - console.log(foundGroup); - found_groups.push(foundGroup) - }); - console.log(found_groups); - if (found_groups.includes(undefined) || found_groups.includes(null)) { + const groupIDs: number[] = this.groupId + let errors = 0 + const validateusergroups = async () => { + let foundgroups = [] + for (const g of groupIDs) { + const found = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + if (found.length === 0) { + errors++ + } else { + foundgroups.push(found) + } + } + newUser.groups = foundgroups + } + await validateusergroups() + if (errors !== 0) { throw new UserGroupNotFoundError(); - } else { - newUser.groups = found_groups } } From c30922e3250ad2a0510ab8fd296e364943994eba Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 19:31:20 +0100 Subject: [PATCH 109/194] 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 a7cf86eae41991956db6b48f8c70ed0d6cec374a Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 19:32:17 +0100 Subject: [PATCH 110/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateUser=20-=20add=20g?= =?UTF-8?q?roup=20as=20object=20instead=20of=20nested=20array?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/creation/CreateUser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 5660227..4bd8cd4 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -49,7 +49,7 @@ export class CreateUser { if (found.length === 0) { errors++ } else { - foundgroups.push(found) + foundgroups.push(found[0]) } } newUser.groups = foundgroups From 056413560ec29cb8d8a1f7a94528d84025e994c1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 19:40:00 +0100 Subject: [PATCH 111/194] 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 112/194] 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 fe46e5d667a34b2b57e0e993a414e8f88026fe79 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 21:29:49 +0100 Subject: [PATCH 113/194] =?UTF-8?q?=E2=9C=85=20-=20close=20#14?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From a437ada3b3cee64c69548cf0cd1e93d4fa2495b3 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 21:38:28 +0100 Subject: [PATCH 114/194] 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 1f3b31267598e1b9e97cf8bed542def68bfd99f6 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 21:38:34 +0100 Subject: [PATCH 115/194] =?UTF-8?q?=F0=9F=9A=A7=20basic=20JWTAuth=20Middle?= =?UTF-8?q?ware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/app.ts | 11 ++++++----- src/middlewares/jwtauth.ts | 33 ++++++++++++++++++--------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/app.ts b/src/app.ts index 9e15de1..e54f0ac 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,17 +1,18 @@ -import "reflect-metadata"; -import * as dotenvSafe from "dotenv-safe"; -import { createExpressServer } from "routing-controllers"; import consola from "consola"; -import loaders from "./loaders/index"; +import * as dotenvSafe from "dotenv-safe"; +import "reflect-metadata"; +import { createExpressServer } from "routing-controllers"; import authchecker from "./authchecker"; +import loaders from "./loaders/index"; import { ErrorHandler } from './middlewares/ErrorHandler'; +import { JWTAuth } from './middlewares/JWTAuth'; dotenvSafe.config(); const PORT = process.env.APP_PORT || 4010; const app = createExpressServer({ authorizationChecker: authchecker, - middlewares: [ErrorHandler], + middlewares: [ErrorHandler, JWTAuth], development: process.env.NODE_ENV === "production", cors: true, routePrefix: "/api", diff --git a/src/middlewares/jwtauth.ts b/src/middlewares/jwtauth.ts index 81c28c3..bdb423b 100644 --- a/src/middlewares/jwtauth.ts +++ b/src/middlewares/jwtauth.ts @@ -1,17 +1,20 @@ -import { Request, Response, NextFunction } from "express"; -// import bodyParser from 'body-parser'; -// import cors from 'cors'; import * as jwt from "jsonwebtoken"; +import { + ExpressMiddlewareInterface, Middleware +} from "routing-controllers"; -export default (req: Request, res: Response, next: NextFunction) => { - const token = req.headers["auth"]; - try { - const jwtPayload = jwt.verify(token, "secretjwtsecret"); - // const jwtPayload = jwt.verify(token, process.env.JWT_SECRET); - res.locals.jwtPayload = jwtPayload; - } catch (error) { - console.log(error); - return res.status(401).send(); - } - next(); -}; +@Middleware({ type: "before" }) +export class JWTAuth implements ExpressMiddlewareInterface { + use(request: any, response: any, next?: (err?: any) => any): any { + const token = request.headers["auth"]; + try { + const jwtPayload = jwt.verify(token, "secretjwtsecret"); + // const jwtPayload = jwt.verify(token, process.env.JWT_SECRET); + response.locals.jwtPayload = jwtPayload; + } catch (error) { + console.log(error); + return response.status(401).send(); + } + next(); + } +} From 7b084895334431445ae2c038bd086c8833a8e530 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 21:55:09 +0100 Subject: [PATCH 116/194] 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 117/194] 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 118/194] 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 b9bbdee82654b5cea26fdd97b38df2c0829ec1e6 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:17:03 +0100 Subject: [PATCH 119/194] =?UTF-8?q?=F0=9F=9A=A7=20basic=20AuthErrors=20?= =?UTF-8?q?=F0=9F=94=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/errors/AuthError.ts | 68 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/errors/AuthError.ts diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts new file mode 100644 index 0000000..24ded8a --- /dev/null +++ b/src/errors/AuthError.ts @@ -0,0 +1,68 @@ +import { IsString } from 'class-validator'; +import { ForbiddenError, NotAcceptableError, UnauthorizedError } from 'routing-controllers'; + +/** + * Error to throw when a jwt is expired + */ +export class ExpiredJWTError extends UnauthorizedError { + @IsString() + name = "ExpiredJWTError" + + @IsString() + message = "your provided jwt is expired" +} + +/** + * Error to throw when a jwt could not be parsed + */ +export class IllegalJWTError extends UnauthorizedError { + @IsString() + name = "IllegalJWTError" + + @IsString() + message = "your provided jwt could not be parsed" +} + +/** + * Error to throw when provided credentials are invalid + */ +export class InvalidCredentialsError extends UnauthorizedError { + @IsString() + name = "InvalidCredentialsError" + + @IsString() + message = "your provided credentials are invalid" +} + +/** + * Error to throw when a jwt does not have permission for this route/ action + */ +export class NoPermissionError extends ForbiddenError { + @IsString() + name = "NoPermissionError" + + @IsString() + message = "your provided jwt does not have permission for this route/ action" +} + +/** + * Error to thow when no username and no email is set + */ +export class UsernameOrEmailNeededError extends NotAcceptableError { + @IsString() + name = "UsernameOrEmailNeededError" + + @IsString() + message = "Auth needs to have email or username set! \n You provided neither." +} + +/** + * Error to thow when no password is provided + */ +export class PasswordNeededError extends NotAcceptableError { + @IsString() + name = "PasswordNeededError" + + @IsString() + message = "no password is provided - you need to provide it" +} \ No newline at end of file From b0a24c6a74d9a20a4ec5c562ece6baa3811cd00a Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:18:40 +0100 Subject: [PATCH 120/194] basic Auth model ref #12 --- src/models/entities/Auth.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/models/entities/Auth.ts diff --git a/src/models/entities/Auth.ts b/src/models/entities/Auth.ts new file mode 100644 index 0000000..75ed27c --- /dev/null +++ b/src/models/entities/Auth.ts @@ -0,0 +1,21 @@ +/** + * Defines a auth object +*/ +export class Auth { + /** + * access_token - JWT shortterm access token + */ + access_token: string; + /** + * refresh_token - longterm refresh token (used for requesting new access tokens) + */ + refresh_token: string; + /** + * access_token_expires_at - unix timestamp of access token expiry + */ + access_token_expires_at: number; + /** + * refresh_token_expires_at - unix timestamp of access token expiry + */ + refresh_token_expires_at: number; +} From f251b7acdbc8e277971cf97cfe619f441ac0362a Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:18:54 +0100 Subject: [PATCH 121/194] authchecker - use new custom Errors ref #12 --- src/authchecker.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/authchecker.ts b/src/authchecker.ts index 9b62dfa..f32ec75 100644 --- a/src/authchecker.ts +++ b/src/authchecker.ts @@ -1,5 +1,6 @@ import * as jwt from "jsonwebtoken"; -import { Action, HttpError } from "routing-controllers"; +import { Action } from "routing-controllers"; +import { IllegalJWTError, NoPermissionError } from './errors/AuthError'; // ----------- const sampletoken = jwt.sign({ "permissions": { @@ -22,7 +23,7 @@ const authchecker = async (action: Action, permissions: string | string[]) => { try { jwtPayload = jwt.verify(provided_token, process.env.JWT_SECRET || "secretjwtsecret"); } catch (error) { - throw new HttpError(401, "jwt_illegal") + throw new IllegalJWTError() } if (jwtPayload.permissions) { action.response.local = {} @@ -34,11 +35,11 @@ const authchecker = async (action: Action, permissions: string | string[]) => { if (actual_accesslevel_for_permission.includes(permission_access_level)) { return true; } else { - throw new HttpError(403, "no") + throw new NoPermissionError() } }); } else { - throw new HttpError(403, "no") + throw new NoPermissionError() } // try { From a7afcf4cd1ab60f856c299eaeace7fe5f3ec3b14 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:19:55 +0100 Subject: [PATCH 122/194] CreateAuth model ref #12 --- src/models/creation/CreateAuth.ts | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/models/creation/CreateAuth.ts diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts new file mode 100644 index 0000000..7f20dbe --- /dev/null +++ b/src/models/creation/CreateAuth.ts @@ -0,0 +1,36 @@ +import { IsEmail, IsOptional, IsString } from 'class-validator'; +import { PasswordNeededError } from '../../errors/AuthError'; +import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { Auth } from '../entities/Auth'; + +export class CreateAuth { + @IsOptional() + @IsString() + username?: string; + @IsString() + password: string; + @IsEmail() + @IsString() + email?: string; + + public async toAuth(): Promise { + let newAuth: Auth = new Auth(); + + if (this.email === undefined && this.username === undefined) { + throw new UsernameOrEmailNeededError(); + } + if (!this.password) { + throw new PasswordNeededError() + } + // const found = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + + // TODO: jwt creation + return + newAuth.access_token = "" + newAuth.refresh_token = "" + newAuth.access_token_expires_at = 1587349200 + newAuth.refresh_token_expires_at = 1587349200 + + console.log(newAuth) + return newAuth; + } +} \ No newline at end of file From c5c3058f3d20aa898bd67c02baa3d995321e0ab5 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:28:17 +0100 Subject: [PATCH 123/194] clean up jwtauth ref #12 --- src/middlewares/jwtauth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/middlewares/jwtauth.ts b/src/middlewares/jwtauth.ts index bdb423b..3f77265 100644 --- a/src/middlewares/jwtauth.ts +++ b/src/middlewares/jwtauth.ts @@ -1,9 +1,9 @@ import * as jwt from "jsonwebtoken"; import { - ExpressMiddlewareInterface, Middleware + ExpressMiddlewareInterface } from "routing-controllers"; -@Middleware({ type: "before" }) +// @Middleware({ type: "before" }) export class JWTAuth implements ExpressMiddlewareInterface { use(request: any, response: any, next?: (err?: any) => any): any { const token = request.headers["auth"]; From c4b7ece974fe548e68415b49fc8cce913ef6cd92 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:34:03 +0100 Subject: [PATCH 124/194] class-validator on Auth model ref #12 --- src/models/entities/Auth.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/models/entities/Auth.ts b/src/models/entities/Auth.ts index 75ed27c..39a16ef 100644 --- a/src/models/entities/Auth.ts +++ b/src/models/entities/Auth.ts @@ -1,3 +1,5 @@ +import { IsInt, IsString } from 'class-validator'; + /** * Defines a auth object */ @@ -5,17 +7,21 @@ export class Auth { /** * access_token - JWT shortterm access token */ + @IsString() access_token: string; /** * refresh_token - longterm refresh token (used for requesting new access tokens) */ + @IsString() refresh_token: string; /** * access_token_expires_at - unix timestamp of access token expiry */ + @IsInt() access_token_expires_at: number; /** * refresh_token_expires_at - unix timestamp of access token expiry */ + @IsInt() refresh_token_expires_at: number; } From 56202ec3094444ec60170953aee5829e2a4ffba1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 22:40:14 +0100 Subject: [PATCH 125/194] 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 From 6cb01090d0f640d25728495f0a80c756ee43e985 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:43:41 +0100 Subject: [PATCH 126/194] working on AuthController + CreateAuth ref #12 --- src/controllers/AuthController.ts | 26 ++++++++++++++++++++++++++ src/models/creation/CreateAuth.ts | 7 +++++-- 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/controllers/AuthController.ts diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts new file mode 100644 index 0000000..8a39093 --- /dev/null +++ b/src/controllers/AuthController.ts @@ -0,0 +1,26 @@ +import { Body, JsonController, Post } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; +import { InvalidCredentialsError } from '../errors/AuthError'; +import { UserNotFoundError } from '../errors/UserErrors'; +import { CreateAuth } from '../models/creation/CreateAuth'; + +@JsonController('/auth') +export class AuthController { + constructor() { + } + + @Post("/login") + @ResponseSchema(InvalidCredentialsError) + @ResponseSchema(UserNotFoundError) + @OpenAPI({ description: 'Create a new access token object' }) + async post(@Body({ validate: true }) createAuth: CreateAuth) { + let auth; + try { + auth = await createAuth.toAuth(); + console.log(auth); + } catch (error) { + return error; + } + return auth + } +} diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 7f20dbe..4e28229 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -1,7 +1,9 @@ import { IsEmail, IsOptional, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; import { PasswordNeededError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { Auth } from '../entities/Auth'; +import { User } from '../entities/User'; export class CreateAuth { @IsOptional() @@ -22,9 +24,10 @@ export class CreateAuth { if (!this.password) { throw new PasswordNeededError() } - // const found = await getConnectionManager().get().getRepository(UserGroup).find({ id: g }); + const found = await getConnectionManager().get().getRepository(User).find({ where: [{ username: this.username }, { email: this.email }] }); + console.log(found); - // TODO: jwt creation + return + // TODO: jwt creation newAuth.access_token = "" newAuth.refresh_token = "" newAuth.access_token_expires_at = 1587349200 From 7f3358d284889687aef92a4dbd562ec2ccb4c499 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:48:42 +0100 Subject: [PATCH 127/194] =?UTF-8?q?=F0=9F=9A=A7=20User=20entity=20-=20add?= =?UTF-8?q?=20@Column?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #11 --- src/models/entities/User.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 6fd3321..7da56db 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -20,18 +20,21 @@ export class User { /** * uuid */ + @Column() @IsUUID("4") uuid: string; /** * user email */ + @Column() @IsEmail() email: string; /** * user phone */ + @Column() @IsPhoneNumber("ZZ") @IsOptional() phone: string; @@ -39,12 +42,14 @@ export class User { /** * username */ + @Column() @IsString() username: string; /** * firstname */ + @Column() @IsString() @IsNotEmpty() firstname: string; @@ -52,6 +57,7 @@ export class User { /** * middlename */ + @Column() @IsString() @IsOptional() middlename: string; @@ -59,6 +65,7 @@ export class User { /** * lastname */ + @Column() @IsString() @IsNotEmpty() lastname: string; @@ -66,6 +73,7 @@ export class User { /** * password */ + @Column() @IsString() @IsNotEmpty() password: string; @@ -86,6 +94,7 @@ export class User { /** * is user enabled? */ + @Column() @IsBoolean() enabled: boolean; @@ -99,6 +108,7 @@ export class User { /** * profilepic */ + @Column() @IsString() profilepic: string; From a7854fbe81b1b8ed1c0df7927e3b8da74ad0fab0 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 22:51:44 +0100 Subject: [PATCH 128/194] CreateUser - remove uuid from params ref #11 --- src/models/creation/CreateUser.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 4bd8cd4..81b0b1e 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -1,5 +1,5 @@ import * as argon2 from "argon2"; -import { IsEmail, IsOptional, IsPhoneNumber, IsString, IsUUID } from 'class-validator'; +import { IsEmail, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/UserErrors'; @@ -26,8 +26,6 @@ export class CreateUser { email?: string; @IsOptional() groupId?: number[] | number - @IsUUID("4") - uuid: string; public async toUser(): Promise { let newUser: User = new User(); From 5d7d80d2e75e0cdf68eea458b97610e6e2322087 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Fri, 4 Dec 2020 22:58:34 +0100 Subject: [PATCH 129/194] A step towards inheritance for the create* objects relating to runner groups ref #13 --- src/models/creation/CreateRunnerGroup.ts | 30 ++++++++++++ .../creation/CreateRunnerOrganisation.ts | 48 ++++++++++++++++--- 2 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 src/models/creation/CreateRunnerGroup.ts diff --git a/src/models/creation/CreateRunnerGroup.ts b/src/models/creation/CreateRunnerGroup.ts new file mode 100644 index 0000000..33cb5a9 --- /dev/null +++ b/src/models/creation/CreateRunnerGroup.ts @@ -0,0 +1,30 @@ +import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator'; +import { GroupContact } from '../entities/GroupContact'; + +export abstract class CreateRunnerGroup { + /** + * The group's name. + */ + @IsNotEmpty() + @IsString() + name: string; + + /** + * The group's contact. + * Optional + */ + @IsObject() + @IsOptional() + contact?: GroupContact; + + /** + * Deals with the contact for groups this. + */ + public async getContact(): Promise { + let newGroupContact: GroupContact; + + //TODO: + + return newGroupContact; + } +} \ No newline at end of file diff --git a/src/models/creation/CreateRunnerOrganisation.ts b/src/models/creation/CreateRunnerOrganisation.ts index ff4ec65..b90162d 100644 --- a/src/models/creation/CreateRunnerOrganisation.ts +++ b/src/models/creation/CreateRunnerOrganisation.ts @@ -1,13 +1,47 @@ -import { IsNotEmpty, IsString } from 'class-validator'; +import { IsInt, IsObject, IsOptional } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { ParticipantOnlyOneAddressAllowedError } from '../../errors/ParticipantErrors'; +import { Address } from '../entities/Address'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; +import { CreateAddress } from './CreateAddress'; +import { CreateRunnerGroup } from './CreateRunnerGroup'; -export class CreateRunnerOrganisation { +export class CreateRunnerOrganisation extends CreateRunnerGroup { /** - * The Organisation's name. + * The new participant's address's id. + * Optional - please provide either addressId or address. */ - @IsString() - @IsNotEmpty() - name: string; + @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(); + } /** * Creates a RunnerOrganisation entity from this. @@ -16,6 +50,8 @@ export class CreateRunnerOrganisation { let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); newRunnerOrganisation.name = this.name; + newRunnerOrganisation.contact = await this.getContact(); + newRunnerOrganisation.address = await this.getAddress(); return newRunnerOrganisation; } From d803704eeebc5d5a188cb9fd5c9afd6882579fd3 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 23:02:23 +0100 Subject: [PATCH 130/194] UserNotFoundError ref #12 --- src/errors/AuthError.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index 24ded8a..a0de93a 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -1,5 +1,5 @@ import { IsString } from 'class-validator'; -import { ForbiddenError, NotAcceptableError, UnauthorizedError } from 'routing-controllers'; +import { ForbiddenError, NotAcceptableError, NotFoundError, UnauthorizedError } from 'routing-controllers'; /** * Error to throw when a jwt is expired @@ -65,4 +65,15 @@ export class PasswordNeededError extends NotAcceptableError { @IsString() message = "no password is provided - you need to provide it" +} + +/** + * Error to thow when no user could be found for provided credential + */ +export class UserNotFoundError extends NotFoundError { + @IsString() + name = "UserNotFoundError" + + @IsString() + message = "no user could be found for provided credential" } \ No newline at end of file From 6244c969afac300183339aaf0dac77d213947d2b Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 23:03:10 +0100 Subject: [PATCH 131/194] integrate UserNotFoundError ref #12 --- src/models/creation/CreateAuth.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 4e28229..5e7cd9f 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -1,6 +1,6 @@ import { IsEmail, IsOptional, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { PasswordNeededError } from '../../errors/AuthError'; +import { PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { Auth } from '../entities/Auth'; import { User } from '../entities/User'; @@ -24,8 +24,11 @@ export class CreateAuth { if (!this.password) { throw new PasswordNeededError() } - const found = await getConnectionManager().get().getRepository(User).find({ where: [{ username: this.username }, { email: this.email }] }); - console.log(found); + const found_users = await getConnectionManager().get().getRepository(User).find({ where: [{ username: this.username }, { email: this.email }] }); + console.log(found_users); + if (found_users.length === 0) { + throw new UserNotFoundError() + } else { // TODO: jwt creation newAuth.access_token = "" @@ -33,7 +36,8 @@ export class CreateAuth { newAuth.access_token_expires_at = 1587349200 newAuth.refresh_token_expires_at = 1587349200 - console.log(newAuth) + console.log(newAuth) + } return newAuth; } } \ No newline at end of file From 6ae0c1b95526530968198ebf78b10ab1e71a8caa Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Fri, 4 Dec 2020 23:03:24 +0100 Subject: [PATCH 132/194] first jwt generation ref #12 --- src/models/creation/CreateAuth.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 5e7cd9f..a1a8884 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -1,4 +1,5 @@ import { IsEmail, IsOptional, IsString } from 'class-validator'; +import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; import { PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; @@ -29,12 +30,14 @@ export class CreateAuth { if (found_users.length === 0) { throw new UserNotFoundError() } else { + const found_user = found_users[0] + const token = jsonwebtoken.sign(found_user, "sec") - // TODO: jwt creation - newAuth.access_token = "" - newAuth.refresh_token = "" - newAuth.access_token_expires_at = 1587349200 - newAuth.refresh_token_expires_at = 1587349200 + // TODO: jwt creation + newAuth.access_token = token + newAuth.refresh_token = token + newAuth.access_token_expires_at = 1587349200 + newAuth.refresh_token_expires_at = 1587349200 console.log(newAuth) } From 74ee77f814c269718cb99847e4a43600fc52b41f Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 09:48:01 +0100 Subject: [PATCH 133/194] Cleaned up some relations for users ref #14 --- src/models/entities/User.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 7da56db..762ec92 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -81,13 +81,15 @@ export class User { /** * permissions */ + @IsOptional() @ManyToOne(() => Permission, permission => permission.users, { nullable: true }) permissions: Permission[]; /** * groups */ - @ManyToMany(() => UserGroup) + @IsOptional() + @ManyToMany(() => UserGroup, { nullable: true }) @JoinTable() groups: UserGroup[]; @@ -96,7 +98,7 @@ export class User { */ @Column() @IsBoolean() - enabled: boolean; + enabled: boolean = true; /** * jwt refresh count @@ -110,12 +112,13 @@ export class User { */ @Column() @IsString() - profilepic: string; + profilePic: string; /** * actions */ - @OneToMany(() => UserAction, action => action.user) + @IsOptional() + @OneToMany(() => UserAction, action => action.user, { nullable: true }) actions: UserAction /** From dadaacfaaeed6f4cc1a1c01abc6428601708fbcb Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 10:16:47 +0100 Subject: [PATCH 134/194] first part of the user class cleanuo ref #11 #14 --- src/models/creation/CreateUser.ts | 16 ++++++++++++---- src/models/entities/User.ts | 32 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index 81b0b1e..a27071d 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -9,21 +9,30 @@ import { UserGroup } from '../entities/UserGroup'; export class CreateUser { @IsString() firstname: string; + @IsString() + @IsOptional() middlename?: string; + @IsOptional() @IsString() username?: string; + @IsPhoneNumber("ZZ") @IsOptional() phone?: string; + @IsString() password: string; + @IsString() lastname: string; + @IsEmail() @IsString() + @IsOptional() email?: string; + @IsOptional() groupId?: number[] | number @@ -58,15 +67,14 @@ export class CreateUser { } } - const new_uuid = uuid.v4() - newUser.email = this.email newUser.username = this.username newUser.firstname = this.firstname newUser.middlename = this.middlename newUser.lastname = this.lastname - newUser.uuid = new_uuid - newUser.password = await argon2.hash(this.password + new_uuid); + newUser.uuid = uuid.v4() + newUser.phone = this.phone + newUser.password = await argon2.hash(this.password + newUser.uuid); console.log(newUser) return newUser; diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 762ec92..1d2065e 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -13,7 +13,6 @@ export class User { * autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; @@ -21,30 +20,30 @@ export class User { * uuid */ @Column() - @IsUUID("4") + @IsUUID(4) uuid: string; /** * user email */ - @Column() + @Column({ nullable: true }) @IsEmail() - email: string; + email?: string; /** * user phone */ - @Column() - @IsPhoneNumber("ZZ") + @Column({ nullable: true }) @IsOptional() - phone: string; + @IsPhoneNumber("ZZ") + phone?: string; /** * username */ - @Column() + @Column({ nullable: true }) @IsString() - username: string; + username?: string; /** * firstname @@ -57,10 +56,10 @@ export class User { /** * middlename */ - @Column() + @Column({ nullable: true }) @IsString() @IsOptional() - middlename: string; + middlename?: string; /** * lastname @@ -83,7 +82,7 @@ export class User { */ @IsOptional() @ManyToOne(() => Permission, permission => permission.users, { nullable: true }) - permissions: Permission[]; + permissions?: Permission[]; /** * groups @@ -105,21 +104,22 @@ export class User { */ @IsInt() @Column({ default: 1 }) - refreshTokenCount: number; + refreshTokenCount?: number; /** * profilepic */ - @Column() + @Column({ nullable: true }) @IsString() - profilePic: string; + @IsOptional() + profilePic?: string; /** * actions */ @IsOptional() @OneToMany(() => UserAction, action => action.user, { nullable: true }) - actions: UserAction + actions: UserAction[] /** * calculate all permissions From a42595bd15ec7646b5b18758aee4c178857e1427 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 10:23:53 +0100 Subject: [PATCH 135/194] Cleaned up the createUser a little bit ref #11 #14 --- src/models/creation/CreateUser.ts | 58 +++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/src/models/creation/CreateUser.ts b/src/models/creation/CreateUser.ts index a27071d..fb7841a 100644 --- a/src/models/creation/CreateUser.ts +++ b/src/models/creation/CreateUser.ts @@ -7,35 +7,71 @@ import { User } from '../entities/User'; import { UserGroup } from '../entities/UserGroup'; export class CreateUser { + /** + * The new user's first name. + */ @IsString() firstname: string; + /** + * The new user's middle name. + * Optinal. + */ @IsString() @IsOptional() middlename?: string; + /** + * The new user's last name. + */ + @IsString() + lastname: string; + + /** + * The new user's username. + * You have to provide at least one of: {email, username}. + */ @IsOptional() @IsString() username?: string; - @IsPhoneNumber("ZZ") - @IsOptional() - phone?: string; - - @IsString() - password: string; - - @IsString() - lastname: string; - + /** + * The new user's email address. + * You have to provide at least one of: {email, username}. + */ @IsEmail() @IsString() @IsOptional() email?: string; + /** + * The new user's phone number. + * Optional + */ + @IsPhoneNumber("ZZ") + @IsOptional() + phone?: string; + + /** + * The new user's password. + * This will of course not be saved in plaintext :) + */ + @IsString() + password: string; + + /** + * The new user's groups' id(s). + * You can provide either one groupId or an array of groupIDs. + * Optional. + */ @IsOptional() groupId?: number[] | number + //TODO: ProfilePics + + /** + * Converts this to a User Entity. + */ public async toUser(): Promise { let newUser: User = new User(); @@ -75,8 +111,8 @@ export class CreateUser { newUser.uuid = uuid.v4() newUser.phone = this.phone newUser.password = await argon2.hash(this.password + newUser.uuid); + //TODO: ProfilePics - console.log(newUser) return newUser; } } \ No newline at end of file From 109e145a1970952a2375df9f1f6cf57f056982ff Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 10:31:24 +0100 Subject: [PATCH 136/194] Cleaned up the createUserGroup a little bit ref #11 #14 --- src/models/creation/CreateUserGroup.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/models/creation/CreateUserGroup.ts b/src/models/creation/CreateUserGroup.ts index d55618c..959bea5 100644 --- a/src/models/creation/CreateUserGroup.ts +++ b/src/models/creation/CreateUserGroup.ts @@ -1,28 +1,30 @@ import { IsOptional, IsString } from 'class-validator'; -import { GroupNameNeededError } from '../../errors/UserGroupErrors'; import { UserGroup } from '../entities/UserGroup'; export class CreateUserGroup { - @IsOptional() + /** + * The new group's name. + */ @IsString() name: string; + + /** + * The new group's description. + * Optinal. + */ @IsOptional() @IsString() description?: string; + /** + * Converts this to a UserGroup entity. + */ public async toUserGroup(): Promise { let newUserGroup: UserGroup = new UserGroup(); - if (this.name === undefined) { - throw new GroupNameNeededError(); - } + newUserGroup.name = this.name; + newUserGroup.description = this.description; - newUserGroup.name = this.name - if (this.description) { - newUserGroup.description = this.description - } - - console.log(newUserGroup) return newUserGroup; } } \ No newline at end of file From a1105f06abf28d72b691c9431fd54083a82d8318 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 10:43:28 +0100 Subject: [PATCH 137/194] Cleaned up a load of relations and optional stuff ref #11 --- src/models/entities/Address.ts | 9 ++++---- src/models/entities/DistanceDonation.ts | 2 +- src/models/entities/Donation.ts | 6 ++---- src/models/entities/Donor.ts | 2 +- src/models/entities/FixedDonation.ts | 4 ++-- src/models/entities/GroupContact.ts | 28 +++++++++---------------- src/models/entities/Participant.ts | 7 +++---- src/models/entities/Permission.ts | 7 +++---- src/models/entities/Runner.ts | 2 +- src/models/entities/RunnerCard.ts | 5 ++--- src/models/entities/RunnerGroup.ts | 3 +-- src/models/entities/RunnerTeam.ts | 2 +- src/models/entities/Scan.ts | 19 +++++------------ src/models/entities/ScanStation.ts | 9 ++++---- src/models/entities/Track.ts | 7 +++---- src/models/entities/TrackScan.ts | 12 +++++------ src/models/entities/UserAction.ts | 5 ++--- src/models/entities/UserGroup.ts | 1 - 18 files changed, 50 insertions(+), 80 deletions(-) diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index 735ecd3..6cc1d11 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -1,11 +1,11 @@ -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, IsPostalCode, - IsString, + IsString } from "class-validator"; +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Participant } from "./Participant"; import { RunnerOrganisation } from "./RunnerOrganisation"; @@ -18,14 +18,13 @@ export class Address { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; /** * The address's description. */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsString() @IsOptional() description?: string; @@ -43,7 +42,7 @@ export class Address { * The address's second line. * Containing optional information. */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsString() @IsOptional() address2?: string; diff --git a/src/models/entities/DistanceDonation.ts b/src/models/entities/DistanceDonation.ts index d5610d7..9051849 100644 --- a/src/models/entities/DistanceDonation.ts +++ b/src/models/entities/DistanceDonation.ts @@ -13,7 +13,7 @@ export class DistanceDonation extends Donation { * The runner associated. */ @IsNotEmpty() - @ManyToOne(() => Runner, runner => runner.distanceDonations, { nullable: true }) + @ManyToOne(() => Runner, runner => runner.distanceDonations) runner: Runner; /** diff --git a/src/models/entities/Donation.ts b/src/models/entities/Donation.ts index 2609a98..4af3485 100644 --- a/src/models/entities/Donation.ts +++ b/src/models/entities/Donation.ts @@ -1,7 +1,6 @@ import { IsInt, - IsNotEmpty, - IsOptional + IsNotEmpty } from "class-validator"; import { Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; import { Participant } from "./Participant"; @@ -16,7 +15,6 @@ export abstract class Donation { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; @@ -24,7 +22,7 @@ export abstract class Donation { * The donations's donor. */ @IsNotEmpty() - @ManyToOne(() => Participant, donor => donor.donations, { nullable: true }) + @ManyToOne(() => Participant, donor => donor.donations) donor: Participant; /** diff --git a/src/models/entities/Donor.ts b/src/models/entities/Donor.ts index ca65afd..b92b06a 100644 --- a/src/models/entities/Donor.ts +++ b/src/models/entities/Donor.ts @@ -1,5 +1,5 @@ -import { Entity, Column, ChildEntity } from "typeorm"; import { IsBoolean } from "class-validator"; +import { ChildEntity, Column } from "typeorm"; import { Participant } from "./Participant"; /** diff --git a/src/models/entities/FixedDonation.ts b/src/models/entities/FixedDonation.ts index dd05c40..8ab366d 100644 --- a/src/models/entities/FixedDonation.ts +++ b/src/models/entities/FixedDonation.ts @@ -1,5 +1,5 @@ -import { Entity, Column, ChildEntity } from "typeorm"; -import { IsInt, IsPositive, } from "class-validator"; +import { IsInt, IsPositive } from "class-validator"; +import { ChildEntity, Column } from "typeorm"; import { Donation } from "./Donation"; /** diff --git a/src/models/entities/GroupContact.ts b/src/models/entities/GroupContact.ts index 5d12066..c7c8f18 100644 --- a/src/models/entities/GroupContact.ts +++ b/src/models/entities/GroupContact.ts @@ -1,15 +1,14 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity } from "typeorm"; import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, - IsPositive, - IsString, + + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Address } from "./Address"; -import { Donation } from "./Donation"; import { RunnerGroup } from "./RunnerGroup"; /** @@ -17,11 +16,10 @@ import { RunnerGroup } from "./RunnerGroup"; */ @Entity() export class GroupContact { - /** - * Autogenerated unique id (primary key). - */ + /** + * Autogenerated unique id (primary key). + */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; @@ -37,7 +35,7 @@ export class GroupContact { * The contact's middle name. * Optional */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsString() middlename?: string; @@ -62,7 +60,7 @@ export class GroupContact { * The contact's phone number. * Optional */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsPhoneNumber("DE") phone?: string; @@ -71,19 +69,13 @@ export class GroupContact { * The contact's email address. * Optional */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsEmail() email?: string; /** - * Used to link the contact as the donor of a donation. - */ - @OneToMany(() => Donation, donation => donation.donor, { nullable: true }) - donations: Donation[]; - - /** - * Used to link runners to donations. + * Used to link contacts to groups. */ @OneToMany(() => RunnerGroup, group => group.contact, { nullable: true }) groups: RunnerGroup[]; diff --git a/src/models/entities/Participant.ts b/src/models/entities/Participant.ts index 53562a3..2e40f5d 100644 --- a/src/models/entities/Participant.ts +++ b/src/models/entities/Participant.ts @@ -1,13 +1,13 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, - IsPositive, - IsString, + + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; import { Address } from "./Address"; import { Donation } from "./Donation"; @@ -21,7 +21,6 @@ export abstract class Participant { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; diff --git a/src/models/entities/Permission.ts b/src/models/entities/Permission.ts index 751efe3..b9e2777 100644 --- a/src/models/entities/Permission.ts +++ b/src/models/entities/Permission.ts @@ -1,10 +1,10 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, - IsOptional, - IsString, + + IsString } from "class-validator"; +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { User } from './User'; import { UserGroup } from './UserGroup'; /** @@ -16,7 +16,6 @@ export abstract class Permission { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index 7a8d0ad..5454f64 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -15,7 +15,7 @@ export class Runner extends Participant { * The runner's associated group. */ @IsNotEmpty() - @ManyToOne(() => RunnerGroup, group => group.runners, { nullable: true }) + @ManyToOne(() => RunnerGroup, group => group.runners, { nullable: false }) group: RunnerGroup; /** diff --git a/src/models/entities/RunnerCard.ts b/src/models/entities/RunnerCard.ts index 0ba0428..49ea0da 100644 --- a/src/models/entities/RunnerCard.ts +++ b/src/models/entities/RunnerCard.ts @@ -1,12 +1,12 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; import { IsBoolean, IsEAN, IsInt, IsNotEmpty, IsOptional, - IsString, + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Runner } from "./Runner"; import { TrackScan } from "./TrackScan"; @@ -19,7 +19,6 @@ export class RunnerCard { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; diff --git a/src/models/entities/RunnerGroup.ts b/src/models/entities/RunnerGroup.ts index 2033d88..c042384 100644 --- a/src/models/entities/RunnerGroup.ts +++ b/src/models/entities/RunnerGroup.ts @@ -18,12 +18,11 @@ export abstract class RunnerGroup { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; /** - * The group's first name. + * The group's name. */ @Column() @IsNotEmpty() diff --git a/src/models/entities/RunnerTeam.ts b/src/models/entities/RunnerTeam.ts index 44a047a..21cdcb4 100644 --- a/src/models/entities/RunnerTeam.ts +++ b/src/models/entities/RunnerTeam.ts @@ -15,7 +15,7 @@ export class RunnerTeam extends RunnerGroup { * Optional */ @IsNotEmpty() - @ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: true }) + @ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: false }) parentGroup?: RunnerOrganisation; /** diff --git a/src/models/entities/Scan.ts b/src/models/entities/Scan.ts index 75ec65d..b8fc079 100644 --- a/src/models/entities/Scan.ts +++ b/src/models/entities/Scan.ts @@ -1,11 +1,11 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, TableInheritance } from "typeorm"; import { IsBoolean, IsInt, IsNotEmpty, - IsOptional, - IsPositive, + + IsPositive } from "class-validator"; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; import { Runner } from "./Runner"; /** @@ -18,7 +18,6 @@ export abstract class Scan { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; @@ -26,7 +25,7 @@ export abstract class Scan { * The associated runner. */ @IsNotEmpty() - @ManyToOne(() => Runner, runner => runner.scans, { nullable: true }) + @ManyToOne(() => Runner, runner => runner.scans, { nullable: false }) runner: Runner; /** @@ -43,12 +42,4 @@ export abstract class Scan { @Column() @IsBoolean() valid: boolean = true; - - /** - * seconds since last scan - */ - @IsInt() - @IsOptional() - secondsSinceLastScan: number; - -} +} \ No newline at end of file diff --git a/src/models/entities/ScanStation.ts b/src/models/entities/ScanStation.ts index c529b25..f014da8 100644 --- a/src/models/entities/ScanStation.ts +++ b/src/models/entities/ScanStation.ts @@ -1,11 +1,11 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from "typeorm"; import { IsBoolean, IsInt, IsNotEmpty, IsOptional, - IsString, + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Track } from "./Track"; import { TrackScan } from "./TrackScan"; @@ -18,14 +18,13 @@ export class ScanStation { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; /** * The station's description. */ - @Column({nullable: true}) + @Column({ nullable: true }) @IsOptional() @IsString() description?: string; @@ -34,7 +33,7 @@ export class ScanStation { * The track this station is associated with. */ @IsNotEmpty() - @ManyToOne(() => Track, track => track.stations, { nullable: true }) + @ManyToOne(() => Track, track => track.stations, { nullable: false }) track: Track; /** diff --git a/src/models/entities/Track.ts b/src/models/entities/Track.ts index ad6f476..34f3bb6 100644 --- a/src/models/entities/Track.ts +++ b/src/models/entities/Track.ts @@ -1,11 +1,11 @@ -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; import { IsInt, IsNotEmpty, - IsOptional, + IsPositive, - IsString, + IsString } from "class-validator"; +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { ScanStation } from "./ScanStation"; import { TrackScan } from "./TrackScan"; @@ -18,7 +18,6 @@ export class Track { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number;; diff --git a/src/models/entities/TrackScan.ts b/src/models/entities/TrackScan.ts index f7bee32..759c3d7 100644 --- a/src/models/entities/TrackScan.ts +++ b/src/models/entities/TrackScan.ts @@ -1,17 +1,15 @@ -import { PrimaryGeneratedColumn, Column, ManyToOne, Entity, ChildEntity } from "typeorm"; import { - IsBoolean, IsDateString, IsInt, IsNotEmpty, - IsOptional, - IsPositive, + + IsPositive } from "class-validator"; -import { Scan } from "./Scan"; -import { Runner } from "./Runner"; -import { Track } from "./Track"; +import { ChildEntity, Column, ManyToOne } from "typeorm"; import { RunnerCard } from "./RunnerCard"; +import { Scan } from "./Scan"; import { ScanStation } from "./ScanStation"; +import { Track } from "./Track"; /** * Defines the scan interface. diff --git a/src/models/entities/UserAction.ts b/src/models/entities/UserAction.ts index c9caf60..92c41f0 100644 --- a/src/models/entities/UserAction.ts +++ b/src/models/entities/UserAction.ts @@ -1,10 +1,10 @@ -import { PrimaryGeneratedColumn, Column, OneToMany, Entity, ManyToOne } from "typeorm"; import { IsInt, IsNotEmpty, IsOptional, - IsString, + IsString } from "class-validator"; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; import { User } from './User'; /** @@ -16,7 +16,6 @@ export class UserAction { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; diff --git a/src/models/entities/UserGroup.ts b/src/models/entities/UserGroup.ts index 2156d14..e86b2a6 100644 --- a/src/models/entities/UserGroup.ts +++ b/src/models/entities/UserGroup.ts @@ -16,7 +16,6 @@ export class UserGroup { * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() - @IsOptional() @IsInt() id: number; From aca13f730865e3660bdc14574af71d1ccfd7e5ad Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 10:59:15 +0100 Subject: [PATCH 138/194] Fixed bugs concerning posts ref #13 --- src/controllers/RunnerController.ts | 3 ++- src/controllers/RunnerOrganisationController.ts | 3 +-- src/models/entities/DistanceDonation.ts | 12 ++---------- src/models/entities/Runner.ts | 17 +++++------------ src/models/entities/RunnerTeam.ts | 2 +- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 14658da..96853cc 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -55,7 +55,8 @@ export class RunnerController { return error; } - return new ResponseRunner(await this.runnerRepository.save(runner)); + runner = await this.runnerRepository.save(runner) + return new ResponseRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] })); } @Put('/:id') diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index bed6eec..451a6e7 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -58,9 +58,8 @@ export class RunnerOrganisationController { } runnerOrganisation = await this.runnerOrganisationRepository.save(runnerOrganisation); - runnerOrganisation = await this.runnerOrganisationRepository.findOne(runnerOrganisation, { relations: ['address', 'contact', 'teams'] }); - return new ResponseRunnerOrganisation(runnerOrganisation); + return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne(runnerOrganisation, { relations: ['address', 'contact', 'teams'] })); } @Put('/:id') diff --git a/src/models/entities/DistanceDonation.ts b/src/models/entities/DistanceDonation.ts index 9051849..3103865 100644 --- a/src/models/entities/DistanceDonation.ts +++ b/src/models/entities/DistanceDonation.ts @@ -28,18 +28,10 @@ export class DistanceDonation extends 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. */ - @IsInt() - public get amount() { - return this.getAmount(); - } - - /** - * The function that calculates the amount based on the runner object's distance. - */ - public async getAmount(): Promise { + public get amount(): number { let calculatedAmount = -1; try { - calculatedAmount = this.amountPerDistance * await this.runner.distance(); + calculatedAmount = this.amountPerDistance * this.runner.distance; } catch (error) { throw error; } diff --git a/src/models/entities/Runner.ts b/src/models/entities/Runner.ts index 5454f64..6f4033a 100644 --- a/src/models/entities/Runner.ts +++ b/src/models/entities/Runner.ts @@ -1,5 +1,5 @@ import { IsInt, IsNotEmpty } from "class-validator"; -import { ChildEntity, getConnectionManager, ManyToOne, OneToMany } from "typeorm"; +import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; import { DistanceDonation } from "./DistanceDonation"; import { Participant } from "./Participant"; import { RunnerCard } from "./RunnerCard"; @@ -36,25 +36,18 @@ export class Runner extends Participant { @OneToMany(() => Scan, scan => scan.runner, { nullable: true }) scans: Scan[]; - /** - * 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 }); + public get validScans(): Scan[] { + return this.scans.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); + public get distance(): number { + return this.validScans.reduce((sum, current) => sum + current.distance, 0); } } \ No newline at end of file diff --git a/src/models/entities/RunnerTeam.ts b/src/models/entities/RunnerTeam.ts index 21cdcb4..44a047a 100644 --- a/src/models/entities/RunnerTeam.ts +++ b/src/models/entities/RunnerTeam.ts @@ -15,7 +15,7 @@ export class RunnerTeam extends RunnerGroup { * Optional */ @IsNotEmpty() - @ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: false }) + @ManyToOne(() => RunnerOrganisation, org => org.teams, { nullable: true }) parentGroup?: RunnerOrganisation; /** From b8bc39d691d10c13e60dc7bd3709d413dcf8b8b6 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 11:14:06 +0100 Subject: [PATCH 139/194] =?UTF-8?q?=F0=9F=9A=A7=20User=20-=20mark=20column?= =?UTF-8?q?s=20as=20unique?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #11 #12 --- src/models/entities/User.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 1d2065e..8e29e27 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -19,14 +19,14 @@ export class User { /** * uuid */ - @Column() + @Column({ unique: true }) @IsUUID(4) uuid: string; /** * user email */ - @Column({ nullable: true }) + @Column({ nullable: true, unique: true }) @IsEmail() email?: string; @@ -41,7 +41,7 @@ export class User { /** * username */ - @Column({ nullable: true }) + @Column({ nullable: true, unique: true }) @IsString() username?: string; @@ -109,7 +109,7 @@ export class User { /** * profilepic */ - @Column({ nullable: true }) + @Column({ nullable: true, unique: true }) @IsString() @IsOptional() profilePic?: string; From d46ad5954676e73ab3c77f2d84c87b22088f4e71 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 11:14:26 +0100 Subject: [PATCH 140/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateAuth=20now=20retur?= =?UTF-8?q?ns=20a=20sample=20jwt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/CreateAuth.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index a1a8884..b2c9727 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -12,6 +12,7 @@ export class CreateAuth { username?: string; @IsString() password: string; + @IsOptional() @IsEmail() @IsString() email?: string; @@ -26,20 +27,17 @@ export class CreateAuth { throw new PasswordNeededError() } const found_users = await getConnectionManager().get().getRepository(User).find({ where: [{ username: this.username }, { email: this.email }] }); - console.log(found_users); if (found_users.length === 0) { throw new UserNotFoundError() } else { const found_user = found_users[0] - const token = jsonwebtoken.sign(found_user, "sec") - - // TODO: jwt creation + // TODO: proper jwt creation + const token = jsonwebtoken.sign({}, "securekey") newAuth.access_token = token newAuth.refresh_token = token newAuth.access_token_expires_at = 1587349200 newAuth.refresh_token_expires_at = 1587349200 - console.log(newAuth) } return newAuth; } From bd0c7ce04213a297cf13f85ac63de34785796306 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 11:18:12 +0100 Subject: [PATCH 141/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateAuth=20-=20credent?= =?UTF-8?q?ial=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/CreateAuth.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index b2c9727..564d6f5 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -1,7 +1,8 @@ +import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; -import { PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; +import { InvalidCredentialsError, PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { Auth } from '../entities/Auth'; import { User } from '../entities/User'; @@ -31,12 +32,23 @@ export class CreateAuth { throw new UserNotFoundError() } else { const found_user = found_users[0] - // TODO: proper jwt creation - const token = jsonwebtoken.sign({}, "securekey") - newAuth.access_token = token - newAuth.refresh_token = token - newAuth.access_token_expires_at = 1587349200 - newAuth.refresh_token_expires_at = 1587349200 + console.log(found_user.password); + // try { + if (await argon2.verify(found_user.password, this.password + found_user.uuid)) { + // password match + // TODO: proper jwt creation + const token = jsonwebtoken.sign({}, "securekey") + newAuth.access_token = token + newAuth.refresh_token = token + newAuth.access_token_expires_at = 1587349200 + newAuth.refresh_token_expires_at = 1587349200 + } else { + // password did not match + throw new InvalidCredentialsError() + } + // } catch (err) { + // // internal failure + // } } return newAuth; From 2a1b65f4249defb535f8464cf5ec3a63e4dd44bb Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 11:22:45 +0100 Subject: [PATCH 142/194] =?UTF-8?q?=F0=9F=9A=A7AuthController=20-=20add=20?= =?UTF-8?q?all=20Error=20response=20schemas=20to=20post?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/controllers/AuthController.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index 8a39093..afa677b 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -1,6 +1,6 @@ import { Body, JsonController, Post } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { InvalidCredentialsError } from '../errors/AuthError'; +import { InvalidCredentialsError, PasswordNeededError, UsernameOrEmailNeededError } from '../errors/AuthError'; import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/creation/CreateAuth'; @@ -12,6 +12,9 @@ export class AuthController { @Post("/login") @ResponseSchema(InvalidCredentialsError) @ResponseSchema(UserNotFoundError) + @ResponseSchema(UsernameOrEmailNeededError) + @ResponseSchema(PasswordNeededError) + @ResponseSchema(InvalidCredentialsError) @OpenAPI({ description: 'Create a new access token object' }) async post(@Body({ validate: true }) createAuth: CreateAuth) { let auth; From 1850dd542d564e91cf55ac9fcdf7902091969163 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 11:22:59 +0100 Subject: [PATCH 143/194] =?UTF-8?q?=F0=9F=A7=B9=20clean=20up=20CreateAuth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/CreateAuth.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 564d6f5..0ba27af 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -33,9 +33,7 @@ export class CreateAuth { } else { const found_user = found_users[0] console.log(found_user.password); - // try { if (await argon2.verify(found_user.password, this.password + found_user.uuid)) { - // password match // TODO: proper jwt creation const token = jsonwebtoken.sign({}, "securekey") newAuth.access_token = token @@ -43,13 +41,8 @@ export class CreateAuth { newAuth.access_token_expires_at = 1587349200 newAuth.refresh_token_expires_at = 1587349200 } else { - // password did not match throw new InvalidCredentialsError() } - // } catch (err) { - // // internal failure - // } - } return newAuth; } From 9c63a34fe13ffed8e35371c42617a43814f7d16c Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 11:36:33 +0100 Subject: [PATCH 144/194] Little bugfix ref #13 --- src/controllers/RunnerController.ts | 2 +- src/controllers/RunnerTeamController.ts | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 96853cc..0c49bea 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -84,7 +84,7 @@ export class RunnerController { @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) async remove(@Param('id') id: number, @QueryParam("force") force: boolean) { - let runner = await this.runnerRepository.findOne({ id: id }); + const runner = await this.runnerRepository.findOne({ id: id }); if (!runner) { throw new RunnerNotFoundError(); diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 6103083..8abc0ff 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -4,7 +4,6 @@ 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 { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam'; import { RunnerController } from './RunnerController'; @@ -89,21 +88,20 @@ 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 }, { relations: ['parentGroup', 'contact'] }); + let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact', 'runners'] }); if (!runnerTeam) { throw new RunnerTeamNotFoundError(); } - let runners: Runner[] = await runnerTeam.getRunners() if (!force) { - if (runners.length != 0) { + if (runnerTeam.runners.length != 0) { throw new RunnerTeamHasRunnersError(); } } const runnerController = new RunnerController() - runners.forEach(runner => { - runnerController.remove(runner.id, true) + await runnerTeam.runners.forEach(async runner => { + await runnerController.remove(runner.id, true); }); const responseTeam = new ResponseRunnerTeam(runnerTeam); From 45675b06994d6754fa0106f46353b48041aaba13 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 12:15:51 +0100 Subject: [PATCH 145/194] All things deletion for runner* now are clean af and cascadeing ref #13 --- src/controllers/RunnerController.ts | 10 ++--- .../RunnerOrganisationController.ts | 41 +++++++++---------- src/controllers/RunnerTeamController.ts | 14 +++---- src/models/entities/RunnerGroup.ts | 2 - src/models/entities/RunnerOrganisation.ts | 26 +----------- src/models/entities/RunnerTeam.ts | 10 +---- 6 files changed, 33 insertions(+), 70 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 0c49bea..026b4a6 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -1,7 +1,7 @@ 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 { EntityFromBody, EntityFromParam } 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'; @@ -65,7 +65,7 @@ export class RunnerController { @ResponseSchema(RunnerIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update a runner object (id can't be changed)." }) async put(@Param('id') id: number, @EntityFromBody() runner: Runner) { - let oldRunner = await this.runnerRepository.findOne({ id: id }); + let oldRunner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] }); if (!oldRunner) { throw new RunnerNotFoundError(); @@ -83,14 +83,14 @@ export class RunnerController { @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) { - const runner = await this.runnerRepository.findOne({ id: id }); + async remove(@EntityFromParam('id') runner: Runner, @QueryParam("force") force: boolean) { + const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] }); if (!runner) { throw new RunnerNotFoundError(); } await this.runnerRepository.delete(runner); - return new ResponseRunner(runner); + return new ResponseRunner(responseRunner); } } diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 451a6e7..127cb55 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -1,12 +1,10 @@ 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 { EntityFromBody, EntityFromParam } 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 { ResponseRunnerOrganisation } from '../models/responses/ResponseRunnerOrganisation'; import { RunnerController } from './RunnerController'; import { RunnerTeamController } from './RunnerTeamController'; @@ -87,39 +85,38 @@ export class RunnerOrganisationController { @Delete('/:id') @ResponseSchema(ResponseRunnerOrganisation) @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) + @ResponseSchema(RunnerOrganisationHasTeamsError, { statusCode: 406 }) @ResponseSchema(RunnerOrganisationHasRunnersError, { 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'] }); + async remove(@EntityFromParam('id') organisation: RunnerOrganisation, @QueryParam("force") force: boolean) { + let runnerOrganisation = await this.runnerOrganisationRepository.findOne(organisation, { relations: ['address', 'contact', 'runners', 'teams'] }); if (!runnerOrganisation) { throw new RunnerOrganisationNotFoundError(); } - let runners: Runner[] = await runnerOrganisation.getRunners() if (!force) { - if (runners.length != 0) { - throw new RunnerOrganisationHasRunnersError(); - } - } - const runnerController = new RunnerController() - runners.forEach(runner => { - runnerController.remove(runner.id, true) - }); - - let teams: RunnerTeam[] = await runnerOrganisation.getTeams() - if (!force) { - if (teams.length != 0) { + if (runnerOrganisation.teams.length != 0) { throw new RunnerOrganisationHasTeamsError(); } } const teamController = new RunnerTeamController() - teams.forEach(team => { - teamController.remove(team.id, true) - }); + for (let team of runnerOrganisation.teams) { + await teamController.remove(team, true); + } + + if (!force) { + if (runnerOrganisation.runners.length != 0) { + throw new RunnerOrganisationHasRunnersError(); + } + } + const runnerController = new RunnerController() + for (let runner of runnerOrganisation.runners) { + await runnerController.remove(runner, true); + } const responseOrganisation = new ResponseRunnerOrganisation(runnerOrganisation); - await this.runnerOrganisationRepository.delete({ id: runnerOrganisation.id }); + await this.runnerOrganisationRepository.delete(organisation); return responseOrganisation; } } diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 8abc0ff..b0061b5 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -1,7 +1,7 @@ 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 { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; import { CreateRunnerTeam } from '../models/creation/CreateRunnerTeam'; import { RunnerTeam } from '../models/entities/RunnerTeam'; @@ -87,8 +87,8 @@ 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: boolean) { - let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact', 'runners'] }); + async remove(@EntityFromParam('id') team: RunnerTeam, @QueryParam("force") force: boolean) { + let runnerTeam = await this.runnerTeamRepository.findOne(team, { relations: ['parentGroup', 'contact', 'runners'] }); if (!runnerTeam) { throw new RunnerTeamNotFoundError(); @@ -100,12 +100,12 @@ export class RunnerTeamController { } } const runnerController = new RunnerController() - await runnerTeam.runners.forEach(async runner => { - await runnerController.remove(runner.id, true); - }); + for (let runner of runnerTeam.runners) { + await runnerController.remove(runner, true); + } const responseTeam = new ResponseRunnerTeam(runnerTeam); - await this.runnerTeamRepository.delete({ id: runnerTeam.id }); + await this.runnerTeamRepository.delete(team); return responseTeam; } } diff --git a/src/models/entities/RunnerGroup.ts b/src/models/entities/RunnerGroup.ts index c042384..a2bd585 100644 --- a/src/models/entities/RunnerGroup.ts +++ b/src/models/entities/RunnerGroup.ts @@ -42,6 +42,4 @@ 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 bfdfe49..28b90d8 100644 --- a/src/models/entities/RunnerOrganisation.ts +++ b/src/models/entities/RunnerOrganisation.ts @@ -1,7 +1,6 @@ import { IsOptional } from "class-validator"; -import { ChildEntity, getConnectionManager, ManyToOne, OneToMany } from "typeorm"; +import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; import { Address } from "./Address"; -import { Runner } from './Runner'; import { RunnerGroup } from "./RunnerGroup"; import { RunnerTeam } from "./RunnerTeam"; @@ -24,27 +23,4 @@ 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 }); - } } \ No newline at end of file diff --git a/src/models/entities/RunnerTeam.ts b/src/models/entities/RunnerTeam.ts index 44a047a..bcae3fd 100644 --- a/src/models/entities/RunnerTeam.ts +++ b/src/models/entities/RunnerTeam.ts @@ -1,6 +1,5 @@ import { IsNotEmpty } from "class-validator"; -import { ChildEntity, getConnectionManager, ManyToOne } from "typeorm"; -import { Runner } from './Runner'; +import { ChildEntity, ManyToOne } from "typeorm"; import { RunnerGroup } from "./RunnerGroup"; import { RunnerOrganisation } from "./RunnerOrganisation"; @@ -17,11 +16,4 @@ 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() { - return await getConnectionManager().get().getRepository(Runner).find({ group: this }); - } } \ No newline at end of file From 179add80f480cd10f2951d8f7077164da840d4d0 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 12:18:24 +0100 Subject: [PATCH 146/194] Now throwing errors even faster ref #13 --- src/controllers/RunnerController.ts | 1 + src/controllers/RunnerOrganisationController.ts | 5 +---- src/controllers/RunnerTeamController.ts | 5 +---- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 026b4a6..6508f67 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -84,6 +84,7 @@ export class RunnerController { @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) async remove(@EntityFromParam('id') runner: Runner, @QueryParam("force") force: boolean) { + if (!runner) { throw new RunnerNotFoundError(); } const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] }); if (!runner) { diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 127cb55..8cfef6a 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -89,12 +89,9 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisationHasRunnersError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) async remove(@EntityFromParam('id') organisation: RunnerOrganisation, @QueryParam("force") force: boolean) { + if (!organisation) { throw new RunnerOrganisationNotFoundError() } let runnerOrganisation = await this.runnerOrganisationRepository.findOne(organisation, { relations: ['address', 'contact', 'runners', 'teams'] }); - if (!runnerOrganisation) { - throw new RunnerOrganisationNotFoundError(); - } - if (!force) { if (runnerOrganisation.teams.length != 0) { throw new RunnerOrganisationHasTeamsError(); diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index b0061b5..1b7627d 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -88,12 +88,9 @@ export class RunnerTeamController { @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) async remove(@EntityFromParam('id') team: RunnerTeam, @QueryParam("force") force: boolean) { + if (!team) { throw new RunnerTeamNotFoundError(); } let runnerTeam = await this.runnerTeamRepository.findOne(team, { relations: ['parentGroup', 'contact', 'runners'] }); - if (!runnerTeam) { - throw new RunnerTeamNotFoundError(); - } - if (!force) { if (runnerTeam.runners.length != 0) { throw new RunnerTeamHasRunnersError(); From 0e3cf07b9147f32cf3da40e764fea19fb1f52c58 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 12:21:47 +0100 Subject: [PATCH 147/194] TrackController now also deletes based on a entityfromparam ref #13 --- src/controllers/TrackController.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index ae451be..02370fc 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -1,7 +1,7 @@ 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 { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; import { CreateTrack } from '../models/creation/CreateTrack'; import { Track } from '../models/entities/Track'; @@ -74,14 +74,10 @@ export class TrackController { @ResponseSchema(ResponseTrack) @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) @OpenAPI({ description: "Delete a specified track (if it exists)." }) - async remove(@Param('id') id: number) { - let track = await this.trackRepository.findOne({ id: id }); - - if (!track) { - throw new TrackNotFoundError(); - } + async remove(@EntityFromParam('id') track: Track) { + if (!track) { throw new TrackNotFoundError(); } await this.trackRepository.delete(track); return new ResponseTrack(track); } -} +} \ No newline at end of file From 8870b26ce63d6294567863c76d9dcfccd18e681d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 12:24:38 +0100 Subject: [PATCH 148/194] Deletes now work based on EntityFromParam ref #14 --- src/controllers/UserController.ts | 12 +++++------- src/controllers/UserGroupController.ts | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index dbc4d3a..01e743e 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -1,7 +1,7 @@ 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 { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { UserGroupNotFoundError, UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; import { CreateUser } from '../models/creation/CreateUser'; import { User } from '../models/entities/User'; @@ -73,14 +73,12 @@ export class UserController { @ResponseSchema(User) @ResponseSchema(UserNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) - async remove(@Param('id') id: number) { - let runner = await this.userRepository.findOne({ id: id }); - - if (!runner) { + async remove(@EntityFromParam('id') user: User) { + if (!user) { throw new UserNotFoundError(); } - await this.userRepository.delete(runner); - return runner; + await this.userRepository.delete(user); + return user; } } diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index fb158ed..c937f36 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -1,7 +1,7 @@ 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 { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUserGroup } from '../models/creation/CreateUserGroup'; import { UserGroup } from '../models/entities/UserGroup'; @@ -73,14 +73,12 @@ export class UserGroupController { @ResponseSchema(UserGroup) @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) @OpenAPI({ description: 'Delete a specified usergroup (if it exists).' }) - async remove(@Param('id') id: number) { - let userGroup = await this.userGroupsRepository.findOne({ id: id }); - - if (!userGroup) { + async remove(@EntityFromParam('id') group: UserGroup) { + if (!group) { throw new UserGroupNotFoundError(); } - await this.userGroupsRepository.delete(userGroup); - return userGroup; + await this.userGroupsRepository.delete(group); + return group; } } From d23ed002b24ee29b1bfca98b7f5b3eb3ce47c2db Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 12:28:06 +0100 Subject: [PATCH 149/194] =?UTF-8?q?=F0=9F=9A=A7=20JwtNotProvidedError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/errors/AuthError.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index a0de93a..cdaa870 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -76,4 +76,15 @@ export class UserNotFoundError extends NotFoundError { @IsString() message = "no user could be found for provided credential" +} + +/** + * Error to thow when no jwt token was provided + */ +export class JwtNotProvidedError extends NotAcceptableError { + @IsString() + name = "JwtNotProvidedError" + + @IsString() + message = "no jwt token was provided" } \ No newline at end of file From 28c2b862f0a070323ea7eb1d2c0a53c71979e46f Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 12:28:43 +0100 Subject: [PATCH 150/194] =?UTF-8?q?=F0=9F=9A=A7=20AuthController=20with=20?= =?UTF-8?q?multiple=20endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/controllers/AuthController.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index afa677b..d14e574 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -3,6 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { InvalidCredentialsError, PasswordNeededError, UsernameOrEmailNeededError } from '../errors/AuthError'; import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/creation/CreateAuth'; +import { RefreshAuth } from '../models/creation/RefreshAuth'; @JsonController('/auth') export class AuthController { @@ -16,7 +17,7 @@ export class AuthController { @ResponseSchema(PasswordNeededError) @ResponseSchema(InvalidCredentialsError) @OpenAPI({ description: 'Create a new access token object' }) - async post(@Body({ validate: true }) createAuth: CreateAuth) { + async login(@Body({ validate: true }) createAuth: CreateAuth) { let auth; try { auth = await createAuth.toAuth(); @@ -26,4 +27,19 @@ export class AuthController { } return auth } + + @Post("/refresh") + @ResponseSchema(InvalidCredentialsError) + @ResponseSchema(UserNotFoundError) + @OpenAPI({ description: 'refresh a access token' }) + async refresh(@Body({ validate: true }) refreshAuth: RefreshAuth) { + let auth; + try { + auth = await refreshAuth.toAuth(); + console.log(auth); + } catch (error) { + return error; + } + return auth + } } From c33097f7738da74cbd50f3cedd15e1ae173a7e3e Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 12:28:59 +0100 Subject: [PATCH 151/194] first accesstoken generation ref #12 --- src/models/creation/CreateAuth.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 0ba27af..4580869 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -32,14 +32,22 @@ export class CreateAuth { throw new UserNotFoundError() } else { const found_user = found_users[0] - console.log(found_user.password); if (await argon2.verify(found_user.password, this.password + found_user.uuid)) { // TODO: proper jwt creation - const token = jsonwebtoken.sign({}, "securekey") - newAuth.access_token = token - newAuth.refresh_token = token - newAuth.access_token_expires_at = 1587349200 - newAuth.refresh_token_expires_at = 1587349200 + const timestamp_accesstoken_expiry = Math.floor(Date.now() / 1000) + 5 * 60 + newAuth.access_token = jsonwebtoken.sign({ + refreshtokencount: 5, + userdetails: {}, + exp: timestamp_accesstoken_expiry + }, "securekey") + const timestamp_refresh_expiry = Math.floor(Date.now() / 1000) + 10 * 36000 + newAuth.refresh_token = jsonwebtoken.sign({ + refreshtokencount: 5, + userdetails: {}, + exp: timestamp_refresh_expiry + }, "securekey") + newAuth.access_token_expires_at = timestamp_accesstoken_expiry + newAuth.refresh_token_expires_at = timestamp_refresh_expiry } else { throw new InvalidCredentialsError() } From a0fe8c0017d4f594d57e0d3c00305077874f5aa0 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 12:34:07 +0100 Subject: [PATCH 152/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateAuth=20-=20basic?= =?UTF-8?q?=20jwt=20creation=20with=20user=20details?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/CreateAuth.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 4580869..f21c026 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -33,20 +33,20 @@ export class CreateAuth { } else { const found_user = found_users[0] if (await argon2.verify(found_user.password, this.password + found_user.uuid)) { - // TODO: proper jwt creation const timestamp_accesstoken_expiry = Math.floor(Date.now() / 1000) + 5 * 60 + delete found_user.password; newAuth.access_token = jsonwebtoken.sign({ - refreshtokencount: 5, - userdetails: {}, + userdetails: found_user, exp: timestamp_accesstoken_expiry }, "securekey") + newAuth.access_token_expires_at = timestamp_accesstoken_expiry + // const timestamp_refresh_expiry = Math.floor(Date.now() / 1000) + 10 * 36000 newAuth.refresh_token = jsonwebtoken.sign({ refreshtokencount: 5, - userdetails: {}, + userid: found_user.id, exp: timestamp_refresh_expiry }, "securekey") - newAuth.access_token_expires_at = timestamp_accesstoken_expiry newAuth.refresh_token_expires_at = timestamp_refresh_expiry } else { throw new InvalidCredentialsError() From 975d30e411c580d8c6a7e6c87f7d0187b97070f1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 12:39:11 +0100 Subject: [PATCH 153/194] Smoothed out the participant creation process regarting addresses ref #13 --- src/errors/ParticipantErrors.ts | 6 ++-- src/models/creation/CreateParticipant.ts | 40 ++++++++++-------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/errors/ParticipantErrors.ts b/src/errors/ParticipantErrors.ts index de672d8..97c5412 100644 --- a/src/errors/ParticipantErrors.ts +++ b/src/errors/ParticipantErrors.ts @@ -1,12 +1,12 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; -export class ParticipantOnlyOneAddressAllowedError extends NotAcceptableError { +export class ParticipantAddressWrongTypeError extends NotAcceptableError { @IsString() - name = "ParticipantOnlyOneAddressAllowedError" + name = "ParticipantAddressWrongTypeError" @IsString() - message = "Participant's can only have one address! \n You provided an id and address object.." + message = "The participant's address must be either a existing address, new address or a existing adress's id. \n You provided a object of another type." } export class ParticipantAddressNotFoundError extends NotFoundError { diff --git a/src/models/creation/CreateParticipant.ts b/src/models/creation/CreateParticipant.ts index 8436186..c70cf56 100644 --- a/src/models/creation/CreateParticipant.ts +++ b/src/models/creation/CreateParticipant.ts @@ -1,6 +1,6 @@ -import { IsEmail, IsInt, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { ParticipantOnlyOneAddressAllowedError } from '../../errors/ParticipantErrors'; +import { ParticipantAddressNotFoundError, ParticipantAddressWrongTypeError } from '../../errors/ParticipantErrors'; import { Address } from '../entities/Address'; import { CreateAddress } from './CreateAddress'; @@ -45,39 +45,33 @@ export abstract class CreateParticipant { @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. + * Must be of type number (address id), createAddress (new address) or address (existing address) + * Optional. */ @IsObject() - @IsOptional() - address?: CreateAddress; + address?: number | CreateAddress | Address; /** * 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) { + if (this.address === undefined) { return null; } - - if (this.addressId) { - return await getConnectionManager().get().getRepository(Address).findOne({ id: this.addressId }); + if (this.address! instanceof Address) { + return this.address; + } + if (this.address! instanceof CreateAddress) { + return this.address.toAddress(); + } + if (!isNaN(this.address)) { + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new ParticipantAddressNotFoundError; } + return address; } - return this.address.toAddress(); + throw new ParticipantAddressWrongTypeError; } } \ No newline at end of file From 2f902755c47af829ae3eb6447a5cabe3c0017cf4 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 12:55:38 +0100 Subject: [PATCH 154/194] =?UTF-8?q?=F0=9F=9A=A7=20starting=20work=20on=20R?= =?UTF-8?q?efreshAuth?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/RefreshAuth.ts | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/models/creation/RefreshAuth.ts diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts new file mode 100644 index 0000000..16233c4 --- /dev/null +++ b/src/models/creation/RefreshAuth.ts @@ -0,0 +1,36 @@ +import { IsString } from 'class-validator'; +import * as jsonwebtoken from 'jsonwebtoken'; +import { getConnectionManager } from 'typeorm'; +import { IllegalJWTError, JwtNotProvidedError, UserNotFoundError } from '../../errors/AuthError'; +import { Auth } from '../entities/Auth'; +import { User } from '../entities/User'; + +export class RefreshAuth { + @IsString() + token: string; + + public async toAuth(): Promise { + let newAuth: Auth = new Auth(); + if (!this.token || this.token === undefined) { + throw new JwtNotProvidedError() + } + let decoded + try { + decoded = jsonwebtoken.verify(this.token, 'securekey') + } catch (error) { + throw new IllegalJWTError() + } + const found_users = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"], refreshTokenCount: decoded["refreshtokencount"] }); + if (!found_users) { + throw new UserNotFoundError() + } else { + const found_user = found_users[0] + delete found_user.password; + newAuth.access_token = "ja" + newAuth.access_token_expires_at = 5555555 + newAuth.refresh_token = "ja" + newAuth.refresh_token_expires_at = 555555 + } + return newAuth; + } +} \ No newline at end of file From 093f6f5f789d58d54f325fcf02fbd1ab3cf09a95 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 12:59:02 +0100 Subject: [PATCH 155/194] =?UTF-8?q?=F0=9F=9A=A7=20UserNotFoundOrRefreshTok?= =?UTF-8?q?enCountInvalidError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/errors/AuthError.ts | 11 +++++++++++ src/models/creation/RefreshAuth.ts | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index cdaa870..e95670d 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -87,4 +87,15 @@ export class JwtNotProvidedError extends NotAcceptableError { @IsString() message = "no jwt token was provided" +} + +/** + * Error to thow when user was not found or refresh token count was invalid + */ +export class UserNotFoundOrRefreshTokenCountInvalidError extends NotAcceptableError { + @IsString() + name = "UserNotFoundOrRefreshTokenCountInvalidError" + + @IsString() + message = "user was not found or refresh token count was invalid" } \ No newline at end of file diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts index 16233c4..97e5e4e 100644 --- a/src/models/creation/RefreshAuth.ts +++ b/src/models/creation/RefreshAuth.ts @@ -1,7 +1,7 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; -import { IllegalJWTError, JwtNotProvidedError, UserNotFoundError } from '../../errors/AuthError'; +import { IllegalJWTError, JwtNotProvidedError, UserNotFoundOrRefreshTokenCountInvalidError } from '../../errors/AuthError'; import { Auth } from '../entities/Auth'; import { User } from '../entities/User'; @@ -22,7 +22,7 @@ export class RefreshAuth { } const found_users = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"], refreshTokenCount: decoded["refreshtokencount"] }); if (!found_users) { - throw new UserNotFoundError() + throw new UserNotFoundOrRefreshTokenCountInvalidError() } else { const found_user = found_users[0] delete found_user.password; From c0c95056bf0401de0c77032928547c13982c932c Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:06:58 +0100 Subject: [PATCH 156/194] better errors ref #12 --- src/errors/AuthError.ts | 11 +++++++++++ src/models/creation/RefreshAuth.ts | 12 +++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index e95670d..a03ff24 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -98,4 +98,15 @@ export class UserNotFoundOrRefreshTokenCountInvalidError extends NotAcceptableEr @IsString() message = "user was not found or refresh token count was invalid" +} + +/** + * Error to thow when refresh token count was invalid + */ +export class RefreshTokenCountInvalidError extends NotAcceptableError { + @IsString() + name = "RefreshTokenCountInvalidError" + + @IsString() + message = "refresh token count was invalid" } \ No newline at end of file diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts index 97e5e4e..f9010ce 100644 --- a/src/models/creation/RefreshAuth.ts +++ b/src/models/creation/RefreshAuth.ts @@ -1,7 +1,7 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; -import { IllegalJWTError, JwtNotProvidedError, UserNotFoundOrRefreshTokenCountInvalidError } from '../../errors/AuthError'; +import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; import { Auth } from '../entities/Auth'; import { User } from '../entities/User'; @@ -20,16 +20,18 @@ export class RefreshAuth { } catch (error) { throw new IllegalJWTError() } - const found_users = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"], refreshTokenCount: decoded["refreshtokencount"] }); - if (!found_users) { - throw new UserNotFoundOrRefreshTokenCountInvalidError() - } else { const found_user = found_users[0] delete found_user.password; newAuth.access_token = "ja" newAuth.access_token_expires_at = 5555555 newAuth.refresh_token = "ja" newAuth.refresh_token_expires_at = 555555 + const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); + if (!found_user) { + throw new UserNotFoundError() + } + if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { + throw new RefreshTokenCountInvalidError() } return newAuth; } From 82f31185a1aaba1cb3d4e3812ff65ef62d042e4c Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:07:18 +0100 Subject: [PATCH 157/194] =?UTF-8?q?=F0=9F=9A=A7=20CreateAuth=20-=20use=20p?= =?UTF-8?q?roper=20refreshTokenCount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/CreateAuth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index f21c026..5642375 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -43,7 +43,7 @@ export class CreateAuth { // const timestamp_refresh_expiry = Math.floor(Date.now() / 1000) + 10 * 36000 newAuth.refresh_token = jsonwebtoken.sign({ - refreshtokencount: 5, + refreshtokencount: found_user.refreshTokenCount, userid: found_user.id, exp: timestamp_refresh_expiry }, "securekey") From 126799dab98a8866be2e3f3812b7a76a50d0812d Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:07:33 +0100 Subject: [PATCH 158/194] basic RefreshAuth checking ref #12 --- src/models/creation/RefreshAuth.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts index f9010ce..96721ab 100644 --- a/src/models/creation/RefreshAuth.ts +++ b/src/models/creation/RefreshAuth.ts @@ -20,12 +20,7 @@ export class RefreshAuth { } catch (error) { throw new IllegalJWTError() } - const found_user = found_users[0] - delete found_user.password; - newAuth.access_token = "ja" - newAuth.access_token_expires_at = 5555555 - newAuth.refresh_token = "ja" - newAuth.refresh_token_expires_at = 555555 + console.log(decoded["userid"]); const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); if (!found_user) { throw new UserNotFoundError() @@ -33,6 +28,13 @@ export class RefreshAuth { if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { throw new RefreshTokenCountInvalidError() } + console.log(found_user); + // delete found_user.password; + newAuth.access_token = "ja" + newAuth.access_token_expires_at = 5555555 + newAuth.refresh_token = "ja" + newAuth.refresh_token_expires_at = 555555 + return newAuth; } } \ No newline at end of file From 51addd4a31e63bdaab64b422f35432571af7da23 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:12:47 +0100 Subject: [PATCH 159/194] =?UTF-8?q?=F0=9F=9A=A7=20RefreshAuth=20-=20refres?= =?UTF-8?q?h=20tokens=20now=20working=20=E2=9C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/models/creation/RefreshAuth.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts index 96721ab..ab947e6 100644 --- a/src/models/creation/RefreshAuth.ts +++ b/src/models/creation/RefreshAuth.ts @@ -20,7 +20,6 @@ export class RefreshAuth { } catch (error) { throw new IllegalJWTError() } - console.log(decoded["userid"]); const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); if (!found_user) { throw new UserNotFoundError() @@ -28,12 +27,22 @@ export class RefreshAuth { if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { throw new RefreshTokenCountInvalidError() } - console.log(found_user); - // delete found_user.password; - newAuth.access_token = "ja" - newAuth.access_token_expires_at = 5555555 - newAuth.refresh_token = "ja" - newAuth.refresh_token_expires_at = 555555 + delete found_user.password; + const timestamp_accesstoken_expiry = Math.floor(Date.now() / 1000) + 5 * 60 + delete found_user.password; + newAuth.access_token = jsonwebtoken.sign({ + userdetails: found_user, + exp: timestamp_accesstoken_expiry + }, "securekey") + newAuth.access_token_expires_at = timestamp_accesstoken_expiry + // + const timestamp_refresh_expiry = Math.floor(Date.now() / 1000) + 10 * 36000 + newAuth.refresh_token = jsonwebtoken.sign({ + refreshtokencount: found_user.refreshTokenCount, + userid: found_user.id, + exp: timestamp_refresh_expiry + }, "securekey") + newAuth.refresh_token_expires_at = timestamp_refresh_expiry return newAuth; } From e5f65d0b801934c0ac3a5d898aa7d8fdee4a35c4 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:30:22 +0100 Subject: [PATCH 160/194] note on refreshtokencount checking ref #12 --- src/middlewares/jwtauth.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/middlewares/jwtauth.ts b/src/middlewares/jwtauth.ts index 3f77265..f83e0cf 100644 --- a/src/middlewares/jwtauth.ts +++ b/src/middlewares/jwtauth.ts @@ -3,11 +3,15 @@ import { ExpressMiddlewareInterface } from "routing-controllers"; -// @Middleware({ type: "before" }) export class JWTAuth implements ExpressMiddlewareInterface { use(request: any, response: any, next?: (err?: any) => any): any { const token = request.headers["auth"]; try { + /** + TODO: idk if we should always check the db if refreshtokencount is valid? + seems like a lot of db overhead + at the same time it's basically our only option to support proper logouts + */ const jwtPayload = jwt.verify(token, "secretjwtsecret"); // const jwtPayload = jwt.verify(token, process.env.JWT_SECRET); response.locals.jwtPayload = jwtPayload; From 0d21497c2fd42602d253067d6f6d42fe7cfee5b5 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:31:46 +0100 Subject: [PATCH 161/194] =?UTF-8?q?=F0=9F=9A=A7=20AuthController=20-=20add?= =?UTF-8?q?=20proper=20response=20schemas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/AuthController.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index d14e574..2264ba5 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -1,6 +1,6 @@ import { Body, JsonController, Post } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; -import { InvalidCredentialsError, PasswordNeededError, UsernameOrEmailNeededError } from '../errors/AuthError'; +import { IllegalJWTError, InvalidCredentialsError, JwtNotProvidedError, PasswordNeededError, RefreshTokenCountInvalidError, UsernameOrEmailNeededError } from '../errors/AuthError'; import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/creation/CreateAuth'; import { RefreshAuth } from '../models/creation/RefreshAuth'; @@ -29,8 +29,10 @@ export class AuthController { } @Post("/refresh") - @ResponseSchema(InvalidCredentialsError) + @ResponseSchema(JwtNotProvidedError) + @ResponseSchema(IllegalJWTError) @ResponseSchema(UserNotFoundError) + @ResponseSchema(RefreshTokenCountInvalidError) @OpenAPI({ description: 'refresh a access token' }) async refresh(@Body({ validate: true }) refreshAuth: RefreshAuth) { let auth; From 675717f8ca495898e87fb73dca12249065301fb0 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:38:59 +0100 Subject: [PATCH 162/194] =?UTF-8?q?=F0=9F=9A=A7=20starting=20work=20on=20L?= =?UTF-8?q?ogoutHandler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #12 --- src/controllers/AuthController.ts | 19 +++++++++++++++++ src/models/creation/HandleLogout.ts | 33 +++++++++++++++++++++++++++++ src/models/entities/Logout.ts | 17 +++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/models/creation/HandleLogout.ts create mode 100644 src/models/entities/Logout.ts diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index 2264ba5..90d1c57 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -3,6 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { IllegalJWTError, InvalidCredentialsError, JwtNotProvidedError, PasswordNeededError, RefreshTokenCountInvalidError, UsernameOrEmailNeededError } from '../errors/AuthError'; import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/creation/CreateAuth'; +import { HandleLogout } from '../models/creation/HandleLogout'; import { RefreshAuth } from '../models/creation/RefreshAuth'; @JsonController('/auth') @@ -28,6 +29,24 @@ export class AuthController { return auth } + @Post("/logout") + @ResponseSchema(InvalidCredentialsError) + @ResponseSchema(UserNotFoundError) + @ResponseSchema(UsernameOrEmailNeededError) + @ResponseSchema(PasswordNeededError) + @ResponseSchema(InvalidCredentialsError) + @OpenAPI({ description: 'Create a new access token object' }) + async logout(@Body({ validate: true }) handleLogout: HandleLogout) { + let logout; + try { + logout = await handleLogout.logout() + console.log(logout); + } catch (error) { + return error; + } + return logout + } + @Post("/refresh") @ResponseSchema(JwtNotProvidedError) @ResponseSchema(IllegalJWTError) diff --git a/src/models/creation/HandleLogout.ts b/src/models/creation/HandleLogout.ts new file mode 100644 index 0000000..5404f9b --- /dev/null +++ b/src/models/creation/HandleLogout.ts @@ -0,0 +1,33 @@ +import { IsString } from 'class-validator'; +import * as jsonwebtoken from 'jsonwebtoken'; +import { IllegalJWTError, JwtNotProvidedError } from '../../errors/AuthError'; +import { Logout } from '../entities/Logout'; + +export class HandleLogout { + @IsString() + token: string; + + public async logout(): Promise { + let logout: Logout = new Logout(); + if (!this.token || this.token === undefined) { + throw new JwtNotProvidedError() + } + let decoded + try { + decoded = jsonwebtoken.verify(this.token, 'securekey') + } catch (error) { + throw new IllegalJWTError() + } + logout.access_token = this.token + logout.timestamp = Math.floor(Date.now() / 1000) + // const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); + // if (!found_user) { + // throw new UserNotFoundError() + // } + // if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { + // throw new RefreshTokenCountInvalidError() + // } + // TODO: increment refreshtokencount WHERE userid===userid && refreshtokencount===refreshtokencount + return logout; + } +} \ No newline at end of file diff --git a/src/models/entities/Logout.ts b/src/models/entities/Logout.ts new file mode 100644 index 0000000..d24d95f --- /dev/null +++ b/src/models/entities/Logout.ts @@ -0,0 +1,17 @@ +import { IsString } from 'class-validator'; + +/** + * Defines a Logout object +*/ +export class Logout { + /** + * access_token - JWT shortterm access token + */ + @IsString() + access_token: string; + /** + * timestamp of logout + */ + @IsString() + timestamp: number; +} From 8c229dba82ebad03a22231ac821d985a49b45611 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 13:40:59 +0100 Subject: [PATCH 163/194] add response schemas to AuthController --- src/controllers/AuthController.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index 90d1c57..e8d81cc 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -5,6 +5,8 @@ import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/creation/CreateAuth'; import { HandleLogout } from '../models/creation/HandleLogout'; import { RefreshAuth } from '../models/creation/RefreshAuth'; +import { Auth } from '../models/entities/Auth'; +import { Logout } from '../models/entities/Logout'; @JsonController('/auth') export class AuthController { @@ -12,6 +14,7 @@ export class AuthController { } @Post("/login") + @ResponseSchema(Auth) @ResponseSchema(InvalidCredentialsError) @ResponseSchema(UserNotFoundError) @ResponseSchema(UsernameOrEmailNeededError) @@ -30,6 +33,7 @@ export class AuthController { } @Post("/logout") + @ResponseSchema(Logout) @ResponseSchema(InvalidCredentialsError) @ResponseSchema(UserNotFoundError) @ResponseSchema(UsernameOrEmailNeededError) @@ -48,6 +52,7 @@ export class AuthController { } @Post("/refresh") + @ResponseSchema(Auth) @ResponseSchema(JwtNotProvidedError) @ResponseSchema(IllegalJWTError) @ResponseSchema(UserNotFoundError) From 4352910d54a74773499704070f35adb95e542ff6 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 15:50:28 +0100 Subject: [PATCH 164/194] More dynamic creation of objects ref #13 --- src/errors/AddressErrors.ts | 18 ++++ src/errors/GroupContactErrors.ts | 18 ++++ src/errors/ParticipantErrors.ts | 18 ---- src/errors/RunnerOrganisationErrors.ts | 10 ++- src/errors/RunnerTeamErrors.ts | 12 +++ src/models/creation/CreateGroupContact.ts | 89 +++++++++++++++++++ src/models/creation/CreateParticipant.ts | 8 +- src/models/creation/CreateRunnerGroup.ts | 24 +++-- .../creation/CreateRunnerOrganisation.ts | 42 ++++----- src/models/creation/CreateRunnerTeam.ts | 40 +++++---- 10 files changed, 212 insertions(+), 67 deletions(-) create mode 100644 src/errors/AddressErrors.ts create mode 100644 src/errors/GroupContactErrors.ts delete mode 100644 src/errors/ParticipantErrors.ts create mode 100644 src/models/creation/CreateGroupContact.ts diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts new file mode 100644 index 0000000..c655032 --- /dev/null +++ b/src/errors/AddressErrors.ts @@ -0,0 +1,18 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +export class AddressWrongTypeError extends NotAcceptableError { + @IsString() + name = "AddressWrongTypeError" + + @IsString() + message = "The address must be either a existing address, new address or a existing adress's id. \n You provided a object of another type." +} + +export class AddressNotFoundError extends NotFoundError { + @IsString() + name = "AddressNotFoundError" + + @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/errors/GroupContactErrors.ts b/src/errors/GroupContactErrors.ts new file mode 100644 index 0000000..503fa47 --- /dev/null +++ b/src/errors/GroupContactErrors.ts @@ -0,0 +1,18 @@ +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +export class GroupContactWrongTypeError extends NotAcceptableError { + @IsString() + name = "GroupContactWrongTypeError" + + @IsString() + message = "The groupContact must be either a existing groupContact, new groupContact or a existing adress's id. \n You provided a object of another type." +} + +export class GroupContactNotFoundError extends NotFoundError { + @IsString() + name = "GroupContactNotFoundError" + + @IsString() + message = "The groupContact you provided couldn't be located in the system. \n Please check your request." +} \ No newline at end of file diff --git a/src/errors/ParticipantErrors.ts b/src/errors/ParticipantErrors.ts deleted file mode 100644 index 97c5412..0000000 --- a/src/errors/ParticipantErrors.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsString } from 'class-validator'; -import { NotAcceptableError, NotFoundError } from 'routing-controllers'; - -export class ParticipantAddressWrongTypeError extends NotAcceptableError { - @IsString() - name = "ParticipantAddressWrongTypeError" - - @IsString() - message = "The participant's address must be either a existing address, new address or a existing adress's id. \n You provided a object of another type." -} - -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/errors/RunnerOrganisationErrors.ts b/src/errors/RunnerOrganisationErrors.ts index 1dfb68e..9c0e6c3 100644 --- a/src/errors/RunnerOrganisationErrors.ts +++ b/src/errors/RunnerOrganisationErrors.ts @@ -48,4 +48,12 @@ export class RunnerOrganisationHasTeamsError extends NotAcceptableError { @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 +} + +export class RunnerOrganisationWrongTypeError extends NotAcceptableError { + @IsString() + name = "RunnerOrganisationWrongTypeError" + + @IsString() + message = "The runner organisation must be either a existing organisation, new organisation or a existing organisation's id. \n You provided a object of another type." +} diff --git a/src/errors/RunnerTeamErrors.ts b/src/errors/RunnerTeamErrors.ts index a482c26..33fbea5 100644 --- a/src/errors/RunnerTeamErrors.ts +++ b/src/errors/RunnerTeamErrors.ts @@ -36,4 +36,16 @@ export class RunnerTeamHasRunnersError extends NotAcceptableError { @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." +} + +/** + * Error to throw when a team still has runners associated. + * Implemented this waysto work with the json-schema conversion for openapi. + */ +export class RunnerTeamNeedsParentError extends NotAcceptableError { + @IsString() + name = "RunnerTeamNeedsParentError" + + @IsString() + message = "You provided no runner organisation as this team's parent group." } \ No newline at end of file diff --git a/src/models/creation/CreateGroupContact.ts b/src/models/creation/CreateGroupContact.ts new file mode 100644 index 0000000..21b42d5 --- /dev/null +++ b/src/models/creation/CreateGroupContact.ts @@ -0,0 +1,89 @@ +import { IsEmail, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; +import { Address } from '../entities/Address'; +import { GroupContact } from '../entities/GroupContact'; +import { CreateAddress } from './CreateAddress'; + +export class CreateGroupContact { + /** + * The contact's first name. + */ + @IsNotEmpty() + @IsString() + firstname: string; + + /** + * The contact's middle name. + * Optional + */ + @IsOptional() + @IsString() + middlename?: string; + + /** + * The contact's last name. + */ + @IsNotEmpty() + @IsString() + lastname: string; + + /** + * The contact's address. + * Optional + */ + @IsOptional() + address?: Address | CreateAddress | number; + + /** + * The contact's phone number. + * Optional + */ + @IsOptional() + @IsPhoneNumber("DE") + phone?: string; + + /** + * The contact's email address. + * Optional + */ + @IsOptional() + @IsEmail() + email?: string; + + /** + * Get's this participant's address from this.address. + */ + public async getAddress(): Promise
{ + if (this.address === undefined) { + return null; + } + if (this.address! instanceof Address) { + return this.address; + } + if (this.address! instanceof CreateAddress) { + return this.address.toAddress(); + } + if (!isNaN(this.address)) { + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new AddressNotFoundError; } + return address; + } + + throw new AddressWrongTypeError; + } + + /** + * Creates a Address object based on this. + */ + public async toGroupContact(): Promise { + let contact: GroupContact = new GroupContact(); + contact.firstname = this.firstname; + contact.middlename = this.middlename; + contact.lastname = this.lastname; + contact.email = this.email; + contact.phone = this.phone; + contact.address = await this.getAddress(); + return null; + } +} \ No newline at end of file diff --git a/src/models/creation/CreateParticipant.ts b/src/models/creation/CreateParticipant.ts index c70cf56..87472ed 100644 --- a/src/models/creation/CreateParticipant.ts +++ b/src/models/creation/CreateParticipant.ts @@ -1,6 +1,6 @@ import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { ParticipantAddressNotFoundError, ParticipantAddressWrongTypeError } from '../../errors/ParticipantErrors'; +import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; import { CreateAddress } from './CreateAddress'; @@ -54,7 +54,7 @@ export abstract class CreateParticipant { address?: number | CreateAddress | Address; /** - * Creates a Participant entity from this. + * Get's this participant's address from this.address. */ public async getAddress(): Promise
{ if (this.address === undefined) { @@ -68,10 +68,10 @@ export abstract class CreateParticipant { } if (!isNaN(this.address)) { let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new ParticipantAddressNotFoundError; } + if (!address) { throw new AddressNotFoundError; } return address; } - throw new ParticipantAddressWrongTypeError; + throw new AddressWrongTypeError; } } \ No newline at end of file diff --git a/src/models/creation/CreateRunnerGroup.ts b/src/models/creation/CreateRunnerGroup.ts index 33cb5a9..20a5dab 100644 --- a/src/models/creation/CreateRunnerGroup.ts +++ b/src/models/creation/CreateRunnerGroup.ts @@ -1,5 +1,8 @@ import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { GroupContactNotFoundError, GroupContactWrongTypeError } from '../../errors/GroupContactErrors'; import { GroupContact } from '../entities/GroupContact'; +import { CreateGroupContact } from './CreateGroupContact'; export abstract class CreateRunnerGroup { /** @@ -15,16 +18,27 @@ export abstract class CreateRunnerGroup { */ @IsObject() @IsOptional() - contact?: GroupContact; + contact?: number | CreateGroupContact | GroupContact; /** * Deals with the contact for groups this. */ public async getContact(): Promise { - let newGroupContact: GroupContact; + if (this.contact === undefined) { + return null; + } + if (this.contact! instanceof GroupContact) { + return this.contact; + } + if (this.contact! instanceof CreateGroupContact) { + return this.contact.toGroupContact(); + } + if (!isNaN(this.contact)) { + let address = await getConnectionManager().get().getRepository(GroupContact).findOne({ id: this.contact }); + if (!address) { throw new GroupContactNotFoundError; } + return address; + } - //TODO: - - return newGroupContact; + throw new GroupContactWrongTypeError; } } \ No newline at end of file diff --git a/src/models/creation/CreateRunnerOrganisation.ts b/src/models/creation/CreateRunnerOrganisation.ts index b90162d..6e40e60 100644 --- a/src/models/creation/CreateRunnerOrganisation.ts +++ b/src/models/creation/CreateRunnerOrganisation.ts @@ -1,6 +1,6 @@ -import { IsInt, IsObject, IsOptional } from 'class-validator'; +import { IsObject } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { ParticipantOnlyOneAddressAllowedError } from '../../errors/ParticipantErrors'; +import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; import { CreateAddress } from './CreateAddress'; @@ -8,39 +8,33 @@ import { CreateRunnerGroup } from './CreateRunnerGroup'; export class CreateRunnerOrganisation extends CreateRunnerGroup { /** - * 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. + * The new organisation's address. + * Must be of type number (address id), createAddress (new address) or address (existing address) + * Optional. */ @IsObject() - @IsOptional() - address?: CreateAddress; + address?: number | CreateAddress | Address; /** * 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) { + if (this.address === undefined) { return null; } - - if (this.addressId) { - return await getConnectionManager().get().getRepository(Address).findOne({ id: this.addressId }); + if (this.address! instanceof Address) { + return this.address; + } + if (this.address! instanceof CreateAddress) { + return this.address.toAddress(); + } + if (!isNaN(this.address)) { + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new AddressNotFoundError; } + return address; } - return this.address.toAddress(); + throw new AddressWrongTypeError; } /** diff --git a/src/models/creation/CreateRunnerTeam.ts b/src/models/creation/CreateRunnerTeam.ts index 101a516..341a765 100644 --- a/src/models/creation/CreateRunnerTeam.ts +++ b/src/models/creation/CreateRunnerTeam.ts @@ -1,23 +1,35 @@ -import { IsInt, IsNotEmpty, IsString } from 'class-validator'; +import { IsNotEmpty } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { RunnerOrganisationNotFoundError } from '../../errors/RunnerOrganisationErrors'; +import { RunnerOrganisationNotFoundError, RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; +import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; import { RunnerTeam } from '../entities/RunnerTeam'; +import { CreateRunnerGroup } from './CreateRunnerGroup'; -export class CreateRunnerTeam { - /** - * The teams's name. - */ - @IsString() - @IsNotEmpty() - name: string; +export class CreateRunnerTeam extends CreateRunnerGroup { /** * The team's parent group (organisation). */ - @IsInt() @IsNotEmpty() - parentId: number + parentGroup: number | RunnerOrganisation + + public async getParent(): Promise { + if (this.parentGroup === undefined) { + throw new RunnerTeamNeedsParentError(); + } + if (this.parentGroup! instanceof RunnerOrganisation) { + return this.parentGroup; + } + if (!isNaN(this.parentGroup)) { + let parentGroup = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.parentGroup }); + if (!parentGroup) { throw new RunnerOrganisationNotFoundError();; } + return parentGroup; + } + console.log(this.parentGroup); + + throw new RunnerOrganisationWrongTypeError; + } /** * Creates a RunnerTeam entity from this. @@ -26,10 +38,8 @@ export class CreateRunnerTeam { 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(); - } + newRunnerTeam.parentGroup = await this.getParent(); + newRunnerTeam.contact = await this.getContact() return newRunnerTeam; } From 65b2399eaa83ae97a8ade0f4eef5a17f15773fcb Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 17:04:22 +0100 Subject: [PATCH 165/194] Reverted to id based relation setter ref #13 --- src/errors/AddressErrors.ts | 2 +- src/errors/GroupContactErrors.ts | 2 +- src/errors/RunnerErrors.ts | 4 +- src/errors/RunnerOrganisationErrors.ts | 2 +- src/models/creation/CreateGroupContact.ts | 12 ++---- src/models/creation/CreateParticipant.ts | 14 ++----- src/models/creation/CreateRunner.ts | 41 ++++++------------- src/models/creation/CreateRunnerGroup.ts | 13 ++---- .../creation/CreateRunnerOrganisation.ts | 14 ++----- src/models/creation/CreateRunnerTeam.ts | 9 ++-- 10 files changed, 34 insertions(+), 79 deletions(-) diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts index c655032..c7ae8af 100644 --- a/src/errors/AddressErrors.ts +++ b/src/errors/AddressErrors.ts @@ -6,7 +6,7 @@ export class AddressWrongTypeError extends NotAcceptableError { name = "AddressWrongTypeError" @IsString() - message = "The address must be either a existing address, new address or a existing adress's id. \n You provided a object of another type." + message = "The address must be an existing adress's id. \n You provided a object of another type." } export class AddressNotFoundError extends NotFoundError { diff --git a/src/errors/GroupContactErrors.ts b/src/errors/GroupContactErrors.ts index 503fa47..c5bf9c7 100644 --- a/src/errors/GroupContactErrors.ts +++ b/src/errors/GroupContactErrors.ts @@ -6,7 +6,7 @@ export class GroupContactWrongTypeError extends NotAcceptableError { name = "GroupContactWrongTypeError" @IsString() - message = "The groupContact must be either a existing groupContact, new groupContact or a existing adress's id. \n You provided a object of another type." + message = "The groupContact must be an existing groupContact's id. \n You provided a object of another type." } export class GroupContactNotFoundError extends NotFoundError { diff --git a/src/errors/RunnerErrors.ts b/src/errors/RunnerErrors.ts index 0be6419..8250d17 100644 --- a/src/errors/RunnerErrors.ts +++ b/src/errors/RunnerErrors.ts @@ -1,5 +1,5 @@ -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. diff --git a/src/errors/RunnerOrganisationErrors.ts b/src/errors/RunnerOrganisationErrors.ts index 9c0e6c3..65736b1 100644 --- a/src/errors/RunnerOrganisationErrors.ts +++ b/src/errors/RunnerOrganisationErrors.ts @@ -55,5 +55,5 @@ export class RunnerOrganisationWrongTypeError extends NotAcceptableError { name = "RunnerOrganisationWrongTypeError" @IsString() - message = "The runner organisation must be either a existing organisation, new organisation or a existing organisation's id. \n You provided a object of another type." + message = "The runner organisation must be an existing organisation's id. \n You provided a object of another type." } diff --git a/src/models/creation/CreateGroupContact.ts b/src/models/creation/CreateGroupContact.ts index 21b42d5..73f43d0 100644 --- a/src/models/creation/CreateGroupContact.ts +++ b/src/models/creation/CreateGroupContact.ts @@ -1,9 +1,8 @@ -import { IsEmail, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; import { GroupContact } from '../entities/GroupContact'; -import { CreateAddress } from './CreateAddress'; export class CreateGroupContact { /** @@ -32,8 +31,9 @@ export class CreateGroupContact { * The contact's address. * Optional */ + @IsInt() @IsOptional() - address?: Address | CreateAddress | number; + address?: number; /** * The contact's phone number. @@ -58,12 +58,6 @@ export class CreateGroupContact { if (this.address === undefined) { return null; } - if (this.address! instanceof Address) { - return this.address; - } - if (this.address! instanceof CreateAddress) { - return this.address.toAddress(); - } if (!isNaN(this.address)) { let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); if (!address) { throw new AddressNotFoundError; } diff --git a/src/models/creation/CreateParticipant.ts b/src/models/creation/CreateParticipant.ts index 87472ed..00c8783 100644 --- a/src/models/creation/CreateParticipant.ts +++ b/src/models/creation/CreateParticipant.ts @@ -1,8 +1,7 @@ -import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; -import { CreateAddress } from './CreateAddress'; export abstract class CreateParticipant { /** @@ -50,8 +49,9 @@ export abstract class CreateParticipant { * Must be of type number (address id), createAddress (new address) or address (existing address) * Optional. */ - @IsObject() - address?: number | CreateAddress | Address; + @IsInt() + @IsOptional() + address?: number; /** * Get's this participant's address from this.address. @@ -60,12 +60,6 @@ export abstract class CreateParticipant { if (this.address === undefined) { return null; } - if (this.address! instanceof Address) { - return this.address; - } - if (this.address! instanceof CreateAddress) { - return this.address.toAddress(); - } if (!isNaN(this.address)) { let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); if (!address) { throw new AddressNotFoundError; } diff --git a/src/models/creation/CreateRunner.ts b/src/models/creation/CreateRunner.ts index b34ec9d..4e5f3e9 100644 --- a/src/models/creation/CreateRunner.ts +++ b/src/models/creation/CreateRunner.ts @@ -1,10 +1,10 @@ -import { IsInt, IsOptional } from 'class-validator'; +import { IsInt } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerOnlyOneGroupAllowedError } from '../../errors/RunnerErrors'; +import { RunnerGroupNotFoundError } from '../../errors/RunnerErrors'; +import { RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; +import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; 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 extends CreateParticipant { @@ -14,16 +14,7 @@ export class CreateRunner extends CreateParticipant { * 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; + group: number; /** * Creates a Runner entity from this. @@ -46,23 +37,15 @@ export class CreateRunner extends CreateParticipant { * 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(); + if (this.group === undefined) { + throw new RunnerTeamNeedsParentError(); } - if (this.teamId === undefined && this.orgId === undefined) { - throw new RunnerGroupNeededError(); + if (!isNaN(this.group)) { + let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); + if (!group) { throw new RunnerGroupNotFoundError; } + return group; } - if (this.teamId) { - group = await getConnectionManager().get().getRepository(RunnerTeam).findOne({ id: this.teamId }); - } - if (this.orgId) { - group = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.orgId }); - } - if (!group) { - throw new RunnerGroupNotFoundError(); - } - return group; + throw new RunnerOrganisationWrongTypeError; } } \ No newline at end of file diff --git a/src/models/creation/CreateRunnerGroup.ts b/src/models/creation/CreateRunnerGroup.ts index 20a5dab..76552ac 100644 --- a/src/models/creation/CreateRunnerGroup.ts +++ b/src/models/creation/CreateRunnerGroup.ts @@ -1,8 +1,7 @@ -import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator'; +import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { GroupContactNotFoundError, GroupContactWrongTypeError } from '../../errors/GroupContactErrors'; import { GroupContact } from '../entities/GroupContact'; -import { CreateGroupContact } from './CreateGroupContact'; export abstract class CreateRunnerGroup { /** @@ -16,9 +15,9 @@ export abstract class CreateRunnerGroup { * The group's contact. * Optional */ - @IsObject() + @IsInt() @IsOptional() - contact?: number | CreateGroupContact | GroupContact; + contact?: number; /** * Deals with the contact for groups this. @@ -27,12 +26,6 @@ export abstract class CreateRunnerGroup { if (this.contact === undefined) { return null; } - if (this.contact! instanceof GroupContact) { - return this.contact; - } - if (this.contact! instanceof CreateGroupContact) { - return this.contact.toGroupContact(); - } if (!isNaN(this.contact)) { let address = await getConnectionManager().get().getRepository(GroupContact).findOne({ id: this.contact }); if (!address) { throw new GroupContactNotFoundError; } diff --git a/src/models/creation/CreateRunnerOrganisation.ts b/src/models/creation/CreateRunnerOrganisation.ts index 6e40e60..01c3732 100644 --- a/src/models/creation/CreateRunnerOrganisation.ts +++ b/src/models/creation/CreateRunnerOrganisation.ts @@ -1,9 +1,8 @@ -import { IsObject } from 'class-validator'; +import { IsInt, IsOptional } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; import { RunnerOrganisation } from '../entities/RunnerOrganisation'; -import { CreateAddress } from './CreateAddress'; import { CreateRunnerGroup } from './CreateRunnerGroup'; export class CreateRunnerOrganisation extends CreateRunnerGroup { @@ -12,8 +11,9 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup { * Must be of type number (address id), createAddress (new address) or address (existing address) * Optional. */ - @IsObject() - address?: number | CreateAddress | Address; + @IsInt() + @IsOptional() + address?: number; /** * Creates a Participant entity from this. @@ -22,12 +22,6 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup { if (this.address === undefined) { return null; } - if (this.address! instanceof Address) { - return this.address; - } - if (this.address! instanceof CreateAddress) { - return this.address.toAddress(); - } if (!isNaN(this.address)) { let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); if (!address) { throw new AddressNotFoundError; } diff --git a/src/models/creation/CreateRunnerTeam.ts b/src/models/creation/CreateRunnerTeam.ts index 341a765..98429cb 100644 --- a/src/models/creation/CreateRunnerTeam.ts +++ b/src/models/creation/CreateRunnerTeam.ts @@ -1,4 +1,4 @@ -import { IsNotEmpty } from 'class-validator'; +import { IsInt, IsNotEmpty } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { RunnerOrganisationNotFoundError, RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; @@ -11,22 +11,19 @@ export class CreateRunnerTeam extends CreateRunnerGroup { /** * The team's parent group (organisation). */ + @IsInt() @IsNotEmpty() - parentGroup: number | RunnerOrganisation + parentGroup: number; public async getParent(): Promise { if (this.parentGroup === undefined) { throw new RunnerTeamNeedsParentError(); } - if (this.parentGroup! instanceof RunnerOrganisation) { - return this.parentGroup; - } if (!isNaN(this.parentGroup)) { let parentGroup = await getConnectionManager().get().getRepository(RunnerOrganisation).findOne({ id: this.parentGroup }); if (!parentGroup) { throw new RunnerOrganisationNotFoundError();; } return parentGroup; } - console.log(this.parentGroup); throw new RunnerOrganisationWrongTypeError; } From 13d568ba3f9a3c1035a2dd48509bee2567bd06f3 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 17:20:18 +0100 Subject: [PATCH 166/194] implemented refreshcount increase ref #12 --- src/models/creation/HandleLogout.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/models/creation/HandleLogout.ts b/src/models/creation/HandleLogout.ts index 5404f9b..f1527f3 100644 --- a/src/models/creation/HandleLogout.ts +++ b/src/models/creation/HandleLogout.ts @@ -1,7 +1,9 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; -import { IllegalJWTError, JwtNotProvidedError } from '../../errors/AuthError'; +import { getConnectionManager } from 'typeorm'; +import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; import { Logout } from '../entities/Logout'; +import { User } from '../entities/User'; export class HandleLogout { @IsString() @@ -12,7 +14,7 @@ export class HandleLogout { if (!this.token || this.token === undefined) { throw new JwtNotProvidedError() } - let decoded + let decoded; try { decoded = jsonwebtoken.verify(this.token, 'securekey') } catch (error) { @@ -20,14 +22,15 @@ export class HandleLogout { } logout.access_token = this.token logout.timestamp = Math.floor(Date.now() / 1000) - // const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); - // if (!found_user) { - // throw new UserNotFoundError() - // } - // if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { - // throw new RefreshTokenCountInvalidError() - // } - // TODO: increment refreshtokencount WHERE userid===userid && refreshtokencount===refreshtokencount + let found_user: User = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); + if (!found_user) { + throw new UserNotFoundError() + } + if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { + throw new RefreshTokenCountInvalidError() + } + found_user.refreshTokenCount++; + getConnectionManager().get().getRepository(User).update({ id: found_user.id }, found_user) return logout; } } \ No newline at end of file From 7e4ce00c30acd4eaa188fdee524cee1c19948faf Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 17:20:39 +0100 Subject: [PATCH 167/194] added await (async stuff und so) ref #12 --- src/models/creation/HandleLogout.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/creation/HandleLogout.ts b/src/models/creation/HandleLogout.ts index f1527f3..d1ee7b8 100644 --- a/src/models/creation/HandleLogout.ts +++ b/src/models/creation/HandleLogout.ts @@ -30,7 +30,7 @@ export class HandleLogout { throw new RefreshTokenCountInvalidError() } found_user.refreshTokenCount++; - getConnectionManager().get().getRepository(User).update({ id: found_user.id }, found_user) + await getConnectionManager().get().getRepository(User).update({ id: found_user.id }, found_user) return logout; } } \ No newline at end of file From e5b605cc55715e92885ddb1fefe930b79449c430 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 17:25:57 +0100 Subject: [PATCH 168/194] =?UTF-8?q?=F0=9F=A7=B9=20cleanups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/AuthController.ts | 4 ++-- src/models/creation/CreateAuth.ts | 2 +- src/models/creation/HandleLogout.ts | 3 +-- src/models/creation/RefreshAuth.ts | 2 +- src/models/{entities => responses}/Auth.ts | 0 src/models/{entities => responses}/Logout.ts | 5 ----- 6 files changed, 5 insertions(+), 11 deletions(-) rename src/models/{entities => responses}/Auth.ts (100%) rename src/models/{entities => responses}/Logout.ts (64%) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index e8d81cc..4e5ced1 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -5,8 +5,8 @@ import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/creation/CreateAuth'; import { HandleLogout } from '../models/creation/HandleLogout'; import { RefreshAuth } from '../models/creation/RefreshAuth'; -import { Auth } from '../models/entities/Auth'; -import { Logout } from '../models/entities/Logout'; +import { Auth } from '../models/responses/Auth'; +import { Logout } from '../models/responses/Logout'; @JsonController('/auth') export class AuthController { diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 5642375..140f9ef 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -4,8 +4,8 @@ import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; import { InvalidCredentialsError, PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; -import { Auth } from '../entities/Auth'; import { User } from '../entities/User'; +import { Auth } from '../responses/Auth'; export class CreateAuth { @IsOptional() diff --git a/src/models/creation/HandleLogout.ts b/src/models/creation/HandleLogout.ts index d1ee7b8..c50edd0 100644 --- a/src/models/creation/HandleLogout.ts +++ b/src/models/creation/HandleLogout.ts @@ -2,8 +2,8 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; -import { Logout } from '../entities/Logout'; import { User } from '../entities/User'; +import { Logout } from '../responses/Logout'; export class HandleLogout { @IsString() @@ -20,7 +20,6 @@ export class HandleLogout { } catch (error) { throw new IllegalJWTError() } - logout.access_token = this.token logout.timestamp = Math.floor(Date.now() / 1000) let found_user: User = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); if (!found_user) { diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts index ab947e6..66e27f8 100644 --- a/src/models/creation/RefreshAuth.ts +++ b/src/models/creation/RefreshAuth.ts @@ -2,8 +2,8 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; -import { Auth } from '../entities/Auth'; import { User } from '../entities/User'; +import { Auth } from '../responses/Auth'; export class RefreshAuth { @IsString() diff --git a/src/models/entities/Auth.ts b/src/models/responses/Auth.ts similarity index 100% rename from src/models/entities/Auth.ts rename to src/models/responses/Auth.ts diff --git a/src/models/entities/Logout.ts b/src/models/responses/Logout.ts similarity index 64% rename from src/models/entities/Logout.ts rename to src/models/responses/Logout.ts index d24d95f..f0c109d 100644 --- a/src/models/entities/Logout.ts +++ b/src/models/responses/Logout.ts @@ -4,11 +4,6 @@ import { IsString } from 'class-validator'; * Defines a Logout object */ export class Logout { - /** - * access_token - JWT shortterm access token - */ - @IsString() - access_token: string; /** * timestamp of logout */ From 5a4a6cdcefd96ddc413232841af8b596580d941f Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 17:47:32 +0100 Subject: [PATCH 169/194] Added basic openapi security scheme for the bearer auth header ref #12 --- src/loaders/openapi.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/loaders/openapi.ts b/src/loaders/openapi.ts index 9a0ff85..fe0c77a 100644 --- a/src/loaders/openapi.ts +++ b/src/loaders/openapi.ts @@ -1,8 +1,8 @@ +import { validationMetadatasToSchemas } from "class-validator-jsonschema"; import { Application } from "express"; -import * as swaggerUiExpress from "swagger-ui-express"; import { getMetadataArgsStorage } from "routing-controllers"; import { routingControllersToSpec } from "routing-controllers-openapi"; -import { validationMetadatasToSchemas } from "class-validator-jsonschema"; +import * as swaggerUiExpress from "swagger-ui-express"; export default async (app: Application) => { const storage = getMetadataArgsStorage(); @@ -17,6 +17,13 @@ export default async (app: Application) => { { components: { schemas, + "securitySchemes": { + "AuthToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" + } + } }, info: { description: "The the backend API for the LfK! runner system.", From 76e19ca28dc183f698049080b62e88f9feddf036 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 17:59:43 +0100 Subject: [PATCH 170/194] implement proper jwt checking in authchecker ref #12 --- src/app.ts | 3 +-- src/authchecker.ts | 13 ++++++++++--- src/errors/AuthError.ts | 11 +++++++++++ src/middlewares/jwtauth.ts | 24 ------------------------ 4 files changed, 22 insertions(+), 29 deletions(-) delete mode 100644 src/middlewares/jwtauth.ts diff --git a/src/app.ts b/src/app.ts index e54f0ac..e19d313 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,14 +5,13 @@ import { createExpressServer } from "routing-controllers"; import authchecker from "./authchecker"; import loaders from "./loaders/index"; import { ErrorHandler } from './middlewares/ErrorHandler'; -import { JWTAuth } from './middlewares/JWTAuth'; dotenvSafe.config(); const PORT = process.env.APP_PORT || 4010; const app = createExpressServer({ authorizationChecker: authchecker, - middlewares: [ErrorHandler, JWTAuth], + middlewares: [ErrorHandler], development: process.env.NODE_ENV === "production", cors: true, routePrefix: "/api", diff --git a/src/authchecker.ts b/src/authchecker.ts index f32ec75..4264130 100644 --- a/src/authchecker.ts +++ b/src/authchecker.ts @@ -1,13 +1,15 @@ import * as jwt from "jsonwebtoken"; import { Action } from "routing-controllers"; -import { IllegalJWTError, NoPermissionError } from './errors/AuthError'; +import { getConnectionManager } from 'typeorm'; +import { IllegalJWTError, NoPermissionError, UserNonexistantOrRefreshtokenInvalidError } from './errors/AuthError'; +import { User } from './models/entities/User'; // ----------- const sampletoken = jwt.sign({ "permissions": { "TRACKS": ["read", "update", "delete", "add"] // "TRACKS": [] } -}, process.env.JWT_SECRET || "secretjwtsecret") +}, "securekey") console.log(`sampletoken: ${sampletoken}`); // ----------- const authchecker = async (action: Action, permissions: string | string[]) => { @@ -21,10 +23,15 @@ const authchecker = async (action: Action, permissions: string | string[]) => { const provided_token = action.request.query["auth"]; let jwtPayload = undefined try { - jwtPayload = jwt.verify(provided_token, process.env.JWT_SECRET || "secretjwtsecret"); + jwtPayload = jwt.verify(provided_token, "securekey"); } catch (error) { + console.log(error); throw new IllegalJWTError() } + const count = await getConnectionManager().get().getRepository(User).count({ id: jwtPayload["userdetails"]["id"], refreshTokenCount: jwtPayload["userdetails"]["refreshTokenCount"] }) + if (count !== 1) { + throw new UserNonexistantOrRefreshtokenInvalidError() + } if (jwtPayload.permissions) { action.response.local = {} action.response.local.jwtPayload = jwtPayload.permissions diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index a03ff24..2c2ce5b 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -23,6 +23,17 @@ export class IllegalJWTError extends UnauthorizedError { message = "your provided jwt could not be parsed" } +/** + * Error to throw when user is nonexistant or refreshtoken is invalid + */ +export class UserNonexistantOrRefreshtokenInvalidError extends UnauthorizedError { + @IsString() + name = "UserNonexistantOrRefreshtokenInvalidError" + + @IsString() + message = "user is nonexistant or refreshtoken is invalid" +} + /** * Error to throw when provided credentials are invalid */ diff --git a/src/middlewares/jwtauth.ts b/src/middlewares/jwtauth.ts deleted file mode 100644 index f83e0cf..0000000 --- a/src/middlewares/jwtauth.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as jwt from "jsonwebtoken"; -import { - ExpressMiddlewareInterface -} from "routing-controllers"; - -export class JWTAuth implements ExpressMiddlewareInterface { - use(request: any, response: any, next?: (err?: any) => any): any { - const token = request.headers["auth"]; - try { - /** - TODO: idk if we should always check the db if refreshtokencount is valid? - seems like a lot of db overhead - at the same time it's basically our only option to support proper logouts - */ - const jwtPayload = jwt.verify(token, "secretjwtsecret"); - // const jwtPayload = jwt.verify(token, process.env.JWT_SECRET); - response.locals.jwtPayload = jwtPayload; - } catch (error) { - console.log(error); - return response.status(401).send(); - } - next(); - } -} From 740d7f10f5ede09846d5a4d3a482bf750cb13698 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 18:02:49 +0100 Subject: [PATCH 171/194] remove routes/v1/test --- src/routes/v1/test.ts | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/routes/v1/test.ts diff --git a/src/routes/v1/test.ts b/src/routes/v1/test.ts deleted file mode 100644 index 7f000f5..0000000 --- a/src/routes/v1/test.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Router } from "express"; -import jwtauth from "../../middlewares/jwtauth"; - -const router = Router(); - -router.use("*", jwtauth, async (req, res, next) => { - return res.send("ok"); -}); - -export default router; From 5c259484ee5a4063bcf71bd847413014ec848a00 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sat, 5 Dec 2020 18:02:57 +0100 Subject: [PATCH 172/194] remove sampletoken generation --- src/authchecker.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/authchecker.ts b/src/authchecker.ts index 4264130..f61f35f 100644 --- a/src/authchecker.ts +++ b/src/authchecker.ts @@ -4,14 +4,6 @@ import { getConnectionManager } from 'typeorm'; import { IllegalJWTError, NoPermissionError, UserNonexistantOrRefreshtokenInvalidError } from './errors/AuthError'; import { User } from './models/entities/User'; // ----------- -const sampletoken = jwt.sign({ - "permissions": { - "TRACKS": ["read", "update", "delete", "add"] - // "TRACKS": [] - } -}, "securekey") -console.log(`sampletoken: ${sampletoken}`); -// ----------- const authchecker = async (action: Action, permissions: string | string[]) => { let required_permissions = undefined if (typeof permissions === "string") { From 0e924449d60c994c29b95abba675309e31c04383 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 18:45:47 +0100 Subject: [PATCH 173/194] Cleanup: Renamed the creation folder to the more fitting "actions" ref #11 #13 --- src/controllers/AuthController.ts | 6 ++--- src/controllers/RunnerController.ts | 2 +- .../RunnerOrganisationController.ts | 2 +- src/controllers/RunnerTeamController.ts | 2 +- src/controllers/TrackController.ts | 2 +- src/controllers/UserController.ts | 2 +- src/controllers/UserGroupController.ts | 2 +- .../{creation => actions}/CreateAddress.ts | 0 .../{creation => actions}/CreateAuth.ts | 0 .../CreateGroupContact.ts | 0 .../CreateParticipant.ts | 0 .../{creation => actions}/CreateRunner.ts | 0 .../CreateRunnerGroup.ts | 0 .../CreateRunnerOrganisation.ts | 0 .../{creation => actions}/CreateRunnerTeam.ts | 0 .../{creation => actions}/CreateTrack.ts | 0 .../{creation => actions}/CreateUser.ts | 0 .../{creation => actions}/CreateUserGroup.ts | 0 .../{creation => actions}/HandleLogout.ts | 0 .../{creation => actions}/RefreshAuth.ts | 0 src/tests.ts | 23 +++++++++++++++++++ 21 files changed, 32 insertions(+), 9 deletions(-) rename src/models/{creation => actions}/CreateAddress.ts (100%) rename src/models/{creation => actions}/CreateAuth.ts (100%) rename src/models/{creation => actions}/CreateGroupContact.ts (100%) rename src/models/{creation => actions}/CreateParticipant.ts (100%) rename src/models/{creation => actions}/CreateRunner.ts (100%) rename src/models/{creation => actions}/CreateRunnerGroup.ts (100%) rename src/models/{creation => actions}/CreateRunnerOrganisation.ts (100%) rename src/models/{creation => actions}/CreateRunnerTeam.ts (100%) rename src/models/{creation => actions}/CreateTrack.ts (100%) rename src/models/{creation => actions}/CreateUser.ts (100%) rename src/models/{creation => actions}/CreateUserGroup.ts (100%) rename src/models/{creation => actions}/HandleLogout.ts (100%) rename src/models/{creation => actions}/RefreshAuth.ts (100%) create mode 100644 src/tests.ts diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index 4e5ced1..d5320a3 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -2,9 +2,9 @@ import { Body, JsonController, Post } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { IllegalJWTError, InvalidCredentialsError, JwtNotProvidedError, PasswordNeededError, RefreshTokenCountInvalidError, UsernameOrEmailNeededError } from '../errors/AuthError'; import { UserNotFoundError } from '../errors/UserErrors'; -import { CreateAuth } from '../models/creation/CreateAuth'; -import { HandleLogout } from '../models/creation/HandleLogout'; -import { RefreshAuth } from '../models/creation/RefreshAuth'; +import { CreateAuth } from '../models/actions/CreateAuth'; +import { HandleLogout } from '../models/actions/HandleLogout'; +import { RefreshAuth } from '../models/actions/RefreshAuth'; import { Auth } from '../models/responses/Auth'; import { Logout } from '../models/responses/Logout'; diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 6508f67..8293f36 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -3,7 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; -import { CreateRunner } from '../models/creation/CreateRunner'; +import { CreateRunner } from '../models/actions/CreateRunner'; import { Runner } from '../models/entities/Runner'; import { ResponseRunner } from '../models/responses/ResponseRunner'; diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 8cfef6a..1a598e5 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -3,7 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { RunnerOrganisationHasRunnersError, RunnerOrganisationHasTeamsError, RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; -import { CreateRunnerOrganisation } from '../models/creation/CreateRunnerOrganisation'; +import { CreateRunnerOrganisation } from '../models/actions/CreateRunnerOrganisation'; import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; import { ResponseRunnerOrganisation } from '../models/responses/ResponseRunnerOrganisation'; import { RunnerController } from './RunnerController'; diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 1b7627d..7b0a841 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -3,7 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; -import { CreateRunnerTeam } from '../models/creation/CreateRunnerTeam'; +import { CreateRunnerTeam } from '../models/actions/CreateRunnerTeam'; import { RunnerTeam } from '../models/entities/RunnerTeam'; import { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam'; import { RunnerController } from './RunnerController'; diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index 02370fc..149b837 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -3,7 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; -import { CreateTrack } from '../models/creation/CreateTrack'; +import { CreateTrack } from '../models/actions/CreateTrack'; import { Track } from '../models/entities/Track'; import { ResponseTrack } from '../models/responses/ResponseTrack'; diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 01e743e..8c2f2ea 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -3,7 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { UserGroupNotFoundError, UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; -import { CreateUser } from '../models/creation/CreateUser'; +import { CreateUser } from '../models/actions/CreateUser'; import { User } from '../models/entities/User'; diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index c937f36..70e3c5a 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -3,7 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; -import { CreateUserGroup } from '../models/creation/CreateUserGroup'; +import { CreateUserGroup } from '../models/actions/CreateUserGroup'; import { UserGroup } from '../models/entities/UserGroup'; diff --git a/src/models/creation/CreateAddress.ts b/src/models/actions/CreateAddress.ts similarity index 100% rename from src/models/creation/CreateAddress.ts rename to src/models/actions/CreateAddress.ts diff --git a/src/models/creation/CreateAuth.ts b/src/models/actions/CreateAuth.ts similarity index 100% rename from src/models/creation/CreateAuth.ts rename to src/models/actions/CreateAuth.ts diff --git a/src/models/creation/CreateGroupContact.ts b/src/models/actions/CreateGroupContact.ts similarity index 100% rename from src/models/creation/CreateGroupContact.ts rename to src/models/actions/CreateGroupContact.ts diff --git a/src/models/creation/CreateParticipant.ts b/src/models/actions/CreateParticipant.ts similarity index 100% rename from src/models/creation/CreateParticipant.ts rename to src/models/actions/CreateParticipant.ts diff --git a/src/models/creation/CreateRunner.ts b/src/models/actions/CreateRunner.ts similarity index 100% rename from src/models/creation/CreateRunner.ts rename to src/models/actions/CreateRunner.ts diff --git a/src/models/creation/CreateRunnerGroup.ts b/src/models/actions/CreateRunnerGroup.ts similarity index 100% rename from src/models/creation/CreateRunnerGroup.ts rename to src/models/actions/CreateRunnerGroup.ts diff --git a/src/models/creation/CreateRunnerOrganisation.ts b/src/models/actions/CreateRunnerOrganisation.ts similarity index 100% rename from src/models/creation/CreateRunnerOrganisation.ts rename to src/models/actions/CreateRunnerOrganisation.ts diff --git a/src/models/creation/CreateRunnerTeam.ts b/src/models/actions/CreateRunnerTeam.ts similarity index 100% rename from src/models/creation/CreateRunnerTeam.ts rename to src/models/actions/CreateRunnerTeam.ts diff --git a/src/models/creation/CreateTrack.ts b/src/models/actions/CreateTrack.ts similarity index 100% rename from src/models/creation/CreateTrack.ts rename to src/models/actions/CreateTrack.ts diff --git a/src/models/creation/CreateUser.ts b/src/models/actions/CreateUser.ts similarity index 100% rename from src/models/creation/CreateUser.ts rename to src/models/actions/CreateUser.ts diff --git a/src/models/creation/CreateUserGroup.ts b/src/models/actions/CreateUserGroup.ts similarity index 100% rename from src/models/creation/CreateUserGroup.ts rename to src/models/actions/CreateUserGroup.ts diff --git a/src/models/creation/HandleLogout.ts b/src/models/actions/HandleLogout.ts similarity index 100% rename from src/models/creation/HandleLogout.ts rename to src/models/actions/HandleLogout.ts diff --git a/src/models/creation/RefreshAuth.ts b/src/models/actions/RefreshAuth.ts similarity index 100% rename from src/models/creation/RefreshAuth.ts rename to src/models/actions/RefreshAuth.ts diff --git a/src/tests.ts b/src/tests.ts new file mode 100644 index 0000000..bf43e1e --- /dev/null +++ b/src/tests.ts @@ -0,0 +1,23 @@ +import * as dotenvSafe from "dotenv-safe"; +import "reflect-metadata"; +import { createExpressServer } from "routing-controllers"; +import authchecker from "./authchecker"; +import loaders from "./loaders/index"; +import { ErrorHandler } from './middlewares/ErrorHandler'; + +dotenvSafe.config(); +const PORT = process.env.APP_PORT || 4010; + +const app = createExpressServer({ + authorizationChecker: authchecker, + middlewares: [ErrorHandler], + development: process.env.NODE_ENV === "production", + cors: true, + routePrefix: "/api", + controllers: [__dirname + "/controllers/*.ts"], +}); + +async function main() { + await loaders(app); +} +main(); From 61e7ae4f86e0453c0efa15f151271d674ea0a44d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 18:49:13 +0100 Subject: [PATCH 174/194] Cleanup: Renamed Responses to represent their response nature ref #11 #13 #14 --- src/controllers/AuthController.ts | 4 ++-- src/models/actions/CreateAuth.ts | 2 +- src/models/actions/HandleLogout.ts | 2 +- src/models/actions/RefreshAuth.ts | 2 +- src/models/responses/{Auth.ts => ResponseAuth.ts} | 0 src/models/responses/{Logout.ts => ResponseLogout.ts} | 0 6 files changed, 5 insertions(+), 5 deletions(-) rename src/models/responses/{Auth.ts => ResponseAuth.ts} (100%) rename src/models/responses/{Logout.ts => ResponseLogout.ts} (100%) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index d5320a3..435f3ca 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -5,8 +5,8 @@ import { UserNotFoundError } from '../errors/UserErrors'; import { CreateAuth } from '../models/actions/CreateAuth'; import { HandleLogout } from '../models/actions/HandleLogout'; import { RefreshAuth } from '../models/actions/RefreshAuth'; -import { Auth } from '../models/responses/Auth'; -import { Logout } from '../models/responses/Logout'; +import { Auth } from '../models/responses/ResponseAuth'; +import { Logout } from '../models/responses/ResponseLogout'; @JsonController('/auth') export class AuthController { diff --git a/src/models/actions/CreateAuth.ts b/src/models/actions/CreateAuth.ts index 140f9ef..bf8584c 100644 --- a/src/models/actions/CreateAuth.ts +++ b/src/models/actions/CreateAuth.ts @@ -5,7 +5,7 @@ import { getConnectionManager } from 'typeorm'; import { InvalidCredentialsError, PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { User } from '../entities/User'; -import { Auth } from '../responses/Auth'; +import { Auth } from '../responses/ResponseAuth'; export class CreateAuth { @IsOptional() diff --git a/src/models/actions/HandleLogout.ts b/src/models/actions/HandleLogout.ts index c50edd0..7b68d91 100644 --- a/src/models/actions/HandleLogout.ts +++ b/src/models/actions/HandleLogout.ts @@ -3,7 +3,7 @@ import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; import { User } from '../entities/User'; -import { Logout } from '../responses/Logout'; +import { Logout } from '../responses/ResponseLogout'; export class HandleLogout { @IsString() diff --git a/src/models/actions/RefreshAuth.ts b/src/models/actions/RefreshAuth.ts index 66e27f8..6e61a75 100644 --- a/src/models/actions/RefreshAuth.ts +++ b/src/models/actions/RefreshAuth.ts @@ -3,7 +3,7 @@ import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; import { User } from '../entities/User'; -import { Auth } from '../responses/Auth'; +import { Auth } from '../responses/ResponseAuth'; export class RefreshAuth { @IsString() diff --git a/src/models/responses/Auth.ts b/src/models/responses/ResponseAuth.ts similarity index 100% rename from src/models/responses/Auth.ts rename to src/models/responses/ResponseAuth.ts diff --git a/src/models/responses/Logout.ts b/src/models/responses/ResponseLogout.ts similarity index 100% rename from src/models/responses/Logout.ts rename to src/models/responses/ResponseLogout.ts From 21ad622c10712ae36fb0c5e1519fc3ecba6341ae Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 18:49:59 +0100 Subject: [PATCH 175/194] Removed console logs ref #11 --- src/controllers/AuthController.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index 435f3ca..00380cb 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -25,7 +25,6 @@ export class AuthController { let auth; try { auth = await createAuth.toAuth(); - console.log(auth); } catch (error) { return error; } @@ -44,7 +43,6 @@ export class AuthController { let logout; try { logout = await handleLogout.logout() - console.log(logout); } catch (error) { return error; } @@ -62,7 +60,6 @@ export class AuthController { let auth; try { auth = await refreshAuth.toAuth(); - console.log(auth); } catch (error) { return error; } From 1ae466a6f4618ca41beaffb64a673a5a66ca4901 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 19:01:30 +0100 Subject: [PATCH 176/194] Error cleanup #11 #13 #14 --- src/controllers/RunnerController.ts | 4 ++-- src/controllers/UserController.ts | 3 ++- src/errors/AddressErrors.ts | 6 ++++++ src/errors/AuthError.ts | 20 ++++++++++---------- src/errors/GroupContactErrors.ts | 6 ++++++ src/errors/RunnerErrors.ts | 20 +++----------------- src/errors/RunnerGroupErrors.ts | 14 ++++++++++++++ src/errors/RunnerOrganisationErrors.ts | 3 +++ src/errors/UserErrors.ts | 10 ---------- src/models/actions/CreateRunner.ts | 2 +- src/models/actions/CreateUser.ts | 3 ++- 11 files changed, 49 insertions(+), 42 deletions(-) create mode 100644 src/errors/RunnerGroupErrors.ts diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 8293f36..acee2eb 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -2,7 +2,8 @@ import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, Query import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; -import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; +import { RunnerGroupNeededError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; +import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { CreateRunner } from '../models/actions/CreateRunner'; import { Runner } from '../models/entities/Runner'; import { ResponseRunner } from '../models/responses/ResponseRunner'; @@ -43,7 +44,6 @@ export class RunnerController { @Post() @ResponseSchema(ResponseRunner) - @ResponseSchema(RunnerOnlyOneGroupAllowedError) @ResponseSchema(RunnerGroupNeededError) @ResponseSchema(RunnerGroupNotFoundError) @OpenAPI({ description: 'Create a new runner object (id will be generated automagicly).' }) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 8c2f2ea..f833f54 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -2,7 +2,8 @@ import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put } from import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; -import { UserGroupNotFoundError, UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; +import { UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; +import { UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUser } from '../models/actions/CreateUser'; import { User } from '../models/entities/User'; diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts index c7ae8af..18cf624 100644 --- a/src/errors/AddressErrors.ts +++ b/src/errors/AddressErrors.ts @@ -1,6 +1,9 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; +/** + * Error to throw, when to provided address doesn't belong to the accepted types. + */ export class AddressWrongTypeError extends NotAcceptableError { @IsString() name = "AddressWrongTypeError" @@ -9,6 +12,9 @@ export class AddressWrongTypeError extends NotAcceptableError { message = "The address must be an existing adress's id. \n You provided a object of another type." } +/** + * Error to throw, when a non-existant address get's loaded. + */ export class AddressNotFoundError extends NotFoundError { @IsString() name = "AddressNotFoundError" diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index 2c2ce5b..ebd5c00 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -2,7 +2,7 @@ import { IsString } from 'class-validator'; import { ForbiddenError, NotAcceptableError, NotFoundError, UnauthorizedError } from 'routing-controllers'; /** - * Error to throw when a jwt is expired + * Error to throw when a jwt is expired. */ export class ExpiredJWTError extends UnauthorizedError { @IsString() @@ -13,7 +13,7 @@ export class ExpiredJWTError extends UnauthorizedError { } /** - * Error to throw when a jwt could not be parsed + * Error to throw when a jwt could not be parsed. */ export class IllegalJWTError extends UnauthorizedError { @IsString() @@ -24,7 +24,7 @@ export class IllegalJWTError extends UnauthorizedError { } /** - * Error to throw when user is nonexistant or refreshtoken is invalid + * Error to throw when user is nonexistant or refreshtoken is invalid. */ export class UserNonexistantOrRefreshtokenInvalidError extends UnauthorizedError { @IsString() @@ -35,7 +35,7 @@ export class UserNonexistantOrRefreshtokenInvalidError extends UnauthorizedError } /** - * Error to throw when provided credentials are invalid + * Error to throw when provided credentials are invalid. */ export class InvalidCredentialsError extends UnauthorizedError { @IsString() @@ -46,7 +46,7 @@ export class InvalidCredentialsError extends UnauthorizedError { } /** - * Error to throw when a jwt does not have permission for this route/ action + * Error to throw when a jwt does not have permission for this route/action. */ export class NoPermissionError extends ForbiddenError { @IsString() @@ -57,7 +57,7 @@ export class NoPermissionError extends ForbiddenError { } /** - * Error to thow when no username and no email is set + * Error to throw when no username and no email is set. */ export class UsernameOrEmailNeededError extends NotAcceptableError { @IsString() @@ -68,7 +68,7 @@ export class UsernameOrEmailNeededError extends NotAcceptableError { } /** - * Error to thow when no password is provided + * Error to throw when no password is provided. */ export class PasswordNeededError extends NotAcceptableError { @IsString() @@ -79,7 +79,7 @@ export class PasswordNeededError extends NotAcceptableError { } /** - * Error to thow when no user could be found for provided credential + * Error to throw when no user could be found mating the provided credential. */ export class UserNotFoundError extends NotFoundError { @IsString() @@ -90,7 +90,7 @@ export class UserNotFoundError extends NotFoundError { } /** - * Error to thow when no jwt token was provided + * Error to throw when no jwt token was provided (but one had to be). */ export class JwtNotProvidedError extends NotAcceptableError { @IsString() @@ -101,7 +101,7 @@ export class JwtNotProvidedError extends NotAcceptableError { } /** - * Error to thow when user was not found or refresh token count was invalid + * Error to throw when user was not found or refresh token count was invalid. */ export class UserNotFoundOrRefreshTokenCountInvalidError extends NotAcceptableError { @IsString() diff --git a/src/errors/GroupContactErrors.ts b/src/errors/GroupContactErrors.ts index c5bf9c7..7dc19e8 100644 --- a/src/errors/GroupContactErrors.ts +++ b/src/errors/GroupContactErrors.ts @@ -1,6 +1,9 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; +/** + * Error to throw, when a provided groupContact doesn't belong to the accepted types. + */ export class GroupContactWrongTypeError extends NotAcceptableError { @IsString() name = "GroupContactWrongTypeError" @@ -9,6 +12,9 @@ export class GroupContactWrongTypeError extends NotAcceptableError { message = "The groupContact must be an existing groupContact's id. \n You provided a object of another type." } +/** + * Error to throw, when a non-existant groupContact get's loaded. + */ export class GroupContactNotFoundError extends NotFoundError { @IsString() name = "GroupContactNotFoundError" diff --git a/src/errors/RunnerErrors.ts b/src/errors/RunnerErrors.ts index 8250d17..06dc78f 100644 --- a/src/errors/RunnerErrors.ts +++ b/src/errors/RunnerErrors.ts @@ -26,27 +26,13 @@ export class RunnerIdsNotMatchingError extends NotAcceptableError { message = "The id's don't match!! \n And if you wanted to change a runner's id: This isn't allowed" } -export class RunnerOnlyOneGroupAllowedError extends NotAcceptableError { - @IsString() - name = "RunnerOnlyOneGroupAllowedError" - - @IsString() - message = "Runner's can only be part of one group (team or organisiation)! \n You provided an id for both." -} - +/** + * Error to throw when a runner is missing his group association. + */ export class RunnerGroupNeededError extends NotAcceptableError { @IsString() name = "RunnerGroupNeededError" @IsString() message = "Runner's need to be part of one group (team or organisiation)! \n You provided neither." -} - - -export class RunnerGroupNotFoundError extends NotFoundError { - @IsString() - name = "RunnerGroupNotFoundError" - - @IsString() - message = "The group you provided couldn't be located in the system. \n Please check your request." } \ No newline at end of file diff --git a/src/errors/RunnerGroupErrors.ts b/src/errors/RunnerGroupErrors.ts new file mode 100644 index 0000000..ed9624c --- /dev/null +++ b/src/errors/RunnerGroupErrors.ts @@ -0,0 +1,14 @@ +import { IsString } from 'class-validator'; +import { NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when a runner group couldn't be found. + * Implemented this ways to work with the json-schema conversion for openapi. + */ +export class RunnerGroupNotFoundError extends NotFoundError { + @IsString() + name = "RunnerGroupNotFoundError" + + @IsString() + message = "RunnerGroup not found!" +} \ No newline at end of file diff --git a/src/errors/RunnerOrganisationErrors.ts b/src/errors/RunnerOrganisationErrors.ts index 65736b1..040befb 100644 --- a/src/errors/RunnerOrganisationErrors.ts +++ b/src/errors/RunnerOrganisationErrors.ts @@ -50,6 +50,9 @@ export class RunnerOrganisationHasTeamsError extends NotAcceptableError { 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." } +/** + * Error to throw, when a provided runnerOrganisation doesn't belong to the accepted types. + */ export class RunnerOrganisationWrongTypeError extends NotAcceptableError { @IsString() name = "RunnerOrganisationWrongTypeError" diff --git a/src/errors/UserErrors.ts b/src/errors/UserErrors.ts index 4c9e98b..cfceff1 100644 --- a/src/errors/UserErrors.ts +++ b/src/errors/UserErrors.ts @@ -1,16 +1,6 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; -/** - * Error to throw when a usergroup couldn't be found. - */ -export class UserGroupNotFoundError extends NotFoundError { - @IsString() - name = "UserGroupNotFoundError" - - @IsString() - message = "User Group not found!" -} /** * Error to throw when no username or email is set diff --git a/src/models/actions/CreateRunner.ts b/src/models/actions/CreateRunner.ts index 4e5f3e9..f3611ce 100644 --- a/src/models/actions/CreateRunner.ts +++ b/src/models/actions/CreateRunner.ts @@ -1,6 +1,6 @@ import { IsInt } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { RunnerGroupNotFoundError } from '../../errors/RunnerErrors'; +import { RunnerGroupNotFoundError } from '../../errors/RunnerGroupErrors'; import { RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; import { Runner } from '../entities/Runner'; diff --git a/src/models/actions/CreateUser.ts b/src/models/actions/CreateUser.ts index fb7841a..ff6771a 100644 --- a/src/models/actions/CreateUser.ts +++ b/src/models/actions/CreateUser.ts @@ -2,7 +2,8 @@ import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; -import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { UserGroupNotFoundError } from '../../errors/UserGroupErrors'; import { User } from '../entities/User'; import { UserGroup } from '../entities/UserGroup'; From 33b3bcb8c2eaec3dd9d92a2f92d1e561920b97a9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 19:01:30 +0100 Subject: [PATCH 177/194] Error cleanup #11 #13 #14 --- src/controllers/RunnerController.ts | 4 ++-- src/controllers/UserController.ts | 3 ++- src/errors/AddressErrors.ts | 6 ++++++ src/errors/AuthError.ts | 22 +++++++++++----------- src/errors/GroupContactErrors.ts | 6 ++++++ src/errors/RunnerErrors.ts | 20 +++----------------- src/errors/RunnerGroupErrors.ts | 14 ++++++++++++++ src/errors/RunnerOrganisationErrors.ts | 3 +++ src/errors/UserErrors.ts | 10 ---------- src/models/actions/CreateRunner.ts | 2 +- src/models/actions/CreateUser.ts | 3 ++- 11 files changed, 50 insertions(+), 43 deletions(-) create mode 100644 src/errors/RunnerGroupErrors.ts diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 8293f36..acee2eb 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -2,7 +2,8 @@ import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, Query import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; -import { RunnerGroupNeededError, RunnerGroupNotFoundError, RunnerIdsNotMatchingError, RunnerNotFoundError, RunnerOnlyOneGroupAllowedError } from '../errors/RunnerErrors'; +import { RunnerGroupNeededError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; +import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { CreateRunner } from '../models/actions/CreateRunner'; import { Runner } from '../models/entities/Runner'; import { ResponseRunner } from '../models/responses/ResponseRunner'; @@ -43,7 +44,6 @@ export class RunnerController { @Post() @ResponseSchema(ResponseRunner) - @ResponseSchema(RunnerOnlyOneGroupAllowedError) @ResponseSchema(RunnerGroupNeededError) @ResponseSchema(RunnerGroupNotFoundError) @OpenAPI({ description: 'Create a new runner object (id will be generated automagicly).' }) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 8c2f2ea..f833f54 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -2,7 +2,8 @@ import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put } from import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-extensions'; -import { UserGroupNotFoundError, UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; +import { UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; +import { UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUser } from '../models/actions/CreateUser'; import { User } from '../models/entities/User'; diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts index c7ae8af..18cf624 100644 --- a/src/errors/AddressErrors.ts +++ b/src/errors/AddressErrors.ts @@ -1,6 +1,9 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; +/** + * Error to throw, when to provided address doesn't belong to the accepted types. + */ export class AddressWrongTypeError extends NotAcceptableError { @IsString() name = "AddressWrongTypeError" @@ -9,6 +12,9 @@ export class AddressWrongTypeError extends NotAcceptableError { message = "The address must be an existing adress's id. \n You provided a object of another type." } +/** + * Error to throw, when a non-existant address get's loaded. + */ export class AddressNotFoundError extends NotFoundError { @IsString() name = "AddressNotFoundError" diff --git a/src/errors/AuthError.ts b/src/errors/AuthError.ts index 2c2ce5b..7687371 100644 --- a/src/errors/AuthError.ts +++ b/src/errors/AuthError.ts @@ -2,7 +2,7 @@ import { IsString } from 'class-validator'; import { ForbiddenError, NotAcceptableError, NotFoundError, UnauthorizedError } from 'routing-controllers'; /** - * Error to throw when a jwt is expired + * Error to throw when a jwt is expired. */ export class ExpiredJWTError extends UnauthorizedError { @IsString() @@ -13,7 +13,7 @@ export class ExpiredJWTError extends UnauthorizedError { } /** - * Error to throw when a jwt could not be parsed + * Error to throw when a jwt could not be parsed. */ export class IllegalJWTError extends UnauthorizedError { @IsString() @@ -24,7 +24,7 @@ export class IllegalJWTError extends UnauthorizedError { } /** - * Error to throw when user is nonexistant or refreshtoken is invalid + * Error to throw when user is nonexistant or refreshtoken is invalid. */ export class UserNonexistantOrRefreshtokenInvalidError extends UnauthorizedError { @IsString() @@ -35,7 +35,7 @@ export class UserNonexistantOrRefreshtokenInvalidError extends UnauthorizedError } /** - * Error to throw when provided credentials are invalid + * Error to throw when provided credentials are invalid. */ export class InvalidCredentialsError extends UnauthorizedError { @IsString() @@ -46,7 +46,7 @@ export class InvalidCredentialsError extends UnauthorizedError { } /** - * Error to throw when a jwt does not have permission for this route/ action + * Error to throw when a jwt does not have permission for this route/action. */ export class NoPermissionError extends ForbiddenError { @IsString() @@ -57,7 +57,7 @@ export class NoPermissionError extends ForbiddenError { } /** - * Error to thow when no username and no email is set + * Error to throw when no username and no email is set. */ export class UsernameOrEmailNeededError extends NotAcceptableError { @IsString() @@ -68,7 +68,7 @@ export class UsernameOrEmailNeededError extends NotAcceptableError { } /** - * Error to thow when no password is provided + * Error to throw when no password is provided. */ export class PasswordNeededError extends NotAcceptableError { @IsString() @@ -79,7 +79,7 @@ export class PasswordNeededError extends NotAcceptableError { } /** - * Error to thow when no user could be found for provided credential + * Error to throw when no user could be found mating the provided credential. */ export class UserNotFoundError extends NotFoundError { @IsString() @@ -90,7 +90,7 @@ export class UserNotFoundError extends NotFoundError { } /** - * Error to thow when no jwt token was provided + * Error to throw when no jwt token was provided (but one had to be). */ export class JwtNotProvidedError extends NotAcceptableError { @IsString() @@ -101,7 +101,7 @@ export class JwtNotProvidedError extends NotAcceptableError { } /** - * Error to thow when user was not found or refresh token count was invalid + * Error to throw when user was not found or refresh token count was invalid. */ export class UserNotFoundOrRefreshTokenCountInvalidError extends NotAcceptableError { @IsString() @@ -112,7 +112,7 @@ export class UserNotFoundOrRefreshTokenCountInvalidError extends NotAcceptableEr } /** - * Error to thow when refresh token count was invalid + * Error to throw when refresh token count was invalid */ export class RefreshTokenCountInvalidError extends NotAcceptableError { @IsString() diff --git a/src/errors/GroupContactErrors.ts b/src/errors/GroupContactErrors.ts index c5bf9c7..7dc19e8 100644 --- a/src/errors/GroupContactErrors.ts +++ b/src/errors/GroupContactErrors.ts @@ -1,6 +1,9 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; +/** + * Error to throw, when a provided groupContact doesn't belong to the accepted types. + */ export class GroupContactWrongTypeError extends NotAcceptableError { @IsString() name = "GroupContactWrongTypeError" @@ -9,6 +12,9 @@ export class GroupContactWrongTypeError extends NotAcceptableError { message = "The groupContact must be an existing groupContact's id. \n You provided a object of another type." } +/** + * Error to throw, when a non-existant groupContact get's loaded. + */ export class GroupContactNotFoundError extends NotFoundError { @IsString() name = "GroupContactNotFoundError" diff --git a/src/errors/RunnerErrors.ts b/src/errors/RunnerErrors.ts index 8250d17..06dc78f 100644 --- a/src/errors/RunnerErrors.ts +++ b/src/errors/RunnerErrors.ts @@ -26,27 +26,13 @@ export class RunnerIdsNotMatchingError extends NotAcceptableError { message = "The id's don't match!! \n And if you wanted to change a runner's id: This isn't allowed" } -export class RunnerOnlyOneGroupAllowedError extends NotAcceptableError { - @IsString() - name = "RunnerOnlyOneGroupAllowedError" - - @IsString() - message = "Runner's can only be part of one group (team or organisiation)! \n You provided an id for both." -} - +/** + * Error to throw when a runner is missing his group association. + */ export class RunnerGroupNeededError extends NotAcceptableError { @IsString() name = "RunnerGroupNeededError" @IsString() message = "Runner's need to be part of one group (team or organisiation)! \n You provided neither." -} - - -export class RunnerGroupNotFoundError extends NotFoundError { - @IsString() - name = "RunnerGroupNotFoundError" - - @IsString() - message = "The group you provided couldn't be located in the system. \n Please check your request." } \ No newline at end of file diff --git a/src/errors/RunnerGroupErrors.ts b/src/errors/RunnerGroupErrors.ts new file mode 100644 index 0000000..ed9624c --- /dev/null +++ b/src/errors/RunnerGroupErrors.ts @@ -0,0 +1,14 @@ +import { IsString } from 'class-validator'; +import { NotFoundError } from 'routing-controllers'; + +/** + * Error to throw when a runner group couldn't be found. + * Implemented this ways to work with the json-schema conversion for openapi. + */ +export class RunnerGroupNotFoundError extends NotFoundError { + @IsString() + name = "RunnerGroupNotFoundError" + + @IsString() + message = "RunnerGroup not found!" +} \ No newline at end of file diff --git a/src/errors/RunnerOrganisationErrors.ts b/src/errors/RunnerOrganisationErrors.ts index 65736b1..040befb 100644 --- a/src/errors/RunnerOrganisationErrors.ts +++ b/src/errors/RunnerOrganisationErrors.ts @@ -50,6 +50,9 @@ export class RunnerOrganisationHasTeamsError extends NotAcceptableError { 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." } +/** + * Error to throw, when a provided runnerOrganisation doesn't belong to the accepted types. + */ export class RunnerOrganisationWrongTypeError extends NotAcceptableError { @IsString() name = "RunnerOrganisationWrongTypeError" diff --git a/src/errors/UserErrors.ts b/src/errors/UserErrors.ts index 4c9e98b..cfceff1 100644 --- a/src/errors/UserErrors.ts +++ b/src/errors/UserErrors.ts @@ -1,16 +1,6 @@ import { IsString } from 'class-validator'; import { NotAcceptableError, NotFoundError } from 'routing-controllers'; -/** - * Error to throw when a usergroup couldn't be found. - */ -export class UserGroupNotFoundError extends NotFoundError { - @IsString() - name = "UserGroupNotFoundError" - - @IsString() - message = "User Group not found!" -} /** * Error to throw when no username or email is set diff --git a/src/models/actions/CreateRunner.ts b/src/models/actions/CreateRunner.ts index 4e5f3e9..f3611ce 100644 --- a/src/models/actions/CreateRunner.ts +++ b/src/models/actions/CreateRunner.ts @@ -1,6 +1,6 @@ import { IsInt } from 'class-validator'; import { getConnectionManager } from 'typeorm'; -import { RunnerGroupNotFoundError } from '../../errors/RunnerErrors'; +import { RunnerGroupNotFoundError } from '../../errors/RunnerGroupErrors'; import { RunnerOrganisationWrongTypeError } from '../../errors/RunnerOrganisationErrors'; import { RunnerTeamNeedsParentError } from '../../errors/RunnerTeamErrors'; import { Runner } from '../entities/Runner'; diff --git a/src/models/actions/CreateUser.ts b/src/models/actions/CreateUser.ts index fb7841a..ff6771a 100644 --- a/src/models/actions/CreateUser.ts +++ b/src/models/actions/CreateUser.ts @@ -2,7 +2,8 @@ import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; -import { UserGroupNotFoundError, UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; +import { UserGroupNotFoundError } from '../../errors/UserGroupErrors'; import { User } from '../entities/User'; import { UserGroup } from '../entities/UserGroup'; From f58a715c45ae5e8ea95041816d78991498461b94 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 19:09:08 +0100 Subject: [PATCH 178/194] Cleaned up the loaders ref #11 --- src/loaders/database.ts | 3 +++ src/loaders/express.ts | 6 ++++-- src/loaders/index.ts | 7 +++++-- src/loaders/openapi.ts | 7 +++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/loaders/database.ts b/src/loaders/database.ts index b8e9607..ee331ae 100644 --- a/src/loaders/database.ts +++ b/src/loaders/database.ts @@ -1,5 +1,8 @@ import { createConnection } from "typeorm"; +/** + * Loader for the database that creates the database connection and initializes the database tabels. + */ export default async () => { const connection = await createConnection(); connection.synchronize(); diff --git a/src/loaders/express.ts b/src/loaders/express.ts index 787497a..32a28cd 100644 --- a/src/loaders/express.ts +++ b/src/loaders/express.ts @@ -1,7 +1,9 @@ import { Application } from "express"; -import bodyParser from 'body-parser'; -import cors from 'cors'; +/** + * Loader for express related configurations. + * Currently only enables the proxy trust. + */ export default async (app: Application) => { app.enable('trust proxy'); return app; diff --git a/src/loaders/index.ts b/src/loaders/index.ts index 704ea47..802a732 100644 --- a/src/loaders/index.ts +++ b/src/loaders/index.ts @@ -1,8 +1,11 @@ +import { Application } from "express"; +import databaseLoader from "./database"; import expressLoader from "./express"; import openapiLoader from "./openapi"; -import databaseLoader from "./database"; -import { Application } from "express"; +/** + * Index Loader that executes the other loaders in the right order. + */ export default async (app: Application) => { await databaseLoader(); await openapiLoader(app); diff --git a/src/loaders/openapi.ts b/src/loaders/openapi.ts index fe0c77a..9c270ed 100644 --- a/src/loaders/openapi.ts +++ b/src/loaders/openapi.ts @@ -4,11 +4,16 @@ import { getMetadataArgsStorage } from "routing-controllers"; import { routingControllersToSpec } from "routing-controllers-openapi"; import * as swaggerUiExpress from "swagger-ui-express"; +/** + * Loader for everything openapi related - from creating the schema to serving it via a static route. + */ export default async (app: Application) => { const storage = getMetadataArgsStorage(); const schemas = validationMetadatasToSchemas({ refPointerPrefix: "#/components/schemas/", }); + + //Spec creation based on the previously created schemas const spec = routingControllersToSpec( storage, { @@ -32,6 +37,8 @@ export default async (app: Application) => { }, } ); + + //Options for swaggerUiExpress const options = { explorer: true, }; From 1fb09e577c539d8261afe4cc4a2cfae88b8f866b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 19:14:04 +0100 Subject: [PATCH 179/194] Cleaned up up the middlewares ref #11 --- src/middlewares/ErrorHandler.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/middlewares/ErrorHandler.ts b/src/middlewares/ErrorHandler.ts index 7f45073..40c1ff6 100644 --- a/src/middlewares/ErrorHandler.ts +++ b/src/middlewares/ErrorHandler.ts @@ -1,20 +1,14 @@ -import { - Middleware, - ExpressErrorMiddlewareInterface -} from "routing-controllers"; +import { ExpressErrorMiddlewareInterface, Middleware } from "routing-controllers"; +/** + * Our Error handling middlware that returns our custom httperrors to the user + */ @Middleware({ type: "after" }) export class ErrorHandler implements ExpressErrorMiddlewareInterface { - public error( - error: any, - request: any, - response: any, - next: (err: any) => any - ) { + public error(error: any, request: any, response: any, next: (err: any) => any) { if (response.headersSent) { return; } - response.json(error); } } From ad6c9e72111f91f432b66bc67001ef8ceea499dc Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 19:15:56 +0100 Subject: [PATCH 180/194] Removed garbage file --- src/tests.ts | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 src/tests.ts diff --git a/src/tests.ts b/src/tests.ts deleted file mode 100644 index bf43e1e..0000000 --- a/src/tests.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as dotenvSafe from "dotenv-safe"; -import "reflect-metadata"; -import { createExpressServer } from "routing-controllers"; -import authchecker from "./authchecker"; -import loaders from "./loaders/index"; -import { ErrorHandler } from './middlewares/ErrorHandler'; - -dotenvSafe.config(); -const PORT = process.env.APP_PORT || 4010; - -const app = createExpressServer({ - authorizationChecker: authchecker, - middlewares: [ErrorHandler], - development: process.env.NODE_ENV === "production", - cors: true, - routePrefix: "/api", - controllers: [__dirname + "/controllers/*.ts"], -}); - -async function main() { - await loaders(app); -} -main(); From 5103e8a6e5db8a3484eae5710f8f4f7712cb8b14 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 5 Dec 2020 20:01:06 +0100 Subject: [PATCH 181/194] Updated folders in the readme --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 077f888..f974523 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,11 @@ docker-compose up --build ## File Structure -- src/models/\* - database models (typeorm entities) +- src/models/entities\* - database models (typeorm entities) +- src/models/actions\* - actions models +- src/models/responses\* - response models - src/controllers/\* - routing-controllers - src/loaders/\* - loaders for the different init steps of the api server -- src/routes/\* - express routes for everything we don't do via routing-controllers (shouldn't be much) - src/middlewares/\* - express middlewares (mainly auth r/n) - src/errors/* - our custom (http) errors +- src/routes/\* - express routes for everything we don't do via routing-controllers (depreciated) \ No newline at end of file From 99d8a0360f3cdb18444a07ba4f3b5f8cd4bb98d0 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sun, 6 Dec 2020 10:29:56 +0100 Subject: [PATCH 182/194] =?UTF-8?q?=F0=9F=9A=9A=20basic=20move=20to=20conf?= =?UTF-8?q?ig.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #18 --- src/app.ts | 11 ++++------- src/authchecker.ts | 5 +++-- src/config.ts | 7 +++++++ src/models/creation/CreateAuth.ts | 5 +++-- src/models/creation/HandleLogout.ts | 3 ++- src/models/creation/RefreshAuth.ts | 7 ++++--- 6 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 src/config.ts diff --git a/src/app.ts b/src/app.ts index e19d313..fd3dc8e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,18 +1,15 @@ import consola from "consola"; -import * as dotenvSafe from "dotenv-safe"; import "reflect-metadata"; import { createExpressServer } from "routing-controllers"; import authchecker from "./authchecker"; +import { config } from './config'; import loaders from "./loaders/index"; import { ErrorHandler } from './middlewares/ErrorHandler'; -dotenvSafe.config(); -const PORT = process.env.APP_PORT || 4010; - const app = createExpressServer({ authorizationChecker: authchecker, middlewares: [ErrorHandler], - development: process.env.NODE_ENV === "production", + development: config.development, cors: true, routePrefix: "/api", controllers: [__dirname + "/controllers/*.ts"], @@ -20,9 +17,9 @@ const app = createExpressServer({ async function main() { await loaders(app); - app.listen(PORT, () => { + app.listen(config.internal_port, () => { consola.success( - `⚡️[server]: Server is running at http://localhost:${PORT}` + `⚡️[server]: Server is running at http://localhost:${config.internal_port}` ); }); } diff --git a/src/authchecker.ts b/src/authchecker.ts index f61f35f..869e0da 100644 --- a/src/authchecker.ts +++ b/src/authchecker.ts @@ -1,6 +1,7 @@ import * as jwt from "jsonwebtoken"; import { Action } from "routing-controllers"; import { getConnectionManager } from 'typeorm'; +import { config } from './config'; import { IllegalJWTError, NoPermissionError, UserNonexistantOrRefreshtokenInvalidError } from './errors/AuthError'; import { User } from './models/entities/User'; // ----------- @@ -15,7 +16,7 @@ const authchecker = async (action: Action, permissions: string | string[]) => { const provided_token = action.request.query["auth"]; let jwtPayload = undefined try { - jwtPayload = jwt.verify(provided_token, "securekey"); + jwtPayload = jwt.verify(provided_token, config.jwt_secret); } catch (error) { console.log(error); throw new IllegalJWTError() @@ -42,7 +43,7 @@ const authchecker = async (action: Action, permissions: string | string[]) => { } // try { - jwt.verify(provided_token, process.env.JWT_SECRET || "secretjwtsecret"); + jwt.verify(provided_token, config.jwt_secret); return true } catch (error) { return false diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..5f54ce3 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,7 @@ +import * as dotenvSafe from "dotenv-safe"; +dotenvSafe.config(); +export const config = { + internal_port: process.env.APP_PORT || 4010, + development: process.env.NODE_ENV === "production", + jwt_secret: process.env.JWT_SECRET || "secretjwtsecret" +} \ No newline at end of file diff --git a/src/models/creation/CreateAuth.ts b/src/models/creation/CreateAuth.ts index 140f9ef..42a0d5d 100644 --- a/src/models/creation/CreateAuth.ts +++ b/src/models/creation/CreateAuth.ts @@ -2,6 +2,7 @@ import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; +import { config } from '../../config'; import { InvalidCredentialsError, PasswordNeededError, UserNotFoundError } from '../../errors/AuthError'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { User } from '../entities/User'; @@ -38,7 +39,7 @@ export class CreateAuth { newAuth.access_token = jsonwebtoken.sign({ userdetails: found_user, exp: timestamp_accesstoken_expiry - }, "securekey") + }, config.jwt_secret) newAuth.access_token_expires_at = timestamp_accesstoken_expiry // const timestamp_refresh_expiry = Math.floor(Date.now() / 1000) + 10 * 36000 @@ -46,7 +47,7 @@ export class CreateAuth { refreshtokencount: found_user.refreshTokenCount, userid: found_user.id, exp: timestamp_refresh_expiry - }, "securekey") + }, config.jwt_secret) newAuth.refresh_token_expires_at = timestamp_refresh_expiry } else { throw new InvalidCredentialsError() diff --git a/src/models/creation/HandleLogout.ts b/src/models/creation/HandleLogout.ts index c50edd0..4b23d5f 100644 --- a/src/models/creation/HandleLogout.ts +++ b/src/models/creation/HandleLogout.ts @@ -1,6 +1,7 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; +import { config } from '../../config'; import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; import { User } from '../entities/User'; import { Logout } from '../responses/Logout'; @@ -16,7 +17,7 @@ export class HandleLogout { } let decoded; try { - decoded = jsonwebtoken.verify(this.token, 'securekey') + decoded = jsonwebtoken.verify(this.token, config.jwt_secret) } catch (error) { throw new IllegalJWTError() } diff --git a/src/models/creation/RefreshAuth.ts b/src/models/creation/RefreshAuth.ts index 66e27f8..96bbc8d 100644 --- a/src/models/creation/RefreshAuth.ts +++ b/src/models/creation/RefreshAuth.ts @@ -1,6 +1,7 @@ import { IsString } from 'class-validator'; import * as jsonwebtoken from 'jsonwebtoken'; import { getConnectionManager } from 'typeorm'; +import { config } from '../../config'; import { IllegalJWTError, JwtNotProvidedError, RefreshTokenCountInvalidError, UserNotFoundError } from '../../errors/AuthError'; import { User } from '../entities/User'; import { Auth } from '../responses/Auth'; @@ -16,7 +17,7 @@ export class RefreshAuth { } let decoded try { - decoded = jsonwebtoken.verify(this.token, 'securekey') + decoded = jsonwebtoken.verify(this.token, config.jwt_secret) } catch (error) { throw new IllegalJWTError() } @@ -33,7 +34,7 @@ export class RefreshAuth { newAuth.access_token = jsonwebtoken.sign({ userdetails: found_user, exp: timestamp_accesstoken_expiry - }, "securekey") + }, config.jwt_secret) newAuth.access_token_expires_at = timestamp_accesstoken_expiry // const timestamp_refresh_expiry = Math.floor(Date.now() / 1000) + 10 * 36000 @@ -41,7 +42,7 @@ export class RefreshAuth { refreshtokencount: found_user.refreshTokenCount, userid: found_user.id, exp: timestamp_refresh_expiry - }, "securekey") + }, config.jwt_secret) newAuth.refresh_token_expires_at = timestamp_refresh_expiry return newAuth; From fcfc10f7d123130a5abfb877035c0e073c885021 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Sun, 6 Dec 2020 11:21:10 +0100 Subject: [PATCH 183/194] =?UTF-8?q?=E2=9C=85=20phone=20countrycode=20valid?= =?UTF-8?q?ation=20in=20env=20vars?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref #18 #16 --- src/config.ts | 3 ++- src/models/creation/CreateGroupContact.ts | 3 ++- src/models/entities/GroupContact.ts | 3 ++- src/models/entities/Participant.ts | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/config.ts b/src/config.ts index 5f54ce3..5dd7d19 100644 --- a/src/config.ts +++ b/src/config.ts @@ -3,5 +3,6 @@ dotenvSafe.config(); export const config = { internal_port: process.env.APP_PORT || 4010, development: process.env.NODE_ENV === "production", - jwt_secret: process.env.JWT_SECRET || "secretjwtsecret" + jwt_secret: process.env.JWT_SECRET || "secretjwtsecret", + phone_validation_countrycode: process.env.PHONE_COUNTRYCODE || "ZZ" } \ No newline at end of file diff --git a/src/models/creation/CreateGroupContact.ts b/src/models/creation/CreateGroupContact.ts index 73f43d0..d8aa815 100644 --- a/src/models/creation/CreateGroupContact.ts +++ b/src/models/creation/CreateGroupContact.ts @@ -1,5 +1,6 @@ import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; +import { config } from '../../config'; import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; import { GroupContact } from '../entities/GroupContact'; @@ -40,7 +41,7 @@ export class CreateGroupContact { * Optional */ @IsOptional() - @IsPhoneNumber("DE") + @IsPhoneNumber(config.phone_validation_countrycode) phone?: string; /** diff --git a/src/models/entities/GroupContact.ts b/src/models/entities/GroupContact.ts index c7c8f18..08c5db3 100644 --- a/src/models/entities/GroupContact.ts +++ b/src/models/entities/GroupContact.ts @@ -8,6 +8,7 @@ import { IsString } from "class-validator"; import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { config } from '../../config'; import { Address } from "./Address"; import { RunnerGroup } from "./RunnerGroup"; @@ -62,7 +63,7 @@ export class GroupContact { */ @Column({ nullable: true }) @IsOptional() - @IsPhoneNumber("DE") + @IsPhoneNumber(config.phone_validation_countrycode) phone?: string; /** diff --git a/src/models/entities/Participant.ts b/src/models/entities/Participant.ts index 2e40f5d..791b30d 100644 --- a/src/models/entities/Participant.ts +++ b/src/models/entities/Participant.ts @@ -8,6 +8,7 @@ import { IsString } from "class-validator"; import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; +import { config } from '../../config'; import { Address } from "./Address"; import { Donation } from "./Donation"; @@ -62,7 +63,7 @@ export abstract class Participant { */ @Column({ nullable: true }) @IsOptional() - @IsPhoneNumber("DE") + @IsPhoneNumber(config.phone_validation_countrycode) phone?: string; /** From 4df63a8cc0c1346dda6758bf5aa20f939dc64ade Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 16:10:56 +0100 Subject: [PATCH 184/194] Fixxed missing plural ref #13 --- src/controllers/RunnerOrganisationController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 8cfef6a..3302913 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -10,7 +10,7 @@ import { RunnerController } from './RunnerController'; import { RunnerTeamController } from './RunnerTeamController'; -@JsonController('/organisation') +@JsonController('/organisations') //@Authorized('RUNNERS:read') export class RunnerOrganisationController { private runnerOrganisationRepository: Repository; From e4cb8eba1d7a4e7623ebf38d84227752c84d1ead Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 17:48:24 +0100 Subject: [PATCH 185/194] Removed relations resolution that broke the update --- src/controllers/RunnerOrganisationController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 1a80809..3ad839e 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -66,7 +66,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 }, { relations: ['address', 'contact', 'teams'] }); + let oldRunnerOrganisation = await this.runnerOrganisationRepository.findOne({ id: id }); if (!oldRunnerOrganisation) { throw new RunnerOrganisationNotFoundError(); From 204e2352a9a580a5579fad98ffcfb8cdb4683eab Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 18:11:23 +0100 Subject: [PATCH 186/194] Fix for getting one ref #13 --- src/controllers/RunnerController.ts | 4 +++- src/controllers/RunnerOrganisationController.ts | 4 +++- src/controllers/RunnerTeamController.ts | 4 +++- src/controllers/TrackController.ts | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index acee2eb..9d7a847 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -39,7 +39,9 @@ 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 }, { relations: ['scans', 'group'] })); + let runner = await this.runnerRepository.findOne({ id: id }, { relations: ['scans', 'group'] }) + if (!runner) { throw new RunnerNotFoundError(); } + return new ResponseRunner(runner); } @Post() diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 3ad839e..5636fa6 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -41,7 +41,9 @@ export class RunnerOrganisationController { @OnUndefined(RunnerOrganisationNotFoundError) @OpenAPI({ description: 'Returns a runnerOrganisation of a specified id (if it exists)' }) async getOne(@Param('id') id: number) { - return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] })); + let runnerOrg = await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); + if (!runnerOrg) { throw new RunnerOrganisationNotFoundError(); } + return new ResponseRunnerOrganisation(runnerOrg); } @Post() diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 7b0a841..148f16d 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -40,7 +40,9 @@ export class RunnerTeamController { @OnUndefined(RunnerTeamNotFoundError) @OpenAPI({ description: 'Returns a runnerTeam of a specified id (if it exists)' }) async getOne(@Param('id') id: number) { - return new ResponseRunnerTeam(await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] })); + let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] }); + if (!runnerTeam) { throw new RunnerTeamNotFoundError(); } + return new ResponseRunnerTeam(runnerTeam); } @Post() diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index 149b837..bf5fb71 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -37,7 +37,9 @@ export class TrackController { @OnUndefined(TrackNotFoundError) @OpenAPI({ description: "Returns a track of a specified id (if it exists)" }) async getOne(@Param('id') id: number) { - return new ResponseTrack(await this.trackRepository.findOne({ id: id })); + let track = await this.trackRepository.findOne({ id: id }); + if (!track) { throw new TrackNotFoundError(); } + return new ResponseTrack(track); } @Post() From 2d603a1467eca50263a0ad2eea427e59da88d263 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 9 Dec 2020 18:45:39 +0100 Subject: [PATCH 187/194] resolve groups + permissions ref #12 --- src/models/actions/CreateAuth.ts | 2 +- src/models/actions/RefreshAuth.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/actions/CreateAuth.ts b/src/models/actions/CreateAuth.ts index 2832e19..d36c17f 100644 --- a/src/models/actions/CreateAuth.ts +++ b/src/models/actions/CreateAuth.ts @@ -28,7 +28,7 @@ export class CreateAuth { if (!this.password) { throw new PasswordNeededError() } - const found_users = await getConnectionManager().get().getRepository(User).find({ where: [{ username: this.username }, { email: this.email }] }); + const found_users = await getConnectionManager().get().getRepository(User).find({ relations: ['groups', 'permissions'], where: [{ username: this.username }, { email: this.email }] }); if (found_users.length === 0) { throw new UserNotFoundError() } else { diff --git a/src/models/actions/RefreshAuth.ts b/src/models/actions/RefreshAuth.ts index 55c124f..77a2728 100644 --- a/src/models/actions/RefreshAuth.ts +++ b/src/models/actions/RefreshAuth.ts @@ -21,7 +21,7 @@ export class RefreshAuth { } catch (error) { throw new IllegalJWTError() } - const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }); + const found_user = await getConnectionManager().get().getRepository(User).findOne({ id: decoded["userid"] }, { relations: ['groups', 'permissions'] }); if (!found_user) { throw new UserNotFoundError() } From 02236caa413c3a374c62a30d9cfe0e77e1cb4c91 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 9 Dec 2020 18:46:09 +0100 Subject: [PATCH 188/194] send empty array for user permissions if null ref #12 --- src/models/actions/CreateAuth.ts | 1 + src/models/actions/RefreshAuth.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/models/actions/CreateAuth.ts b/src/models/actions/CreateAuth.ts index d36c17f..b31bee6 100644 --- a/src/models/actions/CreateAuth.ts +++ b/src/models/actions/CreateAuth.ts @@ -35,6 +35,7 @@ export class CreateAuth { const found_user = found_users[0] if (await argon2.verify(found_user.password, this.password + found_user.uuid)) { const timestamp_accesstoken_expiry = Math.floor(Date.now() / 1000) + 5 * 60 + found_user.permissions = found_user.permissions || [] delete found_user.password; newAuth.access_token = jsonwebtoken.sign({ userdetails: found_user, diff --git a/src/models/actions/RefreshAuth.ts b/src/models/actions/RefreshAuth.ts index 77a2728..dacca59 100644 --- a/src/models/actions/RefreshAuth.ts +++ b/src/models/actions/RefreshAuth.ts @@ -28,6 +28,7 @@ export class RefreshAuth { if (found_user.refreshTokenCount !== decoded["refreshtokencount"]) { throw new RefreshTokenCountInvalidError() } + found_user.permissions = found_user.permissions || [] delete found_user.password; const timestamp_accesstoken_expiry = Math.floor(Date.now() / 1000) + 5 * 60 delete found_user.password; From af1ad482d4dd9cf65cc2895896c19f0ecf9d879d Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 18:53:36 +0100 Subject: [PATCH 189/194] Now throwing errors to the next instance ref #13 --- src/models/actions/CreateRunnerTeam.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/models/actions/CreateRunnerTeam.ts b/src/models/actions/CreateRunnerTeam.ts index 98429cb..21a6af9 100644 --- a/src/models/actions/CreateRunnerTeam.ts +++ b/src/models/actions/CreateRunnerTeam.ts @@ -35,7 +35,12 @@ export class CreateRunnerTeam extends CreateRunnerGroup { let newRunnerTeam: RunnerTeam = new RunnerTeam(); newRunnerTeam.name = this.name; - newRunnerTeam.parentGroup = await this.getParent(); + try { + newRunnerTeam.parentGroup = await this.getParent(); + } catch (error) { + throw error; + } + newRunnerTeam.contact = await this.getContact() return newRunnerTeam; From 77b769446ff443cf0538fcdb9bbeb1b56932e881 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 18:59:01 +0100 Subject: [PATCH 190/194] Now throwing errors --- src/controllers/AuthController.ts | 2 +- src/controllers/RunnerController.ts | 2 +- src/controllers/RunnerOrganisationController.ts | 2 +- src/controllers/RunnerTeamController.ts | 2 +- src/controllers/UserController.ts | 2 +- src/controllers/UserGroupController.ts | 2 +- src/models/actions/CreateRunnerTeam.ts | 6 +----- 7 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/controllers/AuthController.ts b/src/controllers/AuthController.ts index 00380cb..c2002a3 100644 --- a/src/controllers/AuthController.ts +++ b/src/controllers/AuthController.ts @@ -26,7 +26,7 @@ export class AuthController { try { auth = await createAuth.toAuth(); } catch (error) { - return error; + throw error; } return auth } diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 9d7a847..0a6ea34 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -54,7 +54,7 @@ export class RunnerController { try { runner = await createRunner.toRunner(); } catch (error) { - return error; + throw error; } runner = await this.runnerRepository.save(runner) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 5636fa6..a0914e3 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -54,7 +54,7 @@ export class RunnerOrganisationController { try { runnerOrganisation = await createRunnerOrganisation.toRunnerOrganisation(); } catch (error) { - return error; + throw error; } runnerOrganisation = await this.runnerOrganisationRepository.save(runnerOrganisation); diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 148f16d..be44aac 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -53,7 +53,7 @@ export class RunnerTeamController { try { runnerTeam = await createRunnerTeam.toRunnerTeam(); } catch (error) { - return error; + throw error; } runnerTeam = await this.runnerTeamRepository.save(runnerTeam); diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index f833f54..b5550a2 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -44,7 +44,7 @@ export class UserController { try { user = await createUser.toUser(); } catch (error) { - return error; + throw error; } return this.userRepository.save(user); diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index 70e3c5a..89ef3b9 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -43,7 +43,7 @@ export class UserGroupController { try { userGroup = await createUserGroup.toUserGroup(); } catch (error) { - return error; + throw error; } return this.userGroupsRepository.save(userGroup); diff --git a/src/models/actions/CreateRunnerTeam.ts b/src/models/actions/CreateRunnerTeam.ts index 21a6af9..0a4941f 100644 --- a/src/models/actions/CreateRunnerTeam.ts +++ b/src/models/actions/CreateRunnerTeam.ts @@ -35,11 +35,7 @@ export class CreateRunnerTeam extends CreateRunnerGroup { let newRunnerTeam: RunnerTeam = new RunnerTeam(); newRunnerTeam.name = this.name; - try { - newRunnerTeam.parentGroup = await this.getParent(); - } catch (error) { - throw error; - } + newRunnerTeam.parentGroup = await this.getParent(); newRunnerTeam.contact = await this.getContact() From 4cfe9df429366c760a9a2346fe1256a8e0deb285 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 9 Dec 2020 19:09:20 +0100 Subject: [PATCH 191/194] final phone validation move to ZZ default close #16 --- src/models/actions/CreateParticipant.ts | 3 ++- src/models/actions/CreateUser.ts | 3 ++- src/models/entities/User.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/models/actions/CreateParticipant.ts b/src/models/actions/CreateParticipant.ts index 00c8783..9cd51ce 100644 --- a/src/models/actions/CreateParticipant.ts +++ b/src/models/actions/CreateParticipant.ts @@ -1,5 +1,6 @@ import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; +import { config } from '../../config'; import { AddressNotFoundError, AddressWrongTypeError } from '../../errors/AddressErrors'; import { Address } from '../entities/Address'; @@ -32,7 +33,7 @@ export abstract class CreateParticipant { */ @IsString() @IsOptional() - @IsPhoneNumber("ZZ") + @IsPhoneNumber(config.phone_validation_countrycode) phone?: string; /** diff --git a/src/models/actions/CreateUser.ts b/src/models/actions/CreateUser.ts index ff6771a..d3c6ef6 100644 --- a/src/models/actions/CreateUser.ts +++ b/src/models/actions/CreateUser.ts @@ -2,6 +2,7 @@ import * as argon2 from "argon2"; import { IsEmail, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import * as uuid from 'uuid'; +import { config } from '../../config'; import { UsernameOrEmailNeededError } from '../../errors/UserErrors'; import { UserGroupNotFoundError } from '../../errors/UserGroupErrors'; import { User } from '../entities/User'; @@ -49,7 +50,7 @@ export class CreateUser { * The new user's phone number. * Optional */ - @IsPhoneNumber("ZZ") + @IsPhoneNumber(config.phone_validation_countrycode) @IsOptional() phone?: string; diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index 8e29e27..ebc62b1 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -1,5 +1,6 @@ import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString, IsUUID } from "class-validator"; import { Column, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { config } from '../../config'; import { Permission } from './Permission'; import { UserAction } from './UserAction'; import { UserGroup } from './UserGroup'; @@ -35,7 +36,7 @@ export class User { */ @Column({ nullable: true }) @IsOptional() - @IsPhoneNumber("ZZ") + @IsPhoneNumber(config.phone_validation_countrycode) phone?: string; /** From a068c4d318dadc1860b796b231dafd3e2cf53714 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 19:34:49 +0100 Subject: [PATCH 192/194] Deletes now return 204 instead of 404 (better rest compatability) ref #13 --- src/controllers/RunnerController.ts | 6 ++++-- src/controllers/RunnerOrganisationController.ts | 6 ++++-- src/controllers/RunnerTeamController.ts | 6 ++++-- src/controllers/TrackController.ts | 6 ++++-- src/controllers/UserController.ts | 6 ++++-- src/controllers/UserGroupController.ts | 6 ++++-- src/models/responses/ResponseEmpty.ts | 6 ++++++ 7 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/models/responses/ResponseEmpty.ts diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 0a6ea34..1d61eba 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -6,6 +6,7 @@ import { RunnerGroupNeededError, RunnerIdsNotMatchingError, RunnerNotFoundError import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { CreateRunner } from '../models/actions/CreateRunner'; import { Runner } from '../models/entities/Runner'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseRunner } from '../models/responses/ResponseRunner'; @JsonController('/runners') @@ -83,10 +84,11 @@ export class RunnerController { @Delete('/:id') @ResponseSchema(ResponseRunner) - @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) async remove(@EntityFromParam('id') runner: Runner, @QueryParam("force") force: boolean) { - if (!runner) { throw new RunnerNotFoundError(); } + if (!runner) { return null; } const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] }); if (!runner) { diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index a0914e3..f9733d5 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -5,6 +5,7 @@ import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-ext import { RunnerOrganisationHasRunnersError, RunnerOrganisationHasTeamsError, RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; import { CreateRunnerOrganisation } from '../models/actions/CreateRunnerOrganisation'; import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseRunnerOrganisation } from '../models/responses/ResponseRunnerOrganisation'; import { RunnerController } from './RunnerController'; import { RunnerTeamController } from './RunnerTeamController'; @@ -86,12 +87,13 @@ export class RunnerOrganisationController { @Delete('/:id') @ResponseSchema(ResponseRunnerOrganisation) - @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 }) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @ResponseSchema(RunnerOrganisationHasTeamsError, { statusCode: 406 }) @ResponseSchema(RunnerOrganisationHasRunnersError, { statusCode: 406 }) + @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) async remove(@EntityFromParam('id') organisation: RunnerOrganisation, @QueryParam("force") force: boolean) { - if (!organisation) { throw new RunnerOrganisationNotFoundError() } + if (!organisation) { return null; } let runnerOrganisation = await this.runnerOrganisationRepository.findOne(organisation, { relations: ['address', 'contact', 'runners', 'teams'] }); if (!force) { diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index be44aac..4f69c36 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -5,6 +5,7 @@ import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-ext import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; import { CreateRunnerTeam } from '../models/actions/CreateRunnerTeam'; import { RunnerTeam } from '../models/entities/RunnerTeam'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam'; import { RunnerController } from './RunnerController'; @@ -86,11 +87,12 @@ export class RunnerTeamController { @Delete('/:id') @ResponseSchema(ResponseRunnerTeam) - @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) + @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) async remove(@EntityFromParam('id') team: RunnerTeam, @QueryParam("force") force: boolean) { - if (!team) { throw new RunnerTeamNotFoundError(); } + if (!team) { return null; } let runnerTeam = await this.runnerTeamRepository.findOne(team, { relations: ['parentGroup', 'contact', 'runners'] }); if (!force) { diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index bf5fb71..d5fa9b6 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -5,6 +5,7 @@ import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-ext import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; import { CreateTrack } from '../models/actions/CreateTrack'; import { Track } from '../models/entities/Track'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseTrack } from '../models/responses/ResponseTrack'; @JsonController('/tracks') @@ -74,10 +75,11 @@ export class TrackController { @Delete('/:id') @ResponseSchema(ResponseTrack) - @ResponseSchema(TrackNotFoundError, { statusCode: 404 }) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + @OnUndefined(204) @OpenAPI({ description: "Delete a specified track (if it exists)." }) async remove(@EntityFromParam('id') track: Track) { - if (!track) { throw new TrackNotFoundError(); } + if (!track) { return null; } await this.trackRepository.delete(track); return new ResponseTrack(track); diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index b5550a2..a5b3cdd 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -6,6 +6,7 @@ import { UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors import { UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUser } from '../models/actions/CreateUser'; import { User } from '../models/entities/User'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; @JsonController('/users') @@ -72,11 +73,12 @@ export class UserController { @Delete('/:id') @ResponseSchema(User) - @ResponseSchema(UserNotFoundError, { statusCode: 404 }) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) async remove(@EntityFromParam('id') user: User) { if (!user) { - throw new UserNotFoundError(); + return null; } await this.userRepository.delete(user); diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index 89ef3b9..1d4bc38 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -5,6 +5,7 @@ import { EntityFromBody, EntityFromParam } from 'typeorm-routing-controllers-ext import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUserGroup } from '../models/actions/CreateUserGroup'; import { UserGroup } from '../models/entities/UserGroup'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; @JsonController('/usergroups') @@ -71,11 +72,12 @@ export class UserGroupController { @Delete('/:id') @ResponseSchema(UserGroup) - @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + @OnUndefined(204) @OpenAPI({ description: 'Delete a specified usergroup (if it exists).' }) async remove(@EntityFromParam('id') group: UserGroup) { if (!group) { - throw new UserGroupNotFoundError(); + return null; } await this.userGroupsRepository.delete(group); diff --git a/src/models/responses/ResponseEmpty.ts b/src/models/responses/ResponseEmpty.ts new file mode 100644 index 0000000..e1e3b41 --- /dev/null +++ b/src/models/responses/ResponseEmpty.ts @@ -0,0 +1,6 @@ + +/** + * Defines a empty response object +*/ +export class ResponseEmpty { +} From 622bdf7a3fb31e69ee28b43a02b3958c2568c31e Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Wed, 9 Dec 2020 19:40:08 +0100 Subject: [PATCH 193/194] move to dotenv + custom env validations ref #18 --- ormconfig.ts | 2 +- package.json | 4 ++-- src/app.ts | 9 +++++++-- src/config.ts | 22 ++++++++++++++++++---- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/ormconfig.ts b/ormconfig.ts index 852ff37..bf9be07 100644 --- a/ormconfig.ts +++ b/ormconfig.ts @@ -1,4 +1,4 @@ -import { config } from 'dotenv-safe'; +import { config } from 'dotenv'; config(); export default { diff --git a/package.json b/package.json index afc485d..baae298 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "class-validator-jsonschema": "^2.0.3", "consola": "^2.15.0", "cors": "^2.8.5", + "dotenv": "^8.2.0", "express": "^4.17.1", "helmet": "^4.2.0", "jsonwebtoken": "^8.5.1", @@ -45,14 +46,13 @@ }, "devDependencies": { "@types/cors": "^2.8.8", - "@types/dotenv-safe": "^8.1.1", + "@types/dotenv": "^8.2.0", "@types/express": "^4.17.9", "@types/jsonwebtoken": "^8.5.0", "@types/multer": "^1.4.4", "@types/node": "^14.14.9", "@types/swagger-ui-express": "^4.1.2", "@types/uuid": "^8.3.0", - "dotenv-safe": "^8.2.0", "nodemon": "^2.0.6", "sqlite3": "^5.0.0", "ts-node": "^9.0.0", diff --git a/src/app.ts b/src/app.ts index fd3dc8e..664ec6f 100644 --- a/src/app.ts +++ b/src/app.ts @@ -2,7 +2,7 @@ import consola from "consola"; import "reflect-metadata"; import { createExpressServer } from "routing-controllers"; import authchecker from "./authchecker"; -import { config } from './config'; +import { config, e as errors } from './config'; import loaders from "./loaders/index"; import { ErrorHandler } from './middlewares/ErrorHandler'; @@ -23,4 +23,9 @@ async function main() { ); }); } -main(); +if (errors === 0) { + main(); +} else { + console.log("error"); + // something's wrong +} diff --git a/src/config.ts b/src/config.ts index 5dd7d19..567e35b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,8 +1,22 @@ -import * as dotenvSafe from "dotenv-safe"; -dotenvSafe.config(); +import { config as configDotenv } from 'dotenv'; +configDotenv(); export const config = { - internal_port: process.env.APP_PORT || 4010, + internal_port: parseInt(process.env.APP_PORT) || 4010, development: process.env.NODE_ENV === "production", jwt_secret: process.env.JWT_SECRET || "secretjwtsecret", phone_validation_countrycode: process.env.PHONE_COUNTRYCODE || "ZZ" -} \ No newline at end of file +} +let errors = 0 +if (typeof config.internal_port !== "number") { + errors++ +} +if (typeof config.phone_validation_countrycode !== "string") { + errors++ +} +if (config.phone_validation_countrycode.length !== 2) { + errors++ +} +if (typeof config.development !== "boolean") { + errors++ +} +export let e = errors \ No newline at end of file From df5b8ac141ebd009018537ca6107f212dc6403c6 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Wed, 9 Dec 2020 19:41:15 +0100 Subject: [PATCH 194/194] Went back to using id's for deletion (for cleaner query params) ref #13 #17 --- src/controllers/RunnerController.ts | 5 +++-- src/controllers/RunnerOrganisationController.ts | 9 +++++---- src/controllers/RunnerTeamController.ts | 7 ++++--- src/controllers/TrackController.ts | 5 +++-- src/controllers/UserController.ts | 5 +++-- src/controllers/UserGroupController.ts | 5 +++-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/controllers/RunnerController.ts b/src/controllers/RunnerController.ts index 1d61eba..53b869d 100644 --- a/src/controllers/RunnerController.ts +++ b/src/controllers/RunnerController.ts @@ -1,7 +1,7 @@ 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, EntityFromParam } from 'typeorm-routing-controllers-extensions'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { RunnerGroupNeededError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { CreateRunner } from '../models/actions/CreateRunner'; @@ -87,7 +87,8 @@ export class RunnerController { @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) - async remove(@EntityFromParam('id') runner: Runner, @QueryParam("force") force: boolean) { + async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { + let runner = await this.runnerRepository.findOne({ id: id }); if (!runner) { return null; } const responseRunner = await this.runnerRepository.findOne(runner, { relations: ['scans', 'group'] }); diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index f9733d5..8471e90 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -1,7 +1,7 @@ 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, EntityFromParam } from 'typeorm-routing-controllers-extensions'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { RunnerOrganisationHasRunnersError, RunnerOrganisationHasTeamsError, RunnerOrganisationIdsNotMatchingError, RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors'; import { CreateRunnerOrganisation } from '../models/actions/CreateRunnerOrganisation'; import { RunnerOrganisation } from '../models/entities/RunnerOrganisation'; @@ -92,7 +92,8 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisationHasRunnersError, { statusCode: 406 }) @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runnerOrganisation (if it exists).' }) - async remove(@EntityFromParam('id') organisation: RunnerOrganisation, @QueryParam("force") force: boolean) { + async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { + let organisation = await this.runnerOrganisationRepository.findOne({ id: id }); if (!organisation) { return null; } let runnerOrganisation = await this.runnerOrganisationRepository.findOne(organisation, { relations: ['address', 'contact', 'runners', 'teams'] }); @@ -103,7 +104,7 @@ export class RunnerOrganisationController { } const teamController = new RunnerTeamController() for (let team of runnerOrganisation.teams) { - await teamController.remove(team, true); + await teamController.remove(team.id, true); } if (!force) { @@ -113,7 +114,7 @@ export class RunnerOrganisationController { } const runnerController = new RunnerController() for (let runner of runnerOrganisation.runners) { - await runnerController.remove(runner, true); + await runnerController.remove(runner.id, true); } const responseOrganisation = new ResponseRunnerOrganisation(runnerOrganisation); diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 4f69c36..2eeae3e 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -1,7 +1,7 @@ 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, EntityFromParam } from 'typeorm-routing-controllers-extensions'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; import { CreateRunnerTeam } from '../models/actions/CreateRunnerTeam'; import { RunnerTeam } from '../models/entities/RunnerTeam'; @@ -91,7 +91,8 @@ export class RunnerTeamController { @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runnerTeam (if it exists).' }) - async remove(@EntityFromParam('id') team: RunnerTeam, @QueryParam("force") force: boolean) { + async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { + let team = await this.runnerTeamRepository.findOne({ id: id }); if (!team) { return null; } let runnerTeam = await this.runnerTeamRepository.findOne(team, { relations: ['parentGroup', 'contact', 'runners'] }); @@ -102,7 +103,7 @@ export class RunnerTeamController { } const runnerController = new RunnerController() for (let runner of runnerTeam.runners) { - await runnerController.remove(runner, true); + await runnerController.remove(runner.id, true); } const responseTeam = new ResponseRunnerTeam(runnerTeam); diff --git a/src/controllers/TrackController.ts b/src/controllers/TrackController.ts index d5fa9b6..e17e098 100644 --- a/src/controllers/TrackController.ts +++ b/src/controllers/TrackController.ts @@ -1,7 +1,7 @@ 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, EntityFromParam } from 'typeorm-routing-controllers-extensions'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { TrackIdsNotMatchingError, TrackNotFoundError } from "../errors/TrackErrors"; import { CreateTrack } from '../models/actions/CreateTrack'; import { Track } from '../models/entities/Track'; @@ -78,7 +78,8 @@ export class TrackController { @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @OnUndefined(204) @OpenAPI({ description: "Delete a specified track (if it exists)." }) - async remove(@EntityFromParam('id') track: Track) { + async remove(@Param("id") id: number) { + let track = await this.trackRepository.findOne({ id: id }); if (!track) { return null; } await this.trackRepository.delete(track); diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index a5b3cdd..f71fd60 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -1,7 +1,7 @@ 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, EntityFromParam } from 'typeorm-routing-controllers-extensions'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { UserIdsNotMatchingError, UserNotFoundError } from '../errors/UserErrors'; import { UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUser } from '../models/actions/CreateUser'; @@ -76,7 +76,8 @@ export class UserController { @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @OnUndefined(204) @OpenAPI({ description: 'Delete a specified runner (if it exists).' }) - async remove(@EntityFromParam('id') user: User) { + async remove(@Param("id") id: number) { + let user = await this.userRepository.findOne({ id: id }); if (!user) { return null; } diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index 1d4bc38..a317366 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -1,7 +1,7 @@ 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, EntityFromParam } from 'typeorm-routing-controllers-extensions'; +import { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUserGroup } from '../models/actions/CreateUserGroup'; import { UserGroup } from '../models/entities/UserGroup'; @@ -75,7 +75,8 @@ export class UserGroupController { @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @OnUndefined(204) @OpenAPI({ description: 'Delete a specified usergroup (if it exists).' }) - async remove(@EntityFromParam('id') group: UserGroup) { + async remove(@Param("id") id: number) { + let group = await this.userGroupsRepository.findOne({ id: id }); if (!group) { return null; }