From 81a7870cbbfcb3f7d63268c55123bad943c9cb23 Mon Sep 17 00:00:00 2001 From: leonnicolas Date: Mon, 28 Sep 2020 19:08:56 +0200 Subject: [PATCH] bikeevent, bikeeventype read and create --- src/datasources/db/cargobikeAPI.ts | 64 +++++++++++++++++++++--- src/datasources/db/providerAPI.ts | 10 ++++ src/datasources/userserver/permission.ts | 7 ++- src/model/BikeEvent.ts | 35 +++++++------ src/model/BikeEventType.ts | 4 +- src/model/CargoBike.ts | 2 +- src/model/Provider.ts | 6 ++- src/resolvers/cargobikeResolver.ts | 36 +++++++++++-- src/resolvers/providerResolvers.ts | 3 ++ src/schema/type-defs.ts | 62 ++++++++++++++--------- 10 files changed, 174 insertions(+), 55 deletions(-) diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index c26136c..92a9136 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -9,6 +9,7 @@ import { Provider } from '../../model/Provider'; import { TimeFrame } from '../../model/TimeFrame'; import { LockUtils } from './utils'; import { EquipmentType } from '../../model/EquipmentType'; +import { BikeEventType } from '../../model/BikeEventType'; /** * extended datasource to feed resolvers with data about cargoBikes @@ -158,13 +159,62 @@ export class CargoBikeAPI extends DataSource { } async createBikeEvent ({ bikeEvent }: { bikeEvent: any }) { - const event = new BikeEvent(); - event.setValues(bikeEvent); - event.cargoBike = await this.findCargoBikeById(bikeEvent.cargoBikeId) as unknown as CargoBike; - if (event.cargoBike instanceof GraphQLError) { - return event.cargoBike; - } - return await this.connection.manager.save(event); + return (await this.connection.getRepository(BikeEvent) + .createQueryBuilder('be') + .insert() + .values([bikeEvent]) + .returning('*') + .execute()).generatedMaps[0]; + } + + async cargoBikeByEventId (id: number) { + return await this.connection.getRepository(BikeEvent) + .createQueryBuilder('be') + .relation(BikeEvent, 'cargoBikeId') + .of(id) + .loadOne(); + } + + async bikeEventTypeByBikeEventId (id: number) { + return await this.connection.getRepository(BikeEvent) + .createQueryBuilder('be') + .relation(BikeEvent, 'bikeEventTypeId') + .of(id) + .loadOne(); + } + + async createBikeEventType (bikeEventType: any) { + return (await this.connection.getRepository(BikeEventType) + .createQueryBuilder('bet') + .insert() + .values([{ name: bikeEventType }]) + .returning('*') + .execute())?.generatedMaps[0]; + } + + async bikeEventTypes (offset: number, limit: number) { + return await this.connection.getRepository(BikeEventType) + .createQueryBuilder('bet') + .select() + .skip(offset) + .take(limit) + .getMany(); + } + + async responsibleByBikeEventId (id: number) { + return await this.connection.getRepository(BikeEvent) + .createQueryBuilder('be') + .relation(BikeEvent, 'responsibleId') + .of(id) + .loadOne(); + } + + async relatedByBikeEventId (id: number) { + return await this.connection.getRepository(BikeEvent) + .createQueryBuilder('be') + .relation(BikeEvent, 'relatedId') + .of(id) + .loadOne(); } /** diff --git a/src/datasources/db/providerAPI.ts b/src/datasources/db/providerAPI.ts index dbb99cf..3a5232a 100644 --- a/src/datasources/db/providerAPI.ts +++ b/src/datasources/db/providerAPI.ts @@ -2,6 +2,7 @@ import { DataSource } from 'apollo-datasource'; import { Connection, EntityManager, getConnection } from 'typeorm'; import { Provider } from '../../model/Provider'; import { Organisation } from '../../model/Organisation'; +import { UserInputError } from 'apollo-server'; export class ProviderAPI extends DataSource { connection : Connection @@ -51,7 +52,16 @@ export class ProviderAPI extends DataSource { .loadOne(); } + async privatePersonByProviderId (id: number) { + return await this.connection.getRepository(Provider) + .createQueryBuilder('p') + .relation(Provider, 'privatePersonId') + .of(id) + .loadOne(); + } + async createProvider (provider: any) { + if (!provider.privatePersonId === !provider.organisationId) { return new UserInputError('Provider must have either privatePersonId or organisationId'); } let inserts: any = null; await this.connection.transaction(async (entityManager: any) => { inserts = await entityManager.getRepository(Provider) diff --git a/src/datasources/userserver/permission.ts b/src/datasources/userserver/permission.ts index 9ff61ca..b83414a 100644 --- a/src/datasources/userserver/permission.ts +++ b/src/datasources/userserver/permission.ts @@ -6,7 +6,8 @@ export enum Permission { WritePerson = 'PERSON_WRITE', ReadPerson = 'PERSON_READ', WriteProvider = 'PROVIDER_WRITE', - WriteWorkshopType = 'WORKSHOP_TYPE_WRITE' + WriteWorkshopType = 'WORKSHOP_TYPE_WRITE', +WriteEventType = 'BIKE_EVENT_TYPE_WRITE' } // Permissions where the creation will be requested on startup @@ -38,5 +39,9 @@ export const requiredPermissions = [ { name: Permission.WriteWorkshopType, description: 'Allows to create and modify workshop types' + }, + { + name: Permission.WriteEventType, + description: 'Allows modification of bike event types' } ]; diff --git a/src/model/BikeEvent.ts b/src/model/BikeEvent.ts index 091b66a..a0575cc 100644 --- a/src/model/BikeEvent.ts +++ b/src/model/BikeEvent.ts @@ -7,14 +7,6 @@ import { type } from 'os'; @Entity() export class BikeEvent { - public setValues ({ id, remark, date, documents, cargoBike }: { id: number, remark: string, date: Date, documents: string[], cargoBike: CargoBike}): void { - this.id = id; - this.remark = remark; - this.date = date; - this.documents = documents; - this.cargoBike = cargoBike; - } - @PrimaryGeneratedColumn() id: number; @@ -38,22 +30,35 @@ export class BikeEvent { date: Date; @ManyToOne(type => Participant) - responsible: Participant; + @JoinColumn({ + name: 'responsibleId' + }) + responsibleId: number; @ManyToOne(type => Participant) - related: Participant; + @JoinColumn({ + name: 'relatedId' + }) + relatedId: number; @Column('simple-array', { nullable: true }) documents: string[]; - @ManyToOne(tpye => CargoBike, cargoBike => cargoBike.bikeEvents, { + @ManyToOne(type => CargoBike, cargoBike => cargoBike.bikeEvents, { nullable: false }) - @JoinColumn({ name: 'cargoBikeId' }) - cargoBike: CargoBike; + @JoinColumn({ + name: 'cargoBikeId' + }) + cargoBikeId: number; - @ManyToOne(type => BikeEventType) - bikeEventType: BikeEventType; + @ManyToOne(type => BikeEventType, { + nullable: false + }) + @JoinColumn({ + name: 'bikeEventTypeId' + }) + bikeEventTypeId: number; } diff --git a/src/model/BikeEventType.ts b/src/model/BikeEventType.ts index ada338a..994578f 100644 --- a/src/model/BikeEventType.ts +++ b/src/model/BikeEventType.ts @@ -6,7 +6,9 @@ export class BikeEventType implements Lockable { @PrimaryGeneratedColumn() id: number; - @Column() + @Column({ + unique: true + }) name: string; @Column({ diff --git a/src/model/CargoBike.ts b/src/model/CargoBike.ts index 530dc5b..6445735 100644 --- a/src/model/CargoBike.ts +++ b/src/model/CargoBike.ts @@ -186,7 +186,7 @@ export class CargoBike implements Lockable { }) provider: Provider; - @OneToMany(type => BikeEvent, bikeEvent => bikeEvent.cargoBike, { + @OneToMany(type => BikeEvent, bikeEvent => bikeEvent.cargoBikeId, { nullable: true, cascade: true }) diff --git a/src/model/Provider.ts b/src/model/Provider.ts index f0d1096..3509143 100644 --- a/src/model/Provider.ts +++ b/src/model/Provider.ts @@ -41,8 +41,10 @@ export class Provider implements Lockable { @OneToOne(type => ContactInformation, { nullable: true }) - @JoinColumn() - contactInformationId: number; + @JoinColumn({ + name: 'privatePersonId' + }) + privatePersonId: number; // is null when Provider is a private Person @OneToOne(type => Organisation, organization => organization.providerId, { diff --git a/src/resolvers/cargobikeResolver.ts b/src/resolvers/cargobikeResolver.ts index 42c6b67..815da7e 100644 --- a/src/resolvers/cargobikeResolver.ts +++ b/src/resolvers/cargobikeResolver.ts @@ -37,6 +37,13 @@ export default { } else { return new GraphQLError('Insufficiant Permissions'); } + }, + bikeEventTypes: (_:any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.cargoBikeAPI.bikeEventTypes(offset, limit); + } else { + return new GraphQLError('Insufficiant Permissions'); + } } }, CargoBike: { @@ -71,29 +78,43 @@ export default { return dataSources.cargoBikeAPI.cargoBikeByEquipmentId(parent.id); } }, + BikeEvent: { + cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.cargoBikeAPI.cargoBikeByEventId(parent.id); + }, + bikeEventType (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.cargoBikeAPI.bikeEventTypeByBikeEventId(parent.id); + }, + responsible (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.cargoBikeAPI.responsibleByBikeEventId(parent.id); + }, + related (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.cargoBikeAPI.relatedByBikeEventId(parent.id); + } + }, Mutation: { - createCargoBike: (_: any, { cargoBike }: { cargoBike: any }, { dataSources, req }:{dataSources: any, req: any }) => { + createCargoBike: (_: any, { cargoBike }: { cargoBike: any }, { dataSources, req }: { dataSources: any, req: any }) => { if (req.permissions.includes(Permission.WriteBike)) { return dataSources.cargoBikeAPI.createCargoBike({ cargoBike }); } else { return new GraphQLError('Insufficient Permissions'); } }, - lockCargoBikeById: (_: any, { id }: { id: number }, { dataSources, req }:{dataSources: any, req: any }) => { + lockCargoBikeById: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => { if (req.permissions.includes(Permission.WriteBike)) { return dataSources.cargoBikeAPI.lockCargoBike(id, req, dataSources); } else { return new GraphQLError('Insufficient Permissions'); } }, - unlockCargoBikeById: (_: any, { id }: { id: number }, { dataSources, req }:{dataSources: any, req: any }) => { + unlockCargoBikeById: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => { if (req.permissions.includes(Permission.WriteBike)) { return dataSources.cargoBikeAPI.unlockCargoBike(id, req, dataSources); } else { return new GraphQLError('Insufficient Permissions'); } }, - updateCargoBike: (_: any, { cargoBike }: { cargoBike: any }, { dataSources, req }:{dataSources: any, req: any }) => { + updateCargoBike: (_: any, { cargoBike }: { cargoBike: any }, { dataSources, req }: { dataSources: any, req: any }) => { if (req.permissions.includes(Permission.WriteBike)) { return dataSources.cargoBikeAPI.updateCargoBike(cargoBike, req, dataSources); } else { @@ -141,6 +162,13 @@ export default { } else { return new GraphQLError('Insufficient Permissions'); } + }, + createBikeEventType: (_: any, { name }: { name: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteEventType)) { + return dataSources.cargoBikeAPI.createBikeEventType(name); + } else { + return new GraphQLError('Insufficient Permissions'); + } } } }; diff --git a/src/resolvers/providerResolvers.ts b/src/resolvers/providerResolvers.ts index 00e40b4..249c4b8 100644 --- a/src/resolvers/providerResolvers.ts +++ b/src/resolvers/providerResolvers.ts @@ -28,6 +28,9 @@ export default { }, organisation: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { return dataSources.providerAPI.organisationByProviderId(parent.id); + }, + privatePerson: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { + return dataSources.providerAPI.privatePersonByProviderId(parent.id); } }, Organisation: { diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index 3fd07d3..d9c2cd9 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -412,41 +412,41 @@ input EquipmentTypeUpdateInput { "An Event is a point in time, when the state of the bike somehow changed." type BikeEvent { id: ID! - eventType: BikeEventType! + bikeEventType: BikeEventType! cargoBike: CargoBike! + responsible: Participant + related: Participant date: Date! - note: String + description: String """ Path to documents """ - documents: [String]! + documents: [String]! + remark: String } input BikeEventCreateInput { - eventType: BikeEventType! - "it is enough to pass the cargoBike id" + bikeEventTypeId: ID! cargoBikeId: ID! + responsibleId: ID + relatedId: ID date: Date! - note: String + description: String """ Path to documents """ - documents: [String]! + documents: [String] + remark: String } -"TODO: Some eventTypes are missing" -enum BikeEventType { - """ - The enum EventType can also be represented as an enum in postgresQL. - It is possible to add items to an enum in postgresQL without changing the source code. - However, it not possible to change the graphQL schema. - Concluding we should not use an enum here, if users want to add EventTypes to the enum. - """ - KAUF - INBETRIEBNAHME - AUSFALL - WARTUNG - ANDERE +type BikeEventType { + id: ID! + name: String! +} + +input BikeEventTypeInput { + id: ID! + name: String } "How are the dimensions and how much weight can handle a bike. This data is merged in the CargoBike table and the BikeModel table." @@ -803,12 +803,16 @@ type Query { timeframes(offset: Int!, limit: Int!): [TimeFrame]! contactInformation(offset: Int!, limit: Int!): [ContactInformation]! persons(offset: Int!, limit: Int!): [Person] + bikeEventTypes(offset: Int!, limit: Int!): [BikeEventType] "returns BikeEvent with CargoBike" bikeEventById(id:ID!): BikeEvent! } type Mutation { - "creates new cargoBike and returns cargobike with new ID" + """ + CargoBikes + creates new cargoBike and returns cargobike with new ID + """ createCargoBike(cargoBike: CargoBikeCreateInput!): CargoBike! "lock cargoBike returns bike if bike is not locked and locks bike or Error if bike cannot be locked" lockCargoBikeById(id: ID!): CargoBike! @@ -816,7 +820,10 @@ type Mutation { unlockCargoBikeById(id: ID!): Boolean! "updates cargoBike of given ID with supplied fields and returns updated cargoBike" updateCargoBike(cargoBike: CargoBikeUpdateInput!): CargoBike! - "creates new peace of unique Equipment" + """ + EQUIPMENT + creates new peace of unique Equipment + """ createEquipment(equipment: EquipmentCreateInput!): Equipment! "lock equipment returns true if bike is not locked or if it doesnt exist" lockEquipmentById(id: ID!): Equipment! @@ -825,13 +832,20 @@ type Mutation { "update Equipment, returns updated equipment. CargoBike will be null, if cargoBikeId is not set. Pass null for cargoBikeIs to delete the relation" updateEquipment(equipment: EquipmentUpdateInput!): Equipment! createEquipmentType(equipmentType: EquipmentTypeCreateInput!): EquipmentType! - "creates new lendingStation and returns lendingStation with new ID" + """ + LENDINGSTATION + creates new lendingStation and returns lendingStation with new ID + """ createLendingStation(lendingStation: LendingStationCreateInput): LendingStation! "updates lendingStation of given ID with supplied fields and returns updated lendingStation" updateLendingStation(lendingStation: LendingStationUpdateInput!): LendingStation! createTimeFrame(timeFrame: TimeFrameCreateInput!): TimeFrame! + """ + BIKEEVENT + """ + createBikeEventType(name: String!): BikeEventType! "creates new BikeEvent" - createBikeEvent(bikeEvent: BikeEventCreateInput): BikeEvent! + createBikeEvent(bikeEvent: BikeEventCreateInput!): BikeEvent! "create participant" createParticipant(participant: ParticipantCreateInput!): Participant! createWorkshopType(workshopType: WorkshopTypeCreateInput!): WorkshopType!