diff --git a/Dockerfile b/Dockerfile index e13b7b1..824906d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,12 +3,10 @@ WORKDIR / COPY ./src /src COPY ./package*.json ./ -COPY ./gulpfile.js ./tsconfig.json ./ RUN npm install RUN npm install -g gulp RUN npm install gulp RUN gulp EXPOSE 4000 -EXPOSE 5432 CMD ["npm", "start"] diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index f23571c..bd47771 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -14,22 +14,22 @@ export class CargoBikeAPI extends DataSource { /** * Finds cargo bike by id */ - async findCargoBikeById ({ id, token }:{id: any, token:string}) { + async findCargoBikeById ({ id }:{id: any}) { return { id, - name: token + name: 'token' }; } - async updateBike ({ id, token, name }:{id:any, token: string, name: string }) { + async updateBike ({ id, name }:{id:any, name: string }) { const bike = new CargoBike(); bike.id = id; - bike.description = token; + bike.description = 'text'; bike.name = name; await this.connection.manager.save(bike); return { success: true, - message: token, + message: 'bla', bike: { id, name diff --git a/src/model/BikeEvent.ts b/src/model/BikeEvent.ts new file mode 100644 index 0000000..9c32b32 --- /dev/null +++ b/src/model/BikeEvent.ts @@ -0,0 +1,36 @@ +/* eslint no-unused-vars: "off" */ +import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; + +export enum BikeEventType { + KAUF = 'Kauf', + INBETRIEBNAHME = 'Inbetriebnahme', + AUSFALL = 'Ausfall', + WARTUNG = 'Wartung' +} + +@Entity() +export class BikeEvent { + @PrimaryGeneratedColumn() + id: number; + + @Column({ + nullable: true + }) + note: string; + + @Column({ + type: 'date' + }) + date: Date; + + @Column('simple-array', { + nullable: true + }) + documents: string[]; + + @Column({ + type: 'enum', + enum: BikeEventType + }) + eventType: BikeEventType +} diff --git a/src/model/BikeFeatures.ts b/src/model/BikeFeatures.ts new file mode 100644 index 0000000..d912fe5 --- /dev/null +++ b/src/model/BikeFeatures.ts @@ -0,0 +1,71 @@ +import { Column } from 'typeorm'; + +export abstract class Bike { + @Column() + description: string; + + @Column() + modelName: string; + + @Column() + numerOfWheels: number; + + @Column() + forCargo: boolean; + + @Column() + forChildren: boolean; + + @Column() + numberOfChildren: number; + + // technical Information + @Column() + bicycleShift: string; + + @Column() + isEBike: boolean; + + @Column() + hasLightSystem: boolean; + + @Column() + specialFeatures: string; + + // dimensions and load + @Column() + hasCoverBox: boolean; + + @Column() + lockable:boolean; + + @Column() + boxLength: number; + + @Column() + boxWidth: number; + + @Column() + boxHeight: number; + + @Column() + maxWeightBox: number; + + @Column() + maxWeightLuggageRack: number; + + @Column() + maxWeightTotal: number; + + @Column() + bikeLength: number; + + @Column() + bikeWidth: number; + + @Column() + bikeHeight: number; + + @Column() + bikeWeight: number; +} diff --git a/src/model/BikeModel.ts b/src/model/BikeModel.ts new file mode 100644 index 0000000..c1efd90 --- /dev/null +++ b/src/model/BikeModel.ts @@ -0,0 +1,11 @@ +import { PrimaryGeneratedColumn, Column, Entity } from 'typeorm'; +import { Bike } from './BikeFeatures'; + +@Entity() +export class BikeModel extends Bike { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; +} diff --git a/src/model/CargoBike.ts b/src/model/CargoBike.ts index 46de65c..85deab4 100644 --- a/src/model/CargoBike.ts +++ b/src/model/CargoBike.ts @@ -1,13 +1,122 @@ -import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; +/* eslint no-unused-vars: "off" */ +import { Entity, Column, PrimaryGeneratedColumn, OneToMany, ManyToOne, OneToOne } from 'typeorm'; +import { Bike } from './BikeFeatures'; +import { ChainSwap } from './ChainSwap'; +import { Provider } from './Provider'; +import { Participant } from './Participant'; +import { InsuranceData } from './InsuranceData'; +import { LoanPeriod } from './LoanPeriod'; +import { LendingStation } from './LendingStation'; +import { Taxes } from './Taxes'; +import { Equipment } from './Equipment'; + +export enum Group { + KL, + LI, + SP, + FK, + MH, + SZ, + TS, + TK +} + +export enum StickerBikeNameState { + OK, + IMPROVE, + PRODUCED, + NONEED, + MISSING, + UNKNOWN +} @Entity() -export class CargoBike { +export class CargoBike extends Bike { @PrimaryGeneratedColumn() id: number; + @Column() + group: Group; + @Column() name: string; @Column() - description: string; + serialNo: string; + + @OneToMany(type => Equipment, equipment => equipment.cargoBike, { + nullable: true + }) + equipment: Equipment[]; + + @Column({ + nullable: true + }) + otherEquipment: string; + + @OneToMany(type => ChainSwap, chainSwap => chainSwap.cargoBike, { + nullable: true + }) + chainSwaps: ChainSwap[] + + // Security information + @Column() + frameNumber: string; + + @Column() + keyNoFrameLock: string; + + @Column() + keyNoAXAChain: string; + + @Column() + policeCodeing: string; + + @Column() + adfsCoding: string; + + @Column({ + type: 'enum', + enum: StickerBikeNameState + }) + stickerBikeNameState: StickerBikeNameState; + + @Column() + note: string; + + @ManyToOne(type => Provider, { + nullable: true + }) + provider: Provider; + + @ManyToOne(type => Participant, participant => participant.cargoBikes) + coordinator: Participant; + + @Column(type => InsuranceData) + insuranceData: InsuranceData; + + @OneToMany(type => LoanPeriod, loanPeriod => loanPeriod.cargoBike, { + nullable: true + }) + loanPeriods: LoanPeriod[]; + + // This relation is a little redundant because one could also check all LoanPeriods for current station + @ManyToOne(type => LendingStation, lendingStation => lendingStation.cargoBikes, { + nullable: true + }) + lendingStation: LendingStation; + + @Column(type => Taxes) + taxes: Taxes; + + @Column({ + nullable: true + }) + lockedBy: number; + + @Column({ + type: 'date', + nullable: true + }) + lockedUntil: Date; } diff --git a/src/model/ChainSwap.ts b/src/model/ChainSwap.ts new file mode 100644 index 0000000..dbab23b --- /dev/null +++ b/src/model/ChainSwap.ts @@ -0,0 +1,22 @@ +import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm'; +import { CargoBike } from './CargoBike'; + +@Entity() +export class ChainSwap { + @PrimaryGeneratedColumn() + id: number; + + @Column() + mechanic: string; + + @Column({ + type: 'date' + }) + time: Date; + + @Column() + kexNoOldAXAChain: string; + + @ManyToOne(type => CargoBike, cargoBike => cargoBike.chainSwaps) + cargoBike: CargoBike; +} diff --git a/src/model/ContactInformation.ts b/src/model/ContactInformation.ts new file mode 100644 index 0000000..766beb6 --- /dev/null +++ b/src/model/ContactInformation.ts @@ -0,0 +1,56 @@ +import { PrimaryGeneratedColumn, Column, ManyToOne, Entity } from 'typeorm'; +import { Provider } from './Provider'; + +@Entity() +export class ContactInformation { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @Column() + firstName: string; + + @Column({ + type: 'date', + nullable: true + }) + retiredAt: Date; + + @Column({ + nullable: true + }) + phoneExtern: string; + + @Column({ + nullable: true + }) + phone2Extern: string; + + @Column({ + nullable: true + }) + phoneIntern: string; + + @Column({ + nullable: true + }) + phone2Intern: string; + + @Column({ + nullable: true + }) + emailExtern: string; + + @Column({ + nullable: true + }) + emailIntern: string; + + @Column() + note: string; + + @ManyToOne(type => Provider, provider => provider.contactInformation) + provider: Provider; +} diff --git a/src/model/Equipment.ts b/src/model/Equipment.ts new file mode 100644 index 0000000..50a261c --- /dev/null +++ b/src/model/Equipment.ts @@ -0,0 +1,16 @@ +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'; +import { CargoBike } from './CargoBike'; + +@Entity() +export class Equipment { + @PrimaryGeneratedColumn() + id: number; + + @Column() + serialNo: string; + + @OneToMany(type => CargoBike, cargoBike => cargoBike.equipment, { + nullable: true + }) + cargoBike: CargoBike; +} diff --git a/src/model/InsuranceData.ts b/src/model/InsuranceData.ts new file mode 100644 index 0000000..b5c80c2 --- /dev/null +++ b/src/model/InsuranceData.ts @@ -0,0 +1,44 @@ +import { Column } from 'typeorm'; + +export class InsuranceData { + @Column() + name: string; + + @Column() + benefector: string; + + @Column() + noPnP: string; + + @Column() + maintananceResponisble: string; + + @Column() + maintanceBenfector: string; + + @Column({ + nullable: true + }) + maintanceAgreement: string; + + @Column({ + nullable: true + }) + hasFixedRate: boolean; + + @Column({ + nullable: true + }) + fixedRate: number; + + @Column({ + type: 'money', + nullable: true + }) + projectAllowance: number; + + @Column({ + nullable: true + }) + note: string; +} diff --git a/src/model/LendingStation.ts b/src/model/LendingStation.ts new file mode 100644 index 0000000..b7ed521 --- /dev/null +++ b/src/model/LendingStation.ts @@ -0,0 +1,36 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, JoinTable, OneToMany, ManyToOne } from 'typeorm'; +import { ContactInformation } from './ContactInformation'; +import { LoanPeriod } from './LoanPeriod'; +import { CargoBike } from './CargoBike'; +import { Organization } from './Organization'; + +@Entity() +export class LendingStation { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @ManyToMany(type => ContactInformation) + @JoinTable() + contactInformation: ContactInformation[]; + + @Column() + addressStreet: string; + + @Column() + addressStreetNo: string; + + @Column() + addressZip: string; + + @OneToMany(type => LoanPeriod, loanPeriod => loanPeriod.lendingStation) + loanPeriods: LoanPeriod[]; + + @OneToMany(type => CargoBike, cargoBike => cargoBike.lendingStation) + cargoBikes: CargoBike[]; + + @ManyToOne(type => Organization, organization => organization.lendingStations) + organization: Organization; +} diff --git a/src/model/LoanPeriod.ts b/src/model/LoanPeriod.ts new file mode 100644 index 0000000..03f6245 --- /dev/null +++ b/src/model/LoanPeriod.ts @@ -0,0 +1,26 @@ +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; +import { LendingStation } from './LendingStation'; +import { CargoBike } from './CargoBike'; + +@Entity() +export class LoanPeriod { + @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) + lendingStation: LendingStation; + + @Column({ + nullable: true + }) + note: string; + + @ManyToOne(type => CargoBike, cargoBike => cargoBike.loanPeriods) + cargoBike: CargoBike; +} diff --git a/src/model/Organization.ts b/src/model/Organization.ts new file mode 100644 index 0000000..11dac04 --- /dev/null +++ b/src/model/Organization.ts @@ -0,0 +1,26 @@ +import { PrimaryGeneratedColumn, OneToOne, OneToMany, Column } from 'typeorm'; +import { LendingStation } from './LendingStation'; +import { Provider } from './Provider'; + +export class Organization { + @PrimaryGeneratedColumn() + id: number; + + @OneToMany(type => LendingStation, lendingStation => lendingStation.organization) + lendingStations: LendingStation[]; + + @OneToOne(type => Provider, provider => provider.organization, { + nullable: true + }) + provider: Provider; + + @Column({ + nullable: true + }) + registerdAt: string; + + @Column({ + nullable: true + }) + registerNo: string; +} diff --git a/src/model/Participant.ts b/src/model/Participant.ts new file mode 100644 index 0000000..5319f20 --- /dev/null +++ b/src/model/Participant.ts @@ -0,0 +1,73 @@ +import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, OneToMany } from 'typeorm'; +import { ContactInformation } from './ContactInformation'; +import { CargoBike } from './CargoBike'; + +@Entity() +export class Participant { + @PrimaryGeneratedColumn() + id: number; + + @Column({ + type: 'date' + }) + start: Date; + + @Column({ + type: 'date' + }) + end: Date; + + @OneToOne(type => ContactInformation) + @JoinColumn() + contactInformation: ContactInformation; + + @Column() + usernameflotte: string; + + @Column() + usernameSlack: string; + + @Column() + memberADFC: boolean; + + @Column({ + type: 'simple-array' + }) + locationZIPs: string[]; + + @OneToMany(type => CargoBike, cargoBike => cargoBike.coordinator) + cargoBikes: CargoBike[]; + + @Column() + roleCoreTeam: boolean; + + @Column() + roleCoordinator: boolean; + + @Column() + roleEmployeADFC: boolean; + + @Column() + roleMentor: boolean; + + @Column() + roleAmbulance: boolean; + + @Column() + roleBringer: boolean; + + @Column({ + type: 'date' + }) + workshopMentor: Date; + + @Column({ + type: 'date' + }) + workshopAmbulance: Date; + + @Column({ + nullable: true + }) + reserve: string; +} diff --git a/src/model/Provider.ts b/src/model/Provider.ts new file mode 100644 index 0000000..c908d8d --- /dev/null +++ b/src/model/Provider.ts @@ -0,0 +1,40 @@ +/* eslint no-unused-vars: "off" */ + +import { PrimaryGeneratedColumn, Column, OneToMany, Entity, OneToOne } from 'typeorm'; +import { ContactInformation } from './ContactInformation'; +import { LendingStation } from './LendingStation'; +import { Organization } from './Organization'; + +@Entity() +export class Provider { + @PrimaryGeneratedColumn() + id: number; + + @Column() + name: string; + + @Column({ + nullable: false + }) + formularName: String; + + @Column() + street: string; + + @Column() + number: string; + + @Column() + zip: string; + + @OneToMany(type => ContactInformation, contactInformation => contactInformation.provider) + contactInformation: ContactInformation[]; + + @Column() + isPrivatePerson: boolean; + + @OneToOne(type => Organization, organization => organization.provider, { + nullable: true + }) + organization: Organization; +} diff --git a/src/model/Taxes.ts b/src/model/Taxes.ts new file mode 100644 index 0000000..bdbb576 --- /dev/null +++ b/src/model/Taxes.ts @@ -0,0 +1,18 @@ +/* eslint no-unused-vars: "off" */ +import { Column } from 'typeorm'; + +export enum OrganizationArea { + IB, + ZB +} +export class Taxes { + @Column() + costCenter: string; + + @Column({ + type: 'enum', + enum: OrganizationArea, + nullable: true + }) + organizationArea: OrganizationArea; +} diff --git a/src/resolvers/cargobikeResolver.ts b/src/resolvers/cargobikeResolver.ts index 89d05df..1d23d5f 100644 --- a/src/resolvers/cargobikeResolver.ts +++ b/src/resolvers/cargobikeResolver.ts @@ -3,18 +3,18 @@ import { GraphQLError } from 'graphql'; export default { Query: { - CargobikeById: (_: any, { id, token }:{id: any, token: string}, { dataSources, req }:{dataSources: any, req: any }) => { + CargobikeById: (_: any, { id }:{id: any}, { dataSources, req }:{dataSources: any, req: any }) => { if (req.permissions.includes(Permission.ReadBike)) { - return dataSources.cargoBikeAPI.findCargoBikeById({ id, token }); + return dataSources.cargoBikeAPI.findCargoBikeById({ id }); } else { throw new GraphQLError('Insufficient Permissions'); } } }, Mutation: { - addBike: (_: any, { id, token, name }:{id: any, token: string, name:string}, { dataSources, req }:{dataSources: any, req: any }) => { + addBike: (_: any, { id, name }:{id: any, name:string}, { dataSources, req }:{dataSources: any, req: any }) => { if (req.permissions.includes(Permission.WriteBike)) { - return dataSources.cargoBikeAPI.updateBike({ id, token, name }); + return dataSources.cargoBikeAPI.updateBike({ id, name }); } else { throw new GraphQLError('Insufficient Permissions'); } diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index a86a338..7a825ac 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -15,7 +15,7 @@ type CargoBike { forCargo: Boolean forChildren: Boolean numberOfChildren: Int - serialno: String + serialNo: String """ Safety is a custom type, that stores information about security features. TODO: Should this be calles Security? @@ -38,23 +38,32 @@ type CargoBike { stickerBikeNameState: StickerBikeNameState note: String provider: Provider - coordinator: Coordinator - insuranceData: InsuranceData - loantimes: [LoanTimes] + coordinator: Participant + insuranceData: InsuranceData! + lendingstation: LendingStation + taxes: Taxes + "null if not locked by other user" + lockedBy: ID + lockedUntil: Date } type InsuranceData { """ Eventuelly, this field will become an enum or a seperate data table and user can choose from a pool of insurance companies. """ - name: String - -} -type Coordinator { - id:ID! - contactInformation: ContactInformation! - note: String - corgoBikes: [CargoBike]! + name: String! + benefactor: String! + billing: String! + noPnP: String! + "eg. Anbieter, flotte, eigenleistung" + maintananceResponsible: String! + maintanceBenefector: String! + maintananceAgreement: String + hasFixedRate: Boolean! + fixedRate: Float + "Projektzuschuss" + projectAllowance: Float + notes: String } enum Group{ @@ -75,12 +84,12 @@ Even bikes of the same model can have different properties. """ type BikeModel { id: ID! - name: String + name: String! dimensionsAndLoad: DimensionsAndLoad! technicalEquipment: TechnicalEquipment! } -type ActiveMentor { +type Participant { id: ID! start: Date! end: Date! @@ -96,7 +105,7 @@ type ActiveMentor { Wahr, wenn die Person Pate ist. """ roleMentor: Boolean! - roleAmbulanz: Boolean! + roleAmbulance: Boolean! roleBringer: Boolean! "Date of workshop to become Mentor dt. Pate" workshopMentor: Date @@ -140,7 +149,7 @@ type Equipment { id: ID! serialNo: String! """ - TODO unclear what this means + TODO unclear what this means. tomy fragen """ investable: Boolean name: String @@ -149,10 +158,8 @@ type Equipment { "An Event is a point in time, when the state of the bike somehow changed." type BikeEvent { id: ID! - type: BikeEventType - """ - TODO: An Event should have a date field (Leon). - """ + eventType: BikeEventType + date: Date! note: String """ Path to documents @@ -231,9 +238,7 @@ type Provider { name: String! formularName: String address: Address - "If Club, at what court registered" - registeredAt: String - registerNumber: String + providerContactPerson: [ContactInformation] isPrivatePerson: Boolean! organisation: Organisation @@ -259,15 +264,19 @@ type ContactInformation { type Organisation{ id: ID! "(dt. Ausleihstation)" - lendinglocation: [LendingStation] + lendingStations: [LendingStation] "registration number of association" associationNo: String + "If Club, at what court registered" + registeredAt: String + provider: Provider otherdata: String } "(dt. Standort)" type LendingStation { id: ID! + name: String! contactInformation: [ContactInformation]! address: Address loanTimes: LoanTimes @@ -277,6 +286,20 @@ type LendingStation { "(dt. Ausleihzeiten)" type LoanTimes { notes: String + mof: String + mot: String + tuf: String + tut: String + wef: String + wet: String + thf: String + tht: String + frf: String + frt: String + saf: String + sat: String + suf: String + sut: String } "(dt. Zeitscheibe)" @@ -296,13 +319,13 @@ type Address { } type Query { - CargobikeById(token:String!,id:ID!): CargoBike + CargobikeById(id:ID!): CargoBike Cargobikes(token:String!): [CargoBike]! CargobikesByProvider(token:String!,providerId:ID!): [CargoBike]! ProviderById(token:String!,id:ID!): Provider Providers(token:String!): [Provider]! - ActiveMentorById(token:String!,id:ID!): ActiveMentor - ActiveMentors(token:String!): [ActiveMentor]! + ParticipantById(token:String!,id:ID!): Participant + Participants(token:String!): [ Participant]! lendingStationById(token:String!, id:ID!): LendingStation lendingStations(token:String!): [LendingStation]! contactInformation(token:String!): [ContactInformation]! @@ -345,14 +368,13 @@ input CargoBikeInput { stickerBikeNameState: String note: String provider: String - coordinator: String insuranceData: String } type Mutation { "for testing" - addBike(id: ID!, token: String!, name: String): UpdateBikeResponse! + addBike(id: ID!, name: String): UpdateBikeResponse! "if id: null, then new bike will be created, else old bike will be updated" - cargoBike(token:String!,cargoBike: CargoBikeInput): UpdateBikeResponse! + cargoBike(cargoBike: CargoBikeInput): UpdateBikeResponse! } `;