Merge branch 'main' into develop

pull/28/head^2
Trivernis 4 years ago committed by GitHub
commit 712a5c8af3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,37 @@
name: Build Docker Image
on:
push:
branches: [ main, dev ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Copy Repo Files
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Portus
uses: docker/login-action@v1
with:
registry: https://flotte-docker-registry.spdns.org/
username: ${{ secrets.PORTUS_USERNAME }}
password: ${{ secrets.PORTUS_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: true
tags: flotte-docker-registry.spdns.org/apollo-server:latest

@ -4,14 +4,16 @@ Apollo server written in typescript that handles business logic.
[![Build Status](https://travis-ci.com/fLotte-meets-HWR-DB/apollo-server.svg?token=YfRmpHAXqyUafCgSEexw&branch=main)](https://travis-ci.com/fLotte-meets-HWR-DB/apollo-server) [![Build Status](https://travis-ci.com/fLotte-meets-HWR-DB/apollo-server.svg?token=YfRmpHAXqyUafCgSEexw&branch=main)](https://travis-ci.com/fLotte-meets-HWR-DB/apollo-server)
## Assumptions ## Assumptions
Userserver and postgres are running e.g. with Julius' Docker Compose. The [flotte-user-management server](https://github.com/fLotte-meets-HWR-DB/flotte-user-management) and postgres are running. Set the [environment variables](#Environment-Variables) accordingly.
## Usage ## Usage
### Docker ### Docker
You can build and run a docker image with
```bash ```bash
docker build -t <image name> . docker build -t <image name> .
docker run --rm -p 4000:4000 <image name> docker run --rm -p 4000:4000 <image name> -e ...
``` ```
### Compile and run Don't forget to pass all the [environment variables](#Environment-Variables) with the -e option.
### Compile and Run
Install gulp if not installed Install gulp if not installed
```bash ```bash
npm -g gulp npm -g gulp
@ -21,25 +23,28 @@ npm install
gulp gulp
npm start npm start
``` ```
You can set the [environment variables](#Environment-Variables) in a _.env_ file.
### For Development ### For Development
Install node\_modules and gulp Install node\_modules and gulp
```bash ```bash
npm -g gulp npm -g gulp
npm install npm install
``` ```
And start gulp in watch mode Start gulp in watch mode to recompile the type script
```bash ```bash
gulp watch gulp watchTs
``` ```
This will watch *.ts files in _./src_ and recompile to _./dist_ and finally restart the server. This will watch *.ts files in _./src_ and recompile to _./dist_. You will have to restart the server yourself.
## Environment Variables ## Environment Variables
The following environment variables can be used to configure the server: The following environment variables can be used to configure the server:
```bash ```bash
RPC_HOST=host:port RPC_HOST=host:port
NODE_ENV=development/porduction NODE_ENV=develop/production
POSTGRES_CONNECTION_URL=postgres://username:password@host:port/database_name POSTGRES_CONNECTION_URL=postgres://username:password@host:port/database_name
``` ```
- __RPC_HOST__ is used for the connection with the userserver. - __RPC_HOST__ is used for the connection with the [flotte-user-management server](https://github.com/fLotte-meets-HWR-DB/flotte-user-management).
- __NODE_ENV__ will not check authentication if set to development - __NODE_ENV__ will not check authentication if set to development
- __POSTGRES_CONNECTION_URL__ for connection with the postgres database - __POSTGRES_CONNECTION_URL__ for connection with the postgres database
If the API server cannot connect to the [flotte-user-management server](https://github.com/fLotte-meets-HWR-DB/flotte-user-management) or the postgres data base. It will try to reconnect in an endless loop.

@ -26,7 +26,7 @@ import { Equipment } from '../../model/Equipment';
import { Engagement } from '../../model/Engagement'; import { Engagement } from '../../model/Engagement';
import { Provider } from '../../model/Provider'; import { Provider } from '../../model/Provider';
import { TimeFrame } from '../../model/TimeFrame'; import { TimeFrame } from '../../model/TimeFrame';
import { ActionLogger, deleteEntity, LockUtils } from './utils'; import { ActionLogger, DBUtils, genBoxDimensions, LockUtils } from './utils';
import { EquipmentType } from '../../model/EquipmentType'; import { EquipmentType } from '../../model/EquipmentType';
import { BikeEventType } from '../../model/BikeEventType'; import { BikeEventType } from '../../model/BikeEventType';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
@ -42,14 +42,8 @@ export class CargoBikeAPI extends DataSource {
this.connection = getConnection(); this.connection = getConnection();
} }
async getCargoBikes (offset: number, limit: number) { async getCargoBikes (offset?: number, limit?: number) {
return await this.connection.createQueryBuilder() return await DBUtils.getAllEntity(this.connection, CargoBike, 'cb', offset, limit);
.select('cargoBike')
.from(CargoBike, 'cargoBike')
.orderBy('name', 'ASC')
.offset(offset)
.limit(limit)
.getMany();
} }
/** /**
@ -104,12 +98,13 @@ export class CargoBikeAPI extends DataSource {
async updateCargoBike (cargoBike: any, userId:number) { async updateCargoBike (cargoBike: any, userId:number) {
const keepLock = cargoBike?.keepLock; const keepLock = cargoBike?.keepLock;
delete cargoBike.keepLock; delete cargoBike.keepLock;
delete cargoBike.lendingStationId; const equipmentTypeIds = cargoBike?.equipmentTypeIds;
let equipmentTypeIds: any = null; delete cargoBike?.equipmentTypeIds;
if (cargoBike.equipmentTypeIds) { const equipmentIds = cargoBike?.equipmentIds;
equipmentTypeIds = cargoBike.equipmentTypeIds; delete cargoBike?.equipmentIds;
delete cargoBike.equipmentTypeIds; // generate ranges for box dimensions
} genBoxDimensions(cargoBike);
await this.connection.transaction(async (entityManager: EntityManager) => { await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, CargoBike, 'cb', cargoBike.id, userId)) { if (await LockUtils.isLocked(entityManager, CargoBike, 'cb', cargoBike.id, userId)) {
throw new GraphQLError('CargoBike locked by other user'); throw new GraphQLError('CargoBike locked by other user');
@ -125,7 +120,12 @@ export class CargoBikeAPI extends DataSource {
.createQueryBuilder('cb') .createQueryBuilder('cb')
.relation(CargoBike, 'equipmentTypeIds') .relation(CargoBike, 'equipmentTypeIds')
.of(cargoBike.id) .of(cargoBike.id)
.addAndRemove(equipmentTypeIds, await this.equipmentTypeByCargoBikeId(cargoBike.id)); // TODO remove all existing relations .addAndRemove(equipmentTypeIds, await this.equipmentTypeByCargoBikeId(cargoBike.id));
equipmentIds && await entityManager.getRepository(CargoBike)
.createQueryBuilder('cb')
.relation(CargoBike, 'equipmentIds')
.of(cargoBike.id)
.addAndRemove(equipmentIds, await this.equipmentByCargoBikeId(cargoBike.id));
}); });
!keepLock && await LockUtils.unlockEntity(this.connection, CargoBike, 'cb', cargoBike.id, userId); !keepLock && await LockUtils.unlockEntity(this.connection, CargoBike, 'cb', cargoBike.id, userId);
return await this.findCargoBikeById(cargoBike.id); return await this.findCargoBikeById(cargoBike.id);
@ -150,8 +150,9 @@ export class CargoBikeAPI extends DataSource {
* created CargoBike and returns created bike with new ID * created CargoBike and returns created bike with new ID
* @param param0 cargoBike to be created * @param param0 cargoBike to be created
*/ */
async createCargoBike ({ cargoBike }: { cargoBike: any }) { async createCargoBike (cargoBike: any) {
let inserts: any = {}; let inserts: any = {};
genBoxDimensions(cargoBike);
await this.connection.transaction(async (entityManager:any) => { await this.connection.transaction(async (entityManager:any) => {
inserts = await entityManager.getRepository(CargoBike) inserts = await entityManager.getRepository(CargoBike)
.createQueryBuilder('cb') .createQueryBuilder('cb')
@ -164,6 +165,11 @@ export class CargoBikeAPI extends DataSource {
.relation(CargoBike, 'equipmentTypeIds') .relation(CargoBike, 'equipmentTypeIds')
.of(inserts.identifiers[0].id) .of(inserts.identifiers[0].id)
.add(cargoBike.equipmentTypeIds); .add(cargoBike.equipmentTypeIds);
cargoBike?.equipmentIds && await entityManager.getRepository(CargoBike)
.createQueryBuilder('cb')
.relation(CargoBike, 'equipmentIds')
.of(inserts.identifiers[0].id)
.add(cargoBike.equipmentIds);
}); });
inserts.generatedMaps[0].id = inserts?.identifiers[0].id; inserts.generatedMaps[0].id = inserts?.identifiers[0].id;
return inserts?.generatedMaps[0]; return inserts?.generatedMaps[0];
@ -198,11 +204,11 @@ export class CargoBikeAPI extends DataSource {
} }
async deleteBikeEventType (id: number, userId: number) { async deleteBikeEventType (id: number, userId: number) {
return await deleteEntity(this.connection, BikeEventType, 'bet', id, userId); return await DBUtils.deleteEntity(this.connection, BikeEventType, 'bet', id, userId);
} }
async deleteBikeEvent (id: number, userId: number) { async deleteBikeEvent (id: number, userId: number) {
return await deleteEntity(this.connection, BikeEvent, 'be', id, userId); return await DBUtils.deleteEntity(this.connection, BikeEvent, 'be', id, userId);
} }
async cargoBikeByEventId (id: number) { async cargoBikeByEventId (id: number) {
@ -221,7 +227,14 @@ export class CargoBikeAPI extends DataSource {
.loadOne(); .loadOne();
} }
async bikeEventsByCargoBikeId (id: number, offset: number = 0, limit:number = 100) { async bikeEventsByCargoBikeId (id: number, offset?: number, limit?: number) {
if (offset === null || limit === null) {
return await this.connection.getRepository(CargoBike)
.createQueryBuilder('cb')
.relation(CargoBike, 'bikeEvents')
.of(id)
.loadMany();
} else {
return await this.connection.getRepository(CargoBike) return await this.connection.getRepository(CargoBike)
.createQueryBuilder('cb') .createQueryBuilder('cb')
.skip(offset) .skip(offset)
@ -230,6 +243,7 @@ export class CargoBikeAPI extends DataSource {
.of(id) .of(id)
.loadMany(); .loadMany();
} }
}
async createBikeEventType (bikeEventType: any) { async createBikeEventType (bikeEventType: any) {
return (await this.connection.getRepository(BikeEventType) return (await this.connection.getRepository(BikeEventType)
@ -267,22 +281,12 @@ export class CargoBikeAPI extends DataSource {
return await this.bikeEventTypeById(bikeEventType.id); return await this.bikeEventTypeById(bikeEventType.id);
} }
async bikeEventTypes (offset: number, limit: number) { async bikeEventTypes (offset?: number, limit?: number) {
return await this.connection.getRepository(BikeEventType) return await DBUtils.getAllEntity(this.connection, BikeEventType, 'bet', offset, limit);
.createQueryBuilder('bet')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async bikeEvents (offset: number, limit: number) { async bikeEvents (offset?: number, limit?: number) {
return await this.connection.getRepository(BikeEvent) return await DBUtils.getAllEntity(this.connection, BikeEvent, 'be', offset, limit);
.createQueryBuilder('be')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async bikeEventTypeById (id: number) { async bikeEventTypeById (id: number) {
@ -343,12 +347,22 @@ export class CargoBikeAPI extends DataSource {
* @param limit * @param limit
* @param id * @param id
*/ */
async equipmentByCargoBikeId (offset: number, limit: number, id: number) { async equipmentByCargoBikeId (id: number, offset?: number, limit?: number) {
if (offset == null || limit === null) {
return await this.connection.getRepository(Equipment) return await this.connection.getRepository(Equipment)
.createQueryBuilder('equipment') .createQueryBuilder('equipment')
.select() .select()
.where('equipment."cargoBikeId" = :id', { id: id }) .where('equipment."cargoBikeId" = :id', { id: id })
.getMany(); .getMany();
} else {
return await this.connection.getRepository(Equipment)
.createQueryBuilder('equipment')
.select()
.where('equipment."cargoBikeId" = :id', { id: id })
.skip(offset)
.take(limit)
.getMany();
}
} }
async createEquipment ({ equipment }: { equipment: any }) { async createEquipment ({ equipment }: { equipment: any }) {
@ -406,17 +420,11 @@ export class CargoBikeAPI extends DataSource {
} }
async deleteEquipment (id: number, userId: number) { async deleteEquipment (id: number, userId: number) {
return await deleteEntity(this.connection, Equipment, 'e', id, userId); return await DBUtils.deleteEntity(this.connection, Equipment, 'e', id, userId);
} }
async getEquipment (offset: number, limit: number) { async getEquipment (offset?: number, limit?: number) {
return await this.connection.getRepository(Equipment) return await DBUtils.getAllEntity(this.connection, Equipment, 'e', offset, limit);
.createQueryBuilder('equipment')
.leftJoinAndSelect('equipment.cargoBike', 'cargoBike')
.orderBy('title', 'ASC')
.offset(offset)
.limit(limit)
.getMany();
} }
async createEquipmentType (equipmentType: any) { async createEquipmentType (equipmentType: any) {
@ -460,7 +468,7 @@ export class CargoBikeAPI extends DataSource {
} }
async deleteEquipmentType (id:number, userId: number) { async deleteEquipmentType (id:number, userId: number) {
return await deleteEntity(this.connection, EquipmentType, 'et', id, userId); return await DBUtils.deleteEntity(this.connection, EquipmentType, 'et', id, userId);
} }
async equipmentTypeById (id: number) { async equipmentTypeById (id: number) {
@ -471,13 +479,8 @@ export class CargoBikeAPI extends DataSource {
.getOne(); .getOne();
} }
async equipmentTypes (offset: number, limit: number) { async equipmentTypes (offset?: number, limit?: number) {
return await this.connection.getRepository(EquipmentType) return await DBUtils.getAllEntity(this.connection, EquipmentType, 'et', offset, limit);
.createQueryBuilder('et')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async equipmentTypeByCargoBikeId (id: number) { async equipmentTypeByCargoBikeId (id: number) {

@ -21,7 +21,7 @@ import { DataSource } from 'apollo-datasource';
import { Connection, EntityManager, getConnection } from 'typeorm'; import { Connection, EntityManager, getConnection } from 'typeorm';
import { ContactInformation } from '../../model/ContactInformation'; import { ContactInformation } from '../../model/ContactInformation';
import { Person } from '../../model/Person'; import { Person } from '../../model/Person';
import { ActionLogger, deleteEntity, LockUtils } from './utils'; import { ActionLogger, DBUtils, LockUtils } from './utils';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { LendingStation } from '../../model/LendingStation'; import { LendingStation } from '../../model/LendingStation';
@ -32,13 +32,8 @@ export class ContactInformationAPI extends DataSource {
this.connection = getConnection(); this.connection = getConnection();
} }
async contactInformation (offset: number, limit: number) { async contactInformation (offset?: number, limit?: number) {
return await this.connection.getRepository(ContactInformation) return await DBUtils.getAllEntity(this.connection, ContactInformation, 'ci', offset, limit);
.createQueryBuilder('ci')
.select()
.offset(offset)
.limit(limit)
.getMany();
} }
async contactInformationById (id: number) { async contactInformationById (id: number) {
@ -88,16 +83,11 @@ export class ContactInformationAPI extends DataSource {
} }
async deletePerson (id: number, userId: number) { async deletePerson (id: number, userId: number) {
return await deleteEntity(this.connection, Person, 'p', id, userId); return await DBUtils.deleteEntity(this.connection, Person, 'p', id, userId);
} }
async persons (offset: number, limit: number) { async persons (offset?: number, limit?: number) {
return await this.connection.getRepository(Person) return await DBUtils.getAllEntity(this.connection, Person, 'p', offset, limit);
.createQueryBuilder('person')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async personById (id: number) { async personById (id: number) {
@ -171,7 +161,7 @@ export class ContactInformationAPI extends DataSource {
} }
async deleteContactInformation (id: number, userId: number) { async deleteContactInformation (id: number, userId: number) {
return await deleteEntity(this.connection, ContactInformation, 'ci', id, userId); return await DBUtils.deleteEntity(this.connection, ContactInformation, 'ci', id, userId);
} }
async contactInformationByPersonId (id: number) { async contactInformationByPersonId (id: number) {

@ -19,12 +19,11 @@ This file is part of fLotte-API-Server.
import { DataSource } from 'apollo-datasource'; import { DataSource } from 'apollo-datasource';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
import { GraphQLError } from 'graphql';
import { Connection, EntityManager, getConnection } from 'typeorm'; import { Connection, EntityManager, getConnection } from 'typeorm';
import { CargoBike } from '../../model/CargoBike'; import { CargoBike } from '../../model/CargoBike';
import { LendingStation } from '../../model/LendingStation'; import { LendingStation } from '../../model/LendingStation';
import { TimeFrame } from '../../model/TimeFrame'; import { TimeFrame } from '../../model/TimeFrame';
import { ActionLogger, deleteEntity, genDateRange, LockUtils } from './utils'; import { ActionLogger, genDateRange, DBUtils, LockUtils } from './utils';
export class LendingStationAPI extends DataSource { export class LendingStationAPI extends DataSource {
connection : Connection connection : Connection
@ -44,14 +43,8 @@ export class LendingStationAPI extends DataSource {
/** /**
* get all lendingStations * get all lendingStations
*/ */
async lendingStations (offset: number, limit: number) { async lendingStations (offset?: number, limit?: number) {
return await this.connection.getRepository(LendingStation) return await DBUtils.getAllEntity(this.connection, LendingStation, 'ls', offset, limit);
.createQueryBuilder('lendingStation')
.select()
.offset(offset)
.limit(limit)
.orderBy('name', 'ASC')
.getMany() || new GraphQLError('Internal Server Error: could not query data from table lendingStation');
} }
/** /**
@ -79,13 +72,8 @@ export class LendingStationAPI extends DataSource {
.loadOne(); .loadOne();
} }
async timeFrames (offset: number, limit: number) { async timeFrames (offset?: number, limit?: number) {
return await this.connection.getRepository(TimeFrame) return await DBUtils.getAllEntity(this.connection, TimeFrame, 'tf', offset, limit);
.createQueryBuilder('timeframe')
.select()
.offset(offset)
.limit(limit)
.getMany() || [];
} }
async timeFramesByCargoBikeId (id: number) { async timeFramesByCargoBikeId (id: number) {
@ -190,15 +178,12 @@ export class LendingStationAPI extends DataSource {
} }
async deleteLendingStationById (id: number, userId: number) { async deleteLendingStationById (id: number, userId: number) {
return await deleteEntity(this.connection, LendingStation, 'ls', id, userId); return await DBUtils.deleteEntity(this.connection, LendingStation, 'ls', id, userId);
} }
async createTimeFrame (timeFrame: any) { async createTimeFrame (timeFrame: any) {
return await this.connection.transaction(async (entityManager: EntityManager) => { return await this.connection.transaction(async (entityManager: EntityManager) => {
if (timeFrame.to === undefined) { genDateRange(timeFrame);
timeFrame.to = '';
}
timeFrame.dateRange = '[' + timeFrame.from + ',' + timeFrame.to + ')';
// checking for overlapping time frames // checking for overlapping time frames
const overlapping = await entityManager.getRepository(TimeFrame) const overlapping = await entityManager.getRepository(TimeFrame)
.createQueryBuilder('timeframe') .createQueryBuilder('timeframe')
@ -268,6 +253,6 @@ export class LendingStationAPI extends DataSource {
} }
async deleteTimeFrame (id: number, userId: number) { async deleteTimeFrame (id: number, userId: number) {
return await deleteEntity(this.connection, TimeFrame, 'tf', id, userId); return await DBUtils.deleteEntity(this.connection, TimeFrame, 'tf', id, userId);
} }
} }

@ -23,7 +23,7 @@ import { ContactInformation } from '../../model/ContactInformation';
import { Engagement } from '../../model/Engagement'; import { Engagement } from '../../model/Engagement';
import { Participant } from '../../model/Participant'; import { Participant } from '../../model/Participant';
import { EngagementType } from '../../model/EngagementType'; import { EngagementType } from '../../model/EngagementType';
import { ActionLogger, deleteEntity, genDateRange, LockUtils } from './utils'; import { ActionLogger, DBUtils, genDateRange, LockUtils } from './utils';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
@ -42,13 +42,8 @@ export class ParticipantAPI extends DataSource {
.getOne(); .getOne();
} }
async getParticipants (offset: number, limit: number) { async getParticipants (offset?: number, limit?: number) {
return await this.connection.getRepository(Participant) return await DBUtils.getAllEntity(this.connection, Participant, 'p', offset, limit);
.createQueryBuilder('participant')
.select()
.offset(offset)
.limit(limit)
.getMany();
} }
async participantByEngagementId (id: number) { async participantByEngagementId (id: number) {
@ -84,18 +79,25 @@ export class ParticipantAPI extends DataSource {
.getMany(); .getMany();
} }
async engagementByCargoBikeId (offset: number, limit: number, id: number) { async engagementByCargoBikeId (id: number, offset?: number, limit?: number) {
if (limit === null || offset === null) {
return await this.connection.getRepository(Engagement)
.createQueryBuilder('engagement')
.select()
.where('engagement."cargoBikeId" = :id', { id: id })
.orderBy('engagement."dateRange"', 'DESC')
.getMany();
} else {
return await this.connection.getRepository(Engagement) return await this.connection.getRepository(Engagement)
.createQueryBuilder('engagement') .createQueryBuilder('engagement')
.select() .select()
.where('engagement."cargoBikeId" = :id', { .where('engagement."cargoBikeId" = :id', { id: id })
id: id
})
.skip(offset) .skip(offset)
.take(limit) .take(limit)
.orderBy('engagement."dateRange"', 'DESC') .orderBy('engagement."dateRange"', 'DESC')
.getMany(); .getMany();
} }
}
async currentEngagementByCargoBikeId (id: number) { async currentEngagementByCargoBikeId (id: number) {
return await this.connection.getRepository(Engagement) return await this.connection.getRepository(Engagement)
@ -107,13 +109,8 @@ export class ParticipantAPI extends DataSource {
.getMany(); .getMany();
} }
async engagements (offset: number, limit: number) { async engagements (offset?: number, limit?: number) {
return await this.connection.getRepository(Engagement) return await DBUtils.getAllEntity(this.connection, Engagement, 'e', offset, limit);
.createQueryBuilder('e')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async engagementById (id: number) { async engagementById (id: number) {
@ -132,13 +129,8 @@ export class ParticipantAPI extends DataSource {
.getOne(); .getOne();
} }
async engagementTypes (offset: number, limit: number) { async engagementTypes (offset?: number, limit?: number) {
return await this.connection.getRepository(EngagementType) return await DBUtils.getAllEntity(this.connection, EngagementType, 'et', offset, limit);
.createQueryBuilder('et')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async engagementTypeByEngagementId (id: number) { async engagementTypeByEngagementId (id: number) {
@ -178,6 +170,7 @@ export class ParticipantAPI extends DataSource {
* @param participant to be created * @param participant to be created
*/ */
async createParticipant (participant: any) { async createParticipant (participant: any) {
genDateRange(participant);
let inserts: any; let inserts: any;
await this.connection.transaction(async (entityManager: EntityManager) => { await this.connection.transaction(async (entityManager: EntityManager) => {
inserts = await entityManager.getRepository(Participant) inserts = await entityManager.getRepository(Participant)
@ -187,7 +180,7 @@ export class ParticipantAPI extends DataSource {
.values([participant]) .values([participant])
.returning('*') .returning('*')
.execute(); .execute();
await entityManager.getRepository(Participant) participant.workshopIds && await entityManager.getRepository(Participant)
.createQueryBuilder('w') .createQueryBuilder('w')
.relation(Participant, 'workshopIds') .relation(Participant, 'workshopIds')
.of(participant.id) .of(participant.id)
@ -209,8 +202,9 @@ export class ParticipantAPI extends DataSource {
delete participant.keepLock; delete participant.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => { await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, Participant, 'p', participant.id, userId)) { if (await LockUtils.isLocked(entityManager, Participant, 'p', participant.id, userId)) {
throw new GraphQLError('Participant is locked by another user'); throw new UserInputError('Attempting to update locked resource');
} }
genDateRange(participant);
const workshops = participant.workshopIds; const workshops = participant.workshopIds;
delete participant.workshopIds; delete participant.workshopIds;
await ActionLogger.log(entityManager, Participant, 'p', participant, userId); await ActionLogger.log(entityManager, Participant, 'p', participant, userId);
@ -219,8 +213,19 @@ export class ParticipantAPI extends DataSource {
.update() .update()
.set({ ...participant }) .set({ ...participant })
.where('id = :id', { id: participant.id }) .where('id = :id', { id: participant.id })
.execute().then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } }); .execute().then(value => { if (value.affected !== 1) { throw new UserInputError('ID not found'); } });
await entityManager.getRepository(Participant) // check for engagements before or after dateRange
const engagements = await entityManager.getRepository(Engagement)
.createQueryBuilder('e')
.select()
.where('e."participantId" = :pid', { pid: participant.id })
.andWhere('not :pdr @> e."dateRange"', { pdr: participant.dateRange })
.getMany();
if (engagements.length !== 0) {
throw new UserInputError('Engagements with ids: ' + engagements.map((e) => { return `${e.id} ,`; }) + ' are are outside of dataRange');
}
// add and remove workshop relations
workshops && await entityManager.getRepository(Participant)
.createQueryBuilder('w') .createQueryBuilder('w')
.relation(Participant, 'workshopIds') .relation(Participant, 'workshopIds')
.of(participant.id) .of(participant.id)
@ -231,7 +236,7 @@ export class ParticipantAPI extends DataSource {
} }
async deleteParticipant (id: number, userId: number) { async deleteParticipant (id: number, userId: number) {
return await deleteEntity(this.connection, Participant, 'p', id, userId); return await DBUtils.deleteEntity(this.connection, Participant, 'p', id, userId);
} }
async createEngagement (engagement: any) { async createEngagement (engagement: any) {
@ -249,6 +254,16 @@ export class ParticipantAPI extends DataSource {
if (overlapping.length > 0) { if (overlapping.length > 0) {
throw new UserInputError('Engagements with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping'); throw new UserInputError('Engagements with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping');
} }
// check if participant is active
const participant = await entityManager.getRepository(Participant)
.createQueryBuilder('p')
.select()
.where('p.id = :pid', { pid: engagement.participantId })
.andWhere('not p."dateRange" @> :edr', { edr: engagement.dateRange })
.getOne();
if (participant) {
throw new UserInputError('Participant ist not active in the specified dateRange');
}
inserts = await entityManager.getRepository(Engagement) inserts = await entityManager.getRepository(Engagement)
.createQueryBuilder('engagement') .createQueryBuilder('engagement')
.insert() .insert()
@ -288,6 +303,20 @@ export class ParticipantAPI extends DataSource {
if (overlapping.length > 0) { if (overlapping.length > 0) {
throw new UserInputError('Engagements with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping'); throw new UserInputError('Engagements with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping');
} }
// check if participant is active
if (engagement.dateRange && engagement.participantId) {
const participant = await entityManager.getRepository(Participant)
.createQueryBuilder('p')
.select()
.where('p.id = :pid', { pid: engagement.participantId })
.andWhere('not p."dateRange" @> :edr', { edr: engagement.dateRange })
.getOne();
if (participant) {
throw new UserInputError('Participant ist not active in the specified dateRange');
}
} else if (engagement.dateRange || engagement.dateRange) {
throw new UserInputError('Please specify participantId adn the dateRange');
}
await entityManager.getRepository(Engagement) await entityManager.getRepository(Engagement)
.createQueryBuilder('engagement') .createQueryBuilder('engagement')
.update() .update()
@ -300,7 +329,7 @@ export class ParticipantAPI extends DataSource {
} }
async deleteEngagement (id: number, userId: number) { async deleteEngagement (id: number, userId: number) {
return await deleteEntity(this.connection, Engagement, 'e', id, userId); return await DBUtils.deleteEntity(this.connection, Engagement, 'e', id, userId);
} }
async createEngagementType (engagementType: any) { async createEngagementType (engagementType: any) {
@ -342,6 +371,6 @@ export class ParticipantAPI extends DataSource {
} }
async deleteEngagementType (id: number, userId: number) { async deleteEngagementType (id: number, userId: number) {
return await deleteEntity(this.connection, EngagementType, 'et', id, userId); return await DBUtils.deleteEntity(this.connection, EngagementType, 'et', id, userId);
} }
} }

@ -24,7 +24,7 @@ import { Organisation } from '../../model/Organisation';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
import { CargoBike } from '../../model/CargoBike'; import { CargoBike } from '../../model/CargoBike';
import { LendingStation } from '../../model/LendingStation'; import { LendingStation } from '../../model/LendingStation';
import { ActionLogger, deleteEntity, LockUtils } from './utils'; import { ActionLogger, DBUtils, LockUtils } from './utils';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
export class ProviderAPI extends DataSource { export class ProviderAPI extends DataSource {
@ -42,13 +42,8 @@ export class ProviderAPI extends DataSource {
.getOne(); .getOne();
} }
async provider (offset: number, limit: number) { async provider (offset?: number, limit?: number) {
return await this.connection.getRepository(Provider) return await DBUtils.getAllEntity(this.connection, Provider, 'p', offset, limit);
.createQueryBuilder('provider')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async providerByOrganisationId (id: number) { async providerByOrganisationId (id: number) {
@ -75,13 +70,8 @@ export class ProviderAPI extends DataSource {
.loadOne(); .loadOne();
} }
async organisations (offset: number, limit: number) { async organisations (offset?: number, limit?: number) {
return await this.connection.getRepository(Organisation) return await DBUtils.getAllEntity(this.connection, Organisation, 'o', offset, limit);
.createQueryBuilder('o')
.select()
.skip(offset)
.limit(limit)
.getMany();
} }
async organisationById (id: number) { async organisationById (id: number) {
@ -180,7 +170,7 @@ export class ProviderAPI extends DataSource {
} }
async deleteProvider (id: number, userId: number) { async deleteProvider (id: number, userId: number) {
return await deleteEntity(this.connection, Provider, 'p', id, userId); return await DBUtils.deleteEntity(this.connection, Provider, 'p', id, userId);
} }
async createOrganisation (organisation: any) { async createOrganisation (organisation: any) {
@ -223,6 +213,6 @@ export class ProviderAPI extends DataSource {
} }
async deleteOrganisation (id: number, userId: number) { async deleteOrganisation (id: number, userId: number) {
return await deleteEntity(this.connection, Organisation, 'o', id, userId); return await DBUtils.deleteEntity(this.connection, Organisation, 'o', id, userId);
} }
} }

@ -23,34 +23,81 @@ import { ActionLog, Actions } from '../../model/ActionLog';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
export function genDateRange (struct: any) { export function genDateRange (struct: any) {
if (struct.to === undefined) { if (!struct.dateRange || !struct.dateRange.from) {
struct.to = '';
}
struct.dateRange = '[' + struct.from + ',' + struct.to + ')';
if (struct.from === undefined) {
delete struct.dateRange; delete struct.dateRange;
return;
} else if (!struct.dateRange?.to) {
struct.dateRange.to = '';
} else if (struct.dateRange.to === struct.dateRange.from) {
throw new UserInputError('Date Range can not be empty, provide different dates.');
}
struct.dateRange = `[${struct.dateRange.from},${struct.dateRange.to})`;
} }
// delete these keys, so the struct can be used to update the engagement entity
delete struct.from; /**
delete struct.to; * This function helps prepare the cargoBike struct, to be used in an update or create.
* It creates the numrange attributes than can be understood by postgres.
* @param range
*/
function genNumRange (range: { min: number, max: number}) :string {
if (!range || (!range.max && !range.min)) {
return null;
} else if (range.min === null || range.min === undefined) {
range.min = range.max;
} else if (range.max === null || range.max === undefined) {
range.max = range.min;
}
if (range.min < 0) {
throw new UserInputError('Minimal value must be greater or equal to 0');
}
return `[${range.min},${range.max}]`;
}
/**
* This function prepares the cargoBike struct, to be used in an update or create.
* It creates the numrange attributes than can be understood by postgres.
* @param cargoBike
*/
export function genBoxDimensions (cargoBike: any) {
if (!cargoBike.dimensionsAndLoad) { return; }
cargoBike.dimensionsAndLoad.boxLengthRange = genNumRange(cargoBike.dimensionsAndLoad.boxLengthRange);
cargoBike.dimensionsAndLoad.boxWidthRange = genNumRange(cargoBike.dimensionsAndLoad.boxWidthRange);
cargoBike.dimensionsAndLoad.boxHeightRange = genNumRange(cargoBike.dimensionsAndLoad.boxHeightRange);
} }
/** /**
* Can be used in resolvers to specify if entry is locked by other user. * Can be used in resolvers to specify, if entry is locked by other user.
* Returns true if locked by other user. * Returns true if locked by other user.
* @param parent * @param parent
* @param dataSources * @param req
* @param req user request
*/ */
export function isLocked (parent: any, { req }: { req: any }) { export function isLocked (parent: any, { req }: { req: any }) {
return req.userId !== parent.lockedBy && new Date() <= new Date(parent.lockedUntil); return req.userId !== parent.lockedBy && new Date() <= new Date(parent.lockedUntil);
} }
/**
* Can be used in resolvers to specify, if entry is locked by the current user.
* @param parent
* @param req
*/
export function isLockedByMe (parent: any, { req }: { req: any }) { export function isLockedByMe (parent: any, { req }: { req: any }) {
return req.userId === parent.lockedBy && new Date() <= new Date(parent.lockedUntil); return req.userId === parent.lockedBy && new Date() <= new Date(parent.lockedUntil);
} }
export async function deleteEntity (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number): Promise<Boolean> { /**
* Some utility functions for the database
*/
export class DBUtils {
/**
* Delete any instance of an entity that implements the Lockable interface.
* It must implement the interface, so it can be be ensured, that the instance is not locked by another user.
* @param connection
* @param target
* @param alias
* @param id
* @param userId
*/
static async deleteEntity (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number): Promise<Boolean> {
return await connection.transaction(async (entityManger: EntityManager) => { return await connection.transaction(async (entityManger: EntityManager) => {
if (await LockUtils.isLocked(entityManger, target, alias, id, userId)) { if (await LockUtils.isLocked(entityManger, target, alias, id, userId)) {
throw new UserInputError('Attempting to delete locked resource'); throw new UserInputError('Attempting to delete locked resource');
@ -64,8 +111,48 @@ export async function deleteEntity (connection: Connection, target: ObjectType<L
}); });
} }
/**
* Return all instances of the given entity called target.
* When offset or limit is not specified, both values are ignored.
* @param connection
* @param target
* @param alias
* @param offset
* @param limit
*/
static async getAllEntity (connection: Connection, target: ObjectType<any>, alias: string, offset?: number, limit?: number) {
if (offset === null || limit === null) {
return await connection.getRepository(target)
.createQueryBuilder(alias)
.select()
.getMany();
} else {
return await connection.getRepository(target)
.createQueryBuilder(alias)
.select()
.skip(offset)
.take(limit)
.getMany();
}
}
}
/**
* Some static functions for the locking feature.
*/
export class LockUtils { export class LockUtils {
static async findById (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number): Promise<Lockable> { /**
* A helper function to find an instance of any entity that implements Lockable.
* It will throw an error, if nothing is found.
* Using this function only makes sense to use in the context of locking because there is no point in locking
* an instance that does not exist.
* @param connection
* @param target
* @param alias
* @param id
* @private
*/
private static async findById (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number): Promise<Lockable> {
return await connection.getRepository(target) return await connection.getRepository(target)
.createQueryBuilder(alias) .createQueryBuilder(alias)
.select() .select()
@ -75,6 +162,17 @@ export class LockUtils {
}); });
} }
/**
* Lock an instance of an entity target that implements the Lockable interface and return that instance.
* If lock could not be set, it will still return the entity.
* If lock was set or not can be obtained by the field isLockedByMe in the graphql interface,
* or the the fields lockedBy and lockedUntil in the database.
* @param connection
* @param target
* @param alias
* @param id
* @param userId
*/
static async lockEntity (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number): Promise<Lockable> { static async lockEntity (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number): Promise<Lockable> {
const lock = await connection.getRepository(target) const lock = await connection.getRepository(target)
.createQueryBuilder(alias) .createQueryBuilder(alias)
@ -103,6 +201,17 @@ export class LockUtils {
return await this.findById(connection, target, alias, id); return await this.findById(connection, target, alias, id);
} }
/**
* Unlock an instance of an entity target that implements the Lockable interface and return that instance.
* If lock could not be unset, it will still return the entity.
* If lock was set or not can be obtained by the field isLockedByMe in the graphql interface,
* or the the fields lockedBy and lockedUntil in the database.
* @param connection
* @param target
* @param alias
* @param id
* @param userId
*/
static async unlockEntity (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number): Promise<Lockable> { static async unlockEntity (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number): Promise<Lockable> {
await connection.getRepository(target) await connection.getRepository(target)
.createQueryBuilder(alias) .createQueryBuilder(alias)
@ -145,7 +254,18 @@ export class LockUtils {
} }
} }
/**
* Some utility function for the logging features.
*/
export class ActionLogger { export class ActionLogger {
/**
* Create array of strings, that can be used to select them form the database.
* If you want to avoid logging all old values, for an update, but only the ones that are updated,
* use this function. If updates are null, ['*'] will be returned. Use this for delete actions.
* @param updates
* @param alias
* @private
*/
private static buildSelect (updates: any, alias: string) : string[] { private static buildSelect (updates: any, alias: string) : string[] {
// this hacky shit makes it possible to select subfields like the address or insurance data. Only one layer at the moment // this hacky shit makes it possible to select subfields like the address or insurance data. Only one layer at the moment
if (updates === null) { if (updates === null) {
@ -156,15 +276,25 @@ export class ActionLogger {
// sometimes updates[value] is an array, e.g. timePeriods that are saved as a simple array in postgres // sometimes updates[value] is an array, e.g. timePeriods that are saved as a simple array in postgres
if (updates[value] && typeof updates[value] === 'object' && !Array.isArray(updates[value])) { if (updates[value] && typeof updates[value] === 'object' && !Array.isArray(updates[value])) {
Object.keys(updates[value]).forEach(subValue => { Object.keys(updates[value]).forEach(subValue => {
ret.push(alias + '."' + value + subValue[0].toUpperCase() + subValue.substr(1).toLowerCase() + '"'); ret.push(`${alias}."${value}${subValue[0].toUpperCase()}${subValue.substr(1).toLowerCase()}"`);
}); });
} else { } else {
ret.push(alias + '."' + value + '"'); ret.push(`${alias}."${value}"`);
} }
}); });
return ret; return ret;
} }
/**
* Insert an entry in the log. The log ist just another entity in the database.
* You can only use this in a transaction. So you have to pass an entity manager.
* @param em
* @param target
* @param alias
* @param updates
* @param userId
* @param action
*/
static async log (em: EntityManager, target: ObjectType<any>, alias: string, updates: any, userId: number, action: Actions = Actions.UPDATE) { static async log (em: EntityManager, target: ObjectType<any>, alias: string, updates: any, userId: number, action: Actions = Actions.UPDATE) {
const oldValues = await em.getRepository(target).createQueryBuilder(alias) const oldValues = await em.getRepository(target).createQueryBuilder(alias)
.select(this.buildSelect(updates, alias)) .select(this.buildSelect(updates, alias))

@ -21,7 +21,7 @@ import { DataSource } from 'apollo-datasource';
import { Connection, EntityManager, getConnection } from 'typeorm'; import { Connection, EntityManager, getConnection } from 'typeorm';
import { WorkshopType } from '../../model/WorkshopType'; import { WorkshopType } from '../../model/WorkshopType';
import { Workshop } from '../../model/Workshop'; import { Workshop } from '../../model/Workshop';
import { ActionLogger, deleteEntity, LockUtils } from './utils'; import { ActionLogger, DBUtils, LockUtils } from './utils';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { Participant } from '../../model/Participant'; import { Participant } from '../../model/Participant';
@ -73,7 +73,7 @@ export class WorkshopAPI extends DataSource {
} }
async deleteWorkshop (id: number, userId: number) { async deleteWorkshop (id: number, userId: number) {
return await deleteEntity(this.connection, Workshop, 'w', id, userId); return await DBUtils.deleteEntity(this.connection, Workshop, 'w', id, userId);
} }
async createWorkshopType (workshopType: any) { async createWorkshopType (workshopType: any) {
@ -115,7 +115,7 @@ export class WorkshopAPI extends DataSource {
} }
async deleteWorkshopType (id: number, userId: number) { async deleteWorkshopType (id: number, userId: number) {
return await deleteEntity(this.connection, WorkshopType, 'wt', id, userId); return await DBUtils.deleteEntity(this.connection, WorkshopType, 'wt', id, userId);
} }
async workshopTypeById (id: number) { async workshopTypeById (id: number) {
@ -126,13 +126,8 @@ export class WorkshopAPI extends DataSource {
.getOne(); .getOne();
} }
async workshopTypes (offset: number, limit: number) { async workshopTypes (offset?: number, limit?: number) {
return await this.connection.getRepository(WorkshopType) return DBUtils.getAllEntity(this.connection, WorkshopType, 'wt', offset, limit);
.createQueryBuilder('w')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async workshopById (id: number) { async workshopById (id: number) {
@ -148,13 +143,8 @@ export class WorkshopAPI extends DataSource {
* @param offset * @param offset
* @param limit * @param limit
*/ */
async workshops (offset: number, limit: number) { async workshops (offset?: number, limit?: number) {
return await this.connection.getRepository(Workshop) return await DBUtils.getAllEntity(this.connection, Workshop, 'w', offset, limit);
.createQueryBuilder('w')
.select()
.skip(offset)
.take(limit)
.getMany();
} }
async trainer1ByWorkshopId (id: number) { async trainer1ByWorkshopId (id: number) {

@ -30,7 +30,6 @@ import {
DeleteDateColumn DeleteDateColumn
} from 'typeorm'; } from 'typeorm';
import { Provider } from './Provider'; import { Provider } from './Provider';
import { Participant } from './Participant';
import { InsuranceData } from './InsuranceData'; import { InsuranceData } from './InsuranceData';
import { TimeFrame } from './TimeFrame'; import { TimeFrame } from './TimeFrame';
import { Taxes } from './Taxes'; import { Taxes } from './Taxes';
@ -66,7 +65,9 @@ export interface Lockable {
} }
export class Security { export class Security {
@Column() @Column({
nullable: true
})
frameNumber: string; frameNumber: string;
@Column({ @Column({
@ -90,13 +91,19 @@ export class Security {
adfcCoding: string; adfcCoding: string;
} }
export class TechnicalEquipment { export class TechnicalEquipment {
@Column() @Column({
nullable: true
})
bicycleShift: string; bicycleShift: string;
@Column() @Column({
nullable: true
})
isEBike: boolean; isEBike: boolean;
@Column() @Column({
nullable: true
})
hasLightSystem: boolean; hasLightSystem: boolean;
@Column({ @Column({
@ -106,44 +113,55 @@ export class TechnicalEquipment {
} }
export class DimensionsAndLoad { export class DimensionsAndLoad {
@Column() @Column({
nullable: true
})
hasCoverBox: boolean; hasCoverBox: boolean;
@Column() @Column({
nullable: true
})
lockable:boolean; lockable:boolean;
@Column({ @Column({
type: 'decimal' type: 'numrange',
nullable: true
}) })
boxLength: number; boxLengthRange: string;
@Column({ @Column({
type: 'decimal' type: 'numrange',
nullable: true
}) })
boxWidth: number; boxWidthRange: string;
@Column({ @Column({
type: 'decimal' type: 'numrange',
nullable: true
}) })
boxHeight: number; boxHeightRange: string;
@Column({ @Column({
type: 'decimal' type: 'decimal',
nullable: true
}) })
maxWeightBox: number; maxWeightBox: string;
@Column({ @Column({
type: 'decimal' type: 'decimal',
nullable: true
}) })
maxWeightLuggageRack: number; maxWeightLuggageRack: number;
@Column({ @Column({
type: 'decimal' type: 'decimal',
nullable: true
}) })
maxWeightTotal: number; maxWeightTotal: number;
@Column({ @Column({
type: 'decimal' type: 'decimal',
nullable: true
}) })
bikeLength: number; bikeLength: number;
@ -174,23 +192,27 @@ export class CargoBike implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@DeleteDateColumn()
deleteDate: Date;
@Column({ @Column({
type: 'enum', type: 'enum',
enum: Group enum: Group
}) })
group: Group; group: Group;
@Column() @Column({
unique: true
})
name: string; name: string;
@Column({
nullable: true
})
state: string;
@OneToMany(type => Equipment, equipment => equipment.cargoBikeId, { @OneToMany(type => Equipment, equipment => equipment.cargoBikeId, {
nullable: true, nullable: true,
eager: true eager: true
}) })
equipment: Equipment[]; equipmentIds: number[];
// Equipment that is not unique and is supposed to be selected out of a list e.g. drop down // Equipment that is not unique and is supposed to be selected out of a list e.g. drop down
@ManyToMany(type => EquipmentType, equipmentType => equipmentType.cargoBikeIds) @ManyToMany(type => EquipmentType, equipmentType => equipmentType.cargoBikeIds)
@ -245,19 +267,32 @@ export class CargoBike implements Lockable {
}) })
description: string; description: string;
@Column() @Column({
nullable: true
})
modelName: string; modelName: string;
@Column() @Column({
nullable: true
})
numberOfWheels: number; numberOfWheels: number;
@Column() @Column({
type: 'boolean',
nullable: true
})
forCargo: boolean; forCargo: boolean;
@Column() @Column({
type: 'boolean',
nullable: true
})
forChildren: boolean; forChildren: boolean;
@Column() @Column({
type: 'int',
nullable: true
})
numberOfChildren: number; numberOfChildren: number;
@Column(type => TechnicalEquipment) @Column(type => TechnicalEquipment)
@ -267,6 +302,7 @@ export class CargoBike implements Lockable {
dimensionsAndLoad: DimensionsAndLoad; dimensionsAndLoad: DimensionsAndLoad;
@Column({ @Column({
type: 'int',
nullable: true nullable: true
}) })
lockedBy: number; lockedBy: number;

@ -42,9 +42,7 @@ export class ContactInformation implements Lockable {
}) })
participantId: number; participantId: number;
@Column(type => { @Column(type => { return Address; })
return Address;
})
address: Address; address: Address;
@Column({ @Column({

@ -26,7 +26,9 @@ export class EngagementType implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column() @Column({
unique: true
})
name: string; name: string;
@Column({ @Column({

@ -26,7 +26,9 @@ export class Equipment implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column() @Column({
unique: true
})
serialNo: string; serialNo: string;
@Column() @Column()
@ -37,7 +39,7 @@ export class Equipment implements Lockable {
}) })
description: string; description: string;
@ManyToOne(type => CargoBike, cargoBike => cargoBike.equipment, { @ManyToOne(type => CargoBike, cargoBike => cargoBike.equipmentIds, {
nullable: true nullable: true
}) })
@JoinColumn({ @JoinColumn({

@ -25,7 +25,9 @@ export class EquipmentType implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column() @Column({
unique: true
})
name: string; name: string;
@Column({ @Column({

@ -20,22 +20,34 @@ This file is part of fLotte-API-Server.
import { Column } from 'typeorm'; import { Column } from 'typeorm';
export class InsuranceData { export class InsuranceData {
@Column() @Column({
nullable: true
})
name: string; name: string;
@Column() @Column({
nullable: true
})
benefactor: string; benefactor: string;
@Column() @Column({
nullable: true
})
billing: string; billing: string;
@Column() @Column({
nullable: true
})
noPnP: string; noPnP: string;
@Column() @Column({
nullable: true
})
maintenanceResponsible: string; maintenanceResponsible: string;
@Column() @Column({
nullable: true
})
maintenanceBenefactor: string; maintenanceBenefactor: string;
@Column({ @Column({

@ -34,6 +34,12 @@ export class LoanPeriod {
}) })
from: Date; from: Date;
@Column({
nullable: true,
type: 'text'
})
generalRemark: string;
/** /**
* validity for loanPeriods * validity for loanPeriods
*/ */
@ -44,10 +50,39 @@ export class LoanPeriod {
to: Date; to: Date;
@Column({ @Column({
type: 'simple-array',
nullable: true nullable: true
}) })
loanTimes: string[]; mo: string;
@Column({
nullable: true
})
tu: string;
@Column({
nullable: true
})
we: string;
@Column({
nullable: true
})
th: string;
@Column({
nullable: true
})
fr: string;
@Column({
nullable: true
})
sa: string;
@Column({
nullable: true
})
su: string;
} }
@Entity() @Entity()

@ -28,7 +28,9 @@ export class Organisation implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column() @Column({
unique: true
})
name: string; name: string;
@OneToMany(type => LendingStation, lendingStation => lendingStation.organisationId) @OneToMany(type => LendingStation, lendingStation => lendingStation.organisationId)

@ -29,16 +29,9 @@ export class Participant implements Lockable {
id: number; id: number;
@Column({ @Column({
type: 'date', type: 'daterange'
default: () => 'CURRENT_DATE'
}) })
start: Date; dateRange: Date[];
@Column({
type: 'date',
nullable: true
})
end: Date;
@OneToOne(type => ContactInformation, contactInformation => contactInformation.participantId, { @OneToOne(type => ContactInformation, contactInformation => contactInformation.participantId, {
nullable: false nullable: false

@ -18,10 +18,11 @@ This file is part of fLotte-API-Server.
*/ */
import { Lockable } from './CargoBike'; import { Lockable } from './CargoBike';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; import { Column, Entity, OneToMany, PrimaryGeneratedColumn, Unique } from 'typeorm';
import { ContactInformation } from './ContactInformation'; import { ContactInformation } from './ContactInformation';
@Entity() @Entity()
@Unique(['firstName', 'name'])
export class Person implements Lockable { export class Person implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;

@ -25,7 +25,9 @@ export enum OrganisationArea {
ZB = 'ZB' ZB = 'ZB'
} }
export class Taxes { export class Taxes {
@Column() @Column({
nullable: true
})
costCenter: string; costCenter: string;
@Column({ @Column({

@ -26,7 +26,9 @@ export class WorkshopType implements Lockable {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column() @Column({
unique: true
})
name: string; name: string;
@OneToMany(type => Workshop, workshop => workshop.workshopTypeId) @OneToMany(type => Workshop, workshop => workshop.workshopTypeId)

@ -30,14 +30,14 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
cargoBikes: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { cargoBikes: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadBike)) { if (req.permissions.includes(Permission.ReadBike)) {
return dataSources.cargoBikeAPI.getCargoBikes(offset, limit); return dataSources.cargoBikeAPI.getCargoBikes(offset, limit);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
}, },
bikeEvents: (_:any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { bikeEvents: (_:any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadBikeEvent)) { if (req.permissions.includes(Permission.ReadBikeEvent)) {
return dataSources.cargoBikeAPI.bikeEvents(offset, limit); return dataSources.cargoBikeAPI.bikeEvents(offset, limit);
} else { } else {
@ -58,14 +58,14 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
bikeEventTypes: (_:any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { bikeEventTypes: (_:any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadBikeEvent)) { if (req.permissions.includes(Permission.ReadBikeEvent)) {
return dataSources.cargoBikeAPI.bikeEventTypes(offset, limit); return dataSources.cargoBikeAPI.bikeEventTypes(offset, limit);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
}, },
equipment: (_:any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { equipment: (_:any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadEquipment)) { if (req.permissions.includes(Permission.ReadEquipment)) {
return dataSources.cargoBikeAPI.getEquipment(offset, limit); return dataSources.cargoBikeAPI.getEquipment(offset, limit);
} else { } else {
@ -79,7 +79,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
equipmentTypes: (_:any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { equipmentTypes: (_:any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadEquipment)) { if (req.permissions.includes(Permission.ReadEquipment)) {
return dataSources.cargoBikeAPI.equipmentTypes(offset, limit); return dataSources.cargoBikeAPI.equipmentTypes(offset, limit);
} else { } else {
@ -102,23 +102,23 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
engagement (parent: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) { engagement (parent: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadEngagement)) { if (req.permissions.includes(Permission.ReadEngagement)) {
return dataSources.participantAPI.engagementByCargoBikeId(offset, limit, parent.id); return dataSources.participantAPI.engagementByCargoBikeId(parent.id, offset, limit);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
}, },
participants (parent: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) { // TODO should be done with engagements participants (parent: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) { // TODO should be done with engagements
if (req.permissions.includes(Permission.ReadParticipant)) { if (req.permissions.includes(Permission.ReadParticipant)) {
return dataSources.participantAPI.participantsByCargoBikeId(parent.id); return dataSources.participantAPI.participantsByCargoBikeId(parent.id);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
}, },
equipment (parent: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) { equipment (parent: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadEquipment)) { if (req.permissions.includes(Permission.ReadEquipment)) {
return dataSources.cargoBikeAPI.equipmentByCargoBikeId(offset, limit, parent.id); return dataSources.cargoBikeAPI.equipmentByCargoBikeId(parent.id, offset, limit);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
@ -130,15 +130,13 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
bikeEvents (parent: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) { bikeEvents (parent: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadBikeEvent)) { if (req.permissions.includes(Permission.ReadBikeEvent)) {
return dataSources.cargoBikeAPI.bikeEventsByCargoBikeId(parent.id, offset, limit); return dataSources.cargoBikeAPI.bikeEventsByCargoBikeId(parent.id, offset, limit);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
}, },
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req }),
timeFrames (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { timeFrames (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadTimeFrame)) { if (req.permissions.includes(Permission.ReadTimeFrame)) {
return dataSources.lendingStationAPI.timeFramesByCargoBikeId(parent.id); return dataSources.lendingStationAPI.timeFramesByCargoBikeId(parent.id);
@ -159,8 +157,17 @@ export default {
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
},
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req })
},
NumRange: {
min: (parent: string) => {
return parent.replace(/^\[(.*),.*]$/, '$1');
},
max: (parent: string) => {
return parent.replace(/^\[.*,(.*)]$/, '$1');
} }
}, },
Equipment: { Equipment: {
cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
@ -173,6 +180,10 @@ export default {
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }), isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req }) isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req })
}, },
EquipmentType: {
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req })
},
BikeEvent: { BikeEvent: {
cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadBike)) { if (req.permissions.includes(Permission.ReadBike)) {
@ -212,7 +223,7 @@ export default {
Mutation: { 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)) { if (req.permissions.includes(Permission.WriteBike)) {
return dataSources.cargoBikeAPI.createCargoBike({ cargoBike }); return dataSources.cargoBikeAPI.createCargoBike(cargoBike);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }

@ -24,7 +24,7 @@ import { PermissionError } from '../errors/PermissionError';
export default { export default {
Query: { Query: {
contactInformation: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { contactInformation: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadPerson)) { if (req.permissions.includes(Permission.ReadPerson)) {
return dataSources.contactInformationAPI.contactInformation(offset, limit); return dataSources.contactInformationAPI.contactInformation(offset, limit);
} else { } else {
@ -45,7 +45,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
persons: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { persons: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadPerson)) { if (req.permissions.includes(Permission.ReadPerson)) {
return dataSources.contactInformationAPI.persons(offset, limit); return dataSources.contactInformationAPI.persons(offset, limit);
} else { } else {

@ -31,7 +31,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
lendingStations: (_: any, { offset, limit }: { offset: number, limit: number }, { 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.ReadLendingStation)) { if (req.permissions.includes(Permission.ReadLendingStation)) {
return dataSources.lendingStationAPI.lendingStations(offset, limit); return dataSources.lendingStationAPI.lendingStations(offset, limit);
} else { } else {
@ -45,7 +45,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
timeFrames: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { timeFrames: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadTimeFrame)) { if (req.permissions.includes(Permission.ReadTimeFrame)) {
return dataSources.lendingStationAPI.timeFrames(offset, limit); return dataSources.lendingStationAPI.timeFrames(offset, limit);
} else { } else {
@ -99,19 +99,15 @@ export default {
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }), isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req }) isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req })
}, },
LoanPeriod: { DateRange: {
loanTimes (parent: any) { from (parent: string) {
return parent.loanTimes ? parent.loanTimes : []; return parent.replace(/^\[(.*),.*\)$/, '$1');
},
to (parent: string) {
return parent.replace(/^\[.*,(.*)\)$/, '$1');
} }
}, },
TimeFrame: { TimeFrame: {
from (parent: any) {
return (parent.dateRange as string).split(',')[0].replace('[', '');
},
to (parent: any) {
const str = (parent.dateRange as string).split(',')[1].replace(')', '');
return (str.length > 0) ? str : null;
},
cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) { cargoBike (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadBike)) { if (req.permissions.includes(Permission.ReadBike)) {
return dataSources.cargoBikeAPI.cargoBikeByTimeFrameId(parent.id); return dataSources.cargoBikeAPI.cargoBikeByTimeFrameId(parent.id);

@ -30,7 +30,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
participants: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { participants: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadParticipant)) { if (req.permissions.includes(Permission.ReadParticipant)) {
return dataSources.participantAPI.getParticipants(offset, limit); return dataSources.participantAPI.getParticipants(offset, limit);
} else { } else {
@ -44,7 +44,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
engagements: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { engagements: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadEngagement)) { if (req.permissions.includes(Permission.ReadEngagement)) {
return dataSources.participantAPI.engagements(offset, limit); return dataSources.participantAPI.engagements(offset, limit);
} else { } else {
@ -58,7 +58,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
engagementTypes: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { engagementTypes: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadEngagement)) { if (req.permissions.includes(Permission.ReadEngagement)) {
return dataSources.participantAPI.engagementTypes(offset, limit); return dataSources.participantAPI.engagementTypes(offset, limit);
} else { } else {
@ -113,13 +113,6 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
from (parent: any) {
return (parent.dateRange as string).split(',')[0].replace('[', '');
},
to (parent: any) {
const str = (parent.dateRange as string).split(',')[1].replace(')', '');
return (str.length > 0) ? str : null;
},
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }), isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req }) isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req })
}, },

@ -23,7 +23,7 @@ import { PermissionError } from '../errors/PermissionError';
export default { export default {
Query: { Query: {
providers: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { providers: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadProvider)) { if (req.permissions.includes(Permission.ReadProvider)) {
return dataSources.providerAPI.provider(offset, limit); return dataSources.providerAPI.provider(offset, limit);
} else { } else {
@ -37,7 +37,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
organisations: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { organisations: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadOrganisation)) { if (req.permissions.includes(Permission.ReadOrganisation)) {
return dataSources.providerAPI.organisations(offset, limit); return dataSources.providerAPI.organisations(offset, limit);
} else { } else {

@ -31,7 +31,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
workshopTypes: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { workshopTypes: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadWorkshop)) { if (req.permissions.includes(Permission.ReadWorkshop)) {
return dataSources.workshopAPI.workshopTypes(offset, limit); return dataSources.workshopAPI.workshopTypes(offset, limit);
} else { } else {
@ -45,7 +45,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
workshops: (_: any, { offset, limit }: { offset: number, limit: number }, { dataSources, req }: { dataSources: any, req: any }) => { workshops: (_: any, { offset, limit }: { offset?: number, limit?: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadWorkshop)) { if (req.permissions.includes(Permission.ReadWorkshop)) {
return dataSources.workshopAPI.workshops(offset, limit); return dataSources.workshopAPI.workshops(offset, limit);
} else { } else {

@ -21,27 +21,38 @@ import { gql } from 'apollo-server-express';
export default gql` export default gql`
"timestamp object YYYY-MM-ddThh:mm:ss.sssZ" "date object YYYY-MM-dd"
scalar Date scalar Date
"timestamp object YYYY-MM-ddThh:mm:ss.sssZ"
scalar DateTime
"only time hh-mm-ss" "only time hh-mm-ss"
scalar Time scalar Time
"""
is of american format [-]$[0-9]+.[0-9][0-9]
commas every three digits and . for decimals with 2 digits after the .
There can be a leading -.
There is a currency signe at the first position or second position if - is set.
The kind of currency depends on the database.
"""
scalar Money
"The CargoBike type is central to the graph. You could call it the root." "The CargoBike type is central to the graph. You could call it the root."
type CargoBike { type CargoBike {
id: ID! id: ID!
"see column A in info tabelle" "see column A in info tabelle"
group: Group group: Group!
name: String name: String!
state: BikeState
modelName: String modelName: String
numberOfWheels: Int numberOfWheels: Int
forCargo: Boolean forCargo: Boolean
forChildren: Boolean forChildren: Boolean
numberOfChildren: Int! numberOfChildren: Int
""" """
Safety is a custom type, that stores information about security features. Safety is a custom type, that stores information about security features.
TODO: Should this be called Security? TODO: Should this be called Security?
""" """
security: Security! security: Security
""" """
Does not refer to an extra table in the database. Does not refer to an extra table in the database.
""" """
@ -49,9 +60,11 @@ export default gql`
""" """
Does not refer to an extra table in the database. Does not refer to an extra table in the database.
""" """
dimensionsAndLoad: DimensionsAndLoad! dimensionsAndLoad: DimensionsAndLoad
"If offset or limit is not provided, both values are ignored"
bikeEvents(offset: Int, limit: Int): [BikeEvent] bikeEvents(offset: Int, limit: Int): [BikeEvent]
equipment(offset: Int!, limit: Int!): [Equipment] "If offset or limit is not provided, both values are ignored"
equipment(offset: Int, limit: Int): [Equipment]
"Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2" "Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2"
equipmentType: [EquipmentType] equipmentType: [EquipmentType]
"Sticker State" "Sticker State"
@ -60,17 +73,27 @@ export default gql`
provider: Provider provider: Provider
"all participants currently engaged with the cargoBike" "all participants currently engaged with the cargoBike"
participants: [Participant] participants: [Participant]
insuranceData: InsuranceData! insuranceData: InsuranceData
lendingStation: LendingStation lendingStation: LendingStation
taxes: Taxes taxes: Taxes
currentEngagements: [Engagement] currentEngagements: [Engagement]
engagement(offset: Int!, limit: Int!): [Engagement] "If offset or limit is not provided, both values are ignored"
engagement(offset: Int, limit: Int): [Engagement]
timeFrames: [TimeFrame] timeFrames: [TimeFrame]
isLocked: Boolean! isLocked: Boolean!
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
}
"""
Status of the CargoBike. More fields will be added, or removed.
"""
enum BikeState {
ACTIVE
INACTIVE
INPREPARATION
} }
""" """
@ -80,42 +103,52 @@ export default gql`
"see column A in info tabelle" "see column A in info tabelle"
group: Group! group: Group!
name: String! name: String!
modelName: String! state: BikeState
numberOfWheels: Int! modelName: String
forCargo: Boolean! numberOfWheels: Int
forChildren: Boolean! forCargo: Boolean
numberOfChildren: Int! forChildren: Boolean
numberOfChildren: Int
""" """
Safety is a custom type, that stores information about security features. Safety is a custom type, that stores information about security features.
TODO: Should this be called Security?
""" """
security: SecurityCreateInput! security: SecurityCreateInput
""" """
Does not refer to an extra table in the database. Does not refer to an extra table in the database.
""" """
technicalEquipment: TechnicalEquipmentCreateInput! technicalEquipment: TechnicalEquipmentCreateInput
""" """
Does not refer to an extra table in the database. Does not refer to an extra table in the database.
""" """
dimensionsAndLoad: DimensionsAndLoadCreateInput! dimensionsAndLoad: DimensionsAndLoadCreateInput
"Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2" """
Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2
When set to null or [], no relations will be added.
"""
equipmentTypeIds: [ID] equipmentTypeIds: [ID]
"""
Refers to unique equipment
When set to null or [], no relations will be added.
When specified id is in a relation with another bike, this relation will be deleted.
"""
equipmentIds: [ID]
"Sticker State" "Sticker State"
stickerBikeNameState: StickerBikeNameState stickerBikeNameState: StickerBikeNameState
note: String note: String
providerId: ID providerId: ID
insuranceData: InsuranceDataCreateInput! insuranceData: InsuranceDataCreateInput
taxes: TaxesCreateInput! taxes: TaxesCreateInput
} }
""" """
if you want to add bike to a lending station, create a new timeFrame with to: Date = null If you want to add bike to a lending station, create a new timeFrame with to: Date = null
""" """
input CargoBikeUpdateInput { input CargoBikeUpdateInput {
id: ID! id: ID!
"see column A in info tabelle" "see column A in info tabelle"
group: Group group: Group
name: String name: String
state: BikeState
modelName: String modelName: String
numberOfWheels: Int numberOfWheels: Int
forCargo: Boolean forCargo: Boolean
@ -123,7 +156,6 @@ export default gql`
numberOfChildren: Int numberOfChildren: Int
""" """
Safety is a custom type, that stores information about security features. Safety is a custom type, that stores information about security features.
TODO: Should this be called Security?
""" """
security: SecurityUpdateInput security: SecurityUpdateInput
""" """
@ -136,9 +168,19 @@ export default gql`
dimensionsAndLoad: DimensionsAndLoadUpdateInput dimensionsAndLoad: DimensionsAndLoadUpdateInput
""" """
Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2 Refers to equipment that is not unique. See kommentierte info tabelle -> Fragen -> Frage 2
If set, ols relations will be over written. Set [] to delete all When set to null, field will be ignored.
When set to [], all relations will be deleted.
Else all realtions will be deleted and the specified relations will be added.
""" """
equipmentTypeIds: [ID] equipmentTypeIds: [ID]
"""
Refers to unique equipment
When set to null, field will be ignored.
When set to [], all relations will be deleted.
Else all realtions will be deleted and the specified relations will be added.
When specified id is in a relation with another bike, this relation will be deleted.
"""
equipmentIds: [ID]
"Sticker State" "Sticker State"
stickerBikeNameState: StickerBikeNameState stickerBikeNameState: StickerBikeNameState
note: String note: String
@ -153,15 +195,15 @@ export default gql`
""" """
Eventually, this field will become an enum or a separate data table and user can choose from a pool of insurance companies. Eventually, this field will become an enum or a separate data table and user can choose from a pool of insurance companies.
""" """
name: String! name: String
benefactor: String! benefactor: String
billing: String! billing: String
noPnP: String! noPnP: String
"eg. Anbieter, flotte, eigenleistung" "eg. Anbieter, flotte, eigenleistung"
maintenanceResponsible: String! maintenanceResponsible: String
maintenanceBenefactor: String! maintenanceBenefactor: String
maintenanceAgreement: String maintenanceAgreement: String
hasFixedRate: Boolean! hasFixedRate: Boolean
fixedRate: Float fixedRate: Float
""" """
Projektzuschuss: Projektzuschuss:
@ -171,7 +213,7 @@ export default gql`
There is a currency signe at the first position or second position if - is set. There is a currency signe at the first position or second position if - is set.
The kind of currency depends on the database. The kind of currency depends on the database.
""" """
projectAllowance: String projectAllowance: Money
notes: String notes: String
} }
@ -179,15 +221,15 @@ export default gql`
""" """
Eventually, this field will become an enum or a separate data table and user can choose from a pool of insurance companies. Eventually, this field will become an enum or a separate data table and user can choose from a pool of insurance companies.
""" """
name: String! name: String
benefactor: String! benefactor: String
billing: String! billing: String
noPnP: String! noPnP: String
"eg. Anbieter, flotte, eigenleistung" "eg. Anbieter, flotte, eigenleistung"
maintenanceResponsible: String! maintenanceResponsible: String
maintenanceBenefactor: String! maintenanceBenefactor: String
maintenanceAgreement: String maintenanceAgreement: String
hasFixedRate: Boolean! hasFixedRate: Boolean
fixedRate: Float fixedRate: Float
""" """
Projektzuschuss: Projektzuschuss:
@ -197,7 +239,7 @@ export default gql`
You can pass a currency signe at the first position or second position of + or - is set. You can pass a currency signe at the first position or second position of + or - is set.
The kind of currency depends on the database. The kind of currency depends on the database.
""" """
projectAllowance: String projectAllowance: Money
notes: String notes: String
} }
@ -223,37 +265,51 @@ export default gql`
You can pass a currency signe at the first position or second position of + or - is set. You can pass a currency signe at the first position or second position of + or - is set.
The kind of currency depends on the database. The kind of currency depends on the database.
""" """
projectAllowance: String projectAllowance: Money
notes: String notes: String
} }
type NumRange {
min: Float
max: Float
}
"""
If min or max is omitted, the omitted value will be the same as the other given value
So if you pass one as null, both values with be over written with null.
"""
input NumRangeInput {
min: Float
max: Float
}
"How are the dimensions and how much weight can handle a bike. This data is merged in the CargoBike table and the BikeModel table." "How are the dimensions and how much weight can handle a bike. This data is merged in the CargoBike table and the BikeModel table."
type DimensionsAndLoad { type DimensionsAndLoad {
hasCoverBox: Boolean! hasCoverBox: Boolean
"cover box can be locked" "cover box can be locked"
lockable: Boolean! lockable: Boolean
boxLength: Float! boxLengthRange: NumRange
boxWidth: Float! boxWidthRange: NumRange
boxHeight: Float! boxHeightRange: NumRange
maxWeightBox: Float! maxWeightBox: Float
maxWeightLuggageRack: Float! maxWeightLuggageRack: Float
maxWeightTotal: Float! maxWeightTotal: Float
bikeLength: Float! bikeLength: Float
bikeWidth: Float bikeWidth: Float
bikeHeight: Float bikeHeight: Float
bikeWeight: Float bikeWeight: Float
} }
input DimensionsAndLoadCreateInput { input DimensionsAndLoadCreateInput {
hasCoverBox: Boolean! hasCoverBox: Boolean
lockable: Boolean! lockable: Boolean
boxLength: Float! boxLengthRange: NumRangeInput
boxWidth: Float! boxWidthRange: NumRangeInput
boxHeight: Float! boxHeightRange: NumRangeInput
maxWeightBox: Float! maxWeightBox: Float
maxWeightLuggageRack: Float! maxWeightLuggageRack: Float
maxWeightTotal: Float! maxWeightTotal: Float
bikeLength: Float! bikeLength: Float
bikeWidth: Float bikeWidth: Float
bikeHeight: Float bikeHeight: Float
bikeWeight: Float bikeWeight: Float
@ -262,9 +318,9 @@ export default gql`
input DimensionsAndLoadUpdateInput { input DimensionsAndLoadUpdateInput {
hasCoverBox: Boolean hasCoverBox: Boolean
lockable: Boolean lockable: Boolean
boxLength: Float boxLengthRange: NumRangeInput
boxWidth: Float boxWidthRange: NumRangeInput
boxHeight: Float boxHeightRange: NumRangeInput
maxWeightBox: Float maxWeightBox: Float
maxWeightLuggageRack: Float maxWeightLuggageRack: Float
maxWeightTotal: Float maxWeightTotal: Float
@ -280,16 +336,16 @@ export default gql`
So no id needed for mutation. One Mutation for the CargoBike will be enough. So no id needed for mutation. One Mutation for the CargoBike will be enough.
""" """
type TechnicalEquipment { type TechnicalEquipment {
bicycleShift: String! bicycleShift: String
isEBike: Boolean! isEBike: Boolean
hasLightSystem: Boolean! hasLightSystem: Boolean
specialFeatures: String specialFeatures: String
} }
input TechnicalEquipmentCreateInput { input TechnicalEquipmentCreateInput {
bicycleShift: String! bicycleShift: String
isEBike: Boolean! isEBike: Boolean
hasLightSystem: Boolean! hasLightSystem: Boolean
specialFeatures: String specialFeatures: String
} }
@ -306,7 +362,7 @@ export default gql`
So no id needed for mutation. One Mutation for the CargoBike will be enough. So no id needed for mutation. One Mutation for the CargoBike will be enough.
""" """
type Security { type Security {
frameNumber: String! frameNumber: String
keyNumberFrameLock: String keyNumberFrameLock: String
keyNumberAXAChain: String keyNumberAXAChain: String
policeCoding: String policeCoding: String
@ -314,7 +370,7 @@ export default gql`
} }
input SecurityCreateInput { input SecurityCreateInput {
frameNumber: String! frameNumber: String
keyNumberFrameLock: String keyNumberFrameLock: String
keyNumberAXAChain: String keyNumberAXAChain: String
policeCoding: String policeCoding: String
@ -354,8 +410,7 @@ export default gql`
""" """
type Participant { type Participant {
id: ID! id: ID!
start: Date! dateRange: DateRange!
end: Date
contactInformation: ContactInformation! contactInformation: ContactInformation!
usernamefLotte: String usernamefLotte: String
usernameSlack: String usernameSlack: String
@ -375,13 +430,12 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input ParticipantCreateInput { input ParticipantCreateInput {
"if not set, CURRENT_DATE will be used" "if not set, CURRENT_DATE will be used"
start: Date dateRange: DateRangeInput!
end: Date
"must create contactinformation first, if you want to use new" "must create contactinformation first, if you want to use new"
contactInformationId: ID! contactInformationId: ID!
usernamefLotte: String usernamefLotte: String
@ -396,9 +450,7 @@ export default gql`
input ParticipantUpdateInput { input ParticipantUpdateInput {
id: ID! id: ID!
"if not set, CURRENT_DATE will be used" dateRange: DateRangeInput
start: Date
end: Date
"must create contactinformation first, if you want to use new" "must create contactinformation first, if you want to use new"
contactInformationId: ID contactInformationId: ID
usernamefLotte: String usernamefLotte: String
@ -428,7 +480,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input WorkshopCreateInput { input WorkshopCreateInput {
@ -458,7 +510,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input WorkshopTypeCreateInput { input WorkshopTypeCreateInput {
@ -478,7 +530,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input EngagementTypeCreateInput { input EngagementTypeCreateInput {
@ -496,43 +548,38 @@ export default gql`
type Engagement { type Engagement {
id: ID! id: ID!
engagementType: EngagementType! engagementType: EngagementType!
from: Date! dateRange: DateRange!
to: Date
participant: Participant! participant: Participant!
cargoBike: CargoBike! cargoBike: CargoBike!
isLocked: Boolean! isLocked: Boolean!
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input EngagementCreateInput { input EngagementCreateInput {
engagementTypeId: ID! engagementTypeId: ID!
"will use CURRENT_DATE if not set" dateRange: DateRangeInput!
from: Date
"will use infinit if not set"
to: Date
participantId: ID! participantId: ID!
cargoBikeId: ID! cargoBikeId: ID!
} }
input EngagementUpdateInput { input EngagementUpdateInput {
id: ID! id: ID!
engagementTypeId: ID engagementTypeId: ID
from: Date dateRange: DateRangeInput
to: Date
participantId: ID participantId: ID
cargoBikeId: ID cargoBikeId: ID
keepLock: Boolean keepLock: Boolean
} }
type Taxes { type Taxes {
costCenter: String! costCenter: String
organisationArea: OrganisationArea organisationArea: OrganisationArea
} }
input TaxesCreateInput { input TaxesCreateInput {
costCenter: String! costCenter: String
organisationArea: OrganisationArea organisationArea: OrganisationArea
} }
@ -560,7 +607,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input EquipmentCreateInput { input EquipmentCreateInput {
@ -592,11 +639,11 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input EquipmentTypeCreateInput { input EquipmentTypeCreateInput {
name: String name: String!
description: String description: String
} }
@ -607,7 +654,7 @@ export default gql`
keepLock: Boolean keepLock: Boolean
} }
"An Event is a point in time, when the state of the bike somehow changed." "An Event is a point in time concerning one cargo bike of an event type. For example a chain swap."
type BikeEvent { type BikeEvent {
id: ID! id: ID!
bikeEventType: BikeEventType! bikeEventType: BikeEventType!
@ -625,7 +672,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input BikeEventCreateInput { input BikeEventCreateInput {
@ -663,7 +710,9 @@ export default gql`
name: String! name: String!
isLockedByMe: Boolean! isLockedByMe: Boolean!
isLocked: Boolean! isLocked: Boolean!
lockedUntil: Date "null if not locked by other user"
lockedBy: ID
lockedUntil: DateTime
} }
input BikeEventTypeUpdateInput { input BikeEventTypeUpdateInput {
@ -683,7 +732,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
"(dt. Anbieter)" "(dt. Anbieter)"
@ -717,7 +766,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input PersonCreateInput { input PersonCreateInput {
@ -744,7 +793,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input ContactInformationCreateInput { input ContactInformationCreateInput {
@ -784,7 +833,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input OrganisationCreateInput { input OrganisationCreateInput {
@ -828,7 +877,7 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
""" """
@ -862,13 +911,13 @@ export default gql`
""" """
type LoanPeriod { type LoanPeriod {
generalRemark: String generalRemark: String
"notes for each day of the week, starting on Monday" mo: String
notes: [String] tu: String
""" we: String
Loan times from and until for each day of the week. th: String
Starting with Monday from, Monday to, Tuesday from, ..., Sunday to fr: String
""" sa: String
loanTimes: [String] su: String
} }
""" """
@ -876,22 +925,35 @@ export default gql`
""" """
input LoanPeriodInput { input LoanPeriodInput {
generalRemark: String generalRemark: String
"notes for each day of the week, starting on Monday" mo: String
notes: [String!] tu: String
we: String
th: String
fr: String
sa: String
su: String
}
type DateRange{
from: Date!
"will be infinity of not omitted"
to: Date
}
input DateRangeInput {
"format YYYY-MM-dd"
from: Date!
""" """
Loan times from and until for each day of the week. format YYYY-MM-dd
Starting with Monday from, Monday to, Tuesday from, ..., Sunday to will be infinity of not omitted
""" """
loanTimes: [String!] to: Date
} }
"(dt. Zeitscheibe) When was a bike where" "(dt. Zeitscheibe) When was a bike where"
type TimeFrame { type TimeFrame {
id: ID! id: ID!
"format YYYY-MM-dd" dateRange: DateRange!
from: Date!
"format YYYY-MM-dd"
to: Date
note: String note: String
lendingStation: LendingStation! lendingStation: LendingStation!
cargoBike: CargoBike! cargoBike: CargoBike!
@ -899,12 +961,11 @@ export default gql`
isLockedByMe: Boolean! isLockedByMe: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
lockedUntil: Date lockedUntil: DateTime
} }
input TimeFrameCreateInput { input TimeFrameCreateInput {
from: Date! dateRange: DateRangeInput!
to: Date
note: String note: String
lendingStationId: ID! lendingStationId: ID!
cargoBikeId: ID! cargoBikeId: ID!
@ -912,8 +973,7 @@ export default gql`
input TimeFrameUpdateInput { input TimeFrameUpdateInput {
id: ID! id: ID!
from: Date dateRange: DateRangeInput
to: Date
note: String note: String
lendingStationId: ID lendingStationId: ID
cargoBikeId: ID cargoBikeId: ID
@ -953,41 +1013,55 @@ export default gql`
type Query { type Query {
"Will (eventually) return all properties of cargo bike" "Will (eventually) return all properties of cargo bike"
cargoBikeById(id:ID!): CargoBike cargoBikeById(id:ID!): CargoBike
"returns cargoBikes ordered by name ascending, relations are not loaded, use cargoBikeById instead" "Returns cargoBikes ordered by name ascending. If offset or limit is not provided, both values are ignored."
cargoBikes(offset: Int!, limit: Int!): [CargoBike!]! cargoBikes(offset: Int, limit: Int): [CargoBike!]!
engagementById(id: ID!): Engagement engagementById(id: ID!): Engagement
engagements(offset: Int!, limit: Int!): [Engagement!]! "If offset or limit is not provided, both values are ignored"
engagements(offset: Int, limit: Int): [Engagement!]!
engagementTypeById(id: ID!): EngagementType engagementTypeById(id: ID!): EngagementType
engagementTypes(offset: Int!, limit: Int!): [EngagementType!]! "If offset or limit is not provided, both values are ignored"
engagementTypes(offset: Int, limit: Int): [EngagementType!]!
"equipment by id, will return null if id not found" "equipment by id, will return null if id not found"
equipmentById(id: ID!): Equipment equipmentById(id: ID!): Equipment
equipment(offset: Int!, limit: Int!): [Equipment!]! "If offset or limit is not provided, both values are ignored"
equipment(offset: Int, limit: Int): [Equipment!]!
equipmentTypeById(id: ID!): EquipmentType equipmentTypeById(id: ID!): EquipmentType
equipmentTypes(offset: Int!, limit: Int!): [EquipmentType!]! "If offset or limit is not provided, both values are ignored"
equipmentTypes(offset: Int, limit: Int): [EquipmentType!]!
"return null if id not found" "return null if id not found"
providerById(id:ID!): Provider providerById(id:ID!): Provider
"Returns providers with pagination" "Returns providers with pagination. If offset or limit is not provided, both values are ignored"
providers(offset: Int!, limit: Int!): [Provider!]! providers(offset: Int, limit: Int): [Provider!]!
"participant by id" "participant by id"
participantById(id:ID!): Participant participantById(id:ID!): Participant
participants(offset: Int!, limit: Int!): [Participant!]! "If offset or limit is not provided, both values are ignored"
participants(offset: Int, limit: Int): [Participant!]!
workshopTypeById(id: ID!): WorkshopType workshopTypeById(id: ID!): WorkshopType
workshopTypes(offset: Int!, limit: Int!): [WorkshopType!]! "If offset or limit is not provided, both values are ignored"
workshopTypes(offset: Int, limit: Int): [WorkshopType!]!
workshopById(id: ID!): Workshop workshopById(id: ID!): Workshop
workshops(offset: Int!, limit: Int!): [Workshop!]! "If offset or limit is not provided, both values are ignored"
workshops(offset: Int, limit: Int): [Workshop!]!
lendingStationById(id:ID!): LendingStation lendingStationById(id:ID!): LendingStation
lendingStations(offset: Int!, limit: Int!): [LendingStation!]! "If offset or limit is not provided, both values are ignored"
lendingStations(offset: Int, limit: Int): [LendingStation!]!
organisationById(id: ID!): Organisation organisationById(id: ID!): Organisation
organisations(offset: Int!, limit: Int!): [Organisation!]! "If offset or limit is not provided, both values are ignored"
organisations(offset: Int, limit: Int): [Organisation!]!
timeFrameById(id: ID!): TimeFrame timeFrameById(id: ID!): TimeFrame
timeFrames(offset: Int!, limit: Int!): [TimeFrame!]! "If offset or limit is not provided, both values are ignored"
timeFrames(offset: Int, limit: Int): [TimeFrame!]!
contactInformationById(id: ID!): ContactInformation contactInformationById(id: ID!): ContactInformation
contactInformation(offset: Int!, limit: Int!): [ContactInformation!]! "If offset or limit is not provided, both values are ignored"
contactInformation(offset: Int, limit: Int): [ContactInformation!]!
personById(id: ID!): Person personById(id: ID!): Person
persons(offset: Int!, limit: Int!): [Person!] "If offset or limit is not provided, both values are ignored"
bikeEventTypes(offset: Int!, limit: Int!): [BikeEventType!] persons(offset: Int, limit: Int): [Person!]
"If offset or limit is not provided, both values are ignored"
bikeEventTypes(offset: Int, limit: Int): [BikeEventType!]
bikeEventTypeByd(id: ID!): BikeEventType bikeEventTypeByd(id: ID!): BikeEventType
bikeEvents(offset: Int!, limit: Int!): [BikeEvent!]! "If offset or limit is not provided, both values are ignored"
bikeEvents(offset: Int, limit: Int): [BikeEvent!]!
bikeEventById(id:ID!): BikeEvent bikeEventById(id:ID!): BikeEvent
"actionLog for current user" "actionLog for current user"
actionLog: [ActionLog!] actionLog: [ActionLog!]
@ -1062,23 +1136,23 @@ export default gql`
""" """
createParticipant(participant: ParticipantCreateInput!): Participant! createParticipant(participant: ParticipantCreateInput!): Participant!
lockParticipant(id: ID!): Participant! lockParticipant(id: ID!): Participant!
unlockParticipant(id: ID!): Boolean unlockParticipant(id: ID!): Participant
updateParticipant(participant: ParticipantUpdateInput!): Participant! updateParticipant(participant: ParticipantUpdateInput!): Participant!
deleteParticipant(id: ID!): Boolean! deleteParticipant(id: ID!): Boolean!
createWorkshopType(workshopType: WorkshopTypeCreateInput!): WorkshopType! createWorkshopType(workshopType: WorkshopTypeCreateInput!): WorkshopType!
lockWorkshopType(id: ID!): WorkshopType! lockWorkshopType(id: ID!): WorkshopType!
unlockWorkshopType(id: ID!): Boolean! unlockWorkshopType(id: ID!): WorkshopType!
updateWorkshopType(workshopType: WorkshopTypeUpdateInput!): WorkshopType! updateWorkshopType(workshopType: WorkshopTypeUpdateInput!): WorkshopType!
deleteWorkshopType(id: ID!): Boolean! deleteWorkshopType(id: ID!): Boolean!
createWorkshop(workshop: WorkshopCreateInput!): Workshop! createWorkshop(workshop: WorkshopCreateInput!): Workshop!
lockWorkshop(id: ID!): Workshop! lockWorkshop(id: ID!): Workshop!
unlockWorkshop(id: ID!): Boolean! unlockWorkshop(id: ID!): Workshop!
updateWorkshop(workshop: WorkshopUpdateInput!): Workshop! updateWorkshop(workshop: WorkshopUpdateInput!): Workshop!
deleteWorkshop(id: ID!): Boolean! deleteWorkshop(id: ID!): Boolean!
"create new contactInfo" "create new contactInfo"
createContactInformation(contactInformation: ContactInformationCreateInput!): ContactInformation! createContactInformation(contactInformation: ContactInformationCreateInput!): ContactInformation!
lockContactInformation(id: ID!): ContactInformation! lockContactInformation(id: ID!): ContactInformation!
unlockContactInformation(id: ID!): Boolean! unlockContactInformation(id: ID!): ContactInformation!
updateContactInformation(contactInformation: ContactInformationUpdateInput!): ContactInformation! updateContactInformation(contactInformation: ContactInformationUpdateInput!): ContactInformation!
deleteContactInformation(id: ID!): Boolean! deleteContactInformation(id: ID!): Boolean!
createPerson(person: PersonCreateInput!): Person! createPerson(person: PersonCreateInput!): Person!
@ -1089,22 +1163,22 @@ export default gql`
"create Engagement" "create Engagement"
createEngagement(engagement: EngagementCreateInput): Engagement! createEngagement(engagement: EngagementCreateInput): Engagement!
lockEngagement(id: ID!): Engagement! lockEngagement(id: ID!): Engagement!
unlockEngagement(id: ID!): Boolean! unlockEngagement(id: ID!): Engagement!
updateEngagement(engagement: EngagementUpdateInput!): Engagement! updateEngagement(engagement: EngagementUpdateInput!): Engagement!
deleteEngagement(id: ID!): Boolean! deleteEngagement(id: ID!): Boolean!
createEngagementType(engagementType: EngagementTypeCreateInput!): EngagementType! createEngagementType(engagementType: EngagementTypeCreateInput!): EngagementType!
lockEngagementType(id: ID!): EngagementType! lockEngagementType(id: ID!): EngagementType!
unlockEngagementType(id: ID!): Boolean! unlockEngagementType(id: ID!): EngagementType!
updateEngagementType(engagementType: EngagementTypeUpdateInput!): EngagementType! updateEngagementType(engagementType: EngagementTypeUpdateInput!): EngagementType!
deleteEngagementType(id: ID!): Boolean! deleteEngagementType(id: ID!): Boolean!
createProvider(provider: ProviderCreateInput!): Provider! createProvider(provider: ProviderCreateInput!): Provider!
lockProvider(id: ID!): Provider! lockProvider(id: ID!): Provider!
unlockProvider(id: ID!): Boolean! unlockProvider(id: ID!): Provider!
updateProvider(provider: ProviderUpdateInput!): Provider! updateProvider(provider: ProviderUpdateInput!): Provider!
deleteProvider(id: ID!): Boolean! deleteProvider(id: ID!): Boolean!
createOrganisation(organisation: OrganisationCreateInput!): Organisation! createOrganisation(organisation: OrganisationCreateInput!): Organisation!
lockOrganisation(id: ID!): Organisation! lockOrganisation(id: ID!): Organisation!
unlockOrganisation(id: ID!): Boolean! unlockOrganisation(id: ID!): Organisation!
updateOrganisation(organisation: OrganisationUpdateInput!): Organisation! updateOrganisation(organisation: OrganisationUpdateInput!): Organisation!
deleteOrganisation(id: ID!): Boolean! deleteOrganisation(id: ID!): Boolean!
} }

Loading…
Cancel
Save