diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index bab71f0..13bb44c 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -4,6 +4,7 @@ import { CargoBike } from '../../model/CargoBike'; import { GraphQLError } from 'graphql'; import { BikeEvent } from '../../model/BikeEvent'; import { Equipment } from '../../model/Equipment'; +import { Engagement } from '../../model/Engagement'; /** * extended datasource to feed resolvers with data about cargoBikes @@ -29,7 +30,7 @@ export class CargoBikeAPI extends DataSource { * Finds cargo bike by id, retuns null if id was not found * @param param0 id of bike */ - async findCargoBikeById ({ id }:{id: any}) { + async findCargoBikeById (id: number) { return (await this.connection.manager.getRepository(CargoBike).findByIds([id], { relations: ['lendingStation'] }))[0]; /* .createQueryBuilder() .select('cargoBike') @@ -38,6 +39,15 @@ export class CargoBikeAPI extends DataSource { .getOne() || new GraphQLError('ID not found'); */ } + async findCargoBikeByEngagementId (id: number) { + 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; + } + /** * Updates CargoBike and return updated cargoBike * @param param0 cargoBike to be updated @@ -64,7 +74,7 @@ export class CargoBikeAPI extends DataSource { .of(cargoBike.id) .set(lendingStationId); } - return await this.findCargoBikeById({ id: bike.id }); + return await this.findCargoBikeById(bike.id); } else { return new GraphQLError('ID not in database'); } @@ -91,7 +101,7 @@ export class CargoBikeAPI extends DataSource { async createBikeEvent ({ bikeEvent }: { bikeEvent: any }) { const event = new BikeEvent(); event.setValues(bikeEvent); - event.cargoBike = await this.findCargoBikeById({ id: bikeEvent.cargoBikeId }) as unknown as CargoBike; + event.cargoBike = await this.findCargoBikeById(bikeEvent.cargoBikeId) as unknown as CargoBike; if (event.cargoBike instanceof GraphQLError) { return event.cargoBike; } diff --git a/src/datasources/db/participantAPI.ts b/src/datasources/db/participantAPI.ts index 65a8ae4..9521a1a 100644 --- a/src/datasources/db/participantAPI.ts +++ b/src/datasources/db/participantAPI.ts @@ -1,5 +1,10 @@ import { DataSource } from 'apollo-datasource'; +import { GraphQLBoolean, GraphQLError } from 'graphql'; import { Connection, getConnection } from 'typeorm'; +import { CargoBike } from '../../model/CargoBike'; +import { ContactInformation } from '../../model/ContactInformation'; +import { Engagement } from '../../model/Engagement'; +import { Participant } from '../../model/Participant'; export class ParticipantAPI extends DataSource { connection : Connection @@ -9,14 +14,145 @@ export class ParticipantAPI extends DataSource { } async getParticipantById (id: number) { - return { - id: 2 - }; + return await this.connection.getRepository(Participant) + .createQueryBuilder('participant') + .select() + .where('participant.id = :id', { id: id }) + .getOne(); } + async getParticipants (offset: number, limit: number) { + return await this.connection.getRepository(Participant) + .createQueryBuilder('participant') + .select() + .offset(offset) + .limit(limit) + .getMany(); + } + + async participantByEngagementId (id: number) { + 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; + } async engagementByParticipantId (id: number) { - return { - id: 3 - }; + return await this.connection.getRepository(Engagement) + .createQueryBuilder('engagement') + .select() + .where('engagement."participantId" = :id', { id: id }) + .getOne(); + } + + async engagementById (id: number) { + return await this.connection.getRepository(Engagement) + .createQueryBuilder('engagement') + .select() + .where('engagement.id = :id', { id: id }) + .getOne(); + } + + async contactInformationById (id: number) { + return await this.connection.getRepository(ContactInformation) + .createQueryBuilder('contactInformation') + .select() + .where('contactInformation.id = :id', { id: id }) + .getOne(); + } + + async contactInformationByParticipantId (id: number) { + const ret = (await this.connection.getRepository(Participant) + .createQueryBuilder('participant') + .leftJoinAndSelect('participant.contactInformation', 'contactInformation') + .where('participant."contactInformationId" = "contactInformation".id') + .andWhere('participant.id = :id', { id: id }) + .printSql() + .getOne()); + return (ret) ? ret.contactInformation : null; + } + + /** + * creates participant and creates realtion to given contactInformation + * @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.'); + } + 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((await inserts).identifiers[0].id); + } + + async createContactInformation (contactInformation: any) { + const inserts = await this.connection.getRepository(ContactInformation) + .createQueryBuilder('contactInformation') + .insert() + .into(ContactInformation) + .values([contactInformation]) + .returning('*') + .execute(); + return this.contactInformationById(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(); + this.connection.getRepository(Engagement) + .createQueryBuilder('engagement') + .relation(Engagement, 'cargoBike') + .of(inserts.identifiers[0].id) + .set(engagement.cargoBikeId); + this.connection.getRepository(Engagement) + .createQueryBuilder('engagement') + .relation(Engagement, 'participant') + .of(inserts.identifiers[0].id) + .set(engagement.participantId); + console.log(inserts); + return this.engagementById(inserts.identifiers[0].id); } } diff --git a/src/model/ContactInformation.ts b/src/model/ContactInformation.ts index d3578fb..3595bdb 100644 --- a/src/model/ContactInformation.ts +++ b/src/model/ContactInformation.ts @@ -47,6 +47,8 @@ export class ContactInformation { }) emailIntern: string; - @Column() + @Column({ + nullable: true + }) note: string; } diff --git a/src/model/Engagement.ts b/src/model/Engagement.ts index 236e913..6a9e0e1 100644 --- a/src/model/Engagement.ts +++ b/src/model/Engagement.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, ManyToOne, Column } from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, ManyToOne, Column, JoinColumn } from 'typeorm'; import { Participant } from './Participant'; import { CargoBike } from './CargoBike'; @@ -7,7 +7,11 @@ export class Engagement { @PrimaryGeneratedColumn() id: number; - @ManyToOne(type => Participant, participant => participant.engagement) + @ManyToOne(type => Participant, participant => participant.engagement, { + }) + @JoinColumn({ + name: 'participantId' + }) participant: Participant; @ManyToOne(type => CargoBike, cargoBike => cargoBike.engagement) diff --git a/src/model/Participant.ts b/src/model/Participant.ts index b0b4aca..9da9c57 100644 --- a/src/model/Participant.ts +++ b/src/model/Participant.ts @@ -20,14 +20,20 @@ export class Participant { }) end: Date; - @OneToOne(type => ContactInformation) + @OneToOne(type => ContactInformation, { + nullable: true + }) @JoinColumn() contactInformation: ContactInformation; - @Column() + @Column({ + nullable: true + }) usernameflotte: string; - @Column() + @Column({ + nullable: true + }) usernameSlack: string; @Column() @@ -53,12 +59,14 @@ export class Participant { memberCoreTeam: boolean; @Column({ - type: 'date' + type: 'date', + nullable: true }) workshopMentor: Date; @Column({ - type: 'date' + type: 'date', + nullable: true }) workshopAmbulance: Date; diff --git a/src/resolvers/cargobikeResolver.ts b/src/resolvers/cargobikeResolver.ts index 4ece04a..6383be4 100644 --- a/src/resolvers/cargobikeResolver.ts +++ b/src/resolvers/cargobikeResolver.ts @@ -5,7 +5,7 @@ export default { Query: { cargoBikeById: (_: any, { id }:{id: any}, { dataSources, req }:{dataSources: any, req: any }) => { if (req.permissions.includes(Permission.ReadBike)) { - return dataSources.cargoBikeAPI.findCargoBikeById({ id }); + return dataSources.cargoBikeAPI.findCargoBikeById(id); } else { return new GraphQLError('Insufficient Permissions'); } diff --git a/src/resolvers/participantResolvers.ts b/src/resolvers/participantResolvers.ts index 083cf9d..3172997 100644 --- a/src/resolvers/participantResolvers.ts +++ b/src/resolvers/participantResolvers.ts @@ -9,11 +9,52 @@ export default { } else { return new GraphQLError('Insufficient Permissions'); } + }, + participants: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.participantAPI.getParticipants(offset, limit); + } else { + return new GraphQLError('Insufficient Permissions'); + } } }, Participant: { engagement (parent: any, _: any, { dataSources, req }: { dataSources: any, req: any }) { return dataSources.participantAPI.engagementByParticipantId(parent.id); + }, + contactInformation (parent: any, _: any, { dataSources, req }: { dataSources: any, req: any }) { + return (dataSources.participantAPI.contactInformationByParticipantId(parent.id)); + } + }, + Engagement: { + cargoBike (parent: any, _: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.cargoBikeAPI.findCargoBikeByEngagementId(parent.id); + }, + participant (parent: any, _: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.participantAPI.participantByEngagementId(parent.id); + } + }, + Mutation: { + createParticipant: (_: any, { participant }: { participant: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteBike)) { + return dataSources.participantAPI.createParticipant(participant); + } else { + return new GraphQLError('Insufficient Permissions'); + } + }, + createContactInformation: (_: any, { contactInformation }: { contactInformation: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteBike)) { + return dataSources.participantAPI.createContactInformation(contactInformation); + } else { + return new GraphQLError('Insufficient Permissions'); + } + }, + createEngagement: (_: any, { engagement }: { engagement: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteBike)) { + return dataSources.participantAPI.createEngagement(engagement); + } else { + return new GraphQLError('Insufficient Permissions'); + } } } }; diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index 5562dd5..a2374f0 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -193,16 +193,16 @@ type BikeModel { technicalEquipment: TechnicalEquipment! } -type Participant { +type Participant { id: ID! start: Date! - end: Date! - mentor: ContactInformation! + end: Date + contactInformation: ContactInformation! usernamefLotte: String usernameSlack: String memberADFC: Boolean! locationZIPs: [String] - roleCoreTeam: Boolean! + memberCoreTeam: Boolean! roleCoordinator: Boolean! roleEmployeADFC: Boolean! """ @@ -223,13 +223,58 @@ type Participant { """ distributedActiveBikeParte: Boolean! reserve: String - engagement: Engagement + engagement: [Engagement] +} + +input ParticipantCreateInput { + start: Date! + end: Date + "must create contactinformation first, if you want to use new" + contactInformationId: ID! + usernamefLotte: String + usernameSlack: String + memberADFC: Boolean! + locationZIPs: [String] + memberCoreTeam: Boolean! + + "Date of workshop to become Mentor dt. Pate" + workshopMentor: Date + "Date of last Erste Hilfe Kurs?" + workshopAmbulance: Date + + reserve: String } + type Engagement { id: ID! + from: Date! + to: Date participant: Participant - cargobike: CargoBike + cargoBike: CargoBike + roleCoordinator: Boolean! + roleEmployeADFC: Boolean! + """ + Wahr, wenn die Person Pate ist. + """ + roleMentor: Boolean! + roleAmbulance: Boolean! + roleBringer: Boolean! +} + +input EngagementCreateInput { + from: Date! + to: Date + participantId: ID! + cargoBikeId: ID! + roleCoordinator: Boolean! + roleEmployeADFC: Boolean! + """ + Wahr, wenn die Person Pate ist. + """ + roleMentor: Boolean! + roleAmbulance: Boolean! + roleBringer: Boolean! } type Taxes { @@ -479,8 +524,8 @@ type ContactInformation { } input ContactInformationCreateInput { - name: String - firstName: String + name: String! + firstName: String! retiredAt: Date phoneExtern: String phone2Extern: String @@ -654,8 +699,10 @@ type Query { "equipment by id, will return null if id not found" equipmentById(id: ID!): Equipment providers: [Provider]! + "particcipant by id" participantById(id:ID!): Participant - participants: [ Participant]! + "p" + participants(offset: Int!, limit: Int!): [ Participant]! lendingStationById(id:ID!): LendingStation lendingStations: [LendingStation]! contactInformation: [ContactInformation]! @@ -678,6 +725,12 @@ type Mutation { updateLendingStation(lendingstation: LendingStationUpdateInput!): LendingStation! "creates new BikeEvent" createBikeEvent(bikeEvent: BikeEventCreateInput): BikeEvent! + "create participant" + createParticipant(participant: ParticipantCreateInput!): Participant! + "create new contactInfo" + createContactInformation(contactInformation: ContactInformationCreateInput!): ContactInformation! + "create Engagement" + createEngagement(engagement: EngagementCreateInput): Engagement! } `;