diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index 2dc7288..0bc7dd9 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -1,5 +1,5 @@ import { DataSource } from 'apollo-datasource'; -import { getConnection, Connection, ObjectType } from 'typeorm'; +import { getConnection, Connection, ObjectType, EntityManager } from 'typeorm'; import { CargoBike, Lockable } from '../../model/CargoBike'; import { GraphQLError } from 'graphql'; import { BikeEvent } from '../../model/BikeEvent'; @@ -8,6 +8,7 @@ import { Engagement } from '../../model/Engagement'; import { Provider } from '../../model/Provider'; import { TimeFrame } from '../../model/TimeFrame'; import { LockUtils } from './utils'; +import { EquipmentType } from '../../model/EquipmentType'; /** * extended datasource to feed resolvers with data about cargoBikes @@ -104,24 +105,27 @@ export class CargoBikeAPI extends DataSource { } const keepLock = cargoBike?.keepLock; delete cargoBike.keepLock; - const bike = await this.connection.manager.createQueryBuilder() - .select('cargoBike') - .from(CargoBike, 'cargoBike') - .where('cargoBike.id = :id', { id: cargoBike.id }) - .getOne(); - if (bike.id) { - delete cargoBike.lendingStationId; - await this.connection.manager - .createQueryBuilder() - .update(CargoBike) + delete cargoBike.lendingStationId; + let equipmentTypeIds: any = null; + if (cargoBike.equipmentTypeIds) { + equipmentTypeIds = cargoBike.equipmentTypeIds; + delete cargoBike.equipmentTypeIds; + } + await this.connection.transaction(async (entityManager: EntityManager) => { + await entityManager.getRepository(CargoBike) + .createQueryBuilder('cargobike') + .update() .set({ ...cargoBike }) - .where('id = :id', { id: bike.id }) + .where('id = :id', { id: cargoBike.id }) .execute(); - !keepLock && await this.unlockCargoBike(cargoBike.id, req, dataSources); - return await this.findCargoBikeById(bike.id); - } else { - return new GraphQLError('ID not in database'); - } + equipmentTypeIds && await entityManager.getRepository(CargoBike) + .createQueryBuilder('cargobike') + .relation(CargoBike, 'equipmentTypeIds') + .of(cargoBike.id) + .addAndRemove(equipmentTypeIds, await this.equipmentTypeByCargoBikeId(cargoBike.id)); // TODO remove all existing relations + }); + !keepLock && await this.unlockCargoBike(cargoBike.id, req, dataSources); + return await this.findCargoBikeById(cargoBike.id); } /** @@ -144,8 +148,14 @@ export class CargoBikeAPI extends DataSource { .relation(CargoBike, 'provider') .of(inserts.identifiers[0].id) .set(cargoBike?.providerId); + cargoBike?.equipmentTypeIds && await entityManager.getRepository(CargoBike) + .createQueryBuilder('cargobike') + .relation(CargoBike, 'equipmentTypeIds') + .of(inserts.identifiers[0].id) + .add(cargoBike.equipmentTypeIds); }); } catch (e: any) { + console.log(e); return new GraphQLError('Transaction could not be completed'); } const newbike = inserts.generatedMaps[0]; @@ -285,4 +295,31 @@ export class CargoBikeAPI extends DataSource { .limit(limit) .getMany(); } + + async createEquipmentType (equipmentType: any) { + const inserts = await this.connection.getRepository(EquipmentType) + .createQueryBuilder('equipment') + .insert() + .values([equipmentType]) + .returning('*') + .execute(); + inserts.generatedMaps[0].id = inserts.identifiers[0].id; + return inserts.generatedMaps[0]; + } + + async equipmentTypeById (id: number) { + return await this.connection.getRepository(EquipmentType) + .createQueryBuilder('equipmentType') + .select() + .where('"equipmentType".id = :id', { id: id }) + .getOne(); + } + + async equipmentTypeByCargoBikeId (id: number) { + return this.connection.getRepository(CargoBike) + .createQueryBuilder('cargobike') + .relation(CargoBike, 'equipmentTypeIds') + .of(id) + .loadMany(); + } } diff --git a/src/datasources/userserver/permission.ts b/src/datasources/userserver/permission.ts index a7a639a..80ac926 100644 --- a/src/datasources/userserver/permission.ts +++ b/src/datasources/userserver/permission.ts @@ -2,6 +2,7 @@ export enum Permission { ReadBike = 'BIKE_READ', WriteBike = 'BIKE_WRITE', + WriteEquipmentType = 'EQUIPMENT_TYPE_WRITE', } // Permissions where the creation will be requested on startup @@ -13,5 +14,9 @@ export const requiredPermissions = [ { name: Permission.WriteBike, description: 'Allows the modification of bike information' + }, + { + name: Permission.WriteEquipmentType, + description: 'Allows the modification of EquipmentTypes' } ]; diff --git a/src/model/CargoBike.ts b/src/model/CargoBike.ts index 48cf0f0..861a4b7 100644 --- a/src/model/CargoBike.ts +++ b/src/model/CargoBike.ts @@ -1,5 +1,5 @@ /* eslint no-unused-vars: "off" */ -import { Entity, Column, PrimaryGeneratedColumn, OneToMany, ManyToOne, JoinColumn, ManyToMany } from 'typeorm'; +import { Entity, Column, PrimaryGeneratedColumn, OneToMany, ManyToOne, JoinColumn, ManyToMany, JoinTable } from 'typeorm'; import { Provider } from './Provider'; import { Participant } from './Participant'; import { InsuranceData } from './InsuranceData'; @@ -161,8 +161,9 @@ export class CargoBike implements Lockable { equipment: Equipment[]; // Equipment that is not unique and is supposed to be selected out of a list e.g. drop down - @ManyToMany(type => EquipmentType, equipmentType => equipmentType.cargoBikes) - miscellaneousEquipment: EquipmentType[]; + @ManyToMany(type => EquipmentType, equipmentType => equipmentType.cargoBikeIds) + @JoinTable() + equipmentTypeIds: number[]; // Security information @Column(type => Security) @@ -185,11 +186,6 @@ export class CargoBike implements Lockable { }) provider: Provider; - @ManyToOne(type => Participant, participant => participant.cargoBikes, { - nullable: true - }) - coordinator: Participant; - @OneToMany(type => BikeEvent, bikeEvent => bikeEvent.cargoBike, { nullable: true, cascade: true diff --git a/src/model/EquipmentType.ts b/src/model/EquipmentType.ts index 2ce82cc..8b7019e 100644 --- a/src/model/EquipmentType.ts +++ b/src/model/EquipmentType.ts @@ -15,8 +15,8 @@ export class EquipmentType implements Lockable { }) description: string; - @ManyToMany(type => CargoBike, cargoBike => cargoBike.miscellaneousEquipment) - cargoBikes: CargoBike[]; + @ManyToMany(type => CargoBike, cargoBike => cargoBike.equipmentTypeIds) + cargoBikeIds: number[]; @Column({ nullable: true, diff --git a/src/model/Participant.ts b/src/model/Participant.ts index 3fbb94a..f847676 100644 --- a/src/model/Participant.ts +++ b/src/model/Participant.ts @@ -41,10 +41,6 @@ export class Participant { }) locationZIPs: string[]; - // this should go, we dont need it - @OneToMany(type => CargoBike, cargoBike => cargoBike.coordinator) - cargoBikes: CargoBike[]; - @OneToMany(type => Engagement, engagement => engagement.participant) engagement: Engagement[]; diff --git a/src/resolvers/cargobikeResolver.ts b/src/resolvers/cargobikeResolver.ts index cdc2140..42c6b67 100644 --- a/src/resolvers/cargobikeResolver.ts +++ b/src/resolvers/cargobikeResolver.ts @@ -60,7 +60,11 @@ export default { }, timeFrames (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { return dataSources.lendingStationAPI.timeFramesByCargoBikeId(parent.id, req, dataSources); + }, + equipmentType (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.cargoBikeAPI.equipmentTypeByCargoBikeId(parent.id, req, dataSources); } + }, Equipment: { cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { @@ -130,6 +134,13 @@ export default { } else { return new GraphQLError('Insufficient Permissions'); } + }, + createEquipmentType: (_: any, { equipmentType }: { equipmentType: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteEquipmentType)) { + return dataSources.cargoBikeAPI.createEquipmentType(equipmentType); + } else { + return new GraphQLError('Insufficient Permissions'); + } } } }; diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index 1bc79be..65ab0b4 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -34,7 +34,7 @@ type CargoBike { bikeEvents: [BikeEvent] equipment(offset: Int!, limit: Int!): [Equipment] "Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2" - miscellaneousEquipment: [String] + equipmentType: [EquipmentType] "Sticker State" stickerBikeNameState: StickerBikeNameState note: String @@ -77,8 +77,7 @@ input CargoBikeCreateInput { """ dimensionsAndLoad: DimensionsAndLoadCreateInput! "Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2" - miscellaneousEquipment: [String] - + equipmentTypeIds: [ID] "Sticker State" stickerBikeNameState: StickerBikeNameState note: String @@ -113,8 +112,11 @@ input CargoBikeUpdateInput { Does not refer to an extra table in the database. """ dimensionsAndLoad: DimensionsAndLoadUpdateInput - "Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2" - miscellaneousEquipment: [String] + """ + Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2 + If set, ols realtions will be over written. Set [] to delete all + """ + equipmentTypeIds: [ID] "Sticker State" stickerBikeNameState: StickerBikeNameState note: String @@ -353,6 +355,23 @@ input EquipmentUpdateInput { keepLock: Boolean } +type EquipmentType { + id: ID! + name: String! + description: String! +} + +input EquipmentTypeCreateInput { + name: String + description: String +} + +input EquipmentTypeUpdateInput { + id: ID! + name: String + description: String +} + "An Event is a point in time, when the state of the bike somehow changed." type BikeEvent { id: ID! @@ -746,6 +765,7 @@ type Mutation { unlockEquipment(id: ID!): Boolean! "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" createLendingStation(lendingStation: LendingStationCreateInput): LendingStation! "updates lendingStation of given ID with supplied fields and returns updated lendingStation"