From 45a7fa6ec799e05eb25c5a2f03a64c4f8fbd352b Mon Sep 17 00:00:00 2001 From: leonnicolas Date: Sun, 27 Sep 2020 17:27:08 +0200 Subject: [PATCH] Engagement, EngagementType: read and create --- src/datasources/db/cargobikeAPI.ts | 9 +- src/datasources/db/participantAPI.ts | 113 ++++++++------------------ src/datasources/db/utils.ts | 13 +++ src/model/CargoBike.ts | 2 +- src/model/Engagement.ts | 44 +++++++--- src/model/Participant.ts | 3 +- src/resolvers/participantResolvers.ts | 11 ++- src/schema/type-defs.ts | 19 +++-- 8 files changed, 104 insertions(+), 110 deletions(-) diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index 0bc7dd9..e4ff927 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -43,12 +43,11 @@ export class CargoBikeAPI extends DataSource { } async findCargoBikeByEngagementId (id: number) { - return (await this.connection.getRepository(Engagement) + return await this.connection.getRepository(Engagement) .createQueryBuilder('engagement') - .leftJoinAndSelect('engagement.cargoBike', 'cargoBike') - .where('engagement."cargoBikeId" = "cargoBike".id') - .andWhere('engagement.id = :id', { id: id }) - .getOne())?.cargoBike; + .relation(Engagement, 'cargoBikeId') + .of(id) + .loadOne(); } async cargoBikesByProviderId (id: number) { diff --git a/src/datasources/db/participantAPI.ts b/src/datasources/db/participantAPI.ts index 9afce47..5810fd9 100644 --- a/src/datasources/db/participantAPI.ts +++ b/src/datasources/db/participantAPI.ts @@ -1,11 +1,11 @@ import { DataSource } from 'apollo-datasource'; -import { GraphQLError } from 'graphql'; import { Connection, EntityManager, getConnection } from 'typeorm'; -import { CargoBike } from '../../model/CargoBike'; import { ContactInformation } from '../../model/ContactInformation'; import { Engagement } from '../../model/Engagement'; import { Participant } from '../../model/Participant'; import { EngagementType } from '../../model/EngagementType'; +import { genDateRange } from './utils'; +import { UserInputError } from 'apollo-server'; export class ParticipantAPI extends DataSource { connection : Connection @@ -32,12 +32,11 @@ export class ParticipantAPI extends DataSource { } async participantByEngagementId (id: number) { - return (await this.connection.getRepository(Engagement) + return await this.connection.getRepository(Engagement) .createQueryBuilder('engagement') - .leftJoinAndSelect('engagement.participant', 'participant') - .where('engagement.id = :id', { id: id }) - .andWhere('engagement."participantId" = participant.id') - .getOne()).participant; + .relation(Engagement, 'participantId') + .of(id) + .loadOne(); } async participantByCargoBikeId (id:number) { @@ -54,7 +53,7 @@ export class ParticipantAPI extends DataSource { .createQueryBuilder('engagement') .select() .where('engagement."participantId" = :id', { id: id }) - .getOne(); + .getMany(); } async engagementByCargoBikeId (offset: number, limit: number, id: number) { @@ -64,10 +63,9 @@ export class ParticipantAPI extends DataSource { .where('engagement."cargoBikeId" = :id', { id: id }) - .offset(offset) - .limit(limit) - .orderBy('engagement.from', 'DESC') - .addOrderBy('engagement.to', 'DESC', 'NULLS FIRST') + .skip(offset) + .take(limit) + .orderBy('engagement."dateRange"', 'DESC') .getMany(); } @@ -82,7 +80,9 @@ export class ParticipantAPI extends DataSource { async engagementTypeByEngagementId (id: number) { return await this.connection.getRepository(Engagement) .createQueryBuilder('engagement') - .relation(Engagement, 'engageMent'); + .relation(Engagement, 'engagementTypeId') + .of(id) + .loadOne(); } async contactInformationById (id: number) { @@ -106,24 +106,6 @@ export class ParticipantAPI extends DataSource { * @param participant to be created */ async createParticipant (participant: any) { - /* let count = this.connection.getRepository(ContactInformation) - .createQueryBuilder('contactInformation') - .select() - .where('contactInformation.id = :id', { id: participant.contactInformationId }) - .getCount(); - if ((await count) !== 1) { - return new GraphQLError('contactInformationId not found.'); - } - count = this.connection.getRepository(Participant) - .createQueryBuilder('participant') - .select() - .where('participant."contactInformationId" = :id', { - id: participant.contactInformationId - }) - .getCount(); - if ((await count) !== 0) { - return new GraphQLError('contactInformationId already used by other participant.'); - } */ let inserts: any; await this.connection.transaction(async (entityManager: EntityManager) => { inserts = await entityManager.getRepository(Participant) @@ -133,59 +115,32 @@ export class ParticipantAPI extends DataSource { .values([participant]) .returning('*') .execute(); - /* await entityManager.getRepository(Participant) - .createQueryBuilder('participant') - .relation(Participant, 'contactInformation') - .of(inserts.identifiers[0].id) - .set(participant.contactInformationId); - */ }); - /* const inserts = await this.connection.getRepository(Participant) - .createQueryBuilder('participant') - .insert() - .into(Participant) - .values([participant]) - .returning('*') - .execute(); - await this.connection.getRepository(Participant) - .createQueryBuilder('participant') - .relation(Participant, 'contactInformation') - .of(inserts.identifiers[0].id) - .set(participant.contactInformationId); - */ return this.getParticipantById(inserts.identifiers[0].id); } async createEngagement (engagement: any) { - const countB = this.connection.getRepository(CargoBike) - .createQueryBuilder('cargoBike') - .select() - .where('cargoBike.id = :id', { id: engagement.cargoBikeId }) - .getCount(); - const countP = this.connection.getRepository(Participant) - .createQueryBuilder('participant') - .select() - .where('participant.id = :id', { id: engagement.participantId }) - .getCount(); - if ((await countB) !== 1) { return new GraphQLError('BikeId not found'); } - if ((await countP) !== 1) { return new GraphQLError('ParticipantId not found'); } - // TODO check whether someone is already engaged with the bike - const inserts = await this.connection.getRepository(Engagement) - .createQueryBuilder('engagement') - .insert() - .values([engagement]) - .returning('*') - .execute(); - await this.connection.getRepository(Engagement) - .createQueryBuilder('engagement') - .relation(Engagement, 'cargoBike') - .of(inserts.identifiers[0].id) - .set(engagement.cargoBikeId); - await this.connection.getRepository(Engagement) - .createQueryBuilder('engagement') - .relation(Engagement, 'participant') - .of(inserts.identifiers[0].id) - .set(engagement.participantId); + let inserts: any; + genDateRange(engagement); + await this.connection.transaction(async (entityManager: EntityManager) => { + // check for overlapping engagements + const overlapping = await entityManager.getRepository(Engagement) + .createQueryBuilder('e') + .select() + .where('e."cargoBikeId" = :id', { id: engagement.cargoBikeId }) + .andWhere('e."dateRange" && :dr', { dr: engagement.dateRange }) + .andWhere('e."engagementTypeId" = :etId', { etId: engagement.engagementTypeId }) + .getMany(); + if (overlapping.length > 0) { + throw new UserInputError('Engagements with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping'); + } + inserts = await entityManager.getRepository(Engagement) + .createQueryBuilder('engagement') + .insert() + .values([engagement]) + .returning('*') + .execute(); + }); return this.engagementById(inserts.identifiers[0].id); } diff --git a/src/datasources/db/utils.ts b/src/datasources/db/utils.ts index b409165..af0b0f3 100644 --- a/src/datasources/db/utils.ts +++ b/src/datasources/db/utils.ts @@ -1,6 +1,19 @@ import { Connection, ObjectType } from 'typeorm'; import { CargoBike, Lockable } from '../../model/CargoBike'; +export function genDateRange (struct: any) { + if (struct.to === undefined) { + struct.to = ''; + } + if (struct.to === undefined) { + struct.to = ''; + } + struct.dateRange = '[' + struct.from + ',' + struct.to + ')'; + if (struct.from === undefined) { + delete struct.dateRange; + } +} + export class LockUtils { static getToken (req: any) : string { return req.headers.authorization?.replace('Bearer ', ''); diff --git a/src/model/CargoBike.ts b/src/model/CargoBike.ts index 861a4b7..530dc5b 100644 --- a/src/model/CargoBike.ts +++ b/src/model/CargoBike.ts @@ -201,7 +201,7 @@ export class CargoBike implements Lockable { }) timeFrames: TimeFrame[]; - @OneToMany(type => Engagement, engagement => engagement.cargoBike) + @OneToMany(type => Engagement, engagement => engagement.cargoBikeId) engagement: Engagement[]; @Column(type => Taxes) diff --git a/src/model/Engagement.ts b/src/model/Engagement.ts index 6f7c064..665ce61 100644 --- a/src/model/Engagement.ts +++ b/src/model/Engagement.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, ManyToOne, Column, JoinColumn } from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, ManyToOne, Column, JoinColumn, JoinTable } from 'typeorm'; import { Participant } from './Participant'; import { CargoBike } from './CargoBike'; import { EngagementType } from './EngagementType'; @@ -9,33 +9,57 @@ export class Engagement { id: number; @ManyToOne(type => Participant, participant => participant.engagement, { + nullable: false }) @JoinColumn({ name: 'participantId' }) - participant: Participant; + participantId: number; - @ManyToOne(type => CargoBike, cargoBike => cargoBike.engagement) - cargoBike: CargoBike; + @ManyToOne(type => CargoBike, cargoBike => cargoBike.engagement, { + nullable: false + }) + @JoinColumn({ + name: 'cargoBikeId' + }) + cargoBikeId: number; - @ManyToOne(type => EngagementType, engagementType => engagementType.engagementIds) + @ManyToOne(type => EngagementType, engagementType => engagementType.engagementIds, { + nullable: false + }) + @JoinColumn({ + name: 'engagementTypeId' + }) engagementTypeId: number; // I have to find out how typorm will map the datetange data type. @Column({ - type: 'daterange' + type: 'daterange', + default: () => 'daterange(CURRENT_DATE,\'infinity\',\'[)\')' }) dateRange: Date[]; - @Column() + @Column({ + type: 'boolean', + default: false + }) roleCoordinator: boolean; - @Column() + @Column({ + type: 'boolean', + default: false + }) roleMentor: boolean; - @Column() + @Column({ + type: 'boolean', + default: false + }) roleAmbulance: boolean; - @Column() + @Column({ + type: 'boolean', + default: false + }) roleBringer: boolean; } diff --git a/src/model/Participant.ts b/src/model/Participant.ts index 1053825..0c66c95 100644 --- a/src/model/Participant.ts +++ b/src/model/Participant.ts @@ -1,6 +1,5 @@ import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, OneToMany, ManyToMany } from 'typeorm'; import { ContactInformation } from './ContactInformation'; -import { CargoBike } from './CargoBike'; import { Engagement } from './Engagement'; import { Workshop } from './Workshop'; @@ -44,7 +43,7 @@ export class Participant { }) locationZIPs: string[]; - @OneToMany(type => Engagement, engagement => engagement.participant) + @OneToMany(type => Engagement, engagement => engagement.participantId) engagement: Engagement[]; @ManyToMany(type => Workshop, workshop => workshop.participants, { diff --git a/src/resolvers/participantResolvers.ts b/src/resolvers/participantResolvers.ts index 017c8f8..a7b3c3a 100644 --- a/src/resolvers/participantResolvers.ts +++ b/src/resolvers/participantResolvers.ts @@ -37,13 +37,12 @@ export default { engagementType (parent: any, _: any, { dataSources, req }: { dataSources: any; req: any }): Promise { return dataSources.participantAPI.engagementTypeByEngagementId(parent.id); }, - from (parent: any) { - // TODO - return parent.dateRange; + from (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return (parent.dateRange as string).split(',')[0].replace('[', ''); }, - to (parent: any) { - // TODO - return parent.dateRange; + to (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + const str = (parent.dateRange as string).split(',')[1].replace(')', ''); + return (str.length > 0) ? str : null; } }, Mutation: { diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index ef1ad8d..fe41d65 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -258,8 +258,8 @@ type Engagement { engagementType: EngagementType! from: Date! to: Date - participant: Participant - cargoBike: CargoBike + participant: Participant! + cargoBike: CargoBike! roleCoordinator: Boolean! roleEmployeeADFC: Boolean! """ @@ -278,14 +278,19 @@ input EngagementCreateInput { to: Date participantId: ID! cargoBikeId: ID! - roleCoordinator: Boolean! - roleEmployeeADFC: Boolean! + "default: false" + roleCoordinator: Boolean + "default: false" + roleEmployeeADFC: Boolean """ Wahr, wenn die Person Pate ist. + default: false """ - roleMentor: Boolean! - roleAmbulance: Boolean! - roleBringer: Boolean! + roleMentor: Boolean + "default: false" + roleAmbulance: Boolean + "default: false" + roleBringer: Boolean } type Taxes {