Merge branch 'dev' into feature/104-contacts

This commit is contained in:
2021-01-19 16:44:34 +01:00
26 changed files with 1761 additions and 1497 deletions

View File

@@ -1,90 +1,93 @@
import {
IsInt,
IsNotEmpty,
IsOptional,
IsPostalCode,
IsString
} from "class-validator";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { config } from '../../config';
import { IAddressUser } from './IAddressUser';
/**
* Defines the Address entity.
* Implemented this way to prevent any formatting differences.
*/
@Entity()
export class Address {
/**
* Autogenerated unique id (primary key).
*/
@PrimaryGeneratedColumn()
@IsInt()
id: number;
/**
* The address's description.
* Optional and mostly for UX.
*/
@Column({ nullable: true })
@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({ nullable: true })
@IsString()
@IsOptional()
address2?: string;
/**
* The address's postal code.
* This will get checked against the postal code syntax for the configured country.
*/
@Column()
@IsString()
@IsNotEmpty()
@IsPostalCode(config.postalcode_validation_countrycode)
postalcode: string;
/**
* The address's city.
*/
@Column()
@IsString()
@IsNotEmpty()
city: string;
/**
* The address's country.
*/
@Column()
@IsString()
@IsNotEmpty()
country: string;
/**
* Used to link the address to participants.
*/
@OneToMany(() => IAddressUser, addressUser => addressUser.address, { nullable: true })
addressUsers: IAddressUser[];
/**
* Turns this entity into it's response class.
*/
public toResponse() {
return new Error("NotImplemented");
}
}
import {
IsNotEmpty,
IsOptional,
IsPostalCode,
IsString
} from "class-validator";
import { Column } from "typeorm";
import ValidatorJS from 'validator';
import { config } from '../../config';
import { AddressCityEmptyError, AddressCountryEmptyError, AddressFirstLineEmptyError, AddressPostalCodeEmptyError, AddressPostalCodeInvalidError } from '../../errors/AddressErrors';
/**
* Defines the Address class.
* Implemented this way to prevent any formatting differences.
*/
export class Address {
/**
* 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({ nullable: true })
@IsString()
@IsOptional()
address2?: string;
/**
* The address's postal code.
* This will get checked against the postal code syntax for the configured country.
*/
@Column()
@IsString()
@IsNotEmpty()
@IsPostalCode(config.postalcode_validation_countrycode)
postalcode: string;
/**
* The address's city.
*/
@Column()
@IsString()
@IsNotEmpty()
city: string;
/**
* The address's country.
*/
@Column()
@IsString()
@IsNotEmpty()
country: string;
public reset() {
this.address1 = null;
this.address2 = null;
this.city = null;
this.country = null;
this.postalcode = null;
}
/**
* Checks if this is a valid address
*/
public static isValidAddress(address: Address): Boolean {
if (address == null) { return false; }
if (address.address1 == null || address.city == null || address.country == null || address.postalcode == null) { return false; }
if (ValidatorJS.isPostalCode(address.postalcode, config.postalcode_validation_countrycode) == false) { return false; }
return true;
}
/**
* This function validates addresses.
* This is a workaround for non-existant class validation for embedded entities.
* @param address The address that shall get validated.
*/
public static validate(address: Address) {
if (address == null) { return; }
if (address.address1 == null && address.city == null && address.country == null && address.postalcode == null) { return; }
if (address.address1 == null) { throw new AddressFirstLineEmptyError(); }
if (address.postalcode == null) { throw new AddressPostalCodeEmptyError(); }
if (address.city == null) { throw new AddressCityEmptyError(); }
if (address.country == null) { throw new AddressCountryEmptyError(); }
if (ValidatorJS.isPostalCode(address.postalcode.toString(), config.postalcode_validation_countrycode) == false) { throw new AddressPostalCodeInvalidError(); }
}
}

View File

