diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index 6b3ed2d..5cea37d 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -58,7 +58,7 @@ export class CargoBikeAPI extends DataSource { .from(CargoBike, 'cargoBike') .where('cargoBike.id = :id', { id: cargoBike.id }) .getOne(); - if (bike) { + if (bike.id) { const lendingStationId = cargoBike.lendingStationId; delete cargoBike.lendingStationId; await this.connection.manager diff --git a/src/datasources/db/contactinformationAPI.ts b/src/datasources/db/contactinformationAPI.ts new file mode 100644 index 0000000..d433c2b --- /dev/null +++ b/src/datasources/db/contactinformationAPI.ts @@ -0,0 +1,108 @@ +import { DataSource } from 'apollo-datasource'; +import { Connection, getConnection } from 'typeorm'; +import { ContactInformation } from '../../model/ContactInformation'; +import { ContactPerson } from '../../model/ContactPerson'; + +export class ContactInformationAPI extends DataSource { + connection : Connection + constructor () { + super(); + this.connection = getConnection(); + } + + async contactPersonById (id: number) { + return await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .select() + .where('"contactPerson".id = :id', { id: id }) + .getOne(); + } + + async numContactInformationById (id: number) { + return await this.connection.getRepository(ContactInformation) + .createQueryBuilder('contactInformation') + .select() + .where('"contactInformation".id = :id', { id: id }) + .getCount(); + } + + async contactInformationById (id: number) { + return await this.connection.getRepository(ContactInformation) + .createQueryBuilder('contactInformation') + .select() + .where('"contactInformation".id = :id', { id: id }) + .getOne(); + } + + async contactPersonByLendingStationId (id: number) { + return await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .leftJoinAndSelect('contactPerson.lendingStation', 'lendingStation') + .where('"lendingStation".id = :id', { id: id }) + .getMany().catch(() => { return []; }); + } + + async contactInformationByContactPersonId (id: number) { + return (await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .leftJoinAndSelect('contactPerson.contactInformation', 'contactInformation') + .where('"contactPerson".id = :id', { id: id }) + .getOne())?.contactInformation; + } + + async createContactPerson (contactPerson: any) { + if (await this.contactInformationById(contactPerson.contactInformationId)) { + const inserts = await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .insert() + .values([contactPerson]) + .returning('*') + .execute(); + await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .relation(ContactPerson, 'contactInformation') + .of(inserts.identifiers[0].id) + .set(contactPerson.contactInformationId); + return this.contactPersonById(inserts.identifiers[0].id); + } else { + return null; + } + } + + async updateContactPerson (contactPerson: any) { + if (await this.contactPersonById(contactPerson.id)) { + const contactInformationId = contactPerson.contactInformationId; + delete contactPerson.contactInformationId; + if (contactInformationId) { + if (await this.contactInformationById(contactInformationId)) { + await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .update(ContactPerson) + .set({ ...contactPerson }) + .where('id = :id', { id: contactPerson.id }) + .execute(); + await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .relation(ContactPerson, 'contactInformation') + .of(contactPerson.id) + .set(contactInformationId); + } else { + // supplied contactinformationId not found + return null; + } + return this.contactPersonById(contactPerson.id); + } else { + await this.connection.getRepository(ContactPerson) + .createQueryBuilder('contactPerson') + .update(ContactPerson) + .set({ ...contactPerson }) + .where('id = :id', { id: contactPerson.id }) + .execute(); + return this.contactPersonById(contactPerson.id); + } + } else { + // updated bike not found + return null; + } + } +} diff --git a/src/datasources/db/lendingstationAPI.ts b/src/datasources/db/lendingstationAPI.ts index dc63848..a84b91c 100644 --- a/src/datasources/db/lendingstationAPI.ts +++ b/src/datasources/db/lendingstationAPI.ts @@ -1,7 +1,9 @@ import { DataSource } from 'apollo-datasource'; import { GraphQLError } from 'graphql'; import { Connection, getConnection } from 'typeorm'; +import { CargoBike } from '../../model/CargoBike'; import { LendingStation } from '../../model/LendingStation'; +import { TimeFrame } from '../../model/TimeFrame'; export class LendingStationAPI extends DataSource { connection : Connection @@ -22,13 +24,48 @@ export class LendingStationAPI extends DataSource { /** * get all lendingStations */ - async getLendingStations () { - return await this.connection.manager - .createQueryBuilder(LendingStation, 'lendingStation') - .leftJoinAndSelect('CargoBike', 'cargoBike', 'cargoBike.lendingStation = lendingStation.id')// ('lendingStation.cargoBikes', 'cargoBike.lendingStation', 'cargoBike', 'cargoBike.lendingStationId = lendingStation.id') + async lendingStations (offset: number, limit: number) { + return await this.connection.getRepository(LendingStation) + .createQueryBuilder('lendingStation') + .select() + .offset(offset) + .limit(limit) + .orderBy('name', 'ASC') .getMany() || new GraphQLError('Internal Server Error: could not query data from table lendingStation'); } + async lendingStationByCargoBikeId (id: number) { + return await this.connection.getRepository(LendingStation) + .createQueryBuilder('lendingStation') + .leftJoinAndSelect('lendingStation.cargoBikes', 'cargoBikes') + .where('"cargoBikes"."lendingStationId" = :id', { id: id }) + .getOne().catch(() => { return null; }); + } + + async timeFramesByLendingStationId (id: number) { + return await this.connection.getRepository(TimeFrame) + .createQueryBuilder('timeFrame') + .select() + .where('"timeFrame"."lendingStationId" = :id', { id: id }) + .getMany().catch(() => { return []; }); + } + + async numCargoBikesByLendingStationId (id: number) { + return await this.connection.getRepository(CargoBike) + .createQueryBuilder('cargoBike') + .select() + .where('"cargoBike"."lendingStationId" = :id', { id: id }) + .getCount(); + } + + async cargoBikesByLendingStationId (id: number) { + return await this.connection.getRepository(CargoBike) + .createQueryBuilder('cargoBike') + .select() + .where('"cargoBike"."lendingStationId" = :id', { id: id }) + .getMany().catch(() => { return []; }); + } + /** * creates new lendingStation and returns new lendingStation with its new id * @param param0 new lendingStation diff --git a/src/datasources/db/providerAPI.ts b/src/datasources/db/providerAPI.ts new file mode 100644 index 0000000..8bbfd4a --- /dev/null +++ b/src/datasources/db/providerAPI.ts @@ -0,0 +1,19 @@ +import { DataSource } from 'apollo-datasource'; +import { Connection, getConnection } from 'typeorm'; +import { Provider } from '../../model/Provider'; + +export class ProviderAPI extends DataSource { + connection : Connection + constructor () { + super(); + this.connection = getConnection(); + } + + async providerById (id: number) { + await this.connection.getRepository(Provider) + .createQueryBuilder('provider') + .select() + .where('provider.id = :id', { id: id }) + .getOne().catch(() => { return null; }); + } +} diff --git a/src/index.ts b/src/index.ts index f541541..4f9bb18 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,7 @@ import { BikeModel } from './model/BikeModel'; import { ContactInformation } from './model/ContactInformation'; import { Equipment } from './model/Equipment'; import { LendingStation } from './model/LendingStation'; -import { LoanPeriod } from './model/LoanPeriod'; +import { TimeFrame } from './model/TimeFrame'; import { Participant } from './model/Participant'; import { Organization } from './model/Organization'; import { Provider } from './model/Provider'; @@ -24,6 +24,10 @@ import lendingstationResolvers from './resolvers/lendingstationResolvers'; import { ParticipantAPI } from './datasources/db/participantAPI'; import participantResolvers from './resolvers/participantResolvers'; import { ContactPerson } from './model/ContactPerson'; +import { ContactInformationAPI } from './datasources/db/contactinformationAPI'; +import providerResolvers from './resolvers/providerResolvers'; +import { ProviderAPI } from './datasources/db/providerAPI'; +import contactinformationResolvers from './resolvers/contactinformationResolvers'; require('dotenv').config(); @@ -64,7 +68,7 @@ createConnection({ ContactInformation, Equipment, LendingStation, - LoanPeriod, + TimeFrame, Organization, Participant, Provider, @@ -84,13 +88,17 @@ const server = new ApolloServer({ resolvers: [ bikeresolver, lendingstationResolvers, - participantResolvers + participantResolvers, + providerResolvers, + contactinformationResolvers ], typeDefs, dataSources: () => ({ cargoBikeAPI: new CargoBikeAPI(), lendingStationAPI: new LendingStationAPI(), participantAPI: new ParticipantAPI(), + contactInformationAPI: new ContactInformationAPI(), + providerAPI: new ProviderAPI(), userAPI }), context: (req: any) => { diff --git a/src/model/CargoBike.ts b/src/model/CargoBike.ts index baeddc1..5adc52b 100644 --- a/src/model/CargoBike.ts +++ b/src/model/CargoBike.ts @@ -4,7 +4,7 @@ import { Bike } from './BikeFeatures'; import { Provider } from './Provider'; import { Participant } from './Participant'; import { InsuranceData } from './InsuranceData'; -import { LoanPeriod } from './LoanPeriod'; +import { TimeFrame } from './TimeFrame'; import { LendingStation } from './LendingStation'; import { Taxes } from './Taxes'; import { Equipment } from './Equipment'; @@ -118,10 +118,10 @@ export class CargoBike extends Bike { @Column(type => InsuranceData) insuranceData: InsuranceData; - @OneToMany(type => LoanPeriod, loanPeriod => loanPeriod.cargoBike, { + @OneToMany(type => TimeFrame, loanPeriod => loanPeriod.cargoBike, { nullable: true }) - loanPeriods: LoanPeriod[]; + loanPeriods: TimeFrame[]; // This relation is a little redundant because one could also check all LoanPeriods for current station @ManyToOne(type => LendingStation, lendingStation => lendingStation.cargoBikes, { diff --git a/src/model/LendingStation.ts b/src/model/LendingStation.ts index 9186739..08db24c 100644 --- a/src/model/LendingStation.ts +++ b/src/model/LendingStation.ts @@ -1,5 +1,5 @@ import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable, OneToMany, ManyToOne } from 'typeorm'; -import { LoanPeriod } from './LoanPeriod'; +import { TimeFrame } from './TimeFrame'; import { CargoBike } from './CargoBike'; import { Organization } from './Organization'; import { Address } from './Provider'; @@ -20,8 +20,26 @@ export class LendingStation { @Column(type => Address) address: Address; - @OneToMany(type => LoanPeriod, loanPeriod => loanPeriod.lendingStation) - loanPeriods: LoanPeriod[]; + /** + * validity for loanPeriods + */ + @Column({ + nullable: true, + type: 'date' + }) + from: Date; + + /** + * validity for loanPeriods + */ + @Column({ + nullable: true, + type: 'date' + }) + to: Date; + + @OneToMany(type => TimeFrame, loanPeriod => loanPeriod.lendingStation) + loanPeriods: TimeFrame[]; @OneToMany(type => CargoBike, cargoBike => cargoBike.lendingStation, { eager: false diff --git a/src/model/LoanPeriod.ts b/src/model/TimeFrame.ts similarity index 89% rename from src/model/LoanPeriod.ts rename to src/model/TimeFrame.ts index 03f6245..78115da 100644 --- a/src/model/LoanPeriod.ts +++ b/src/model/TimeFrame.ts @@ -2,8 +2,11 @@ import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; import { LendingStation } from './LendingStation'; import { CargoBike } from './CargoBike'; +/** + * When was a cargoBike at what lendingStation + */ @Entity() -export class LoanPeriod { +export class TimeFrame { @PrimaryGeneratedColumn() id: number; diff --git a/src/resolvers/cargobikeResolver.ts b/src/resolvers/cargobikeResolver.ts index 590753c..0a2b70b 100644 --- a/src/resolvers/cargobikeResolver.ts +++ b/src/resolvers/cargobikeResolver.ts @@ -48,6 +48,9 @@ export default { }, equipment (parent: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) { return dataSources.cargoBikeAPI.equipmentByCargoBikeId(offset, limit, parent.id); + }, + lendingStation (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.lendingStationAPI.lendingStationByCargoBikeId(parent.id); } }, Equipment: { diff --git a/src/resolvers/contactinformationResolvers.ts b/src/resolvers/contactinformationResolvers.ts new file mode 100644 index 0000000..7c7eb72 --- /dev/null +++ b/src/resolvers/contactinformationResolvers.ts @@ -0,0 +1,30 @@ +import { GraphQLError } from 'graphql'; +import { Permission } from '../datasources/userserver/permission'; + +export default { + ContactPerson: { + contactInformation: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.contactInformationAPI.contactInformationByContactPersonId(parent.id); + } else { + return new GraphQLError('Insufficient Permissions'); + } + } + }, + Mutation: { + createContactPerson: (_: any, { contactPerson }: { contactPerson: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteBike)) { + return dataSources.contactInformationAPI.createContactPerson(contactPerson); + } else { + return new GraphQLError('Insufficient Permissions'); + } + }, + updateContactPerson: (_: any, { contactPerson }: { contactPerson: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteBike)) { + return dataSources.contactInformationAPI.updateContactPerson(contactPerson); + } else { + return new GraphQLError('Insufficient Permissions'); + } + } + } +}; diff --git a/src/resolvers/lendingstationResolvers.ts b/src/resolvers/lendingstationResolvers.ts index 20cc5fe..3682c0c 100644 --- a/src/resolvers/lendingstationResolvers.ts +++ b/src/resolvers/lendingstationResolvers.ts @@ -11,14 +11,28 @@ export default { return new GraphQLError('Insufficient Permissions'); } }, - lendingStations: (_: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { + lendingStations: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { if (req.permissions.includes(Permission.ReadBike)) { - return dataSources.lendingStationAPI.getLendingStations(); + return dataSources.lendingStationAPI.lendingStations(offset, limit); } else { return new GraphQLError('Insufficient Permissions'); } } }, + LendingStation: { + contactPersons (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.contactInformationAPI.contactPersonByLendingStationId(parent.id); + }, + timeFrames (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.lendingStationAPI.timeFramesByLendingStationId(parent.id); + }, + numCargoBikes (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.lendingStationAPI.numCargoBikesByLendingStationId(parent.id); + }, + cargoBikes (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { + return dataSources.lendingStationAPI.cargoBikesByLendingStationId(parent.id); + } + }, Mutation: { createLendingStation: (_: any, { lendingStation }:{ lendingStation: LendingStation }, { dataSources, req }:{dataSources: any, req: any }) => { if (req.permissions.includes(Permission.WriteBike)) { diff --git a/src/resolvers/participantResolvers.ts b/src/resolvers/participantResolvers.ts index 3172997..c7b1ec5 100644 --- a/src/resolvers/participantResolvers.ts +++ b/src/resolvers/participantResolvers.ts @@ -23,7 +23,7 @@ export default { return dataSources.participantAPI.engagementByParticipantId(parent.id); }, contactInformation (parent: any, _: any, { dataSources, req }: { dataSources: any, req: any }) { - return (dataSources.participantAPI.contactInformationByParticipantId(parent.id)); + return (dataSources.contactInformationAPI.contactInformationByParticipantId(parent.id)); } }, Engagement: { diff --git a/src/resolvers/providerResolvers.ts b/src/resolvers/providerResolvers.ts new file mode 100644 index 0000000..1bcc659 --- /dev/null +++ b/src/resolvers/providerResolvers.ts @@ -0,0 +1,16 @@ +import { GraphQLError } from 'graphql'; +import { Permission } from '../datasources/userserver/permission'; + +export default { + Query: { + providerById: (_: any, { id }: { id: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.providerAPI.providerById(id); + } else { + return new GraphQLError('Insufficient Permissions'); + } + } + }, + Mutation: { + } +}; diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index fdd6a1d..f6e8275 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -550,23 +550,21 @@ input ContactInformationUpdateInput { note: String } -type contactPerson { +type ContactPerson { id: ID! intern: Boolean! contactInformation: ContactInformation! } -input contactPersonCreateInput { +input ContactPersonCreateInput { intern: Boolean! - contactInformationCreate: ContactInformationCreateInput - contactInformationExisting: ContactInformationUpdateInput + contactInformationId: ID! } -input contactPersonUpdateInput { +input ContactPersonUpdateInput { id: ID! intern: Boolean - contactInformationCreate: ContactInformationCreateInput - contactInformationExisting: ContactInformationUpdateInput + contactInformationId: ID } type Organisation { @@ -586,10 +584,10 @@ type Organisation { type LendingStation { id: ID! name: String! - contactInformation: [ContactInformation]! + contactPersons: [ContactPerson]! address: Address! - loanTimes: LoanTimes - loanPeriods: [LoanPeriod]! + timeFrames: [TimeFrame]! + loanPeriods: LoanPeriods cargoBikes: [CargoBike] "Totola Amount of cargoBikes currently assigned to the lending station" numCargoBikes: Int! @@ -599,8 +597,8 @@ input LendingStationCreateInput { name: String! contactInformation: [ContactInformationCreateInput]! address: AddressCreateInput! - loanTimes: LoanTimesInput - loanPeriods: [LoanPeriodCreateInput]! + loanPeriods: LoanPeriodsInput + timeFrames: [TimeFrameCreateInput]! } input LendingStationUpdateInput { @@ -608,15 +606,15 @@ input LendingStationUpdateInput { name: String contactInformation: [ContactInformationUpdateInput] address: AddressUpdateInput - loanTimes: LoanTimesInput - loanPeriods: [LoanPeriodUpdateInput] + loanPeriods: LoanPeriodsInput + timeFrames: [TimeFrameUpdateInput] } """ -(dt. Ausleihzeiten) +(dt. Ausleihzeiten) not implemented """ -type LoanTimes { +type LoanPeriods { generalRemark: String "notes for each day of the week, starting on Monday" notes: [String] @@ -630,7 +628,7 @@ type LoanTimes { """ (dt. Ausleihzeiten) """ -input LoanTimesInput { +input LoanPeriodsInput { generalRemark: String "notes for each day of the week, starting on Monday" notes: [String] @@ -641,9 +639,8 @@ input LoanTimesInput { times: [String] } - "(dt. Zeitscheibe)" -type LoanPeriod { +type TimeFrame { id: ID! from: Date! to: Date @@ -652,7 +649,7 @@ type LoanPeriod { cargoBike: CargoBike! } -input LoanPeriodCreateInput { +input TimeFrameCreateInput { from: Date to: Date note: String @@ -660,7 +657,7 @@ input LoanPeriodCreateInput { cargoBikeID: CargoBikeCreateInput } -input LoanPeriodUpdateInput { +input TimeFrameUpdateInput { id: ID! from: Date to: Date @@ -692,7 +689,7 @@ type Query { cargoBikeById(id:ID!): CargoBike "returns cargoBikes ordered by name ascending, relations are not loaded, use cargoBikeById instead" cargoBikes(offset: Int!, limit: Int!): [CargoBike]! - "not important, you can just use providerById {cargoBikes}" + "return null if id not found" providerById(id:ID!): Provider "unique equipment with pagination, contains relation to bike (with no further joins), so if you wanna know more about the bike, use cargoBikeById" equipment(offset: Int!, limit: Int!): [Equipment]! @@ -704,7 +701,7 @@ type Query { "p" participants(offset: Int!, limit: Int!): [ Participant]! lendingStationById(id:ID!): LendingStation - lendingStations: [LendingStation]! + lendingStations(offset: Int!, limit: Int!): [LendingStation]! contactInformation: [ContactInformation]! "returns BikeEvent with CargoBike" bikeEventById(id:ID!): BikeEvent! @@ -715,7 +712,7 @@ type Mutation { createCargoBike(cargoBike: CargoBikeCreateInput!): CargoBike! "updates cargoBike of given ID with supplied fields and returns updated cargoBike" updateCargoBike(cargoBike: CargoBikeUpdateInput!): CargoBike! - "creates new peace of unique Equipment, the returned Equipment will contain a cargoBike, but that cargoBike will not contain equipment" + "creates new peace of unique Equipment" createEquipment(equipment: EquipmentCreateInput!): Equipment! "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! @@ -731,6 +728,10 @@ type Mutation { createContactInformation(contactInformation: ContactInformationCreateInput!): ContactInformation! "create Engagement" createEngagement(engagement: EngagementCreateInput): Engagement! + "createContacPerson ,return null if contactInformationId does not exist" + createContactPerson(contactPerson: ContactPersonCreateInput): ContactPerson + updateContactPerson(contactPerson: ContactPersonUpdateInput): ContactPerson + } `;