diff --git a/src/datasources/db/providerAPI.ts b/src/datasources/db/providerAPI.ts index e1ed65a..dbb99cf 100644 --- a/src/datasources/db/providerAPI.ts +++ b/src/datasources/db/providerAPI.ts @@ -1,6 +1,7 @@ import { DataSource } from 'apollo-datasource'; -import { Connection, getConnection } from 'typeorm'; +import { Connection, EntityManager, getConnection } from 'typeorm'; import { Provider } from '../../model/Provider'; +import { Organisation } from '../../model/Organisation'; export class ProviderAPI extends DataSource { connection : Connection @@ -26,6 +27,30 @@ export class ProviderAPI extends DataSource { .getMany(); } + async providerByOrganisationId (id: number) { + return await this.connection.getRepository(Provider) + .createQueryBuilder('p') + .select() + .where('p."organisationId" = :id', { id: id }) + .getOne(); + } + + async organisationByProviderId (id: number) { + return await this.connection.getRepository(Provider) + .createQueryBuilder() + .relation(Provider, 'organisationId') + .of(id) + .loadOne(); + } + + async contactInformationByOrganisationId (id: number) { + return await this.connection.getRepository(Organisation) + .createQueryBuilder('o') + .relation(Organisation, 'contactInformationId') + .of(id) + .loadOne(); + } + async createProvider (provider: any) { let inserts: any = null; await this.connection.transaction(async (entityManager: any) => { @@ -40,15 +65,25 @@ export class ProviderAPI extends DataSource { .relation(Provider, 'cargoBikes') .of(inserts.identifiers[0].id) .add(provider.cargoBikeIds); - await entityManager.getRepository(Provider) - .createQueryBuilder('provider') - .relation(Provider, 'contactPersons') - .of(inserts.identifiers[0].id) - .add(provider.contactPersonIds); }); - const ret = inserts.generatedMaps[0]; ret.id = inserts.identifiers[0].id; return ret; } + + async createOrganisation (organisation: any) { + let inserts: any = null; + await this.connection.transaction(async (entityManager: EntityManager) => { + inserts = await entityManager.getRepository(Organisation) + .createQueryBuilder('o') + .insert() + .values([organisation]) + .execute(); + await entityManager.getRepository(Organisation).createQueryBuilder() + .relation(Organisation, 'providerId') + .of(inserts.identifiers[0].id) + .set(organisation.providerId); + }); + return inserts.generatedMaps[0]; + } } diff --git a/src/datasources/db/workshopAPI.ts b/src/datasources/db/workshopAPI.ts new file mode 100644 index 0000000..517260c --- /dev/null +++ b/src/datasources/db/workshopAPI.ts @@ -0,0 +1,72 @@ +import { DataSource } from 'apollo-datasource'; +import { Connection, getConnection } from 'typeorm'; +import { WorkshopType } from '../../model/WorkshopType'; +import { Workshop } from '../../model/Workshop'; + +export class WorkshopAPI extends DataSource { + connection: Connection + + constructor () { + super(); + this.connection = getConnection(); + } + + async createWorkshop (workshop: any) { + const inserts = await this.connection.getRepository(Workshop) + .createQueryBuilder('w') + .insert() + .values([workshop]) + .returning('*') + .execute(); + return inserts.generatedMaps[0]; + } + + async createWorkshopType (workshopType: any) { + const inserts = await this.connection.getRepository(WorkshopType) + .createQueryBuilder('wt') + .insert() + .values([workshopType]) + .returning('*') + .execute(); + return inserts.generatedMaps[0]; + } + + async workshopTypes (offset: number, limit: number) { + return await this.connection.getRepository(WorkshopType) + .createQueryBuilder('w') + .select() + .skip(offset) + .take(limit) + .getMany(); + } + + /** + * finds workshops with pagination + * @param offset + * @param limit + */ + async workshops (offset: number, limit: number) { + return await this.connection.getRepository(Workshop) + .createQueryBuilder('w') + .select() + .skip(offset) + .take(limit) + .getMany(); + } + + async trainer1ByWorkshopId (id: number) { + return await this.connection.getRepository(Workshop) + .createQueryBuilder('w') + .relation(Workshop, 'trainer1Id') + .of(id) + .loadOne(); + } + + async trainer2ByWorkshopId (id: number) { + return await this.connection.getRepository(Workshop) + .createQueryBuilder('w') + .relation(Workshop, 'trainer2Id') + .of(id) + .loadOne(); + } +} diff --git a/src/datasources/userserver/permission.ts b/src/datasources/userserver/permission.ts index 92cc838..9ff61ca 100644 --- a/src/datasources/userserver/permission.ts +++ b/src/datasources/userserver/permission.ts @@ -4,7 +4,9 @@ export enum Permission { WriteBike = 'BIKE_WRITE', WriteEquipmentType = 'EQUIPMENT_TYPE_WRITE', WritePerson = 'PERSON_WRITE', - ReadPerson = 'PERSON_READ' + ReadPerson = 'PERSON_READ', + WriteProvider = 'PROVIDER_WRITE', + WriteWorkshopType = 'WORKSHOP_TYPE_WRITE' } // Permissions where the creation will be requested on startup @@ -28,5 +30,13 @@ export const requiredPermissions = [ { name: Permission.ReadPerson, description: 'Allows reading of contact information' + }, + { + name: Permission.WriteProvider, + description: 'Allows to modify providers and organisations' + }, + { + name: Permission.WriteWorkshopType, + description: 'Allows to create and modify workshop types' } ]; diff --git a/src/index.ts b/src/index.ts index 19b41a1..f434e7d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,6 +31,8 @@ import { WorkshopType } from './model/WorkshopType'; import { EngagementType } from './model/EngagementType'; import { EquipmentType } from './model/EquipmentType'; import { BikeEventType } from './model/BikeEventType'; +import { WorkshopAPI } from './datasources/db/workshopAPI'; +import workshopResolvers from './resolvers/workshopResolvers'; require('dotenv').config(); @@ -96,7 +98,8 @@ const server = new ApolloServer({ lendingstationResolvers, participantResolvers, providerResolvers, - contactinformationResolvers + contactinformationResolvers, + workshopResolvers ], typeDefs, dataSources: () => ({ @@ -105,6 +108,7 @@ const server = new ApolloServer({ participantAPI: new ParticipantAPI(), contactInformationAPI: new ContactInformationAPI(), providerAPI: new ProviderAPI(), + workshopAPI: new WorkshopAPI(), userAPI }), context: (req: any) => { diff --git a/src/model/LendingStation.ts b/src/model/LendingStation.ts index 9c84b92..d5004fa 100644 --- a/src/model/LendingStation.ts +++ b/src/model/LendingStation.ts @@ -1,26 +1,10 @@ -import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne } from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToOne, JoinColumn } from 'typeorm'; import { TimeFrame } from './TimeFrame'; import { Organisation } from './Organisation'; import { Address } from './Provider'; import { ContactInformation } from './ContactInformation'; -@Entity() -export class LendingStation { - @PrimaryGeneratedColumn() - id: number; - - @Column() - name: string; - - @ManyToOne(type => ContactInformation) - contactInformationIntern: ContactInformation; - - @ManyToOne(type => ContactInformation) - contactInformationExtern: ContactInformation; - - @Column(type => Address) - address: Address; - +export class LoanPeriod { /** * validity for loanPeriods */ @@ -39,9 +23,44 @@ export class LendingStation { }) to: Date; - @OneToMany(type => TimeFrame, loanPeriod => loanPeriod.lendingStation) - loanPeriods: TimeFrame[]; + @Column({ + type: 'simple-array' + }) + loanTimes: string[]; +} + +@Entity() +export class LendingStation { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @ManyToOne(type => ContactInformation) + @JoinColumn({ + name: 'contactInformationInternId' + }) + contactInformationInternId: number; + + @ManyToOne(type => ContactInformation) + @JoinColumn({ + name: 'contactInformationExternId' + }) + contactInformationExternId: number; + + @Column(type => Address) + address: Address; + + @Column(type => LoanPeriod) + loanPeriod: LoanPeriod; + + @OneToMany(type => TimeFrame, timeFrame => timeFrame.lendingStation) + timeFrames: TimeFrame[]; @ManyToOne(type => Organisation, organization => organization.lendingStations) - organization: Organisation; + @JoinColumn({ + name: 'organisationId' + }) + organisationId: number; } diff --git a/src/model/Organisation.ts b/src/model/Organisation.ts index 374f403..2bcffb4 100644 --- a/src/model/Organisation.ts +++ b/src/model/Organisation.ts @@ -1,21 +1,31 @@ -import { PrimaryGeneratedColumn, OneToOne, OneToMany, Column, Entity } from 'typeorm'; +import { PrimaryGeneratedColumn, OneToOne, OneToMany, Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; import { LendingStation } from './LendingStation'; import { Address, Provider } from './Provider'; +import { ContactInformation } from './ContactInformation'; @Entity() export class Organisation { @PrimaryGeneratedColumn() id: number; - @OneToMany(type => LendingStation, lendingStation => lendingStation.organization) + @Column() + name: string; + + @OneToMany(type => LendingStation, lendingStation => lendingStation.organisationId) lendingStations: LendingStation[]; - @OneToOne(type => Provider, provider => provider.organization, { + @OneToOne(type => Provider, provider => provider.organisationId, { nullable: true }) - provider: Provider; + providerId: number; + + @ManyToOne(type => ContactInformation) + @JoinColumn({ + name: 'contactInformationId' + }) + contactInformationId: number; - // Court where association was registerd + // Court where association was registered @Column({ nullable: true }) diff --git a/src/model/Provider.ts b/src/model/Provider.ts index 992e41b..f0d1096 100644 --- a/src/model/Provider.ts +++ b/src/model/Provider.ts @@ -45,11 +45,13 @@ export class Provider implements Lockable { contactInformationId: number; // is null when Provider is a private Person - @OneToOne(type => Organisation, organization => organization.provider, { + @OneToOne(type => Organisation, organization => organization.providerId, { nullable: true }) - @JoinColumn() - organization: Organisation; + @JoinColumn({ + name: 'organisationId' + }) + organisationId: number; @OneToMany(type => CargoBike, cargoBike => cargoBike.provider) cargoBikes: CargoBike[]; diff --git a/src/model/TimeFrame.ts b/src/model/TimeFrame.ts index 51d261a..053517b 100644 --- a/src/model/TimeFrame.ts +++ b/src/model/TimeFrame.ts @@ -10,13 +10,12 @@ export class TimeFrame { @PrimaryGeneratedColumn() id: number; - // I have to find out how typorm will map the datetange data type. @Column({ type: 'daterange' }) dateRange: Date[]; - @ManyToOne(type => LendingStation, lendingStation => lendingStation.loanPeriods) + @ManyToOne(type => LendingStation, lendingStation => lendingStation.timeFrames) lendingStation: LendingStation; @Column({ diff --git a/src/model/Workshop.ts b/src/model/Workshop.ts index c5b0acc..b193ba0 100644 --- a/src/model/Workshop.ts +++ b/src/model/Workshop.ts @@ -1,4 +1,4 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, ManyToOne } from 'typeorm'; +import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, ManyToOne, JoinColumn } from 'typeorm'; import { Participant } from './Participant'; import { WorkshopType } from './WorkshopType'; import { Lockable } from './CargoBike'; @@ -30,11 +30,17 @@ export class Workshop implements Lockable { @ManyToOne(type => Participant, { nullable: false }) + @JoinColumn({ + name: 'trainer1Id' + }) trainer1Id: number; @ManyToOne(type => Participant, { nullable: true }) + @JoinColumn({ + name: 'trainer2Id' + }) trainer2: Participant; @Column({ diff --git a/src/resolvers/lendingstationResolvers.ts b/src/resolvers/lendingstationResolvers.ts index 10fe8e3..813a242 100644 --- a/src/resolvers/lendingstationResolvers.ts +++ b/src/resolvers/lendingstationResolvers.ts @@ -27,9 +27,6 @@ export default { } }, LendingStation: { - contactPersons (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { - return dataSources.contactInformationAPI.contactPersonsByLendingStationId(parent.id); - }, timeFrames (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { return dataSources.lendingStationAPI.timeFramesByLendingStationId(parent.id); }, diff --git a/src/resolvers/providerResolvers.ts b/src/resolvers/providerResolvers.ts index 9177790..00e40b4 100644 --- a/src/resolvers/providerResolvers.ts +++ b/src/resolvers/providerResolvers.ts @@ -25,6 +25,17 @@ export default { } else { return new GraphQLError('Insufficient Permissions'); } + }, + organisation: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { + return dataSources.providerAPI.organisationByProviderId(parent.id); + } + }, + Organisation: { + provider: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { + return dataSources.providerAPI.providerByOrganisationId(parent.id); + }, + contactInformation: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => { + return dataSources.providerAPI.contactInformationByOrganisationId(parent.id); } }, Mutation: { @@ -34,6 +45,13 @@ export default { } else { return new GraphQLError('Insufficient Permissions'); } + }, + createOrganisation: (_: any, { organisation }: { organisation: any }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteProvider)) { + return dataSources.providerAPI.createOrganisation(organisation); + } else { + return new GraphQLError('Insufficient Permissions'); + } } } }; diff --git a/src/resolvers/workshopResolvers.ts b/src/resolvers/workshopResolvers.ts new file mode 100644 index 0000000..78c9ac1 --- /dev/null +++ b/src/resolvers/workshopResolvers.ts @@ -0,0 +1,45 @@ +import { Permission } from '../datasources/userserver/permission'; +import { GraphQLError } from 'graphql'; + +export default { + Query: { + workshopTypes: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.workshopAPI.workshopTypes(offset, limit); + } else { + return new GraphQLError('Insufficient Permissions'); + } + }, + workshops: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.workshopAPI.workshops(offset, limit); + } else { + return new GraphQLError('Insufficient Permissions'); + } + } + }, + Workshop: { + trainer1: (parent: any, __:any, { dataSources, req }: { dataSources: any, req: any }) => { + return dataSources.workshopAPI.trainer1ByWorkshopId(parent.id); + }, + trainer2: (parent: any, __:any, { dataSources, req }: { dataSources: any, req: any }) => { + return dataSources.workshopAPI.trainer2ByWorkshopId(parent.id); + } + }, + Mutation: { + createWorkshop: (_: any, { workshop }: { workshop: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteWorkshopType)) { + return dataSources.workshopAPI.createWorkshop(workshop); + } else { + return new GraphQLError('Insufficient Permissions'); + } + }, + createWorkshopType: (_: any, { workshopType }: { workshopType: number }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.WriteWorkshopType)) { + return dataSources.workshopAPI.createWorkshopType(workshopType); + } else { + return new GraphQLError('Insufficient Permissions'); + } + } + } +}; diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index fe41d65..3fd07d3 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -242,6 +242,34 @@ input ParticipantCreateInput { memberCoreTeam: Boolean } +type Workshop { + id: ID! + title: String! + description: String! + date: Date! + workshopType: WorkshopType! + trainer1: Participant! + trainer2: Participant +} + +input WorkshopCreateInput { + title: String! + description: String + date: Date! + workshopTypeId: ID! + trainer1Id: ID! + trainer2Id: ID +} + +type WorkshopType { + id: ID! + name: String! +} + +input WorkshopTypeCreateInput { + name: String! +} + type EngagementType { id: ID! name: String! @@ -295,20 +323,20 @@ input EngagementCreateInput { type Taxes { costCenter: String! - organizationArea: OrganizationArea + organisationArea: OrganisationArea } input TaxesCreateInput { costCenter: String! - organizationArea: OrganizationArea + organisationArea: OrganisationArea } input TaxesUpdateInput { costCenter: String - organizationArea: OrganizationArea + organisationArea: OrganisationArea } -enum OrganizationArea { +enum OrganisationArea { IB ZB } @@ -535,20 +563,17 @@ enum StickerBikeNameState { type Provider { id: ID! formName: String - contactPersons: [ContactInformation]! - isPrivatePerson: Boolean! + privatePerson: ContactInformation organisation: Organisation - cargoBikes: [CargoBike]! + cargoBikes: [CargoBike] } "(dt. Anbieter)" input ProviderCreateInput { formName: String! - "i think it makes more sense to create Provider and then add new ContactPersons" - contactPersonIds: [ID]! - isPrivatePerson: Boolean! + privatePersonId: ID organisationId: ID - cargoBikeIds: [ID]! + cargoBikeIds: [ID] } """ @@ -616,6 +641,7 @@ input ContactPersonUpdateInput { type Organisation { id: ID! + name: String! address: Address "(dt. Ausleihstation)" lendingStations: [LendingStation] @@ -624,6 +650,21 @@ type Organisation { "If Club, at what court registered" registeredAt: String provider: Provider + contactInformation: ContactInformation + otherData: String +} + +input OrganisationCreateInput { + address: AddressCreateInput! + name: String! + "(dt. Ausleihstation)" + lendingStationIds: [ID] + "registration number of association" + associationNo: String! + "If Club, at what court registered" + registeredAt: String + providerId: ID + contactInformationId: ID otherData: String } @@ -631,10 +672,11 @@ type Organisation { type LendingStation { id: ID! name: String! - contactPersons: [ContactPerson]! + contactInformationIntern: ContactInformation + contactInformationExtern: ContactInformation address: Address! timeFrames: [TimeFrame]! - loanPeriods: LoanPeriods + loanPeriod: LoanPeriod cargoBikes: [CargoBike] "Total amount of cargoBikes currently assigned to the lending station" numCargoBikes: Int! @@ -645,9 +687,10 @@ If you want to create LendingStation with cargoBikes, use createTimeFrame and se """ input LendingStationCreateInput { name: String! - contactPersonIds: [ID]! + contactInformationInternId: ID + contactInformationExternId: ID address: AddressCreateInput! - loanPeriods: LoanPeriodsInput + loanPeriod: LoanPeriodInput } """ @@ -658,13 +701,13 @@ input LendingStationUpdateInput { name: String contactInformation: [ContactInformationUpdateInput] address: AddressUpdateInput - loanPeriods: LoanPeriodsInput + loanPeriod: LoanPeriodInput } """ (dt. Ausleihzeiten) not implemented """ -type LoanPeriods { +type LoanPeriod { generalRemark: String "notes for each day of the week, starting on Monday" notes: [String] @@ -678,7 +721,7 @@ type LoanPeriods { """ (dt. Ausleihzeiten) """ -input LoanPeriodsInput { +input LoanPeriodInput { generalRemark: String "notes for each day of the week, starting on Monday" notes: [String] @@ -753,6 +796,8 @@ type Query { participantById(id:ID!): Participant "p" participants(offset: Int!, limit: Int!): [ Participant]! + workshopTypes(offset: Int!, limit: Int!): [WorkshopType] + workshops(offset: Int!, limit: Int!): [Workshop] lendingStationById(id:ID!): LendingStation lendingStations(offset: Int!, limit: Int!): [LendingStation]! timeframes(offset: Int!, limit: Int!): [TimeFrame]! @@ -789,6 +834,8 @@ type Mutation { createBikeEvent(bikeEvent: BikeEventCreateInput): BikeEvent! "create participant" createParticipant(participant: ParticipantCreateInput!): Participant! + createWorkshopType(workshopType: WorkshopTypeCreateInput!): WorkshopType! + createWorkshop(workshop: WorkshopCreateInput!): Workshop! "create new contactInfo" createContactInformation(contactInformation: ContactInformationCreateInput!): ContactInformation! createPerson(person: PersonCreateInput!): Person! @@ -800,6 +847,7 @@ type Mutation { updateContactPerson(contactPerson: ContactPersonUpdateInput): ContactPerson "create Provider, if cargoBikeIds or contactPersonIds are not valid, provider will still be created" createProvider(provider: ProviderCreateInput!): Provider! + createOrganisation(organisation: OrganisationCreateInput!): Organisation! }