@@ -7,11 +7,10 @@ import {
IsString
} from "class-validator";
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { config } from '../../config';
import { ResponseGroupContact } from '../responses/ResponseGroupContact';
import { Address } from "./Address";
import { IAddressUser } from './IAddressUser';
import { RunnerGroup } from "./RunnerGroup";
/**
@@ -19,7 +18,7 @@ import { RunnerGroup } from "./RunnerGroup";
* Mainly it's own class to reduce duplicate code and enable contact's to be associated with multiple groups.
*/
@Entity()
export class GroupContact implements IAddressUser {
export class GroupContact {
/**
* Autogenerated unique id (primary key).
*/
@@ -56,7 +55,7 @@ export class GroupContact implements IAddressUser {
* This is a address object to prevent any formatting differences.
*/
@IsOptional()
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
@Column(type => Address)
address?: Address;
/**

View File

@@ -1,20 +0,0 @@
import { Entity, ManyToOne, PrimaryColumn } from 'typeorm';
import { Address } from './Address';
/**
* The interface(tm) all entities using addresses have to implement.
* This is a abstract class, because apparently typeorm can't really work with interfaces :/
*/
@Entity()
export abstract class IAddressUser {
@PrimaryColumn()
id: number;
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
address?: Address
/**
* Turns this entity into it's response class.
*/
public abstract toResponse();
}

View File

@@ -7,11 +7,10 @@ import {
IsString
} from "class-validator";
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
import { Column, Entity, PrimaryGeneratedColumn, TableInheritance } from "typeorm";
import { config } from '../../config';
import { ResponseParticipant } from '../responses/ResponseParticipant';
import { Address } from "./Address";
import { IAddressUser } from './IAddressUser';
/**
* Defines the Participant entity.
@@ -19,7 +18,7 @@ import { IAddressUser } from './IAddressUser';
*/
@Entity()
@TableInheritance({ column: { name: "type", type: "varchar" } })
export abstract class Participant implements IAddressUser {
export abstract class Participant {
/**
* Autogenerated unique id (primary key).
*/
@@ -55,7 +54,7 @@ export abstract class Participant implements IAddressUser {
* The participant's address.
* This is a address object to prevent any formatting differences.
*/
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
@Column(type => Address)
address?: Address;
/**

View File

@@ -1,65 +1,64 @@
import { IsInt, IsOptional } from "class-validator";
import { ChildEntity, ManyToOne, OneToMany } from "typeorm";
import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation';
import { Address } from './Address';
import { IAddressUser } from './IAddressUser';
import { Runner } from './Runner';
import { RunnerGroup } from "./RunnerGroup";
import { RunnerTeam } from "./RunnerTeam";
/**
* Defines the RunnerOrganisation entity.
* This usually is a school, club or company.
*/
@ChildEntity()
export class RunnerOrganisation extends RunnerGroup implements IAddressUser {
/**
* The organisations's address.
*/
@IsOptional()
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
address?: Address;
/**
* The organisation's teams.
* Used to link teams to a organisation.
*/
@OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true })
teams: RunnerTeam[];
/**
* Returns all runners associated with this organisation (directly or indirectly via teams).
*/
public get allRunners(): Runner[] {
let returnRunners: Runner[] = new Array<Runner>();
returnRunners.push(...this.runners);
for (let team of this.teams) {
returnRunners.push(...team.runners)
}
return returnRunners;
}
/**
* Returns the total distance ran by this group's runners based on all their valid scans.
*/
@IsInt()
public get distance(): number {
return this.allRunners.reduce((sum, current) => sum + current.distance, 0);
}
/**
* Returns the total donations a runner has collected based on his linked donations and distance ran.
*/
@IsInt()
public get distanceDonationAmount(): number {
return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0);
}
/**
* Turns this entity into it's response class.
*/
public toResponse(): ResponseRunnerOrganisation {
return new ResponseRunnerOrganisation(this);
}
import { IsInt, IsOptional } from "class-validator";
import { ChildEntity, Column, OneToMany } from "typeorm";
import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation';
import { Address } from './Address';
import { Runner } from './Runner';
import { RunnerGroup } from "./RunnerGroup";
import { RunnerTeam } from "./RunnerTeam";
/**
* Defines the RunnerOrganisation entity.
* This usually is a school, club or company.
*/
@ChildEntity()
export class RunnerOrganisation extends RunnerGroup {
/**
* The organisations's address.
*/
@IsOptional()
@Column(type => Address)
address?: Address;
/**
* The organisation's teams.
* Used to link teams to a organisation.
*/
@OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true })
teams: RunnerTeam[];
/**
* Returns all runners associated with this organisation (directly or indirectly via teams).
*/
public get allRunners(): Runner[] {
let returnRunners: Runner[] = new Array<Runner>();
returnRunners.push(...this.runners);
for (let team of this.teams) {
returnRunners.push(...team.runners)
}
return returnRunners;
}
/**
* Returns the total distance ran by this group's runners based on all their valid scans.
*/
@IsInt()
public get distance(): number {
return this.allRunners.reduce((sum, current) => sum + current.distance, 0);
}
/**
* Returns the total donations a runner has collected based on his linked donations and distance ran.
*/
@IsInt()
public get distanceDonationAmount(): number {
return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0);
}
/**
* Turns this entity into it's response class.
*/
public toResponse(): ResponseRunnerOrganisation {
return new ResponseRunnerOrganisation(this);
}
}