From 7fbe649dc90f4bb9f240c5a80fed447048e5e105 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:45:49 +0100 Subject: [PATCH 01/52] Switched Address to embedded entity ref #105 --- src/models/entities/Address.ts | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index dbb7eb3..d2dd961 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -1,36 +1,17 @@ import { - IsInt, IsNotEmpty, IsOptional, IsPostalCode, IsString } from "class-validator"; -import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { Column } 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. @@ -75,12 +56,6 @@ export class Address { @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. */ From 673dea2e5754e99ff77f7556d4fc03d4cca28a94 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:48:20 +0100 Subject: [PATCH 02/52] Removed (now useless) relations ref #105 --- src/controllers/RunnerOrganisationController.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index 15c12ff..fbbd03a 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -29,7 +29,7 @@ export class RunnerOrganisationController { @OpenAPI({ description: 'Lists all organisations.
This includes their address, contact and teams (if existing/associated).' }) async getAll() { let responseTeams: ResponseRunnerOrganisation[] = new Array(); - const runners = await this.runnerOrganisationRepository.find({ relations: ['address', 'contact', 'teams'] }); + const runners = await this.runnerOrganisationRepository.find({ relations: ['contact', 'teams'] }); runners.forEach(runner => { responseTeams.push(new ResponseRunnerOrganisation(runner)); }); @@ -43,7 +43,7 @@ export class RunnerOrganisationController { @OnUndefined(RunnerOrganisationNotFoundError) @OpenAPI({ description: 'Lists all information about the organisation whose id got provided.' }) async getOne(@Param('id') id: number) { - let runnerOrg = await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['address', 'contact', 'teams'] }); + let runnerOrg = await this.runnerOrganisationRepository.findOne({ id: id }, { relations: ['contact', 'teams'] }); if (!runnerOrg) { throw new RunnerOrganisationNotFoundError(); } return new ResponseRunnerOrganisation(runnerOrg); } @@ -62,7 +62,7 @@ export class RunnerOrganisationController { runnerOrganisation = await this.runnerOrganisationRepository.save(runnerOrganisation); - return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne(runnerOrganisation, { relations: ['address', 'contact', 'teams'] })); + return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne(runnerOrganisation, { relations: ['contact', 'teams'] })); } @Put('/:id') @@ -84,7 +84,7 @@ export class RunnerOrganisationController { await this.runnerOrganisationRepository.save(await updateOrganisation.update(oldRunnerOrganisation)); - return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne(id, { relations: ['address', 'contact', 'teams'] })); + return new ResponseRunnerOrganisation(await this.runnerOrganisationRepository.findOne(id, { relations: ['contact', 'teams'] })); } @Delete('/:id') @@ -98,7 +98,7 @@ export class RunnerOrganisationController { 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'] }); + let runnerOrganisation = await this.runnerOrganisationRepository.findOne(organisation, { relations: ['contact', 'runners', 'teams'] }); if (!force) { if (runnerOrganisation.teams.length != 0) { From e2651728c5abf2273bf51a7652c51d55d8fa0a2f Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:50:04 +0100 Subject: [PATCH 03/52] Removed the IAddressUser Interface entity ref #105 - It was only needed b/c addresses were implemented as their own class --- src/models/entities/GroupContact.ts | 7 +++---- src/models/entities/IAddressUser.ts | 20 -------------------- src/models/entities/Participant.ts | 7 +++---- src/models/entities/RunnerOrganisation.ts | 7 +++---- 4 files changed, 9 insertions(+), 32 deletions(-) delete mode 100644 src/models/entities/IAddressUser.ts diff --git a/src/models/entities/GroupContact.ts b/src/models/entities/GroupContact.ts index f650259..d2b0abe 100644 --- a/src/models/entities/GroupContact.ts +++ b/src/models/entities/GroupContact.ts @@ -7,10 +7,9 @@ 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 { Address } from "./Address"; -import { IAddressUser } from './IAddressUser'; import { RunnerGroup } from "./RunnerGroup"; /** @@ -18,7 +17,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). */ @@ -55,7 +54,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; /** diff --git a/src/models/entities/IAddressUser.ts b/src/models/entities/IAddressUser.ts deleted file mode 100644 index 3d8eaf9..0000000 --- a/src/models/entities/IAddressUser.ts +++ /dev/null @@ -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(); -} diff --git a/src/models/entities/Participant.ts b/src/models/entities/Participant.ts index fa40a8f..ccf7656 100644 --- a/src/models/entities/Participant.ts +++ b/src/models/entities/Participant.ts @@ -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; /** diff --git a/src/models/entities/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts index ebae8fd..e5f3330 100644 --- a/src/models/entities/RunnerOrganisation.ts +++ b/src/models/entities/RunnerOrganisation.ts @@ -1,8 +1,7 @@ import { IsInt, IsOptional } from "class-validator"; -import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; +import { ChildEntity, Column, 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"; @@ -12,13 +11,13 @@ import { RunnerTeam } from "./RunnerTeam"; * This usually is a school, club or company. */ @ChildEntity() -export class RunnerOrganisation extends RunnerGroup implements IAddressUser { +export class RunnerOrganisation extends RunnerGroup { /** * The organisations's address. */ @IsOptional() - @ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) + @Column(type => Address) address?: Address; /** From dafac06bc84d1b237096a561b3adcd3ca5cb1dd8 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:53:18 +0100 Subject: [PATCH 04/52] Updated the responseclasses to use the new address implementation ref #105 --- src/models/responses/ResponseParticipant.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/models/responses/ResponseParticipant.ts b/src/models/responses/ResponseParticipant.ts index 148bcff..93a6918 100644 --- a/src/models/responses/ResponseParticipant.ts +++ b/src/models/responses/ResponseParticipant.ts @@ -1,4 +1,5 @@ -import { IsInt, IsString } from "class-validator"; +import { IsInt, IsObject, IsOptional, IsString } from "class-validator"; +import { Address } from '../entities/Address'; import { Participant } from '../entities/Participant'; /** @@ -41,6 +42,13 @@ export abstract class ResponseParticipant { @IsString() email?: string; + /** + * The participant's address. + */ + @IsOptional() + @IsObject() + address?: Address; + /** * Creates a ResponseParticipant object from a participant. * @param participant The participant the response shall be build for. @@ -52,5 +60,6 @@ export abstract class ResponseParticipant { this.lastname = participant.lastname; this.phone = participant.phone; this.email = participant.email; + this.address = participant.address; } } From 2cd15d25e934a5439bfea4de901f136e360e17f6 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:55:30 +0100 Subject: [PATCH 05/52] Switched the create classes over to the new address implementation ref #105 --- src/models/actions/create/CreateDonor.ts | 2 +- .../actions/create/CreateGroupContact.ts | 19 ++++-------------- .../actions/create/CreateParticipant.ts | 18 +++-------------- src/models/actions/create/CreateRunner.ts | 2 +- .../create/CreateRunnerOrganisation.ts | 20 ++++--------------- 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/src/models/actions/create/CreateDonor.ts b/src/models/actions/create/CreateDonor.ts index 791461a..a99d139 100644 --- a/src/models/actions/create/CreateDonor.ts +++ b/src/models/actions/create/CreateDonor.ts @@ -26,8 +26,8 @@ export class CreateDonor extends CreateParticipant { newDonor.lastname = this.lastname; newDonor.phone = this.phone; newDonor.email = this.email; - newDonor.address = await this.getAddress(); newDonor.receiptNeeded = this.receiptNeeded; + newDonor.address = this.address; if (this.receiptNeeded == true && this.address == null) { throw new DonorReceiptAddressNeededError() diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index b9a9d41..f74d633 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -1,7 +1,5 @@ -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; +import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { config } from '../../../config'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; import { Address } from '../../entities/Address'; import { GroupContact } from '../../entities/GroupContact'; @@ -33,9 +31,9 @@ export class CreateGroupContact { /** * The new contact's address's id. */ - @IsInt() @IsOptional() - address?: number; + @IsObject() + address?: Address; /** * The contact's phone number. @@ -52,15 +50,6 @@ export class CreateGroupContact { @IsEmail() email?: string; - /** - * Gets the new contact's address by it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } /** * Creates a new Address entity from this. @@ -72,7 +61,7 @@ export class CreateGroupContact { contact.lastname = this.lastname; contact.email = this.email; contact.phone = this.phone; - contact.address = await this.getAddress(); + contact.address = this.address; return null; } } \ No newline at end of file diff --git a/src/models/actions/create/CreateParticipant.ts b/src/models/actions/create/CreateParticipant.ts index 6518666..16ef7cc 100644 --- a/src/models/actions/create/CreateParticipant.ts +++ b/src/models/actions/create/CreateParticipant.ts @@ -1,7 +1,5 @@ -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; +import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { config } from '../../../config'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; import { Address } from '../../entities/Address'; /** @@ -49,17 +47,7 @@ export abstract class CreateParticipant { /** * The new participant's address's id. */ - @IsInt() @IsOptional() - address?: number; - - /** - * Gets the new participant's address by it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } + @IsObject() + address?: Address; } \ No newline at end of file diff --git a/src/models/actions/create/CreateRunner.ts b/src/models/actions/create/CreateRunner.ts index 2286fdf..83d9ca9 100644 --- a/src/models/actions/create/CreateRunner.ts +++ b/src/models/actions/create/CreateRunner.ts @@ -30,7 +30,7 @@ export class CreateRunner extends CreateParticipant { newRunner.phone = this.phone; newRunner.email = this.email; newRunner.group = await this.getGroup(); - newRunner.address = await this.getAddress(); + newRunner.address = this.address; return newRunner; } diff --git a/src/models/actions/create/CreateRunnerOrganisation.ts b/src/models/actions/create/CreateRunnerOrganisation.ts index 1938eee..eba73b6 100644 --- a/src/models/actions/create/CreateRunnerOrganisation.ts +++ b/src/models/actions/create/CreateRunnerOrganisation.ts @@ -1,6 +1,4 @@ -import { IsInt, IsOptional } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; +import { IsObject, IsOptional } from 'class-validator'; import { Address } from '../../entities/Address'; import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; import { CreateRunnerGroup } from './CreateRunnerGroup'; @@ -12,19 +10,9 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup { /** * The new organisation's address's id. */ - @IsInt() @IsOptional() - address?: number; - - /** - * Gets the org's address by it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } + @IsObject() + address?: Address; /** * Creates a new RunnerOrganisation entity from this. @@ -34,7 +22,7 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup { newRunnerOrganisation.name = this.name; newRunnerOrganisation.contact = await this.getContact(); - newRunnerOrganisation.address = await this.getAddress(); + newRunnerOrganisation.address = this.address; return newRunnerOrganisation; } From d0df5dd641ac17f1fd8ad6bd8b46afa9dd2745c3 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:56:46 +0100 Subject: [PATCH 06/52] Switched the update classes over to the new address implementation ref #105 --- src/models/actions/update/UpdateDonor.ts | 2 +- src/models/actions/update/UpdateRunner.ts | 2 +- .../update/UpdateRunnerOrganisation.ts | 20 ++++--------------- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/models/actions/update/UpdateDonor.ts b/src/models/actions/update/UpdateDonor.ts index 8c77ff1..e01f61a 100644 --- a/src/models/actions/update/UpdateDonor.ts +++ b/src/models/actions/update/UpdateDonor.ts @@ -33,7 +33,7 @@ export class UpdateDonor extends CreateParticipant { donor.phone = this.phone; donor.email = this.email; donor.receiptNeeded = this.receiptNeeded; - donor.address = await this.getAddress(); + donor.address = this.address; if (this.receiptNeeded == true && this.address == null) { throw new DonorReceiptAddressNeededError() diff --git a/src/models/actions/update/UpdateRunner.ts b/src/models/actions/update/UpdateRunner.ts index e9e323b..cb24f1f 100644 --- a/src/models/actions/update/UpdateRunner.ts +++ b/src/models/actions/update/UpdateRunner.ts @@ -35,7 +35,7 @@ export class UpdateRunner extends CreateParticipant { runner.phone = this.phone; runner.email = this.email; runner.group = await this.getGroup(); - runner.address = await this.getAddress(); + runner.address = this.address; return runner; } diff --git a/src/models/actions/update/UpdateRunnerOrganisation.ts b/src/models/actions/update/UpdateRunnerOrganisation.ts index 20656ec..6023b84 100644 --- a/src/models/actions/update/UpdateRunnerOrganisation.ts +++ b/src/models/actions/update/UpdateRunnerOrganisation.ts @@ -1,6 +1,4 @@ -import { IsInt, IsOptional } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; +import { IsInt, IsObject, IsOptional } from 'class-validator'; import { Address } from '../../entities/Address'; import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; import { CreateRunnerGroup } from '../create/CreateRunnerGroup'; @@ -20,19 +18,9 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup { /** * The updated organisation's address's id. */ - @IsInt() @IsOptional() - address?: number; - - /** - * Loads the organisation's address based on it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } + @IsObject() + address?: Address; /** * Updates a provided RunnerOrganisation entity based on this. @@ -41,7 +29,7 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup { organisation.name = this.name; organisation.contact = await this.getContact(); - organisation.address = await this.getAddress(); + organisation.address = this.address; return organisation; } From 8bc01d3f2406ce8e58c2ab2963c858495c510dcf Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:57:58 +0100 Subject: [PATCH 07/52] Updated comments ref #105 --- src/models/actions/create/CreateGroupContact.ts | 2 +- src/models/actions/create/CreateParticipant.ts | 2 +- src/models/actions/create/CreateRunnerOrganisation.ts | 2 +- src/models/actions/update/UpdateRunnerOrganisation.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index f74d633..c38682a 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -29,7 +29,7 @@ export class CreateGroupContact { lastname: string; /** - * The new contact's address's id. + * The new contact's address. */ @IsOptional() @IsObject() diff --git a/src/models/actions/create/CreateParticipant.ts b/src/models/actions/create/CreateParticipant.ts index 16ef7cc..8e008e7 100644 --- a/src/models/actions/create/CreateParticipant.ts +++ b/src/models/actions/create/CreateParticipant.ts @@ -45,7 +45,7 @@ export abstract class CreateParticipant { email?: string; /** - * The new participant's address's id. + * The new participant's address. */ @IsOptional() @IsObject() diff --git a/src/models/actions/create/CreateRunnerOrganisation.ts b/src/models/actions/create/CreateRunnerOrganisation.ts index eba73b6..8edcebb 100644 --- a/src/models/actions/create/CreateRunnerOrganisation.ts +++ b/src/models/actions/create/CreateRunnerOrganisation.ts @@ -8,7 +8,7 @@ import { CreateRunnerGroup } from './CreateRunnerGroup'; */ export class CreateRunnerOrganisation extends CreateRunnerGroup { /** - * The new organisation's address's id. + * The new organisation's address. */ @IsOptional() @IsObject() diff --git a/src/models/actions/update/UpdateRunnerOrganisation.ts b/src/models/actions/update/UpdateRunnerOrganisation.ts index 6023b84..ef52c2c 100644 --- a/src/models/actions/update/UpdateRunnerOrganisation.ts +++ b/src/models/actions/update/UpdateRunnerOrganisation.ts @@ -16,7 +16,7 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup { id: number; /** - * The updated organisation's address's id. + * The updated organisation's address. */ @IsOptional() @IsObject() From 58ae9b589aef3580f4b8558c3d5ddbd7171e7915 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 16:58:55 +0100 Subject: [PATCH 08/52] Removed the address errors ref #105 --- src/errors/AddressErrors.ts | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 src/errors/AddressErrors.ts diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts deleted file mode 100644 index 27f2e5f..0000000 --- a/src/errors/AddressErrors.ts +++ /dev/null @@ -1,24 +0,0 @@ -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" - - @IsString() - message = "The address must be an existing address's id. \n You provided a object of another type." -} - -/** - * Error to throw, when a non-existent address get's loaded. - */ -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 From 2a465f88c58c0b4be3ecd99d96a04c177a40b312 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 17:03:05 +0100 Subject: [PATCH 09/52] Removed old create address class ref #105 --- src/models/actions/create/CreateAddress.ts | 69 ---------------------- src/models/entities/Address.ts | 2 +- 2 files changed, 1 insertion(+), 70 deletions(-) delete mode 100644 src/models/actions/create/CreateAddress.ts diff --git a/src/models/actions/create/CreateAddress.ts b/src/models/actions/create/CreateAddress.ts deleted file mode 100644 index 0241301..0000000 --- a/src/models/actions/create/CreateAddress.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator'; -import { config } from '../../../config'; -import { Address } from '../../entities/Address'; - -/** - * This classed is used to create a new Address entity from a json body (post request). - */ -export class CreateAddress { - /** - * The newaddress's description. - */ - @IsString() - @IsOptional() - description?: string; - - /** - * The new address's first line. - * Containing the street and house number. - */ - @IsString() - @IsNotEmpty() - address1: string; - - /** - * The new address's second line. - * Containing optional information. - */ - @IsString() - @IsOptional() - address2?: string; - - /** - * The new address's postal code. - * This will get checked against the postal code syntax for the configured country. - */ - @IsString() - @IsNotEmpty() - @IsPostalCode(config.postalcode_validation_countrycode) - postalcode: string; - - /** - * The new address's city. - */ - @IsString() - @IsNotEmpty() - city: string; - - /** - * The new address's country. - */ - @IsString() - @IsNotEmpty() - country: string; - - /** - * Creates a new Address entity from this. - */ - public async toEntity(): Promise
{ - 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/entities/Address.ts b/src/models/entities/Address.ts index d2dd961..ed0d624 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -8,7 +8,7 @@ import { Column } from "typeorm"; import { config } from '../../config'; /** - * Defines the Address entity. + * Defines the Address class. * Implemented this way to prevent any formatting differences. */ export class Address { From ae7c5ff0c387e9337d01a9dd819a4dddc208f6dd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 18:28:19 +0100 Subject: [PATCH 10/52] Added address validity check ref #105 --- src/models/actions/create/CreateDonor.ts | 2 +- src/models/entities/Address.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/models/actions/create/CreateDonor.ts b/src/models/actions/create/CreateDonor.ts index a99d139..5143a2c 100644 --- a/src/models/actions/create/CreateDonor.ts +++ b/src/models/actions/create/CreateDonor.ts @@ -29,7 +29,7 @@ export class CreateDonor extends CreateParticipant { newDonor.receiptNeeded = this.receiptNeeded; newDonor.address = this.address; - if (this.receiptNeeded == true && this.address == null) { + if (this.receiptNeeded == true && this.address.isValidAddress == false) { throw new DonorReceiptAddressNeededError() } diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index ed0d624..9ccc026 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -57,9 +57,10 @@ export class Address { country: string; /** - * Turns this entity into it's response class. + * Checks if this is a valid address */ - public toResponse() { - return new Error("NotImplemented"); + public get isValidAddress(): Boolean { + if (!this.address1 || !this.city || !this.country || !this.postalcode) { return false; } + return true; } } From 8dbee32eeec8ee3d013e4446e8f53544ee4cb577 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 18:34:53 +0100 Subject: [PATCH 11/52] Test's now accept the new address format ref #105 --- src/tests/donors/donor_add.spec.ts | 10 ---------- src/tests/runnerOrgs/org_add.spec.ts | 16 ++++++++++++++-- src/tests/runnerOrgs/org_delete.spec.ts | 16 ++++++++++++++-- src/tests/runnerOrgs/org_update.spec.ts | 8 +++++++- src/tests/runnerTeams/team_update.spec.ts | 1 - src/tests/runners/runner_update.spec.ts | 6 +----- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/tests/donors/donor_add.spec.ts b/src/tests/donors/donor_add.spec.ts index 954fd2e..3226b8c 100644 --- a/src/tests/donors/donor_add.spec.ts +++ b/src/tests/donors/donor_add.spec.ts @@ -29,16 +29,6 @@ describe('POST /api/donors with errors', () => { expect(res2.status).toEqual(400); expect(res2.headers['content-type']).toContain("application/json") }); - it('creating a new donor with a invalid address should return 404', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "middlename": "middle", - "lastname": "last", - "address": 99999999999999999999999999 - }, axios_config); - expect(res2.status).toEqual(404); - expect(res2.headers['content-type']).toContain("application/json") - }); it('creating a new donor with a invalid phone number should return 400', async () => { const res2 = await axios.post(base + '/api/donors', { "firstname": "first", diff --git a/src/tests/runnerOrgs/org_add.spec.ts b/src/tests/runnerOrgs/org_add.spec.ts index 6ee0606..9d939ef 100644 --- a/src/tests/runnerOrgs/org_add.spec.ts +++ b/src/tests/runnerOrgs/org_add.spec.ts @@ -56,7 +56,13 @@ describe('adding + getting from all orgs', () => { expect(added_org).toEqual({ "name": "test123", "contact": null, - "address": null, + "address": { + "address1": null, + "address2": null, + "city": null, + "country": null, + "postalcode": null, + }, "teams": [] }) }); @@ -83,7 +89,13 @@ describe('adding + getting explicitly', () => { expect(added_org2).toEqual({ "name": "test123", "contact": null, - "address": null, + "address": { + "address1": null, + "address2": null, + "city": null, + "country": null, + "postalcode": null, + }, "teams": [] }) }); diff --git a/src/tests/runnerOrgs/org_delete.spec.ts b/src/tests/runnerOrgs/org_delete.spec.ts index 08891f8..d28faa1 100644 --- a/src/tests/runnerOrgs/org_delete.spec.ts +++ b/src/tests/runnerOrgs/org_delete.spec.ts @@ -44,7 +44,13 @@ describe('adding + deletion (successfull)', () => { expect(added_org2).toEqual({ "name": "test123", "contact": null, - "address": null, + "address": { + "address1": null, + "address2": null, + "city": null, + "country": null, + "postalcode": null, + }, "teams": [] }); }); @@ -121,7 +127,13 @@ describe('adding + deletion with teams still existing (with force)', () => { expect(added_org2).toEqual({ "name": "test123", "contact": null, - "address": null + "address": { + "address1": null, + "address2": null, + "city": null, + "country": null, + "postalcode": null, + }, }); }); it('check if org really was deleted', async () => { diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts index e6a1055..3db1254 100644 --- a/src/tests/runnerOrgs/org_update.spec.ts +++ b/src/tests/runnerOrgs/org_update.spec.ts @@ -42,7 +42,13 @@ describe('adding + updating name', () => { expect(added_org2).toEqual({ "name": "testlelele", "contact": null, - "address": null, + "address": { + "address1": null, + "address2": null, + "city": null, + "country": null, + "postalcode": null, + }, "teams": [] }) }); diff --git a/src/tests/runnerTeams/team_update.spec.ts b/src/tests/runnerTeams/team_update.spec.ts index 7acacd7..0257bfd 100644 --- a/src/tests/runnerTeams/team_update.spec.ts +++ b/src/tests/runnerTeams/team_update.spec.ts @@ -123,7 +123,6 @@ describe('add+update parent org (valid)', () => { let updated_team = res4.data; expect(res4.status).toEqual(200); expect(res4.headers['content-type']).toContain("application/json") - delete added_org2.address; delete added_org2.contact; delete added_org2.teams; expect(updated_team.parentGroup).toEqual(added_org2) diff --git a/src/tests/runners/runner_update.spec.ts b/src/tests/runners/runner_update.spec.ts index 39acc5f..92c80e7 100644 --- a/src/tests/runners/runner_update.spec.ts +++ b/src/tests/runners/runner_update.spec.ts @@ -44,7 +44,6 @@ describe('Update runner name after adding', () => { expect(res3.status).toEqual(200); expect(res3.headers['content-type']).toContain("application/json") updated_runner = res3.data; - delete added_org.address; delete added_org.contact; delete added_org.teams; runnercopy.group = added_org; @@ -56,7 +55,6 @@ describe('Update runner group after adding', () => { let added_org_id; let added_org_2; let added_runner; - let updated_runner; it('creating a new org with just a name should return 200', async () => { const res1 = await axios.post(base + '/api/organisations', { "name": "test123" @@ -81,7 +79,6 @@ describe('Update runner group after adding', () => { "name": "test123" }, axios_config); added_org_2 = res3.data - delete added_org_2.address; delete added_org_2.contact; delete added_org_2.teams; expect(res3.status).toEqual(200); @@ -92,8 +89,7 @@ describe('Update runner group after adding', () => { const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); expect(res3.status).toEqual(200); expect(res3.headers['content-type']).toContain("application/json") - updated_runner = res3.data - expect(updated_runner.group).toEqual(added_org_2); + expect(res3.data.group).toEqual(added_org_2); }); }); // --------------- From 4824547dde4d7f90e9e2377a26df34cabf082fdb Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 18:52:57 +0100 Subject: [PATCH 12/52] Fixed donor address check ref #105 --- src/models/actions/create/CreateDonor.ts | 3 ++- src/models/actions/update/UpdateDonor.ts | 4 ++-- src/models/entities/Address.ts | 5 +++-- src/tests/donors/donor_update.spec.ts | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/models/actions/create/CreateDonor.ts b/src/models/actions/create/CreateDonor.ts index 5143a2c..2a9051f 100644 --- a/src/models/actions/create/CreateDonor.ts +++ b/src/models/actions/create/CreateDonor.ts @@ -1,5 +1,6 @@ import { IsBoolean, IsOptional } from 'class-validator'; import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; +import { Address } from '../../entities/Address'; import { Donor } from '../../entities/Donor'; import { CreateParticipant } from './CreateParticipant'; @@ -29,7 +30,7 @@ export class CreateDonor extends CreateParticipant { newDonor.receiptNeeded = this.receiptNeeded; newDonor.address = this.address; - if (this.receiptNeeded == true && this.address.isValidAddress == false) { + if (this.receiptNeeded == true && Address.isValidAddress(newDonor.address) == false) { throw new DonorReceiptAddressNeededError() } diff --git a/src/models/actions/update/UpdateDonor.ts b/src/models/actions/update/UpdateDonor.ts index e01f61a..3896309 100644 --- a/src/models/actions/update/UpdateDonor.ts +++ b/src/models/actions/update/UpdateDonor.ts @@ -1,5 +1,6 @@ import { IsBoolean, IsInt, IsOptional } from 'class-validator'; import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; +import { Address } from '../../entities/Address'; import { Donor } from '../../entities/Donor'; import { CreateParticipant } from '../create/CreateParticipant'; @@ -34,8 +35,7 @@ export class UpdateDonor extends CreateParticipant { donor.email = this.email; donor.receiptNeeded = this.receiptNeeded; donor.address = this.address; - - if (this.receiptNeeded == true && this.address == null) { + if (this.receiptNeeded == true && Address.isValidAddress(donor.address) == false) { throw new DonorReceiptAddressNeededError() } diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index 9ccc026..f25b5e8 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -59,8 +59,9 @@ export class Address { /** * Checks if this is a valid address */ - public get isValidAddress(): Boolean { - if (!this.address1 || !this.city || !this.country || !this.postalcode) { return false; } + 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; } return true; } } diff --git a/src/tests/donors/donor_update.spec.ts b/src/tests/donors/donor_update.spec.ts index 26f6eb6..a317adc 100644 --- a/src/tests/donors/donor_update.spec.ts +++ b/src/tests/donors/donor_update.spec.ts @@ -60,7 +60,7 @@ describe('Update donor without address but receiptNeeded=true should fail', () = it('creating a new donor with only needed params should return 200', async () => { const res2 = await axios.post(base + '/api/donors', { "firstname": "first", - "lastname": "last", + "lastname": "testtest", }, axios_config); added_donor = res2.data; expect(res2.status).toEqual(200); From f245840cde5726611197b00730aca72ea133c427 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 18:59:06 +0100 Subject: [PATCH 13/52] Implemented postal code validation for the validaton function ref #105 --- src/models/entities/Address.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index f25b5e8..038f5d6 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -5,6 +5,7 @@ import { IsString } from "class-validator"; import { Column } from "typeorm"; +import ValidatorJS from 'validator'; import { config } from '../../config'; /** @@ -62,6 +63,7 @@ export class 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; } } From 9dc9ce37d8fbfc92842e4e05bbde68398324a186 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 20:12:17 +0100 Subject: [PATCH 14/52] Implemented deep address validation ref #105 --- src/errors/AddressErrors.ts | 57 +++++++++++++++++++ src/models/actions/create/CreateDonor.ts | 2 +- .../actions/create/CreateGroupContact.ts | 3 +- src/models/actions/create/CreateRunner.ts | 2 + .../create/CreateRunnerOrganisation.ts | 1 + src/models/actions/update/UpdateDonor.ts | 1 + src/models/actions/update/UpdateRunner.ts | 2 + .../update/UpdateRunnerOrganisation.ts | 1 + src/models/entities/Address.ts | 16 ++++++ 9 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/errors/AddressErrors.ts diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts new file mode 100644 index 0000000..300bb31 --- /dev/null +++ b/src/errors/AddressErrors.ts @@ -0,0 +1,57 @@ +import { IsString } from 'class-validator'; +import { BadRequestError } from 'routing-controllers'; + +/** + * Error to throw when an address's postal code fails validation. + */ +export class AddressPostalCodeInvalidError extends BadRequestError { + @IsString() + name = "AddressPostalCodeInvalidError" + + @IsString() + message = "The postal code you provided is invalid. \n Please check if your postal code follows the postal code validation guidelines." +} + +/** + * Error to throw when an non-empty address's first line isn't set. + */ +export class AddressFirstLineEmptyError extends BadRequestError { + @IsString() + name = "AddressFirstLineEmptyError" + + @IsString() + message = "You provided a empty first address line. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} + +/** + * Error to throw when an non-empty address's postal code isn't set. + */ +export class AddressPostalCodeEmptyError extends BadRequestError { + @IsString() + name = "AddressPostalCodeEmptyError" + + @IsString() + message = "You provided a empty postal code. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} + +/** + * Error to throw when an non-empty address's city isn't set. + */ +export class AddressCityEmptyError extends BadRequestError { + @IsString() + name = "AddressCityEmptyError" + + @IsString() + message = "You provided a empty city. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} + +/** + * Error to throw when an non-empty address's country isn't set. + */ +export class AddressCountryEmptyError extends BadRequestError { + @IsString() + name = "AddressCountryEmptyError" + + @IsString() + message = "You provided a empty country. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} \ No newline at end of file diff --git a/src/models/actions/create/CreateDonor.ts b/src/models/actions/create/CreateDonor.ts index 2a9051f..397a8d7 100644 --- a/src/models/actions/create/CreateDonor.ts +++ b/src/models/actions/create/CreateDonor.ts @@ -29,7 +29,7 @@ export class CreateDonor extends CreateParticipant { newDonor.email = this.email; newDonor.receiptNeeded = this.receiptNeeded; newDonor.address = this.address; - + Address.validate(newDonor.address); if (this.receiptNeeded == true && Address.isValidAddress(newDonor.address) == false) { throw new DonorReceiptAddressNeededError() } diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index c38682a..c6538e8 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -62,6 +62,7 @@ export class CreateGroupContact { contact.email = this.email; contact.phone = this.phone; contact.address = this.address; - return null; + Address.validate(contact.address); + return contact; } } \ No newline at end of file diff --git a/src/models/actions/create/CreateRunner.ts b/src/models/actions/create/CreateRunner.ts index 83d9ca9..9916ba8 100644 --- a/src/models/actions/create/CreateRunner.ts +++ b/src/models/actions/create/CreateRunner.ts @@ -3,6 +3,7 @@ import { getConnectionManager } from 'typeorm'; import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; +import { Address } from '../../entities/Address'; import { Runner } from '../../entities/Runner'; import { RunnerGroup } from '../../entities/RunnerGroup'; import { CreateParticipant } from './CreateParticipant'; @@ -31,6 +32,7 @@ export class CreateRunner extends CreateParticipant { newRunner.email = this.email; newRunner.group = await this.getGroup(); newRunner.address = this.address; + Address.validate(newRunner.address); return newRunner; } diff --git a/src/models/actions/create/CreateRunnerOrganisation.ts b/src/models/actions/create/CreateRunnerOrganisation.ts index 8edcebb..e99d851 100644 --- a/src/models/actions/create/CreateRunnerOrganisation.ts +++ b/src/models/actions/create/CreateRunnerOrganisation.ts @@ -23,6 +23,7 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup { newRunnerOrganisation.name = this.name; newRunnerOrganisation.contact = await this.getContact(); newRunnerOrganisation.address = this.address; + Address.validate(newRunnerOrganisation.address); return newRunnerOrganisation; } diff --git a/src/models/actions/update/UpdateDonor.ts b/src/models/actions/update/UpdateDonor.ts index 3896309..db6a745 100644 --- a/src/models/actions/update/UpdateDonor.ts +++ b/src/models/actions/update/UpdateDonor.ts @@ -35,6 +35,7 @@ export class UpdateDonor extends CreateParticipant { donor.email = this.email; donor.receiptNeeded = this.receiptNeeded; donor.address = this.address; + Address.validate(donor.address); if (this.receiptNeeded == true && Address.isValidAddress(donor.address) == false) { throw new DonorReceiptAddressNeededError() } diff --git a/src/models/actions/update/UpdateRunner.ts b/src/models/actions/update/UpdateRunner.ts index cb24f1f..1202ad0 100644 --- a/src/models/actions/update/UpdateRunner.ts +++ b/src/models/actions/update/UpdateRunner.ts @@ -2,6 +2,7 @@ import { IsInt, IsPositive } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; +import { Address } from '../../entities/Address'; import { Runner } from '../../entities/Runner'; import { RunnerGroup } from '../../entities/RunnerGroup'; import { CreateParticipant } from '../create/CreateParticipant'; @@ -36,6 +37,7 @@ export class UpdateRunner extends CreateParticipant { runner.email = this.email; runner.group = await this.getGroup(); runner.address = this.address; + Address.validate(runner.address); return runner; } diff --git a/src/models/actions/update/UpdateRunnerOrganisation.ts b/src/models/actions/update/UpdateRunnerOrganisation.ts index ef52c2c..e53ade1 100644 --- a/src/models/actions/update/UpdateRunnerOrganisation.ts +++ b/src/models/actions/update/UpdateRunnerOrganisation.ts @@ -30,6 +30,7 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup { organisation.name = this.name; organisation.contact = await this.getContact(); organisation.address = this.address; + Address.validate(organisation.address); return organisation; } diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index 038f5d6..ea35f7c 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -7,6 +7,7 @@ import { 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. @@ -66,4 +67,19 @@ export class Address { 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(); } + } } From 57b9c2babcd68d69d1cbb240a86c2897717ba758 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 20:19:09 +0100 Subject: [PATCH 15/52] Implemented adress deletion (through reset) ref #105 --- src/models/actions/update/UpdateDonor.ts | 3 ++- src/models/actions/update/UpdateRunner.ts | 3 ++- src/models/actions/update/UpdateRunnerOrganisation.ts | 3 ++- src/models/entities/Address.ts | 8 ++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/models/actions/update/UpdateDonor.ts b/src/models/actions/update/UpdateDonor.ts index db6a745..7b6fcfa 100644 --- a/src/models/actions/update/UpdateDonor.ts +++ b/src/models/actions/update/UpdateDonor.ts @@ -34,7 +34,8 @@ export class UpdateDonor extends CreateParticipant { donor.phone = this.phone; donor.email = this.email; donor.receiptNeeded = this.receiptNeeded; - donor.address = this.address; + if (!this.address) { donor.address.reset(); } + else { donor.address = this.address; } Address.validate(donor.address); if (this.receiptNeeded == true && Address.isValidAddress(donor.address) == false) { throw new DonorReceiptAddressNeededError() diff --git a/src/models/actions/update/UpdateRunner.ts b/src/models/actions/update/UpdateRunner.ts index 1202ad0..2df10fe 100644 --- a/src/models/actions/update/UpdateRunner.ts +++ b/src/models/actions/update/UpdateRunner.ts @@ -36,7 +36,8 @@ export class UpdateRunner extends CreateParticipant { runner.phone = this.phone; runner.email = this.email; runner.group = await this.getGroup(); - runner.address = this.address; + if (!this.address) { runner.address.reset(); } + else { runner.address = this.address; } Address.validate(runner.address); return runner; diff --git a/src/models/actions/update/UpdateRunnerOrganisation.ts b/src/models/actions/update/UpdateRunnerOrganisation.ts index e53ade1..b34bc8f 100644 --- a/src/models/actions/update/UpdateRunnerOrganisation.ts +++ b/src/models/actions/update/UpdateRunnerOrganisation.ts @@ -29,7 +29,8 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup { organisation.name = this.name; organisation.contact = await this.getContact(); - organisation.address = this.address; + if (!this.address) { organisation.address.reset(); } + else { organisation.address = this.address; } Address.validate(organisation.address); return organisation; diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index ea35f7c..21ec92a 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -58,6 +58,14 @@ export class Address { @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 */ From 4d40225a4491e8eb3f41ef0fd558a599f63729be Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 20:26:58 +0100 Subject: [PATCH 16/52] Added first address update tests ref #105 --- src/tests/runnerOrgs/org_update.spec.ts | 101 ++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts index 3db1254..9fa8f8d 100644 --- a/src/tests/runnerOrgs/org_update.spec.ts +++ b/src/tests/runnerOrgs/org_update.spec.ts @@ -76,4 +76,105 @@ describe('adding + try updating id (should return 406)', () => { expect(res2.status).toEqual(406); expect(res2.headers['content-type']).toContain("application/json") }); +}); +// --------------- +describe('adding + updateing address valid)', () => { + let added_org_id + let added_org + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('adding address to org should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test1", + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test1", + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + }, + "teams": [] + }); + }); + it('updateing address\'s first line should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + }, + "teams": [] + }); + }); + it('updateing address\'s second line should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + }, + "teams": [] + }); + }); }); \ No newline at end of file From 230cdb0e37e2b7a21e7feb156f2b91a69ad200fd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 20:37:48 +0100 Subject: [PATCH 17/52] Added address update valid tests ref #105 --- src/tests/runnerOrgs/org_update.spec.ts | 87 +++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts index 9fa8f8d..8c66a24 100644 --- a/src/tests/runnerOrgs/org_update.spec.ts +++ b/src/tests/runnerOrgs/org_update.spec.ts @@ -177,4 +177,91 @@ describe('adding + updateing address valid)', () => { "teams": [] }); }); + it('updateing address\'s city should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "Kaya", + "country": "Burkina Faso", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "Kaya", + "country": "Burkina Faso", + "postalcode": "90174" + }, + "teams": [] + }); + }); + it('updateing address\'s country should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "Kaya", + "country": "Germany", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "Kaya", + "country": "Germany", + "postalcode": "90174" + }, + "teams": [] + }); + }); + it('updateing address\'s postal code should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "Kaya", + "country": "Germany", + "postalcode": "91065" + } + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test2", + "address2": "Test3", + "city": "Kaya", + "country": "Germany", + "postalcode": "91065" + }, + "teams": [] + }); + }); }); \ No newline at end of file From 427dfaafabd243e94aba27c2dec2705fd8ed5d64 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 21:26:45 +0100 Subject: [PATCH 18/52] Added address update ivalid tests ref #105 --- src/tests/runnerOrgs/org_update.spec.ts | 116 ++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts index 8c66a24..b9ffb84 100644 --- a/src/tests/runnerOrgs/org_update.spec.ts +++ b/src/tests/runnerOrgs/org_update.spec.ts @@ -264,4 +264,120 @@ describe('adding + updateing address valid)', () => { "teams": [] }); }); + it('removing org\'s should return 200', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json"); + expect(res2.data).toEqual({ + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": null, + "address2": null, + "city": null, + "country": null, + "postalcode": null + }, + "teams": [] + }); + }); +}); +// --------------- +describe('adding + updateing address invalid)', () => { + let added_org_id + let added_org + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('adding address to org w/o address1 should return 400', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": null, + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json"); + }); + it('adding address to org w/o city should return 400', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test1", + "address2": null, + "city": null, + "country": "Burkina Faso", + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json"); + }); + it('adding address to org w/o country should return 400', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test1", + "address2": null, + "city": "TestCity", + "country": null, + "postalcode": "90174" + } + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json"); + }); + it('adding address to org w/o postal code should return 400', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test1", + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": null + } + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json"); + }); + it('adding address to org w/ invalid postal code should return 400', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": { + "address1": "Test1", + "address2": null, + "city": "TestCity", + "country": "Burkina Faso", + "postalcode": "-1" + } + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json"); + }); }); \ No newline at end of file From f53894b16ac1c06ecbeeb0b63a56ac438b2fbe1b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Sat, 16 Jan 2021 20:33:37 +0000 Subject: [PATCH 19/52] =?UTF-8?q?=F0=9F=A7=BENew=20changelog=20file=20vers?= =?UTF-8?q?ion=20[CI=20SKIP]=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e824ec5..88fc77b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. Dates are d #### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.0...v0.1.1) +- Merge pull request 'Alpha Release 0.1.1 - Hotfix release' (#106) from dev into main [`7533c34`](https://git.odit.services/lfk/backend/commit/7533c349ef98ed328151259fca68621b3eb5fd98) - 🚀Bumped version to v0.1.1 [`9445c6f`](https://git.odit.services/lfk/backend/commit/9445c6f21e376329b9200664a44a94ba1f1dd463) - 🧾New changelog file version [CI SKIP] [skip ci] [`1b9d296`](https://git.odit.services/lfk/backend/commit/1b9d2969ebdca4dca84898b1e8307be7b781b90b) - Implemented the /me controller that allows a user to get and update themselves [`8ef5f90`](https://git.odit.services/lfk/backend/commit/8ef5f90abda97a73d5c5a7767a144ac3fb5288c1) @@ -14,12 +15,13 @@ All notable changes to this project will be documented in this file. Dates are d - automaticly merge main into dev after building a latest image [`02efb9a`](https://git.odit.services/lfk/backend/commit/02efb9a8e55831ecce4109e17b2f07a56e491fd5) - User deletion now requires confirmation [`6b7ecd3`](https://git.odit.services/lfk/backend/commit/6b7ecd3044c45b2eed46ee5010bed4dab4f02df9) - 🧾New changelog file version [CI SKIP] [skip ci] [`3766899`](https://git.odit.services/lfk/backend/commit/3766899c8393545a89986a98dafd542edc4a1d39) -- 🧾New changelog file version [CI SKIP] [skip ci] [`6febb99`](https://git.odit.services/lfk/backend/commit/6febb994990b4cab7ee54b0368f74dd95664bfdf) -- 🧾New changelog file version [CI SKIP] [skip ci] [`de36a24`](https://git.odit.services/lfk/backend/commit/de36a24191a8cdc4ff6b23637ea9f91109b59bbb) -- Merge pull request 'User self-management feature/100-me_endpoints' (#103) from feature/100-me_endpoints into dev [`a6c7d54`](https://git.odit.services/lfk/backend/commit/a6c7d54fe72ffe23add926afa0be150a7a370099) - Created barebones file for the userchecker [`e586a11`](https://git.odit.services/lfk/backend/commit/e586a11e2ad42af9c9bb5d2a47f48e3306fe49b2) +- 🧾New changelog file version [CI SKIP] [skip ci] [`6febb99`](https://git.odit.services/lfk/backend/commit/6febb994990b4cab7ee54b0368f74dd95664bfdf) - Updated descriptions and responses [`fc7b8f4`](https://git.odit.services/lfk/backend/commit/fc7b8f4c16cef0e72b04f096d5a17d4144b5feb7) - 🧾New changelog file version [CI SKIP] [skip ci] [`50b893f`](https://git.odit.services/lfk/backend/commit/50b893f5370902ccc40f8bb45ed160103400f529) +- 🧾New changelog file version [CI SKIP] [skip ci] [`de36a24`](https://git.odit.services/lfk/backend/commit/de36a24191a8cdc4ff6b23637ea9f91109b59bbb) +- 🧾New changelog file version [CI SKIP] [skip ci] [`91569ce`](https://git.odit.services/lfk/backend/commit/91569ced40402a63017a90a01efaf48578b5c806) +- Merge pull request 'User self-management feature/100-me_endpoints' (#103) from feature/100-me_endpoints into dev [`a6c7d54`](https://git.odit.services/lfk/backend/commit/a6c7d54fe72ffe23add926afa0be150a7a370099) - Moved the me endpoints to /users/me [`f9834b5`](https://git.odit.services/lfk/backend/commit/f9834b5f4d80b11ee5f7773b339dd421341c6e7f) - Moved optional param to being optional [`a334adf`](https://git.odit.services/lfk/backend/commit/a334adffc6d07c8ab340263123e00a96f21acecb) From a3c93f0d394833f1a6f78d862b094ca751c85561 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 15:48:06 +0100 Subject: [PATCH 20/52] Cleaned up var names ref #105 --- src/tests/runnerOrgs/org_update.spec.ts | 136 ++++++++++++------------ 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts index b9ffb84..389dc84 100644 --- a/src/tests/runnerOrgs/org_update.spec.ts +++ b/src/tests/runnerOrgs/org_update.spec.ts @@ -19,24 +19,24 @@ describe('adding + updating name', () => { let added_org_id let added_org it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { + const res = await axios.post(base + '/api/organisations', { "name": "test123" }, axios_config); - added_org = res1.data + added_org = res.data added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") }); it('update org', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, "address": null, }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - let added_org2 = res2.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let added_org2 = res.data added_org_id = added_org2.id; delete added_org2.id expect(added_org2).toEqual({ @@ -58,23 +58,23 @@ describe('adding + try updating id (should return 406)', () => { let added_org_id let added_org it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { + const res = await axios.post(base + '/api/organisations', { "name": "test123" }, axios_config); - added_org = res1.data + added_org = res.data added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") }); it('update org', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id + 1, "name": "testlelele", "contact": null, "address": null, }, axios_config); - expect(res2.status).toEqual(406); - expect(res2.headers['content-type']).toContain("application/json") + expect(res.status).toEqual(406); + expect(res.headers['content-type']).toContain("application/json") }); }); // --------------- @@ -82,37 +82,37 @@ describe('adding + updateing address valid)', () => { let added_org_id let added_org it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { + const res = await axios.post(base + '/api/organisations', { "name": "test123" }, axios_config); - added_org = res1.data + added_org = res.data added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") }); it('adding address to org should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, "address": { "address1": "Test1", "address2": null, - "city": "TestCity", + "city": "Herzogenaurach", "country": "Burkina Faso", "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, "address": { "address1": "Test1", "address2": null, - "city": "TestCity", + "city": "Herzogenaurach", "country": "Burkina Faso", "postalcode": "90174" }, @@ -120,7 +120,7 @@ describe('adding + updateing address valid)', () => { }); }); it('updateing address\'s first line should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -132,9 +132,9 @@ describe('adding + updateing address valid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, @@ -149,7 +149,7 @@ describe('adding + updateing address valid)', () => { }); }); it('updateing address\'s second line should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -161,9 +161,9 @@ describe('adding + updateing address valid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, @@ -178,7 +178,7 @@ describe('adding + updateing address valid)', () => { }); }); it('updateing address\'s city should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -190,9 +190,9 @@ describe('adding + updateing address valid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, @@ -207,7 +207,7 @@ describe('adding + updateing address valid)', () => { }); }); it('updateing address\'s country should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -219,9 +219,9 @@ describe('adding + updateing address valid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, @@ -236,7 +236,7 @@ describe('adding + updateing address valid)', () => { }); }); it('updateing address\'s postal code should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -248,9 +248,9 @@ describe('adding + updateing address valid)', () => { "postalcode": "91065" } }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, @@ -265,14 +265,14 @@ describe('adding + updateing address valid)', () => { }); }); it('removing org\'s should return 200', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json"); - expect(res2.data).toEqual({ + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ "id": added_org_id, "name": "testlelele", "contact": null, @@ -292,16 +292,16 @@ describe('adding + updateing address invalid)', () => { let added_org_id let added_org it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { + const res = await axios.post(base + '/api/organisations', { "name": "test123" }, axios_config); - added_org = res1.data + added_org = res.data added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") }); it('adding address to org w/o address1 should return 400', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -313,11 +313,11 @@ describe('adding + updateing address invalid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json"); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json"); }); it('adding address to org w/o city should return 400', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -329,11 +329,11 @@ describe('adding + updateing address invalid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json"); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json"); }); it('adding address to org w/o country should return 400', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -345,11 +345,11 @@ describe('adding + updateing address invalid)', () => { "postalcode": "90174" } }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json"); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json"); }); it('adding address to org w/o postal code should return 400', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -361,11 +361,11 @@ describe('adding + updateing address invalid)', () => { "postalcode": null } }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json"); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json"); }); it('adding address to org w/ invalid postal code should return 400', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + const res = await axios.put(base + '/api/organisations/' + added_org_id, { "id": added_org_id, "name": "testlelele", "contact": null, @@ -377,7 +377,7 @@ describe('adding + updateing address invalid)', () => { "postalcode": "-1" } }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json"); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json"); }); }); \ No newline at end of file From 30b585c0c12b3b9818778110d33d5b3ab84d4192 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 15:49:35 +0100 Subject: [PATCH 21/52] Set country code for the ci env to DE ref #105 --- .env.ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.ci b/.env.ci index 00bfa4a..389c0fa 100644 --- a/.env.ci +++ b/.env.ci @@ -6,4 +6,4 @@ DB_USER=unused DB_PASSWORD=bla DB_NAME=./test.sqlite NODE_ENV=dev -POSTALCODE_COUNTRYCODE=null \ No newline at end of file +POSTALCODE_COUNTRYCODE=DE \ No newline at end of file From 3e7190e279181c5f99d890ca141489b24908b904 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 15:56:03 +0100 Subject: [PATCH 22/52] Added barebones contact controller from donor-controller ref #104 --- src/controllers/ContactController.ts | 107 +++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/controllers/ContactController.ts diff --git a/src/controllers/ContactController.ts b/src/controllers/ContactController.ts new file mode 100644 index 0000000..75a9c26 --- /dev/null +++ b/src/controllers/ContactController.ts @@ -0,0 +1,107 @@ +import { JsonController } from 'routing-controllers'; +import { OpenAPI } from 'routing-controllers-openapi'; +import { getConnectionManager, Repository } from 'typeorm'; +import { GroupContact } from '../models/entities/GroupContact'; + +@JsonController('/contacts') +@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) +export class ContactController { + private contactRepository: Repository; + + /** + * Gets the repository of this controller's model/entity. + */ + constructor() { + this.contactRepository = getConnectionManager().get().getRepository(GroupContact); + } + + // @Get() + // @Authorized("DONOR:GET") + // @ResponseSchema(ResponseDonor, { isArray: true }) + // @OpenAPI({ description: 'Lists all contact.
This includes the contact\'s current donation amount.' }) + // async getAll() { + // let responseDonors: ResponseDonor[] = new Array(); + // const contacts = await this.contactRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); + // contacts.forEach(contact => { + // responseDonors.push(new ResponseDonor(contact)); + // }); + // return responseDonors; + // } + + // @Get('/:id') + // @Authorized("DONOR:GET") + // @ResponseSchema(ResponseDonor) + // @ResponseSchema(DonorNotFoundError, { statusCode: 404 }) + // @OnUndefined(DonorNotFoundError) + // @OpenAPI({ description: 'Lists all information about the contact whose id got provided.
This includes the contact\'s current donation amount.' }) + // async getOne(@Param('id') id: number) { + // let contact = await this.contactRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }) + // if (!contact) { throw new DonorNotFoundError(); } + // return new ResponseDonor(contact); + // } + + // @Post() + // @Authorized("DONOR:CREATE") + // @ResponseSchema(ResponseDonor) + // @OpenAPI({ description: 'Create a new contact.' }) + // async post(@Body({ validate: true }) createRunner: CreateDonor) { + // let contact; + // try { + // contact = await createRunner.toEntity(); + // } catch (error) { + // throw error; + // } + + // contact = await this.contactRepository.save(contact) + // return new ResponseDonor(await this.contactRepository.findOne(contact, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); + // } + + // @Put('/:id') + // @Authorized("DONOR:UPDATE") + // @ResponseSchema(ResponseDonor) + // @ResponseSchema(DonorNotFoundError, { statusCode: 404 }) + // @ResponseSchema(DonorIdsNotMatchingError, { statusCode: 406 }) + // @OpenAPI({ description: "Update the contact whose id you provided.
Please remember that ids can't be changed." }) + // async put(@Param('id') id: number, @Body({ validate: true }) contact: UpdateDonor) { + // let oldDonor = await this.contactRepository.findOne({ id: id }); + + // if (!oldDonor) { + // throw new DonorNotFoundError(); + // } + + // if (oldDonor.id != contact.id) { + // throw new DonorIdsNotMatchingError(); + // } + + // await this.contactRepository.save(await contact.update(oldDonor)); + // return new ResponseDonor(await this.contactRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); + // } + + // @Delete('/:id') + // @Authorized("DONOR:DELETE") + // @ResponseSchema(ResponseDonor) + // @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + // @OnUndefined(204) + // @OpenAPI({ description: 'Delete the contact whose id you provided.
If no contact with this id exists it will just return 204(no content).
If the contact still has donations associated this will fail, please provide the query param ?force=true to delete the contact with all associated donations.' }) + // async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { + // let contact = await this.contactRepository.findOne({ id: id }); + // if (!contact) { return null; } + // const responseDonor = await this.contactRepository.findOne(contact, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); + + // if (!contact) { + // throw new DonorNotFoundError(); + // } + + // const contactDonations = (await this.contactRepository.findOne({ id: contact.id }, { relations: ["donations"] })).donations; + // if (contactDonations.length > 0 && !force) { + // throw new DonorHasDonationsError(); + // } + // const donationController = new DonationController(); + // for (let donation of contactDonations) { + // await donationController.remove(donation.id, force); + // } + + // await this.contactRepository.delete(contact); + // return new ResponseDonor(responseDonor); + // } +} From d12801e34d57cec0e867f69123e3be39ed2fe2f5 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 15:56:55 +0100 Subject: [PATCH 23/52] Added contact permission target ref #104 --- src/models/enums/PermissionTargets.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/models/enums/PermissionTargets.ts b/src/models/enums/PermissionTargets.ts index 86c547d..1dac026 100644 --- a/src/models/enums/PermissionTargets.ts +++ b/src/models/enums/PermissionTargets.ts @@ -14,5 +14,6 @@ export enum PermissionTarget { SCAN = 'SCAN', STATION = 'STATION', CARD = 'CARD', - DONATION = 'DONATION' + DONATION = 'DONATION', + CONTACT = 'CONTACT' } \ No newline at end of file From 1407fe36f3637d6c53024c48788b318d985f8960 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 16:02:13 +0100 Subject: [PATCH 24/52] Added a contact response class ref #104 --- src/models/entities/GroupContact.ts | 5 +- src/models/responses/ResponseGroupContact.ts | 67 ++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/models/responses/ResponseGroupContact.ts diff --git a/src/models/entities/GroupContact.ts b/src/models/entities/GroupContact.ts index f650259..bc624dd 100644 --- a/src/models/entities/GroupContact.ts +++ b/src/models/entities/GroupContact.ts @@ -9,6 +9,7 @@ import { } from "class-validator"; import { Column, Entity, ManyToOne, 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"; @@ -85,7 +86,7 @@ export class GroupContact implements IAddressUser { /** * Turns this entity into it's response class. */ - public toResponse() { - return new Error("NotImplemented"); + public toResponse(): ResponseGroupContact { + return new ResponseGroupContact(this); } } \ No newline at end of file diff --git a/src/models/responses/ResponseGroupContact.ts b/src/models/responses/ResponseGroupContact.ts new file mode 100644 index 0000000..f7e63b3 --- /dev/null +++ b/src/models/responses/ResponseGroupContact.ts @@ -0,0 +1,67 @@ +import { IsInt, IsString } from "class-validator"; +import { GroupContact } from '../entities/GroupContact'; +import { ResponseRunnerGroup } from './ResponseRunnerGroup'; + +/** + * Defines the group contact response. +*/ +export class ResponseGroupContact { + /** + * The contact's id. + */ + @IsInt() + id: number; + + /** + * The contact's first name. + */ + @IsString() + firstname: string; + + /** + * The contact's middle name. + */ + @IsString() + middlename?: string; + + /** + * The contact's last name. + */ + @IsString() + lastname: string; + + /** + * The contact's phone number. + */ + @IsString() + phone?: string; + + /** + * The contact's e-mail address. + */ + @IsString() + email?: string; + + /** + * The contact's associated runner groups. + */ + groups: ResponseRunnerGroup[]; + + //TODO: Address + + /** + * Creates a ResponseGroupContact object from a contact. + * @param contact The contact the response shall be build for. + */ + public constructor(contact: GroupContact) { + this.id = contact.id; + this.firstname = contact.firstname; + this.middlename = contact.middlename; + this.lastname = contact.lastname; + this.phone = contact.phone; + this.email = contact.email; + for (let group of contact.groups) { + this.groups.push(group.toResponse()); + } + } +} From ab70f7e49893344dde8f5af93f571a5d67818e19 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 16:05:35 +0100 Subject: [PATCH 25/52] Implemented the get endpoints ref #104 --- src/controllers/ContactController.ts | 52 +++++++++++++++------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/controllers/ContactController.ts b/src/controllers/ContactController.ts index 75a9c26..f9fc752 100644 --- a/src/controllers/ContactController.ts +++ b/src/controllers/ContactController.ts @@ -1,7 +1,9 @@ -import { JsonController } from 'routing-controllers'; -import { OpenAPI } from 'routing-controllers-openapi'; +import { Authorized, Get, JsonController, OnUndefined, Param } from 'routing-controllers'; +import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; +import { GroupContactNotFoundError } from '../errors/GroupContactErrors'; import { GroupContact } from '../models/entities/GroupContact'; +import { ResponseGroupContact } from '../models/responses/ResponseGroupContact'; @JsonController('/contacts') @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) @@ -15,30 +17,30 @@ export class ContactController { this.contactRepository = getConnectionManager().get().getRepository(GroupContact); } - // @Get() - // @Authorized("DONOR:GET") - // @ResponseSchema(ResponseDonor, { isArray: true }) - // @OpenAPI({ description: 'Lists all contact.
This includes the contact\'s current donation amount.' }) - // async getAll() { - // let responseDonors: ResponseDonor[] = new Array(); - // const contacts = await this.contactRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); - // contacts.forEach(contact => { - // responseDonors.push(new ResponseDonor(contact)); - // }); - // return responseDonors; - // } + @Get() + @Authorized("CONTACT:GET") + @ResponseSchema(ResponseGroupContact, { isArray: true }) + @OpenAPI({ description: 'Lists all contacts.
This includes the contact\'s associated groups.' }) + async getAll() { + let responseContacts: ResponseGroupContact[] = new Array(); + const contacts = await this.contactRepository.find({ relations: ['groups'] }); + contacts.forEach(contact => { + responseContacts.push(contact.toResponse()); + }); + return responseContacts; + } - // @Get('/:id') - // @Authorized("DONOR:GET") - // @ResponseSchema(ResponseDonor) - // @ResponseSchema(DonorNotFoundError, { statusCode: 404 }) - // @OnUndefined(DonorNotFoundError) - // @OpenAPI({ description: 'Lists all information about the contact whose id got provided.
This includes the contact\'s current donation amount.' }) - // async getOne(@Param('id') id: number) { - // let contact = await this.contactRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }) - // if (!contact) { throw new DonorNotFoundError(); } - // return new ResponseDonor(contact); - // } + @Get('/:id') + @Authorized("DONOR:GET") + @ResponseSchema(ResponseGroupContact) + @ResponseSchema(GroupContactNotFoundError, { statusCode: 404 }) + @OnUndefined(GroupContactNotFoundError) + @OpenAPI({ description: 'Lists all information about the contact whose id got provided.
This includes the contact\'s associated groups.' }) + async getOne(@Param('id') id: number) { + let contact = await this.contactRepository.findOne({ id: id }, { relations: ['groups'] }) + if (!contact) { throw new GroupContactNotFoundError(); } + return contact.toResponse(); + } // @Post() // @Authorized("DONOR:CREATE") From a9a5eb673570bb3c0d1a55bb1292e208493663bd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 16:06:42 +0100 Subject: [PATCH 26/52] Updated the contact errors ref #104 --- src/errors/GroupContactErrors.ts | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/errors/GroupContactErrors.ts b/src/errors/GroupContactErrors.ts index 92fd4a2..fffbdd5 100644 --- a/src/errors/GroupContactErrors.ts +++ b/src/errors/GroupContactErrors.ts @@ -2,18 +2,7 @@ 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" - - @IsString() - message = "The groupContact must be an existing groupContact's id. \n You provided a object of another type." -} - -/** - * Error to throw, when a non-existent groupContact get's loaded. + * Error to throw, when a non-existent contact get's requested. */ export class GroupContactNotFoundError extends NotFoundError { @IsString() @@ -21,4 +10,16 @@ export class GroupContactNotFoundError extends NotFoundError { @IsString() message = "The groupContact you provided couldn't be located in the system. \n Please check your request." -} \ No newline at end of file +} + +/** + * Error to throw when two contacts' ids don't match. + * Usually occurs when a user tries to change a contact's id. + */ +export class GroupContactIdsNotMatchingError extends NotAcceptableError { + @IsString() + name = "GroupContactIdsNotMatchingError" + + @IsString() + message = "The ids don't match! \n And if you wanted to change a contact's id: This isn't allowed!" +} From 0379786cbda057ad95d709fa135d34beb0db8de1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 16:09:23 +0100 Subject: [PATCH 27/52] Implemented contact deletion ref #104 --- src/controllers/ContactController.ts | 44 ++++++++++------------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/controllers/ContactController.ts b/src/controllers/ContactController.ts index f9fc752..1a20d82 100644 --- a/src/controllers/ContactController.ts +++ b/src/controllers/ContactController.ts @@ -1,8 +1,9 @@ -import { Authorized, Get, JsonController, OnUndefined, Param } from 'routing-controllers'; +import { Authorized, Delete, Get, JsonController, OnUndefined, Param, QueryParam } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { GroupContactNotFoundError } from '../errors/GroupContactErrors'; import { GroupContact } from '../models/entities/GroupContact'; +import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseGroupContact } from '../models/responses/ResponseGroupContact'; @JsonController('/contacts') @@ -31,7 +32,7 @@ export class ContactController { } @Get('/:id') - @Authorized("DONOR:GET") + @Authorized("CONTACT:GET") @ResponseSchema(ResponseGroupContact) @ResponseSchema(GroupContactNotFoundError, { statusCode: 404 }) @OnUndefined(GroupContactNotFoundError) @@ -79,31 +80,18 @@ export class ContactController { // return new ResponseDonor(await this.contactRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); // } - // @Delete('/:id') - // @Authorized("DONOR:DELETE") - // @ResponseSchema(ResponseDonor) - // @ResponseSchema(ResponseEmpty, { statusCode: 204 }) - // @OnUndefined(204) - // @OpenAPI({ description: 'Delete the contact whose id you provided.
If no contact with this id exists it will just return 204(no content).
If the contact still has donations associated this will fail, please provide the query param ?force=true to delete the contact with all associated donations.' }) - // async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { - // let contact = await this.contactRepository.findOne({ id: id }); - // if (!contact) { return null; } - // const responseDonor = await this.contactRepository.findOne(contact, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); + @Delete('/:id') + @Authorized("CONTACT:DELETE") + @ResponseSchema(ResponseGroupContact) + @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + @OnUndefined(204) + @OpenAPI({ description: 'Delete the contact whose id you provided.
If no contact with this id exists it will just return 204(no content).
This won\'t delete any groups associated with the contact.' }) + async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { + let contact = await this.contactRepository.findOne({ id: id }); + if (!contact) { return null; } + const responseContact = await this.contactRepository.findOne(contact, { relations: ['groups'] }); - // if (!contact) { - // throw new DonorNotFoundError(); - // } - - // const contactDonations = (await this.contactRepository.findOne({ id: contact.id }, { relations: ["donations"] })).donations; - // if (contactDonations.length > 0 && !force) { - // throw new DonorHasDonationsError(); - // } - // const donationController = new DonationController(); - // for (let donation of contactDonations) { - // await donationController.remove(donation.id, force); - // } - - // await this.contactRepository.delete(contact); - // return new ResponseDonor(responseDonor); - // } + await this.contactRepository.delete(contact); + return responseContact.toResponse(); + } } From 32e054eb84c869210fd483583ae5a6d0e2249cf9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 15:37:52 +0000 Subject: [PATCH 28/52] =?UTF-8?q?=F0=9F=A7=BENew=20changelog=20file=20vers?= =?UTF-8?q?ion=20[CI=20SKIP]=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88fc77b..2bddfa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,35 @@ All notable changes to this project will be documented in this file. Dates are displayed in UTC. +#### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.1...v0.1.1) + +- Merge pull request 'Fully implemented addresses feature/105-addresses' (#107) from feature/105-addresses into dev [`5e36855`](https://git.odit.services/lfk/backend/commit/5e368552ea810ea1d3963b1207ba98f7b46c4abc) +- Cleaned up var names [`a3c93f0`](https://git.odit.services/lfk/backend/commit/a3c93f0d394833f1a6f78d862b094ca751c85561) +- Added address update ivalid tests [`427dfaa`](https://git.odit.services/lfk/backend/commit/427dfaafabd243e94aba27c2dec2705fd8ed5d64) +- Added first address update tests [`4d40225`](https://git.odit.services/lfk/backend/commit/4d40225a4491e8eb3f41ef0fd558a599f63729be) +- Added address update valid tests [`230cdb0`](https://git.odit.services/lfk/backend/commit/230cdb0e37e2b7a21e7feb156f2b91a69ad200fd) +- Implemented deep address validation [`9dc9ce3`](https://git.odit.services/lfk/backend/commit/9dc9ce37d8fbfc92842e4e05bbde68398324a186) +- Removed old create address class [`2a465f8`](https://git.odit.services/lfk/backend/commit/2a465f88c58c0b4be3ecd99d96a04c177a40b312) +- Switched the create classes over to the new address implementation [`2cd15d2`](https://git.odit.services/lfk/backend/commit/2cd15d25e934a5439bfea4de901f136e360e17f6) +- Test's now accept the new address format [`8dbee32`](https://git.odit.services/lfk/backend/commit/8dbee32eeec8ee3d013e4446e8f53544ee4cb577) +- Removed the IAddressUser Interface entity [`e265172`](https://git.odit.services/lfk/backend/commit/e2651728c5abf2273bf51a7652c51d55d8fa0a2f) +- Switched Address to embedded entity [`7fbe649`](https://git.odit.services/lfk/backend/commit/7fbe649dc90f4bb9f240c5a80fed447048e5e105) +- Removed the address errors [`58ae9b5`](https://git.odit.services/lfk/backend/commit/58ae9b589aef3580f4b8558c3d5ddbd7171e7915) +- Switched the update classes over to the new address implementation [`d0df5dd`](https://git.odit.services/lfk/backend/commit/d0df5dd641ac17f1fd8ad6bd8b46afa9dd2745c3) +- Implemented adress deletion (through reset) [`57b9c2b`](https://git.odit.services/lfk/backend/commit/57b9c2babcd68d69d1cbb240a86c2897717ba758) +- Fixed donor address check [`4824547`](https://git.odit.services/lfk/backend/commit/4824547dde4d7f90e9e2377a26df34cabf082fdb) +- Updated the responseclasses to use the new address implementation [`dafac06`](https://git.odit.services/lfk/backend/commit/dafac06bc84d1b237096a561b3adcd3ca5cb1dd8) +- Removed (now useless) relations [`673dea2`](https://git.odit.services/lfk/backend/commit/673dea2e5754e99ff77f7556d4fc03d4cca28a94) +- Added address validity check [`ae7c5ff`](https://git.odit.services/lfk/backend/commit/ae7c5ff0c387e9337d01a9dd819a4dddc208f6dd) +- 🧾New changelog file version [CI SKIP] [skip ci] [`f53894b`](https://git.odit.services/lfk/backend/commit/f53894b16ac1c06ecbeeb0b63a56ac438b2fbe1b) +- Updated comments [`8bc01d3`](https://git.odit.services/lfk/backend/commit/8bc01d3f2406ce8e58c2ab2963c858495c510dcf) +- Set country code for the ci env to DE [`30b585c`](https://git.odit.services/lfk/backend/commit/30b585c0c12b3b9818778110d33d5b3ab84d4192) +- Implemented postal code validation for the validaton function [`f245840`](https://git.odit.services/lfk/backend/commit/f245840cde5726611197b00730aca72ea133c427) + #### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.0...v0.1.1) +> 16 January 2021 + - Merge pull request 'Alpha Release 0.1.1 - Hotfix release' (#106) from dev into main [`7533c34`](https://git.odit.services/lfk/backend/commit/7533c349ef98ed328151259fca68621b3eb5fd98) - 🚀Bumped version to v0.1.1 [`9445c6f`](https://git.odit.services/lfk/backend/commit/9445c6f21e376329b9200664a44a94ba1f1dd463) - 🧾New changelog file version [CI SKIP] [skip ci] [`1b9d296`](https://git.odit.services/lfk/backend/commit/1b9d2969ebdca4dca84898b1e8307be7b781b90b) From 09e429fc676c7dd370bba0495b072f81867bd250 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:13:46 +0100 Subject: [PATCH 29/52] Added address to contact response ref #104 --- src/models/responses/ResponseGroupContact.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/models/responses/ResponseGroupContact.ts b/src/models/responses/ResponseGroupContact.ts index f7e63b3..d7ac6f2 100644 --- a/src/models/responses/ResponseGroupContact.ts +++ b/src/models/responses/ResponseGroupContact.ts @@ -1,4 +1,5 @@ -import { IsInt, IsString } from "class-validator"; +import { IsInt, IsObject, IsString } from "class-validator"; +import { Address } from '../entities/Address'; import { GroupContact } from '../entities/GroupContact'; import { ResponseRunnerGroup } from './ResponseRunnerGroup'; @@ -45,9 +46,15 @@ export class ResponseGroupContact { /** * The contact's associated runner groups. */ + @IsObject() groups: ResponseRunnerGroup[]; - //TODO: Address + /** + * The contact's address. + * This is a address object to prevent any formatting differences. + */ + @IsObject() + address?: Address; /** * Creates a ResponseGroupContact object from a contact. @@ -60,6 +67,7 @@ export class ResponseGroupContact { this.lastname = contact.lastname; this.phone = contact.phone; this.email = contact.email; + this.address = contact.address; for (let group of contact.groups) { this.groups.push(group.toResponse()); } From 11af9c02d977dcd6919652256dbdb9fd5438cabd Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:14:05 +0100 Subject: [PATCH 30/52] Implemented contact posting ref #104 --- src/controllers/ContactController.ts | 33 +++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/controllers/ContactController.ts b/src/controllers/ContactController.ts index 1a20d82..0ed2eca 100644 --- a/src/controllers/ContactController.ts +++ b/src/controllers/ContactController.ts @@ -1,7 +1,9 @@ -import { Authorized, Delete, Get, JsonController, OnUndefined, Param, QueryParam } from 'routing-controllers'; +import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { GroupContactNotFoundError } from '../errors/GroupContactErrors'; +import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; +import { CreateGroupContact } from '../models/actions/create/CreateGroupContact'; import { GroupContact } from '../models/entities/GroupContact'; import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseGroupContact } from '../models/responses/ResponseGroupContact'; @@ -43,21 +45,22 @@ export class ContactController { return contact.toResponse(); } - // @Post() - // @Authorized("DONOR:CREATE") - // @ResponseSchema(ResponseDonor) - // @OpenAPI({ description: 'Create a new contact.' }) - // async post(@Body({ validate: true }) createRunner: CreateDonor) { - // let contact; - // try { - // contact = await createRunner.toEntity(); - // } catch (error) { - // throw error; - // } + @Post() + @Authorized("CONTACT:CREATE") + @ResponseSchema(ResponseGroupContact) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Create a new contact.' }) + async post(@Body({ validate: true }) createContact: CreateGroupContact) { + let contact; + try { + contact = await createContact.toEntity(); + } catch (error) { + throw error; + } - // contact = await this.contactRepository.save(contact) - // return new ResponseDonor(await this.contactRepository.findOne(contact, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); - // } + contact = await this.contactRepository.save(contact) + return (await this.contactRepository.findOne(contact, { relations: ['groups'] })).toResponse(); + } // @Put('/:id') // @Authorized("DONOR:UPDATE") From de824375d3a1da6ee4d78ea39b7da66fc05f2a02 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:27:43 +0100 Subject: [PATCH 31/52] Fixed key null constraint ref #104 --- .../actions/create/CreateGroupContact.ts | 20 +++++++++---------- src/models/entities/Address.ts | 17 +++++----------- src/models/entities/GroupContact.ts | 1 - 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index c6538e8..e261f51 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -29,7 +29,7 @@ export class CreateGroupContact { lastname: string; /** - * The new contact's address. + * The new participant's address. */ @IsOptional() @IsObject() @@ -55,14 +55,14 @@ export class CreateGroupContact { * Creates a new Address entity from this. */ public async toEntity(): 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 = this.address; - Address.validate(contact.address); - return contact; + let newContact: GroupContact = new GroupContact(); + newContact.firstname = this.firstname; + newContact.middlename = this.middlename; + newContact.lastname = this.lastname; + newContact.email = this.email; + newContact.phone = this.phone; + newContact.address = this.address; + Address.validate(newContact.address); + return newContact; } } \ No newline at end of file diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index 21ec92a..6526c08 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -1,6 +1,4 @@ import { - IsNotEmpty, - IsOptional, IsPostalCode, IsString } from "class-validator"; @@ -18,10 +16,9 @@ export class Address { * The address's first line. * Containing the street and house number. */ - @Column() + @Column({ nullable: true }) @IsString() - @IsNotEmpty() - address1: string; + address1?: string; /** * The address's second line. @@ -29,33 +26,29 @@ export class Address { */ @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() + @Column({ nullable: true }) @IsString() - @IsNotEmpty() @IsPostalCode(config.postalcode_validation_countrycode) postalcode: string; /** * The address's city. */ - @Column() + @Column({ nullable: true }) @IsString() - @IsNotEmpty() city: string; /** * The address's country. */ - @Column() + @Column({ nullable: true }) @IsString() - @IsNotEmpty() country: string; public reset() { diff --git a/src/models/entities/GroupContact.ts b/src/models/entities/GroupContact.ts index 17b0b9b..dd6eed1 100644 --- a/src/models/entities/GroupContact.ts +++ b/src/models/entities/GroupContact.ts @@ -54,7 +54,6 @@ export class GroupContact { * The contact's address. * This is a address object to prevent any formatting differences. */ - @IsOptional() @Column(type => Address) address?: Address; From 3b06d1a6ef3c95eb5bb7d485accddabba0a8e4f7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:29:52 +0100 Subject: [PATCH 32/52] Implemented contact group setting on creation ref #104 --- .../actions/create/CreateGroupContact.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index e261f51..8d7e0bd 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -1,7 +1,10 @@ import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; import { config } from '../../../config'; +import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; import { Address } from '../../entities/Address'; import { GroupContact } from '../../entities/GroupContact'; +import { RunnerGroup } from '../../entities/RunnerGroup'; /** * This classed is used to create a new Group entity from a json body (post request). @@ -50,6 +53,30 @@ export class CreateGroupContact { @IsEmail() email?: string; + /** + * The new contacts's groups' ids. + * You can provide either one groupId or an array of groupIDs. + */ + @IsOptional() + groups?: number[] | number + + + /** + * Get's all groups for this contact by their id's; + */ + public async getGroups(): Promise { + if (!this.groups) { return null; } + let groups = new Array(); + if (!Array.isArray(this.groups)) { + this.groups = [this.groups] + } + for (let group of this.groups) { + let found = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: group }); + if (!found) { throw new RunnerGroupNotFoundError(); } + groups.push(found); + } + return groups; + } /** * Creates a new Address entity from this. @@ -63,6 +90,8 @@ export class CreateGroupContact { newContact.phone = this.phone; newContact.address = this.address; Address.validate(newContact.address); + newContact.groups = await this.getGroups(); + return newContact; } } \ No newline at end of file From 2eb26e4e381a97fd829a294501fa42ac7b712b56 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:41:00 +0100 Subject: [PATCH 33/52] Fixed push undefined eror ref #104 --- src/models/responses/ResponseGroupContact.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/models/responses/ResponseGroupContact.ts b/src/models/responses/ResponseGroupContact.ts index d7ac6f2..291dde5 100644 --- a/src/models/responses/ResponseGroupContact.ts +++ b/src/models/responses/ResponseGroupContact.ts @@ -68,6 +68,7 @@ export class ResponseGroupContact { this.phone = contact.phone; this.email = contact.email; this.address = contact.address; + this.groups = new Array(); for (let group of contact.groups) { this.groups.push(group.toResponse()); } From 321d291b4bf983ff4930cadecf3a013430a97649 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:52:51 +0100 Subject: [PATCH 34/52] Fixed column not getting resolved --- src/controllers/ContactController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ContactController.ts b/src/controllers/ContactController.ts index 0ed2eca..4e469c7 100644 --- a/src/controllers/ContactController.ts +++ b/src/controllers/ContactController.ts @@ -59,7 +59,7 @@ export class ContactController { } contact = await this.contactRepository.save(contact) - return (await this.contactRepository.findOne(contact, { relations: ['groups'] })).toResponse(); + return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups'] })).toResponse(); } // @Put('/:id') From 2b658ac381f318cf37fc2051ea3e83976e3c5773 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:52:51 +0100 Subject: [PATCH 35/52] Fixed column not getting resolved ref #104 --- src/controllers/ContactController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ContactController.ts b/src/controllers/ContactController.ts index 0ed2eca..4e469c7 100644 --- a/src/controllers/ContactController.ts +++ b/src/controllers/ContactController.ts @@ -59,7 +59,7 @@ export class ContactController { } contact = await this.contactRepository.save(contact) - return (await this.contactRepository.findOne(contact, { relations: ['groups'] })).toResponse(); + return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups'] })).toResponse(); } // @Put('/:id') From c172aa8bf8083500828743ed696955a1fe3caef2 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:55:56 +0100 Subject: [PATCH 36/52] Added a contact update class ref #104 --- .../actions/update/UpdateGroupContact.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/models/actions/update/UpdateGroupContact.ts diff --git a/src/models/actions/update/UpdateGroupContact.ts b/src/models/actions/update/UpdateGroupContact.ts new file mode 100644 index 0000000..62d4149 --- /dev/null +++ b/src/models/actions/update/UpdateGroupContact.ts @@ -0,0 +1,98 @@ +import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { config } from '../../../config'; +import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; +import { Address } from '../../entities/Address'; +import { GroupContact } from '../../entities/GroupContact'; +import { RunnerGroup } from '../../entities/RunnerGroup'; + +/** + * This classed is used to create a new Group entity from a json body (post request). + */ +export class UpdateGroupContact { + /** + * The updated contact's first name. + */ + @IsNotEmpty() + @IsString() + firstname: string; + + /** + * The updated contact's middle name. + */ + @IsOptional() + @IsString() + middlename?: string; + + /** + * The updated contact's last name. + */ + @IsNotEmpty() + @IsString() + lastname: string; + + /** + * The updated contact's address. + */ + @IsOptional() + @IsObject() + address?: Address; + + /** + * The updated contact's phone number. + * This will be validated against the configured country phone numer syntax (default: international). + */ + @IsOptional() + @IsPhoneNumber(config.phone_validation_countrycode) + phone?: string; + + /** + * The updated contact's email address. + */ + @IsOptional() + @IsEmail() + email?: string; + + /** + * The updated contacts's groups' ids. + * You can provide either one groupId or an array of groupIDs. + */ + @IsOptional() + groups?: number[] | number + + + /** + * Get's all groups for this contact by their id's; + */ + public async getGroups(): Promise { + if (!this.groups) { return null; } + let groups = new Array(); + if (!Array.isArray(this.groups)) { + this.groups = [this.groups] + } + for (let group of this.groups) { + let found = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: group }); + if (!found) { throw new RunnerGroupNotFoundError(); } + groups.push(found); + } + return groups; + } + + /** + * Updates a provided Donor entity based on this. + * @param contact the contact you want to update. + */ + public async update(contact: GroupContact): Promise { + contact.firstname = this.firstname; GroupContact + contact.middlename = this.middlename; + contact.lastname = this.lastname; + contact.phone = this.phone; + contact.email = this.email; + if (!this.address) { contact.address.reset(); } + else { contact.address = this.address; } + Address.validate(contact.address); + contact.groups = await this.getGroups(); + + return contact; + } +} \ No newline at end of file From a4e8311cbd22588ecb4dc2fdbe05397b07d336f8 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:57:15 +0100 Subject: [PATCH 37/52] Updated comments ref #104 --- src/models/actions/create/CreateGroupContact.ts | 8 ++++---- src/models/actions/update/UpdateGroupContact.ts | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index 8d7e0bd..0775b2d 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -7,7 +7,7 @@ import { GroupContact } from '../../entities/GroupContact'; import { RunnerGroup } from '../../entities/RunnerGroup'; /** - * This classed is used to create a new Group entity from a json body (post request). + * This classed is used to create a new GroupContact entity from a json body (post request). */ export class CreateGroupContact { /** @@ -32,7 +32,7 @@ export class CreateGroupContact { lastname: string; /** - * The new participant's address. + * The new contact's address. */ @IsOptional() @IsObject() @@ -47,7 +47,7 @@ export class CreateGroupContact { phone?: string; /** - * The contact's email address. + * The new contact's email address. */ @IsOptional() @IsEmail() @@ -79,7 +79,7 @@ export class CreateGroupContact { } /** - * Creates a new Address entity from this. + * Creates a new GroupContact entity from this. */ public async toEntity(): Promise { let newContact: GroupContact = new GroupContact(); diff --git a/src/models/actions/update/UpdateGroupContact.ts b/src/models/actions/update/UpdateGroupContact.ts index 62d4149..9ec4418 100644 --- a/src/models/actions/update/UpdateGroupContact.ts +++ b/src/models/actions/update/UpdateGroupContact.ts @@ -6,8 +6,9 @@ import { Address } from '../../entities/Address'; import { GroupContact } from '../../entities/GroupContact'; import { RunnerGroup } from '../../entities/RunnerGroup'; + /** - * This classed is used to create a new Group entity from a json body (post request). + * This class is used to update a GroupContact entity (via put request). */ export class UpdateGroupContact { /** From d743f7ee1277256ada8fe39f900349ff2643118a Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 17:58:03 +0100 Subject: [PATCH 38/52] Renamed controller to better fit the overall nameing scheme ref #104 --- .../{ContactController.ts => GroupContactController.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/controllers/{ContactController.ts => GroupContactController.ts} (99%) diff --git a/src/controllers/ContactController.ts b/src/controllers/GroupContactController.ts similarity index 99% rename from src/controllers/ContactController.ts rename to src/controllers/GroupContactController.ts index 4e469c7..3030de8 100644 --- a/src/controllers/ContactController.ts +++ b/src/controllers/GroupContactController.ts @@ -10,7 +10,7 @@ import { ResponseGroupContact } from '../models/responses/ResponseGroupContact'; @JsonController('/contacts') @OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] }) -export class ContactController { +export class GroupContactController { private contactRepository: Repository; /** From 6b4b16c13b0c2f55745ded3431cad2f4986be296 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 18:00:45 +0100 Subject: [PATCH 39/52] Added missing id property --- src/models/actions/update/UpdateGroupContact.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/models/actions/update/UpdateGroupContact.ts b/src/models/actions/update/UpdateGroupContact.ts index 9ec4418..72f919a 100644 --- a/src/models/actions/update/UpdateGroupContact.ts +++ b/src/models/actions/update/UpdateGroupContact.ts @@ -1,4 +1,4 @@ -import { IsEmail, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { IsEmail, IsInt, IsNotEmpty, IsObject, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { getConnectionManager } from 'typeorm'; import { config } from '../../../config'; import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; @@ -11,6 +11,13 @@ import { RunnerGroup } from '../../entities/RunnerGroup'; * This class is used to update a GroupContact entity (via put request). */ export class UpdateGroupContact { + /** + * The updated contact's id. + * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). + */ + @IsInt() + id: number; + /** * The updated contact's first name. */ From 28fb9834e18bde012c5b51cc49a39585d20f7cc1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 18:01:37 +0100 Subject: [PATCH 40/52] Implemented contact updateing ref #104 --- src/controllers/GroupContactController.ts | 40 ++++++++++++----------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/controllers/GroupContactController.ts b/src/controllers/GroupContactController.ts index 3030de8..12518c2 100644 --- a/src/controllers/GroupContactController.ts +++ b/src/controllers/GroupContactController.ts @@ -1,9 +1,10 @@ -import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam } from 'routing-controllers'; +import { Authorized, 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 { GroupContactNotFoundError } from '../errors/GroupContactErrors'; +import { GroupContactIdsNotMatchingError, GroupContactNotFoundError } from '../errors/GroupContactErrors'; import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { CreateGroupContact } from '../models/actions/create/CreateGroupContact'; +import { UpdateGroupContact } from '../models/actions/update/UpdateGroupContact'; import { GroupContact } from '../models/entities/GroupContact'; import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseGroupContact } from '../models/responses/ResponseGroupContact'; @@ -62,26 +63,27 @@ export class GroupContactController { return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups'] })).toResponse(); } - // @Put('/:id') - // @Authorized("DONOR:UPDATE") - // @ResponseSchema(ResponseDonor) - // @ResponseSchema(DonorNotFoundError, { statusCode: 404 }) - // @ResponseSchema(DonorIdsNotMatchingError, { statusCode: 406 }) - // @OpenAPI({ description: "Update the contact whose id you provided.
Please remember that ids can't be changed." }) - // async put(@Param('id') id: number, @Body({ validate: true }) contact: UpdateDonor) { - // let oldDonor = await this.contactRepository.findOne({ id: id }); + @Put('/:id') + @Authorized("CONTACT:UPDATE") + @ResponseSchema(ResponseGroupContact) + @ResponseSchema(GroupContactNotFoundError, { statusCode: 404 }) + @ResponseSchema(GroupContactIdsNotMatchingError, { statusCode: 406 }) + @ResponseSchema(RunnerGroupNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: "Update the contact whose id you provided.
Please remember that ids can't be changed." }) + async put(@Param('id') id: number, @Body({ validate: true }) contact: UpdateGroupContact) { + let oldContact = await this.contactRepository.findOne({ id: id }); - // if (!oldDonor) { - // throw new DonorNotFoundError(); - // } + if (!oldContact) { + throw new GroupContactNotFoundError(); + } - // if (oldDonor.id != contact.id) { - // throw new DonorIdsNotMatchingError(); - // } + if (oldContact.id != contact.id) { + throw new GroupContactIdsNotMatchingError(); + } - // await this.contactRepository.save(await contact.update(oldDonor)); - // return new ResponseDonor(await this.contactRepository.findOne({ id: id }, { relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] })); - // } + await this.contactRepository.save(await contact.update(oldContact)); + return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups'] })).toResponse(); + } @Delete('/:id') @Authorized("CONTACT:DELETE") From 56c73c2555d4d12ffb088ec5550667022d3a8694 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 18:03:29 +0100 Subject: [PATCH 41/52] Added openapi description about non-deletion ref #104 --- src/controllers/RunnerOrganisationController.ts | 2 +- src/controllers/RunnerTeamController.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/RunnerOrganisationController.ts b/src/controllers/RunnerOrganisationController.ts index fbbd03a..4d985e4 100644 --- a/src/controllers/RunnerOrganisationController.ts +++ b/src/controllers/RunnerOrganisationController.ts @@ -94,7 +94,7 @@ export class RunnerOrganisationController { @ResponseSchema(RunnerOrganisationHasTeamsError, { statusCode: 406 }) @ResponseSchema(RunnerOrganisationHasRunnersError, { statusCode: 406 }) @OnUndefined(204) - @OpenAPI({ description: 'Delete the organsisation whose id you provided.
If the organisation still has runners and/or teams associated this will fail.
To delete the organisation with all associated runners and teams set the force QueryParam to true (cascading deletion might take a while).
If no organisation with this id exists it will just return 204(no content).' }) + @OpenAPI({ description: 'Delete the organsisation whose id you provided.
If the organisation still has runners and/or teams associated this will fail.
To delete the organisation with all associated runners and teams set the force QueryParam to true (cascading deletion might take a while).
This won\'t delete the associated contact.
If no organisation with this id exists it will just return 204(no content).' }) async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { let organisation = await this.runnerOrganisationRepository.findOne({ id: id }); if (!organisation) { return null; } diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index 5b97f2b..ce706ec 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -93,7 +93,7 @@ export class RunnerTeamController { @ResponseSchema(ResponseEmpty, { statusCode: 204 }) @ResponseSchema(RunnerTeamHasRunnersError, { statusCode: 406 }) @OnUndefined(204) - @OpenAPI({ description: 'Delete the team whose id you provided.
If the team still has runners associated this will fail.
To delete the team with all associated runners set the force QueryParam to true (cascading deletion might take a while).
If no team with this id exists it will just return 204(no content).' }) + @OpenAPI({ description: 'Delete the team whose id you provided.
If the team still has runners associated this will fail.
To delete the team with all associated runners set the force QueryParam to true (cascading deletion might take a while).
This won\'t delete the associated contact.
If no team with this id exists it will just return 204(no content).' }) async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { let team = await this.runnerTeamRepository.findOne({ id: id }); if (!team) { return null; } From b002cf2df1eafc722fbfb51b3bffb02bee002305 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 18:13:39 +0100 Subject: [PATCH 42/52] Added contact get tests ref #104 --- src/tests/contacts/contact_get.spec.ts | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/tests/contacts/contact_get.spec.ts diff --git a/src/tests/contacts/contact_get.spec.ts b/src/tests/contacts/contact_get.spec.ts new file mode 100644 index 0000000..31aa895 --- /dev/null +++ b/src/tests/contacts/contact_get.spec.ts @@ -0,0 +1,57 @@ +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('GET /api/contacts', () => { + it('basic get should return 200', async () => { + const res = await axios.get(base + '/api/contacts', axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('GET /api/contacts/0', () => { + it('basic get should return 404', async () => { + const res = await axios.get(base + '/api/contacts/0', axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('GET /api/contacts after adding', () => { + let added_contact; + it('creating a new donor with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last" + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('explicit get should return 200', async () => { + const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let gotten_donor = res.data + expect(gotten_donor).toEqual(added_contact); + }); + it('get from all runners should return 200', async () => { + const res = await axios.get(base + '/api/contacts/', axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let gotten_donors = res.data + expect(gotten_donors).toContainEqual(added_contact); + }); +}); \ No newline at end of file From 940d62cde4cf7be7780904d681a5e4c9efaa2ba5 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 18:14:09 +0100 Subject: [PATCH 43/52] Added contact add invalid tests ref #104 --- src/tests/contacts/contact_add.spec.ts | 75 ++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/tests/contacts/contact_add.spec.ts diff --git a/src/tests/contacts/contact_add.spec.ts b/src/tests/contacts/contact_add.spec.ts new file mode 100644 index 0000000..2136aa3 --- /dev/null +++ b/src/tests/contacts/contact_add.spec.ts @@ -0,0 +1,75 @@ +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +// --------------- +describe('POST /api/contacts with errors', () => { + it('creating a new contact without any parameters should return 400', async () => { + const res = await axios.post(base + '/api/contacts', null, axios_config); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact without a last name should return 400', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "middlename": "middle" + }, axios_config); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a invalid phone number should return 400', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "middlename": "middle", + "lastname": "last", + "phone": "123" + }, axios_config); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a invalid mail address should return 400', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "string", + "middlename": "string", + "lastname": "string", + "phone": null, + "email": "123", + }, axios_config); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with an invalid address 400', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "string", + "middlename": "string", + "lastname": "string", + "address": { + "city": "Testcity" + } + }, axios_config); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a invalid group should return 404', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "string", + "middlename": "string", + "lastname": "string", + "groups": 9999999999999 + }, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json") + }); +}); From e165f019307e7745357493eacf3e2fa31538122b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 18:48:37 +0100 Subject: [PATCH 44/52] Added contact add valid tests ref #104 --- src/tests/contacts/contact_add.spec.ts | 141 +++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/tests/contacts/contact_add.spec.ts b/src/tests/contacts/contact_add.spec.ts index 2136aa3..0cb5160 100644 --- a/src/tests/contacts/contact_add.spec.ts +++ b/src/tests/contacts/contact_add.spec.ts @@ -73,3 +73,144 @@ describe('POST /api/contacts with errors', () => { expect(res.headers['content-type']).toContain("application/json") }); }); +// --------------- +describe('POST /api/contacts working (simple)', () => { + it('creating a new contact with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last" + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with all non-relationship optional params should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "middlename": "middle", + "lastname": "last", + "email": "testContact@lauf-fuer-kaya.de", + "phone": "+49017612345678", + "address": { + "address1": "test", + "address2": null, + "city": "herzogenaurach", + "country": "germany", + "postalcode": "91074", + } + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('POST /api/contacts working (with group)', () => { + let added_org; + let added_team; + let added_contact; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + delete res.data.contact; + delete res.data.teams; + added_org = res.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a parent org should return 200', async () => { + const res = await axios.post(base + '/api/teams', { + "name": "test_team", + "parentGroup": added_org.id + }, axios_config); + delete res.data.contact; + delete res.data.parentGroup; + added_team = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a valid org should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": added_org.id + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + delete res.data.id; + expect(res.data).toEqual({ + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_org] + }); + }); + it('creating a new contact with a valid team should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": added_team.id + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + delete res.data.id; + expect(res.data).toEqual({ + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_team] + }); + }); + it('creating a new contact with a valid org and team should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": [added_org.id, added_team.id] + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + added_contact = res.data + delete res.data.id; + expect(res.data).toEqual({ + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_org, added_team] + }); + }); + it('checking if the added team\'s contact is the new contact should return 200', async () => { + const res = await axios.get(base + '/api/teams/' + added_team.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + delete res.data.contact.groups; + delete res.data.contact.id; + delete added_contact.groups; + expect(res.data.contact).toEqual(added_contact); + }); +}); \ No newline at end of file From dd7e5dae368a8decd79357f658dda2164fa6f1e7 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:04:09 +0100 Subject: [PATCH 45/52] Added contact delete tests ref #104 --- src/tests/contacts/contact_delete.spec.ts | 182 ++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 src/tests/contacts/contact_delete.spec.ts diff --git a/src/tests/contacts/contact_delete.spec.ts b/src/tests/contacts/contact_delete.spec.ts new file mode 100644 index 0000000..e8f22fc --- /dev/null +++ b/src/tests/contacts/contact_delete.spec.ts @@ -0,0 +1,182 @@ +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('adding + deletion (non-existant)', () => { + it('delete', async () => { + const res = await axios.delete(base + '/api/contacts/0', axios_config); + expect(res.status).toEqual(204); + }); +}); +// --------------- +describe('add+delete (simple)', () => { + let added_contact; + it('creating a new contact with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last" + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('delete contact', async () => { + const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let deleted_contact = res.data + expect(deleted_contact).toEqual(added_contact); + }); + it('check if contact really was deleted', async () => { + const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('add+delete (with org)', () => { + let added_org; + let added_contact; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + delete res.data.contact; + delete res.data.teams; + added_org = res.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a valid org should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": added_org.id + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete contact', async () => { + const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let deleted_contact = res.data + expect(deleted_contact).toEqual(added_contact); + }); + it('check if contact really was deleted', async () => { + const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('add+delete (with team)', () => { + let added_org; + let added_team; + let added_contact; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + delete res.data.contact; + delete res.data.teams; + added_org = res.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a parent org should return 200', async () => { + const res = await axios.post(base + '/api/teams', { + "name": "test_team", + "parentGroup": added_org.id + }, axios_config); + delete res.data.contact; + delete res.data.parentGroup; + added_team = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a valid team should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": added_team.id + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete contact', async () => { + const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let deleted_contact = res.data + expect(deleted_contact).toEqual(added_contact); + }); + it('check if contact really was deleted', async () => { + const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('add+delete (with org&team)', () => { + let added_org; + let added_team; + let added_contact; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + delete res.data.contact; + delete res.data.teams; + added_org = res.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a parent org should return 200', async () => { + const res = await axios.post(base + '/api/teams', { + "name": "test_team", + "parentGroup": added_org.id + }, axios_config); + delete res.data.contact; + delete res.data.parentGroup; + added_team = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a valid org and team should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": [added_org.id, added_team.id] + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete contact', async () => { + const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let deleted_contact = res.data + expect(deleted_contact).toEqual(added_contact); + }); + it('check if contact really was deleted', async () => { + const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json") + }); +}); \ No newline at end of file From 179c2a5157fca036acf8d0e6a51821d377860bc1 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:04:46 +0100 Subject: [PATCH 46/52] Fixed contact cascading ref #104 --- src/controllers/GroupContactController.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/controllers/GroupContactController.ts b/src/controllers/GroupContactController.ts index 12518c2..3d6f83e 100644 --- a/src/controllers/GroupContactController.ts +++ b/src/controllers/GroupContactController.ts @@ -1,11 +1,12 @@ import { Authorized, 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 { getConnection, getConnectionManager, Repository } from 'typeorm'; import { GroupContactIdsNotMatchingError, GroupContactNotFoundError } from '../errors/GroupContactErrors'; import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { CreateGroupContact } from '../models/actions/create/CreateGroupContact'; import { UpdateGroupContact } from '../models/actions/update/UpdateGroupContact'; import { GroupContact } from '../models/entities/GroupContact'; +import { RunnerGroup } from '../models/entities/RunnerGroup'; import { ResponseEmpty } from '../models/responses/ResponseEmpty'; import { ResponseGroupContact } from '../models/responses/ResponseGroupContact'; @@ -95,6 +96,10 @@ export class GroupContactController { let contact = await this.contactRepository.findOne({ id: id }); if (!contact) { return null; } const responseContact = await this.contactRepository.findOne(contact, { relations: ['groups'] }); + for (let group of responseContact.groups) { + group.contact = null; + await getConnection().getRepository(RunnerGroup).save(group); + } await this.contactRepository.delete(contact); return responseContact.toResponse(); From 8ae53f1c4930e2fd72eb230a5314336f3a45a611 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:12:53 +0100 Subject: [PATCH 47/52] Updated contact delete tests ref #104 --- src/tests/contacts/contact_delete.spec.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tests/contacts/contact_delete.spec.ts b/src/tests/contacts/contact_delete.spec.ts index e8f22fc..f8d8c64 100644 --- a/src/tests/contacts/contact_delete.spec.ts +++ b/src/tests/contacts/contact_delete.spec.ts @@ -73,8 +73,8 @@ describe('add+delete (with org)', () => { const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); expect(res.status).toEqual(200); expect(res.headers['content-type']).toContain("application/json") - let deleted_contact = res.data - expect(deleted_contact).toEqual(added_contact); + delete res.data.groups[0].contact; + expect(res.data).toEqual(added_contact); }); it('check if contact really was deleted', async () => { const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); @@ -122,8 +122,8 @@ describe('add+delete (with team)', () => { const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); expect(res.status).toEqual(200); expect(res.headers['content-type']).toContain("application/json") - let deleted_contact = res.data - expect(deleted_contact).toEqual(added_contact); + delete res.data.groups[0].contact; + expect(res.data).toEqual(added_contact); }); it('check if contact really was deleted', async () => { const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); @@ -171,8 +171,9 @@ describe('add+delete (with org&team)', () => { const res = await axios.delete(base + '/api/contacts/' + added_contact.id, axios_config); expect(res.status).toEqual(200); expect(res.headers['content-type']).toContain("application/json") - let deleted_contact = res.data - expect(deleted_contact).toEqual(added_contact); + delete res.data.groups[0].contact; + delete res.data.groups[1].contact; + expect(res.data).toEqual(added_contact); }); it('check if contact really was deleted', async () => { const res = await axios.get(base + '/api/contacts/' + added_contact.id, axios_config); From c3d008ec0ff92f80addbdb93ffc1fa2b3278a8a6 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:32:39 +0100 Subject: [PATCH 48/52] Updated contact update tests ref #104 --- src/tests/contacts/contact_update.spec.ts | 237 ++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 src/tests/contacts/contact_update.spec.ts diff --git a/src/tests/contacts/contact_update.spec.ts b/src/tests/contacts/contact_update.spec.ts new file mode 100644 index 0000000..e6d733d --- /dev/null +++ b/src/tests/contacts/contact_update.spec.ts @@ -0,0 +1,237 @@ +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('Update contact name after adding', () => { + let added_contact; + it('creating a new contact with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last" + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('valid update should return 200', async () => { + let contact_copy = added_contact + contact_copy.firstname = "second" + const res = await axios.put(base + '/api/contacts/' + added_contact.id, contact_copy, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + expect(res.data).toEqual(contact_copy); + }); +}); +// --------------- +describe('Update contact id after adding(should fail)', () => { + let added_contact; + it('creating a new donor with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last" + }, axios_config); + added_contact = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('invalid update should return 406', async () => { + added_contact.id++; + const res = await axios.put(base + '/api/contacts/' + (added_contact.id - 1), added_contact, axios_config); + expect(res.status).toEqual(406); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('Update contact group after adding (should work)', () => { + let added_org; + let added_team; + let added_contact; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + delete res.data.contact; + delete res.data.teams; + added_org = res.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a parent org should return 200', async () => { + const res = await axios.post(base + '/api/teams', { + "name": "test_team", + "parentGroup": added_org.id + }, axios_config); + delete res.data.contact; + delete res.data.parentGroup; + added_team = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a valid org should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": added_org.id + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + added_contact = res.data + expect(res.data).toEqual({ + "id": res.data.id, + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_org] + }); + }); + it('valid group update to single team should return 200', async () => { + const res = await axios.put(base + '/api/contacts/' + added_contact.id, { + "id": added_contact.id, + "firstname": "first", + "lastname": "last", + "groups": added_team.id + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ + "id": res.data.id, + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_team] + }); + }); + it('valid group update to org and team should return 200', async () => { + const res = await axios.put(base + '/api/contacts/' + added_contact.id, { + "id": added_contact.id, + "firstname": "first", + "lastname": "last", + "groups": [added_org.id, added_team.id] + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ + "id": res.data.id, + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_org, added_team] + }); + }); + it('valid group update to none should return 200', async () => { + const res = await axios.put(base + '/api/contacts/' + added_contact.id, { + "id": added_contact.id, + "firstname": "first", + "lastname": "last", + "groups": null + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ + "id": res.data.id, + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [] + }); + }); +}); +// --------------- +describe('Update contact group invalid after adding (should fail)', () => { + let added_org; + let added_contact; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + delete res.data.contact; + delete res.data.teams; + added_org = res.data + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new contact with a valid org should return 200', async () => { + const res = await axios.post(base + '/api/contacts', { + "firstname": "first", + "lastname": "last", + "groups": added_org.id + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + added_contact = res.data + expect(res.data).toEqual({ + "id": res.data.id, + "firstname": "first", + "middlename": null, + "lastname": "last", + "phone": null, + "email": null, + "address": { + "address1": null, + "address2": null, + "postalcode": null, + "city": null, + "country": null + }, + "groups": [added_org] + }); + }); + it('invalid group update to single team should return 404', async () => { + const res = await axios.put(base + '/api/contacts/' + added_contact.id, { + "id": added_contact.id, + "firstname": "first", + "lastname": "last", + "groups": 999999999999999 + }, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json"); + }); +}); \ No newline at end of file From a1acd3519f66965202da0cd15df61a807230619c Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:33:11 +0100 Subject: [PATCH 49/52] Adjusted env sample ref #104 ref #105 --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 5595533..076d8cd 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,4 @@ DB_USER=bla DB_PASSWORD=bla DB_NAME=bla NODE_ENV=production -POSTALCODE_COUNTRYCODE=null \ No newline at end of file +POSTALCODE_COUNTRYCODE=DE \ No newline at end of file From 8960aa5545ddeb57d4ef42c21c0ca6001dfeaea9 Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:09:11 +0000 Subject: [PATCH 50/52] =?UTF-8?q?=F0=9F=A7=BENew=20changelog=20file=20vers?= =?UTF-8?q?ion=20[CI=20SKIP]=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bddfa7..e619e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,26 +4,55 @@ All notable changes to this project will be documented in this file. Dates are d #### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.1...v0.1.1) -- Merge pull request 'Fully implemented addresses feature/105-addresses' (#107) from feature/105-addresses into dev [`5e36855`](https://git.odit.services/lfk/backend/commit/5e368552ea810ea1d3963b1207ba98f7b46c4abc) +- Merge pull request 'Implemented group contacts feature/104-contacts' (#108) from feature/104-contacts into dev [`a0c2b5a`](https://git.odit.services/lfk/backend/commit/a0c2b5ade8d198ec16d33b39e47205e8b03a669f) +- Updated contact update tests [`c3d008e`](https://git.odit.services/lfk/backend/commit/c3d008ec0ff92f80addbdb93ffc1fa2b3278a8a6) +- Added contact delete tests [`dd7e5da`](https://git.odit.services/lfk/backend/commit/dd7e5dae368a8decd79357f658dda2164fa6f1e7) +- Added contact add valid tests [`e165f01`](https://git.odit.services/lfk/backend/commit/e165f019307e7745357493eacf3e2fa31538122b) - Cleaned up var names [`a3c93f0`](https://git.odit.services/lfk/backend/commit/a3c93f0d394833f1a6f78d862b094ca751c85561) - Added address update ivalid tests [`427dfaa`](https://git.odit.services/lfk/backend/commit/427dfaafabd243e94aba27c2dec2705fd8ed5d64) +- Added barebones contact controller from donor-controller [`3e7190e`](https://git.odit.services/lfk/backend/commit/3e7190e279181c5f99d890ca141489b24908b904) - Added first address update tests [`4d40225`](https://git.odit.services/lfk/backend/commit/4d40225a4491e8eb3f41ef0fd558a599f63729be) +- Added a contact update class [`c172aa8`](https://git.odit.services/lfk/backend/commit/c172aa8bf8083500828743ed696955a1fe3caef2) - Added address update valid tests [`230cdb0`](https://git.odit.services/lfk/backend/commit/230cdb0e37e2b7a21e7feb156f2b91a69ad200fd) - Implemented deep address validation [`9dc9ce3`](https://git.odit.services/lfk/backend/commit/9dc9ce37d8fbfc92842e4e05bbde68398324a186) +- Added contact add invalid tests [`940d62c`](https://git.odit.services/lfk/backend/commit/940d62cde4cf7be7780904d681a5e4c9efaa2ba5) +- Added a contact response class [`1407fe3`](https://git.odit.services/lfk/backend/commit/1407fe36f3637d6c53024c48788b318d985f8960) - Removed old create address class [`2a465f8`](https://git.odit.services/lfk/backend/commit/2a465f88c58c0b4be3ecd99d96a04c177a40b312) - Switched the create classes over to the new address implementation [`2cd15d2`](https://git.odit.services/lfk/backend/commit/2cd15d25e934a5439bfea4de901f136e360e17f6) +- Added contact get tests [`b002cf2`](https://git.odit.services/lfk/backend/commit/b002cf2df1eafc722fbfb51b3bffb02bee002305) - Test's now accept the new address format [`8dbee32`](https://git.odit.services/lfk/backend/commit/8dbee32eeec8ee3d013e4446e8f53544ee4cb577) +- Implemented the get endpoints [`ab70f7e`](https://git.odit.services/lfk/backend/commit/ab70f7e49893344dde8f5af93f571a5d67818e19) +- Implemented contact deletion [`0379786`](https://git.odit.services/lfk/backend/commit/0379786cbda057ad95d709fa135d34beb0db8de1) - Removed the IAddressUser Interface entity [`e265172`](https://git.odit.services/lfk/backend/commit/e2651728c5abf2273bf51a7652c51d55d8fa0a2f) +- Implemented contact updateing [`28fb983`](https://git.odit.services/lfk/backend/commit/28fb9834e18bde012c5b51cc49a39585d20f7cc1) +- Fixed key null constraint [`de82437`](https://git.odit.services/lfk/backend/commit/de824375d3a1da6ee4d78ea39b7da66fc05f2a02) +- Implemented contact posting [`11af9c0`](https://git.odit.services/lfk/backend/commit/11af9c02d977dcd6919652256dbdb9fd5438cabd) +- Implemented contact group setting on creation [`3b06d1a`](https://git.odit.services/lfk/backend/commit/3b06d1a6ef3c95eb5bb7d485accddabba0a8e4f7) +- 🧾New changelog file version [CI SKIP] [skip ci] [`32e054e`](https://git.odit.services/lfk/backend/commit/32e054eb84c869210fd483583ae5a6d0e2249cf9) - Switched Address to embedded entity [`7fbe649`](https://git.odit.services/lfk/backend/commit/7fbe649dc90f4bb9f240c5a80fed447048e5e105) - Removed the address errors [`58ae9b5`](https://git.odit.services/lfk/backend/commit/58ae9b589aef3580f4b8558c3d5ddbd7171e7915) - Switched the update classes over to the new address implementation [`d0df5dd`](https://git.odit.services/lfk/backend/commit/d0df5dd641ac17f1fd8ad6bd8b46afa9dd2745c3) +- Updated the contact errors [`a9a5eb6`](https://git.odit.services/lfk/backend/commit/a9a5eb673570bb3c0d1a55bb1292e208493663bd) - Implemented adress deletion (through reset) [`57b9c2b`](https://git.odit.services/lfk/backend/commit/57b9c2babcd68d69d1cbb240a86c2897717ba758) - Fixed donor address check [`4824547`](https://git.odit.services/lfk/backend/commit/4824547dde4d7f90e9e2377a26df34cabf082fdb) +- Updated contact delete tests [`8ae53f1`](https://git.odit.services/lfk/backend/commit/8ae53f1c4930e2fd72eb230a5314336f3a45a611) +- Added address to contact response [`09e429f`](https://git.odit.services/lfk/backend/commit/09e429fc676c7dd370bba0495b072f81867bd250) +- Updated comments [`a4e8311`](https://git.odit.services/lfk/backend/commit/a4e8311cbd22588ecb4dc2fdbe05397b07d336f8) - Updated the responseclasses to use the new address implementation [`dafac06`](https://git.odit.services/lfk/backend/commit/dafac06bc84d1b237096a561b3adcd3ca5cb1dd8) - Removed (now useless) relations [`673dea2`](https://git.odit.services/lfk/backend/commit/673dea2e5754e99ff77f7556d4fc03d4cca28a94) +- Added missing id property [`6b4b16c`](https://git.odit.services/lfk/backend/commit/6b4b16c13b0c2f55745ded3431cad2f4986be296) - Added address validity check [`ae7c5ff`](https://git.odit.services/lfk/backend/commit/ae7c5ff0c387e9337d01a9dd819a4dddc208f6dd) - 🧾New changelog file version [CI SKIP] [skip ci] [`f53894b`](https://git.odit.services/lfk/backend/commit/f53894b16ac1c06ecbeeb0b63a56ac438b2fbe1b) - Updated comments [`8bc01d3`](https://git.odit.services/lfk/backend/commit/8bc01d3f2406ce8e58c2ab2963c858495c510dcf) +- Fixed contact cascading [`179c2a5`](https://git.odit.services/lfk/backend/commit/179c2a5157fca036acf8d0e6a51821d377860bc1) +- Added openapi description about non-deletion [`56c73c2`](https://git.odit.services/lfk/backend/commit/56c73c2555d4d12ffb088ec5550667022d3a8694) +- Added contact permission target [`d12801e`](https://git.odit.services/lfk/backend/commit/d12801e34d57cec0e867f69123e3be39ed2fe2f5) +- Adjusted env sample [`a1acd35`](https://git.odit.services/lfk/backend/commit/a1acd3519f66965202da0cd15df61a807230619c) +- Renamed controller to better fit the overall nameing scheme [`d743f7e`](https://git.odit.services/lfk/backend/commit/d743f7ee1277256ada8fe39f900349ff2643118a) +- Fixed column not getting resolved [`2b658ac`](https://git.odit.services/lfk/backend/commit/2b658ac381f318cf37fc2051ea3e83976e3c5773) +- Fixed column not getting resolved [`321d291`](https://git.odit.services/lfk/backend/commit/321d291b4bf983ff4930cadecf3a013430a97649) +- Fixed push undefined eror [`2eb26e4`](https://git.odit.services/lfk/backend/commit/2eb26e4e381a97fd829a294501fa42ac7b712b56) +- Merge pull request 'Fully implemented addresses feature/105-addresses' (#107) from feature/105-addresses into dev [`5e36855`](https://git.odit.services/lfk/backend/commit/5e368552ea810ea1d3963b1207ba98f7b46c4abc) - Set country code for the ci env to DE [`30b585c`](https://git.odit.services/lfk/backend/commit/30b585c0c12b3b9818778110d33d5b3ab84d4192) - Implemented postal code validation for the validaton function [`f245840`](https://git.odit.services/lfk/backend/commit/f245840cde5726611197b00730aca72ea133c427) From ddafd90d3e41fb9ee37172a8306c30d8483dfe2c Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 20:09:30 +0100 Subject: [PATCH 51/52] =?UTF-8?q?=F0=9F=9A=80Bumped=20version=20to=20v0.2.?= =?UTF-8?q?0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 20b9711..718d8d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@odit/lfk-backend", - "version": "0.1.1", + "version": "0.2.0", "main": "src/app.ts", "repository": "https://git.odit.services/lfk/backend", "author": { From da9a3592510eacd2d67a127dc61e954343e0444b Mon Sep 17 00:00:00 2001 From: Nicolai Ort Date: Tue, 19 Jan 2021 19:10:15 +0000 Subject: [PATCH 52/52] =?UTF-8?q?=F0=9F=A7=BENew=20changelog=20file=20vers?= =?UTF-8?q?ion=20[CI=20SKIP]=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e619e88..99d01f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,10 @@ All notable changes to this project will be documented in this file. Dates are displayed in UTC. -#### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.1...v0.1.1) +#### [v0.2.0](https://git.odit.services/lfk/backend/compare/v0.1.1...v0.2.0) +- 🧾New changelog file version [CI SKIP] [skip ci] [`8960aa5`](https://git.odit.services/lfk/backend/commit/8960aa5545ddeb57d4ef42c21c0ca6001dfeaea9) +- 🚀Bumped version to v0.2.0 [`ddafd90`](https://git.odit.services/lfk/backend/commit/ddafd90d3e41fb9ee37172a8306c30d8483dfe2c) - Merge pull request 'Implemented group contacts feature/104-contacts' (#108) from feature/104-contacts into dev [`a0c2b5a`](https://git.odit.services/lfk/backend/commit/a0c2b5ade8d198ec16d33b39e47205e8b03a669f) - Updated contact update tests [`c3d008e`](https://git.odit.services/lfk/backend/commit/c3d008ec0ff92f80addbdb93ffc1fa2b3278a8a6) - Added contact delete tests [`dd7e5da`](https://git.odit.services/lfk/backend/commit/dd7e5dae368a8decd79357f658dda2164fa6f1e7) @@ -51,10 +53,10 @@ All notable changes to this project will be documented in this file. Dates are d - Renamed controller to better fit the overall nameing scheme [`d743f7e`](https://git.odit.services/lfk/backend/commit/d743f7ee1277256ada8fe39f900349ff2643118a) - Fixed column not getting resolved [`2b658ac`](https://git.odit.services/lfk/backend/commit/2b658ac381f318cf37fc2051ea3e83976e3c5773) - Fixed column not getting resolved [`321d291`](https://git.odit.services/lfk/backend/commit/321d291b4bf983ff4930cadecf3a013430a97649) -- Fixed push undefined eror [`2eb26e4`](https://git.odit.services/lfk/backend/commit/2eb26e4e381a97fd829a294501fa42ac7b712b56) -- Merge pull request 'Fully implemented addresses feature/105-addresses' (#107) from feature/105-addresses into dev [`5e36855`](https://git.odit.services/lfk/backend/commit/5e368552ea810ea1d3963b1207ba98f7b46c4abc) - Set country code for the ci env to DE [`30b585c`](https://git.odit.services/lfk/backend/commit/30b585c0c12b3b9818778110d33d5b3ab84d4192) - Implemented postal code validation for the validaton function [`f245840`](https://git.odit.services/lfk/backend/commit/f245840cde5726611197b00730aca72ea133c427) +- Fixed push undefined eror [`2eb26e4`](https://git.odit.services/lfk/backend/commit/2eb26e4e381a97fd829a294501fa42ac7b712b56) +- Merge pull request 'Fully implemented addresses feature/105-addresses' (#107) from feature/105-addresses into dev [`5e36855`](https://git.odit.services/lfk/backend/commit/5e368552ea810ea1d3963b1207ba98f7b46c4abc) #### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.0...v0.1.1)