src/*: implemented update and action log on overwrites

pull/14/head
leonnicolas 4 years ago
parent b593465bc3
commit 1b9030c85e
No known key found for this signature in database
GPG Key ID: 088D0743E2B65C07

@ -1,14 +1,14 @@
const { src, dest, watch, series, task } = require('gulp')
const ts = require('gulp-typescript')
const del = require('delete')
const eslint = require('gulp-eslint')
const nodemon = require('gulp-nodemon')
const { src, dest, watch, series, task } = require('gulp');
const ts = require('gulp-typescript');
const del = require('delete');
const eslint = require('gulp-eslint');
const nodemon = require('gulp-nodemon');
/**
* Clears the dist folder by deleting all files inside.
* @param cb
*/
function clearDist (cb) {
del('dist/*', cb)
del('dist/*', cb);
}
/**
@ -16,11 +16,11 @@ function clearDist (cb) {
* @returns {*}
*/
function compileTypescript () {
const tsProject = ts.createProject('tsconfig.json')
const tsResult = tsProject.src().pipe(tsProject())
const tsProject = ts.createProject('tsconfig.json');
const tsResult = tsProject.src().pipe(tsProject());
return tsResult
// .pipe(minify())
.pipe(dest('dist'))
.pipe(dest('dist'));
}
/**
@ -29,7 +29,7 @@ function compileTypescript () {
*/
function moveRemaining () {
return src(['src/**/*', '!src/**/*.ts'])
.pipe(dest('dist'))
.pipe(dest('dist'));
}
function runEslint () {
@ -42,7 +42,7 @@ function runEslint () {
.pipe(eslint.format())
// To have the process exit with an error code (1) on
// lint error, return the stream and pipe to failAfterError last.
.pipe(eslint.failAfterError())
.pipe(eslint.failAfterError());
}
task('eslint', () => {
return src(['src/**/*.ts'])
@ -54,30 +54,35 @@ task('eslint', () => {
.pipe(eslint.format())
// To have the process exit with an error code (1) on
// lint error, return the stream and pipe to failAfterError last.
.pipe(eslint.failAfterError())
})
.pipe(eslint.failAfterError());
});
task('default', series(clearDist, compileTypescript, moveRemaining))
task('default', series(clearDist, compileTypescript, moveRemaining));
task('watch', () => {
runEslint()
compileTypescript()
watch('**/*.ts', runEslint)
watch('**/*.ts', compileTypescript)
runEslint();
compileTypescript();
watch('**/*.ts', runEslint);
watch('**/*.ts', compileTypescript);
// watch(['src/**/*', '!src/**/*.ts'], moveRemaining());
nodemon({
script: 'dist/index.js',
watch: ['dist/**/*.js'],
ext: 'js'
})
})
});
});
task('watchTs', () => {
compileTypescript();
watch('**/*.ts', compileTypescript);
});
task('watchnolint', () => {
watch('**/*.ts', compileTypescript)
watch('**/*.ts', compileTypescript);
// watch(['src/**/*', '!src/**/*.ts'], moveRemaining());
nodemon({
script: 'dist/index.js',
watch: ['dist/**/*.js'],
ext: 'js'
})
})
});
});

@ -7,7 +7,7 @@ import { Equipment } from '../../model/Equipment';
import { Engagement } from '../../model/Engagement';
import { Provider } from '../../model/Provider';
import { TimeFrame } from '../../model/TimeFrame';
import { LockUtils } from './utils';
import { ActionLogger, LockUtils } from './utils';
import { EquipmentType } from '../../model/EquipmentType';
import { BikeEventType } from '../../model/BikeEventType';
@ -54,7 +54,7 @@ export class CargoBikeAPI extends DataSource {
async cargoBikesByProviderId (id: number) {
return await this.connection
.createQueryBuilder()
.relation(Provider, 'cargoBikes')
.relation(Provider, 'cargoBikeIds')
.of(id)
.loadMany();
}
@ -62,7 +62,7 @@ export class CargoBikeAPI extends DataSource {
async cargoBikeByTimeFrameId (id: number) {
return await this.connection.getRepository(TimeFrame)
.createQueryBuilder('timeframe')
.relation(TimeFrame, 'cargoBike')
.relation(TimeFrame, 'cargoBikeId')
.of(id)
.loadOne();
}
@ -72,13 +72,6 @@ export class CargoBikeAPI extends DataSource {
* @param param0 cargoBike to be updated
*/
async updateCargoBike (cargoBike: any, userId:number) {
// TODO lock cargoBike can return error to save one sql query, this will be a complex sql query
if (!await this.checkId(CargoBike, 'cargobike', cargoBike.id)) {
return new GraphQLError('ID not found');
}
if (!await LockUtils.lockEntity(this.connection, CargoBike, 'cb', cargoBike.id, userId)) {
return new GraphQLError('Bike locked by other user');
}
const keepLock = cargoBike?.keepLock;
delete cargoBike.keepLock;
delete cargoBike.lendingStationId;
@ -88,6 +81,10 @@ export class CargoBikeAPI extends DataSource {
delete cargoBike.equipmentTypeIds;
}
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, CargoBike, 'cb', cargoBike.id, userId)) {
throw new GraphQLError('CargoBike locked by other user');
}
await ActionLogger.log(entityManager, CargoBike, 'cb', cargoBike, userId);
await entityManager.getRepository(CargoBike)
.createQueryBuilder('cargobike')
.update()
@ -138,6 +135,25 @@ export class CargoBikeAPI extends DataSource {
.execute()).generatedMaps[0];
}
async updateBikeEvent (bikeEvent: any, userId: number) {
const keepLock = bikeEvent.keepLock;
delete bikeEvent.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, BikeEvent, 'be', bikeEvent.id, userId)) {
throw new GraphQLError('BikeEvent locked by other user');
}
await ActionLogger.log(entityManager, BikeEvent, 'be', bikeEvent, userId);
await entityManager.getRepository(BikeEvent)
.createQueryBuilder('be')
.update()
.set({ ...bikeEvent })
.where('id = :id', { id: bikeEvent.id })
.execute();
});
!keepLock && await LockUtils.unlockEntity(this.connection, BikeEvent, 'be', bikeEvent.id, userId);
return await this.bikeEventById(bikeEvent.id);
}
async cargoBikeByEventId (id: number) {
return await this.connection.getRepository(BikeEvent)
.createQueryBuilder('be')
@ -173,6 +189,33 @@ export class CargoBikeAPI extends DataSource {
.execute())?.generatedMaps[0];
}
async lockBikeEventType (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, BikeEventType, 'bet', id, userId);
}
async unlockBikeEventType (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, BikeEventType, 'bet', id, userId);
}
async updateBikeEventType (bikeEventType: any, userId: number) {
const keepLock = bikeEventType.keepLock;
delete bikeEventType.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, BikeEventType, 'bet', bikeEventType.id, userId)) {
throw new GraphQLError('BikeEventType locked by other user');
}
await ActionLogger.log(entityManager, BikeEventType, 'bet', bikeEventType, userId);
await entityManager.getRepository(BikeEventType)
.createQueryBuilder('bet')
.update()
.set({ ...bikeEventType })
.where('id = :id', { id: bikeEventType.id })
.execute();
});
!keepLock && await LockUtils.unlockEntity(this.connection, BikeEventType, 'bet', bikeEventType.id, userId);
return await this.bikeEventTypeById(bikeEventType.id);
}
async bikeEventTypes (offset: number, limit: number) {
return await this.connection.getRepository(BikeEventType)
.createQueryBuilder('bet')
@ -191,7 +234,7 @@ export class CargoBikeAPI extends DataSource {
.getMany();
}
async findBikeEventTypeById (id: number) {
async bikeEventTypeById (id: number) {
return await this.connection.getRepository(BikeEventType)
.createQueryBuilder('bet')
.select()
@ -219,11 +262,11 @@ export class CargoBikeAPI extends DataSource {
* return bikeEvent including CargoBike
* @param id of event
*/
async findBikeEventById (id: number) {
async bikeEventById (id: number) {
return await this.connection.getRepository(BikeEvent)
.createQueryBuilder('bikeEvent')
.leftJoinAndSelect('bikeEvent.cargoBike', 'cargoBike')
.where('bikeEvent.id = :id', { id: id })
.select()
.where('id = :id', { id: id })
.getOne();
}
@ -276,22 +319,15 @@ export class CargoBikeAPI extends DataSource {
.values([equipment])
.returning('*')
.execute();
if (equipment.cargoBikeId) {
await this.connection
.createQueryBuilder()
.relation(Equipment, 'cargoBike')
.of(equipment.id)
.set(equipment.cargoBikeId);
}
return this.equipmentById(inserts.identifiers[0].id);
}
async cargoBikeByEquipmentId (id: number) {
return (await this.connection.getRepository(Equipment)
return await this.connection.getRepository(Equipment)
.createQueryBuilder('equipment')
.leftJoinAndSelect('equipment.cargoBike', 'cargoBike')
.where('equipment.id = :id', { id: id })
.getOne())?.cargoBike;
.relation(Equipment, 'cargoBikeId')
.of(id)
.loadOne();
}
async lockEquipment (id: number, userId: number) {
@ -308,33 +344,33 @@ export class CargoBikeAPI extends DataSource {
* @param param0 struct with equipment properites
*/
async updateEquipment (equipment: any, userId: number) {
// TODO let lock cargoBike can return error to save one sql query, this will be a complex sql query
if (!await this.checkId(Equipment, 'alias', equipment.id)) {
return new GraphQLError('ID not found in DB');
}
if (!await LockUtils.lockEntity(this.connection, Equipment, 'equipment', equipment.id, userId)) {
return new GraphQLError('Equipment locked by other user');
}
const keepLock = equipment.keepLock;
delete equipment.keepLock;
const cargoBikeId = equipment.cargoBikeId;
delete equipment.cargoBikeId;
await this.connection.getRepository(Equipment)
.createQueryBuilder('equipment')
.update()
.set({ ...equipment })
.where('id = :id', { id: equipment.id })
.returning('*')
.execute();
if (cargoBikeId || cargoBikeId === null) {
await this.connection.getRepository(Equipment)
.createQueryBuilder()
.relation(Equipment, 'cargoBike')
.of(equipment.id)
.set(cargoBikeId);
!keepLock && LockUtils.unlockEntity(this.connection, Equipment, 'e', equipment.id, userId);
return this.equipmentById(equipment.id);
// const cargoBikeId = equipment.cargoBikeId;
// delete equipment.cargoBikeId;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, Equipment, 'equipment', equipment.id, userId)) {
return new GraphQLError('Equipment is locked by other user');
}
await ActionLogger.log(entityManager, Equipment, 'e', equipment, userId);
await entityManager.getRepository(Equipment)
.createQueryBuilder('equipment')
.update()
.set({ ...equipment })
.where('id = :id', { id: equipment.id })
.execute();
/* if (cargoBikeId || cargoBikeId === null) {
await this.connection.getRepository(Equipment)
.createQueryBuilder()
.relation(Equipment, 'cargoBike')
.of(equipment.id)
.set(cargoBikeId);
}
*/
}
);
!keepLock && await LockUtils.unlockEntity(this.connection, Equipment, 'e', equipment.id, userId);
return this.equipmentById(equipment.id);
}
@ -359,6 +395,32 @@ export class CargoBikeAPI extends DataSource {
return inserts.generatedMaps[0];
}
async lockEquipmentType (id: number, userId : number) {
return await LockUtils.lockEntity(this.connection, EquipmentType, 'et', id, userId);
}
async unlockEquipmentType (id: number, userId : number) {
return await LockUtils.unlockEntity(this.connection, EquipmentType, 'et', id, userId);
}
async updateEquipmentType (equipmentType: any, userId: number) {
const keepLock = equipmentType.keepLock;
delete equipmentType.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, EquipmentType, 'et', equipmentType.id, userId)) {
throw new GraphQLError('EquipmentType is locked by other user');
}
await entityManager.getRepository(EquipmentType)
.createQueryBuilder('et')
.update()
.set({ ...equipmentType })
.where('id = :id', { id: equipmentType.id })
.execute();
});
!keepLock && await this.unlockEquipmentType(equipmentType.id, userId);
return await this.equipmentTypeById(equipmentType.id);
}
async equipmentTypeById (id: number) {
return await this.connection.getRepository(EquipmentType)
.createQueryBuilder('equipmentType')

@ -1,7 +1,10 @@
import { DataSource } from 'apollo-datasource';
import { Connection, getConnection } from 'typeorm';
import { Connection, EntityManager, getConnection } from 'typeorm';
import { ContactInformation } from '../../model/ContactInformation';
import { Person } from '../../model/Person';
import { ActionLogger, LockUtils } from './utils';
import { GraphQLError } from 'graphql';
import { LendingStation } from '../../model/LendingStation';
export class ContactInformationAPI extends DataSource {
connection : Connection
@ -46,6 +49,33 @@ export class ContactInformationAPI extends DataSource {
return inserts.generatedMaps[0];
}
async lockPerson (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, Person, 'p', id, userId);
}
async unlockPerson (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, Person, 'p', id, userId);
}
async updatePerson (person: any, userId: number) {
const keepLock = person.keepLock;
delete person.keepLock;
await this.connection.transaction(async (entityManger: EntityManager) => {
if (await LockUtils.isLocked(entityManger, Person, 'p', person.id, userId)) {
throw new GraphQLError('Person is locker by another user');
}
await ActionLogger.log(entityManger, Person, 'p', person, userId);
await entityManger.getRepository(Person)
.createQueryBuilder('p')
.update()
.set({ ...person })
.where('id = :id', { id: person.id })
.execute().then(value => { if (value.affected !== 1) { throw new GraphQLError('Id not found'); } });
});
!keepLock && await this.unlockPerson(person.id, userId);
return this.personById(person.id);
}
async persons (offset: number, limit: number) {
return await this.connection.getRepository(Person)
.createQueryBuilder('person')
@ -71,6 +101,22 @@ export class ContactInformationAPI extends DataSource {
.loadOne();
}
async contactInternByLendingStationId (id: number) {
return this.connection.getRepository(LendingStation)
.createQueryBuilder('ls')
.relation(LendingStation, 'contactInformationInternId')
.of(id)
.loadOne();
}
async contactExternByLendingStationId (id: number) {
return this.connection.getRepository(LendingStation)
.createQueryBuilder('ls')
.relation(LendingStation, 'contactInformationExternId')
.of(id)
.loadOne();
}
async createContactInformation (contactInformation: any) {
const inserts = await this.connection.getRepository(ContactInformation)
.createQueryBuilder('contactInformation')
@ -82,6 +128,33 @@ export class ContactInformationAPI extends DataSource {
return inserts.generatedMaps[0];
}
async lockContactInformation (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, ContactInformation, 'ci', id, userId);
}
async unlockContactInformation (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, ContactInformation, 'ci', id, userId);
}
async updateContactInformation (contactInformation: any, userId: number) {
const keepLock = contactInformation.keepLock;
delete contactInformation.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, ContactInformation, 'ci', contactInformation.id, userId)) {
throw new GraphQLError('ContactInformation is locked by other user');
}
await ActionLogger.log(entityManager, ContactInformation, 'ci', contactInformation, userId);
await entityManager.getRepository(ContactInformation)
.createQueryBuilder('ci')
.update()
.set({ ...contactInformation })
.where('id = :id', { id: contactInformation.id })
.execute();
});
!keepLock && await LockUtils.unlockEntity(this.connection, ContactInformation, 'ci', contactInformation.id, userId);
return await this.contactInformationById(contactInformation.id);
}
async contactInformationByPersonId (id: number) {
const res = await this.connection.getRepository(ContactInformation)
.createQueryBuilder('ci')

@ -5,7 +5,7 @@ import { Connection, EntityManager, getConnection, QueryFailedError } from 'type
import { CargoBike } from '../../model/CargoBike';
import { LendingStation } from '../../model/LendingStation';
import { TimeFrame } from '../../model/TimeFrame';
import { LockUtils } from './utils';
import { ActionLogger, genDateRange, LockUtils } from './utils';
export class LendingStationAPI extends DataSource {
connection : Connection
@ -14,12 +14,11 @@ export class LendingStationAPI extends DataSource {
this.connection = getConnection();
}
async lendingStationById ({ id }: { id: any }) {
return await this.connection.manager
.createQueryBuilder()
.select('lendingStation')
.from(LendingStation, 'lendingStation')
.where('lendingStation.id = :id', { id: id })
async lendingStationById (id:number) {
return await this.connection.getRepository(LendingStation)
.createQueryBuilder('ls')
.select()
.where('id = :id', { id: id })
.getOne();
}
@ -102,10 +101,6 @@ export class LendingStationAPI extends DataSource {
return LockUtils.unlockEntity(this.connection, LendingStation, 'ls', id, uId);
}
async lockTimeFrame (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, TimeFrame, 'tf', id, userId);
}
/**
* Counts all timeframes with one lendingStation that overlap with today's date
* @param id of lendingStation
@ -140,40 +135,38 @@ export class LendingStationAPI extends DataSource {
*/
async createLendingStation (lendingStation: any) {
let inserts: any;
await this.connection.transaction(async entiyManager => {
inserts = await entiyManager.createQueryBuilder(LendingStation, 'lendingstation')
await this.connection.transaction(async entityManager => {
inserts = await entityManager.createQueryBuilder(LendingStation, 'lendingstation')
.insert()
.values([lendingStation])
.returning('*')
.execute();
});
const newLendingStaion = inserts.generatedMaps[0];
newLendingStaion.id = inserts.identifiers[0].id;
return newLendingStaion;
// when using the return values, the simple array has a different format and must treated in another way, so this is the more expansive solution
return await this.lendingStationById(inserts.generatedMaps[0].id);
}
/**
* updates lendingStation and return updated lendingStation
* @param param0 lendingStation to be updated
*/
async updateLendingStation ({ lendingStation }:{ lendingStation: any }) {
const oldLendingStation = await this.connection.manager.createQueryBuilder()
.select('lendingStation')
.from(LendingStation, 'lendingStation')
.where('lendingStation.id = :id', { id: lendingStation.id })
.getOne();
if (oldLendingStation) {
await this.connection
.createQueryBuilder()
.update(LendingStation)
async updateLendingStation (lendingStation: any, userId: number) {
const keepLock = lendingStation.keepLock;
delete lendingStation.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, LendingStation, 'ls', lendingStation.id, userId)) {
throw new GraphQLError('LendingStation is locked by another user');
}
await ActionLogger.log(entityManager, LendingStation, 'ls', lendingStation, userId);
await entityManager.getRepository(LendingStation)
.createQueryBuilder('ls')
.update()
.set({ ...lendingStation })
.where('id = :id', { id: lendingStation.id })
.execute();
return this.lendingStationById({ id: lendingStation.id });
} else {
return new GraphQLError('ID not in database');
}
});
!keepLock && await LockUtils.unlockEntity(this.connection, LendingStation, 'ls', lendingStation.id, userId);
return await this.lendingStationById(lendingStation.id);
}
async createTimeFrame (timeFrame: any) {
@ -202,17 +195,6 @@ export class LendingStationAPI extends DataSource {
.returning('*')
.values([timeFrame])
.execute();
/* await entityManager.getRepository(TimeFrame)
.createQueryBuilder()
.relation(TimeFrame, 'cargoBike')
.of(inserts.identifiers[0].id)
.set(timeFrame.cargoBikeId);
await entityManager.getRepository(TimeFrame)
.createQueryBuilder()
.relation(TimeFrame, 'lendingStation')
.of(inserts.identifiers[0].id)
.set(timeFrame.lendingStationId);
*/
});
} catch (e) {
if (e instanceof UserInputError) {
@ -226,55 +208,46 @@ export class LendingStationAPI extends DataSource {
return inserts.generatedMaps[0];
}
/* async updateTimeFrame (timeFrame: any) {
try {
await this.connection.transaction(async (entityManager: EntityManager) => {
if (timeFrame.to === undefined) {
timeFrame.to = '';
}
timeFrame.dateRange = '[' + timeFrame.from + ',' + timeFrame.to + ')';
// checking for overlapping time frames
const overlapping = await entityManager.getRepository(TimeFrame)
.createQueryBuilder('timeframe')
.update()
values([])
.select([
'timeframe.id'
])
.where('timeframe."cargoBikeId" = :id', { id: timeFrame.cargoBikeId })
.andWhere('timeframe."dateRange" && :tr', { tr: timeFrame.dateRange })
.getMany();
console.log(overlapping);
if (overlapping.length !== 0) {
throw new UserInputError('TimeFrames with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping');
}
inserts = await entityManager.getRepository(TimeFrame)
.createQueryBuilder('timeframe')
.insert()
.returning('*')
.values([timeFrame])
.execute();
await entityManager.getRepository(TimeFrame)
.createQueryBuilder()
.relation(TimeFrame, 'cargoBike')
.of(inserts.identifiers[0].id)
.set(timeFrame.cargoBikeId);
await entityManager.getRepository(TimeFrame)
.createQueryBuilder()
.relation(TimeFrame, 'lendingStation')
.of(inserts.identifiers[0].id)
.set(timeFrame.lendingStationId);
});
} catch (e) {
console.log(e);
if (e instanceof UserInputError) {
return e;
} else if (e instanceof QueryFailedError) {
return e;
async lockTimeFrame (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, TimeFrame, 'tf', id, userId);
}
async unlockTimeFrame (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, TimeFrame, 'tf', id, userId);
}
async updateTimeFrame (timeFrame: any, userId: number) {
const keepLock = timeFrame.keepLock;
delete timeFrame.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, TimeFrame, 'tf', timeFrame.id, userId)) {
throw new UserInputError('Attempting to update locked resource');
}
return new ApolloError('Transaction could not be completed');
}
inserts.generatedMaps[0].id = inserts.identifiers[0].id;
return inserts.generatedMaps[0];
} */
genDateRange(timeFrame);
await ActionLogger.log(entityManager, TimeFrame, 'tf', timeFrame, userId);
await entityManager.getRepository(TimeFrame)
.createQueryBuilder('timeframe')
.select([
'timeframe.id'
])
.where('timeframe."cargoBikeId" = :id', { id: timeFrame.cargoBikeId })
.andWhere('timeframe."dateRange" && :tr', { tr: timeFrame.dateRange })
.andWhere('timeFrame.id != :tid', { tid: timeFrame.id })
.getMany().then(overlapping => {
if (overlapping.length !== 0) {
throw new UserInputError('TimeFrames with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping');
}
});
await entityManager.getRepository(TimeFrame)
.createQueryBuilder('tf')
.update()
.set({ ...timeFrame })
.where('id = :id', { id: timeFrame.id })
.execute()
.then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } });
});
!keepLock && await this.unlockTimeFrame(timeFrame.id, userId);
return this.timeFrameById(timeFrame.id);
}
}

@ -4,8 +4,9 @@ import { ContactInformation } from '../../model/ContactInformation';
import { Engagement } from '../../model/Engagement';
import { Participant } from '../../model/Participant';
import { EngagementType } from '../../model/EngagementType';
import { genDateRange } from './utils';
import { ActionLogger, genDateRange, LockUtils } from './utils';
import { UserInputError } from 'apollo-server';
import { GraphQLError } from 'graphql';
export class ParticipantAPI extends DataSource {
connection : Connection
@ -14,11 +15,11 @@ export class ParticipantAPI extends DataSource {
this.connection = getConnection();
}
async getParticipantById (id: number) {
async participantById (id: number) {
return await this.connection.getRepository(Participant)
.createQueryBuilder('participant')
.select()
.where('participant.id = :id', { id: id })
.where('id = :id', { id: id })
.getOne();
}
@ -146,7 +147,34 @@ export class ParticipantAPI extends DataSource {
.returning('*')
.execute();
});
return this.getParticipantById(inserts?.identifiers[0].id);
return this.participantById(inserts?.identifiers[0].id);
}
async lockParticipant (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, Participant, 'p', id, userId);
}
async unlockParticipant (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, Participant, 'p', id, userId);
}
async updateParticipant (participant: any, userId: number) {
const keepLock = participant.keepLock;
delete participant.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, Participant, 'p', participant.id, userId)) {
throw new GraphQLError('Participant is locked by another user');
}
await ActionLogger.log(entityManager, Participant, 'p', participant, userId);
await entityManager.getRepository(Participant)
.createQueryBuilder('p')
.update()
.set({ ...participant })
.where('id = :id', { id: participant.id })
.execute().then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } });
});
!keepLock && await this.unlockParticipant(participant.id, userId);
return await this.participantById(participant.id);
}
async createEngagement (engagement: any) {
@ -174,6 +202,46 @@ export class ParticipantAPI extends DataSource {
return this.engagementById(inserts?.identifiers[0].id);
}
async lockEngagement (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, Engagement, 'e', id, userId);
}
async unlockEngagement (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, Engagement, 'e', id, userId);
}
async updateEngagement (engagement: any, userId: number) {
const keepLock = engagement.keepLock;
delete engagement.keepLock;
genDateRange(engagement);
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, Engagement, 'e', engagement.id, userId)) {
throw new GraphQLError('Engagement is locked by other user');
}
await ActionLogger.log(entityManager, Engagement, 'e', engagement, userId);
// check for overlapping engagements
const overlapping = await entityManager.getRepository(Engagement)
.createQueryBuilder('e')
.select()
.where('e."cargoBikeId" = :id', { id: engagement.cargoBikeId })
.andWhere('e."dateRange" && :dr', { dr: engagement.dateRange })
.andWhere('e."engagementTypeId" = :etId', { etId: engagement.engagementTypeId })
.andWhere('e.id != :eid', { eid: engagement.id })
.getMany();
if (overlapping.length > 0) {
throw new UserInputError('Engagements with ids: ' + overlapping.map((e) => { return e.id + ', '; }) + 'are overlapping');
}
await entityManager.getRepository(Engagement)
.createQueryBuilder('engagement')
.update()
.set({ ...engagement })
.where('id = :id', { id: engagement.id })
.execute();
});
!keepLock && await LockUtils.unlockEntity(this.connection, Engagement, 'e', engagement.id, userId);
return await this.engagementById(engagement.id);
}
async createEngagementType (engagementType: any) {
const inserts = await this.connection.getRepository(EngagementType)
.createQueryBuilder('et')
@ -184,4 +252,31 @@ export class ParticipantAPI extends DataSource {
inserts.generatedMaps[0].id = inserts.identifiers[0].id;
return inserts.generatedMaps[0];
}
async lockEngagementType (id:number, userId: number) {
return await LockUtils.lockEntity(this.connection, EngagementType, 'e', id, userId);
}
async unlockEngagementType (id:number, userId: number) {
return await LockUtils.unlockEntity(this.connection, EngagementType, 'e', id, userId);
}
async updateEngagementType (engagementType: any, userId: number) {
const keepLock = engagementType.keepLock;
delete engagementType.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, EngagementType, 'et', engagementType.id, userId)) {
throw new GraphQLError('EngagementType is locked by other user');
}
await ActionLogger.log(entityManager, EngagementType, 'et', engagementType, userId);
await entityManager.getRepository(EngagementType)
.createQueryBuilder('et')
.update()
.set({ ...engagementType })
.where('id = :id', { id: engagementType.id })
.execute();
});
!keepLock && await LockUtils.unlockEntity(this.connection, EngagementType, 'et', engagementType.id, userId);
return await this.engagementTypeById(engagementType.id);
}
}

@ -4,6 +4,9 @@ import { Provider } from '../../model/Provider';
import { Organisation } from '../../model/Organisation';
import { UserInputError } from 'apollo-server';
import { CargoBike } from '../../model/CargoBike';
import { LendingStation } from '../../model/LendingStation';
import { ActionLogger, LockUtils } from './utils';
import { GraphQLError } from 'graphql';
export class ProviderAPI extends DataSource {
connection : Connection
@ -13,19 +16,19 @@ export class ProviderAPI extends DataSource {
}
async providerById (id: number) {
await this.connection.getRepository(Provider)
return await this.connection.getRepository(Provider)
.createQueryBuilder('provider')
.select()
.where('provider.id = :id', { id: id })
.getOne().catch(() => { return null; });
.where('id = :id', { id: id })
.getOne();
}
async provider (offset: number, limit: number) {
return await this.connection.getRepository(Provider)
.createQueryBuilder('provider')
.select()
.offset(offset)
.limit(limit)
.skip(offset)
.take(limit)
.getMany();
}
@ -70,6 +73,22 @@ export class ProviderAPI extends DataSource {
.getOne();
}
async organisationByLendingStationId (id: number) {
return await this.connection.getRepository(LendingStation)
.createQueryBuilder('ls')
.relation(LendingStation, 'organisationId')
.of(id)
.loadOne();
}
async lendingStationByOrganisationId (id: number) {
return await this.connection.getRepository(Organisation)
.createQueryBuilder('o')
.relation(Organisation, 'lendingStations')
.of(id)
.loadMany();
}
async contactInformationByOrganisationId (id: number) {
return await this.connection.getRepository(Organisation)
.createQueryBuilder('o')
@ -107,6 +126,40 @@ export class ProviderAPI extends DataSource {
return ret;
}
async lockProvider (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, Provider, 'p', id, userId);
}
async unlockProvider (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, Provider, 'p', id, userId);
}
async updateProvider (provider: any, userId: number) {
const keepLock = provider.keepLock;
delete provider.keepLock;
const cargoBikes = provider.cargoBikeIds;
delete provider.cargoBikeIds;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, Provider, 'p', provider.id, userId)) {
throw new GraphQLError('Provider is locked by another user');
}
await ActionLogger.log(entityManager, Provider, 'p', provider, userId);
await entityManager.getRepository(Provider)
.createQueryBuilder('p')
.update()
.set({ ...provider })
.where('id = :id', { id: provider.id })
.execute().then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } });
await entityManager.getRepository(Provider)
.createQueryBuilder('p')
.relation(Provider, 'cargoBikeIds')
.of(provider.id)
.add(cargoBikes);
});
!keepLock && await this.unlockProvider(provider.id, userId);
return await this.providerById(provider.id);
}
async createOrganisation (organisation: any) {
let inserts: any = null;
await this.connection.transaction(async (entityManager: EntityManager) => {
@ -115,11 +168,34 @@ export class ProviderAPI extends DataSource {
.insert()
.values([organisation])
.execute();
await entityManager.getRepository(Organisation).createQueryBuilder()
.relation(Organisation, 'providerId')
.of(inserts.identifiers[0].id)
.set(organisation.providerId);
});
return inserts.generatedMaps[0];
}
async lockOrganisation (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, Organisation, 'o', id, userId);
}
async unlockOrganisation (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, Organisation, 'o', id, userId);
}
async updateOrganisation (organisation: any, userId: number) {
const keepLock = organisation.keepLock;
delete organisation.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, Organisation, 'o', organisation.id, userId)) {
throw new GraphQLError('Organisation is locked by another user');
}
await ActionLogger.log(entityManager, Organisation, 'o', organisation, userId);
await entityManager.getRepository(Organisation)
.createQueryBuilder('o')
.update()
.set({ ...organisation })
.where('id = :id', { id: organisation.id })
.execute();
});
!keepLock && await LockUtils.unlockEntity(this.connection, Organisation, 'o', organisation.id, userId);
return this.organisationById(organisation.id);
}
}

@ -1,11 +1,9 @@
import { Connection, ObjectType } from 'typeorm';
import { CargoBike, Lockable } from '../../model/CargoBike';
import { Connection, EntityManager, ObjectType } from 'typeorm';
import { Lockable } from '../../model/CargoBike';
import { GraphQLError } from 'graphql';
import { ActionLog } from '../../model/ActionLog';
export function genDateRange (struct: any) {
if (struct.to === undefined) {
struct.to = '';
}
if (struct.to === undefined) {
struct.to = '';
}
@ -13,6 +11,9 @@ export function genDateRange (struct: any) {
if (struct.from === undefined) {
delete struct.dateRange;
}
// delete these keys, so the struct can be used to update the engagement entity
delete struct.from;
delete struct.to;
}
/**
@ -115,8 +116,8 @@ export class LockUtils {
* @param req
* @param dataSources
*/
static async isLocked (connection: Connection, target: ObjectType<Lockable>, alias: string, id: number, userId: number) {
const lock = await connection.getRepository(CargoBike)
static async isLocked (connection: EntityManager, target: ObjectType<Lockable>, alias: string, id: number, userId: number) {
const lock = await connection.getRepository(target)
.createQueryBuilder(alias)
.select([
alias + '.lockedUntil',
@ -152,3 +153,61 @@ export class LockUtils {
return result === 1;
}
}
export class ActionLogger {
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
const ret :string[] = [];
Object.keys(updates).forEach(value => {
if (typeof updates[value] === 'object' && !Array.isArray(updates[value])) {
Object.keys(updates[value]).forEach(subValue => {
ret.push(alias + '."' + value + subValue[0].toUpperCase() + subValue.substr(1).toLowerCase() + '"');
});
} else {
ret.push(alias + '."' + value + '"');
}
});
return ret;
}
static async log (em: EntityManager, target: ObjectType<any>, alias: string, updates: any, userId: number) {
const oldValues = await em.getRepository(target).createQueryBuilder(alias)
.select(this.buildSelect(updates, alias))
.where('id = :id', { id: updates.id })
.getRawOne().then(value => {
if (value === undefined) {
throw new GraphQLError('Id not found');
}
return value;
}); // use getRawOne to also get ids of related entities
Object.keys(oldValues).forEach(value => {
if (value.match(alias + '_')) {
oldValues[value.replace(alias + '_', '')] = oldValues[value];
delete oldValues[value];
}
});
// TODO: check if new values are different from old note: the commented section will probably fail for nested objects.
/*
const newValues = { ...updates }; // copy updates to mimic call by value
Object.keys(updates).forEach((key, i) => {
// eslint-disable-next-line eqeqeq
if (newValues[key] == oldValues[key]) {
delete newValues[key];
delete oldValues[key];
}
});
*/
const logEntry : ActionLog = {
userId: userId,
entity: target.name,
entriesOld: JSON.stringify(oldValues),
entriesNew: JSON.stringify(updates)
};
await em.getRepository(ActionLog)
.createQueryBuilder('al')
.insert()
.values([logEntry])
.execute();
}
}

@ -1,7 +1,10 @@
import { DataSource } from 'apollo-datasource';
import { Connection, getConnection } from 'typeorm';
import { Connection, EntityManager, getConnection } from 'typeorm';
import { WorkshopType } from '../../model/WorkshopType';
import { Workshop } from '../../model/Workshop';
import { ActionLogger, LockUtils } from './utils';
import { UserInputError } from 'apollo-server-express';
import { GraphQLError } from 'graphql';
export class WorkshopAPI extends DataSource {
connection: Connection
@ -21,6 +24,34 @@ export class WorkshopAPI extends DataSource {
return inserts.generatedMaps[0];
}
async lockWorkshop (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, Workshop, 'w', id, userId);
}
async unlockWorkshop (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, Workshop, 'w', id, userId);
}
async updateWorkshop (workshop: any, userId: number) {
const keepLock = workshop.keepLock;
delete workshop.keepLock;
await this.connection.transaction(async (entityManger: EntityManager) => {
if (await LockUtils.isLocked(entityManger, Workshop, 'w', workshop.id, userId)) {
throw new UserInputError('Attempting to update locked resource');
}
await ActionLogger.log(entityManger, Workshop, 'w', workshop, userId);
await entityManger.getRepository(Workshop)
.createQueryBuilder('w')
.update()
.set({ ...workshop })
.where('id = :id', { id: workshop.id })
.execute()
.then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } });
});
!keepLock && await this.unlockWorkshop(workshop.id, userId);
return await this.workshopById(workshop.id);
}
async createWorkshopType (workshopType: any) {
const inserts = await this.connection.getRepository(WorkshopType)
.createQueryBuilder('wt')
@ -31,6 +62,34 @@ export class WorkshopAPI extends DataSource {
return inserts.generatedMaps[0];
}
async lockWorkshopType (id: number, userId: number) {
return await LockUtils.lockEntity(this.connection, WorkshopType, 'wt', id, userId);
}
async unlockWorkshopType (id: number, userId: number) {
return await LockUtils.unlockEntity(this.connection, WorkshopType, 'wt', id, userId);
}
async updateWorkshopType (workshopType : any, userId: number) {
const keepLock = workshopType.keepLock;
delete workshopType.keepLock;
await this.connection.transaction(async (entityManager: EntityManager) => {
if (await LockUtils.isLocked(entityManager, WorkshopType, 'wt', workshopType.id, userId)) {
throw new UserInputError('Attempting to update locked resource');
}
await ActionLogger.log(entityManager, WorkshopType, 'wt', workshopType, userId);
await entityManager.getRepository(WorkshopType)
.createQueryBuilder('wt')
.update()
.set({ ...workshopType })
.where('id = :id', { id: workshopType.id })
.execute()
.then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } });
});
!keepLock && await this.unlockWorkshopType(workshopType.id, userId);
return await this.workshopTypeById(workshopType.id);
}
async workshopTypeById (id: number) {
return await this.connection.getRepository(WorkshopType)
.createQueryBuilder('wt')

@ -33,6 +33,7 @@ import { EquipmentType } from './model/EquipmentType';
import { BikeEventType } from './model/BikeEventType';
import { WorkshopAPI } from './datasources/db/workshopAPI';
import workshopResolvers from './resolvers/workshopResolvers';
import { ActionLog } from './model/ActionLog';
require('dotenv').config();
@ -84,7 +85,8 @@ createConnection({
EngagementType,
Workshop,
Person,
WorkshopType
WorkshopType,
ActionLog
],
synchronize: true,
logging: false

@ -0,0 +1,26 @@
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class ActionLog {
@PrimaryGeneratedColumn()
id?: number;
@CreateDateColumn()
date?: Date;
@Column()
userId: number;
@Column()
entity: string;
@Column({
type: 'text'
})
entriesOld: string;
@Column({
type: 'text'
})
entriesNew: string;
}

@ -154,7 +154,7 @@ export class CargoBike implements Lockable {
@Column()
name: string;
@OneToMany(type => Equipment, equipment => equipment.cargoBike, {
@OneToMany(type => Equipment, equipment => equipment.cargoBikeId, {
nullable: true,
eager: true
})

@ -39,30 +39,6 @@ export class Engagement implements Lockable {
})
dateRange: Date[];
@Column({
type: 'boolean',
default: false
})
roleCoordinator: boolean;
@Column({
type: 'boolean',
default: false
})
roleMentor: boolean;
@Column({
type: 'boolean',
default: false
})
roleAmbulance: boolean;
@Column({
type: 'boolean',
default: false
})
roleBringer: boolean;
@Column({
nullable: true,
type: 'timestamp'

@ -23,7 +23,7 @@ export class Equipment implements Lockable {
@JoinColumn({
name: 'cargoBikeId', referencedColumnName: 'id'
})
cargoBike: CargoBike;
cargoBikeId: number;
@Column({
type: 'timestamp',

@ -25,7 +25,8 @@ export class LoanPeriod {
to: Date;
@Column({
type: 'simple-array'
type: 'simple-array',
nullable: true
})
loanTimes: string[];
}

@ -35,7 +35,7 @@ export class Organisation implements Lockable {
@Column({
nullable: true
})
registerNo: string;
associationNo: string;
@Column(type => Address)
address: Address;

@ -15,7 +15,7 @@ export class TimeFrame implements Lockable {
})
dateRange: Date[];
@ManyToOne(type => LendingStation, lendingStation => lendingStation.timeFrames)
@ManyToOne(type => LendingStation, lendingStation => lendingStation.timeFrames, { nullable: false })
@JoinColumn({
name: 'lendingStationId'
})
@ -26,7 +26,7 @@ export class TimeFrame implements Lockable {
})
note: string;
@ManyToOne(type => CargoBike, cargoBike => cargoBike.timeFrames)
@ManyToOne(type => CargoBike, cargoBike => cargoBike.timeFrames, { nullable: false })
@JoinColumn({
name: 'cargoBikeId'
})

@ -27,14 +27,14 @@ export default {
},
bikeEventById: (_:any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadBikeEvent)) {
return dataSources.cargoBikeAPI.findBikeEventById(id);
return dataSources.cargoBikeAPI.bikeEventById(id);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
bikeEventTypeByd: (_:any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadBikeEvent)) {
return dataSources.cargoBikeAPI.findBikeEventTypeById(id);
return dataSources.cargoBikeAPI.bikeEventTypeById(id);
} else {
return new GraphQLError('Insufficient Permissions');
}
@ -229,6 +229,13 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
updateBikeEvent: (_: any, { bikeEvent }: { bikeEvent: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteBikeEvent)) {
return dataSources.cargoBikeAPI.updateBikeEvent(bikeEvent, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
createEquipment: (_: any, { equipment }: { equipment: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEquipment)) {
return dataSources.cargoBikeAPI.createEquipment({ equipment });
@ -264,12 +271,54 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
lockEquipmentType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEquipmentType)) {
return dataSources.cargoBikeAPI.lockEquipmentType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockEquipmentType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEquipmentType)) {
return dataSources.cargoBikeAPI.unlockEquipmentType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateEquipmentType: (_: any, { equipmentType }: { equipmentType: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEquipmentType)) {
return dataSources.cargoBikeAPI.updateEquipmentType(equipmentType, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
createBikeEventType: (_: any, { name }: { name: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEventType)) {
return dataSources.cargoBikeAPI.createBikeEventType(name);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockBikeEventType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEventType)) {
return dataSources.cargoBikeAPI.lockBikeEventType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockBikeEventType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEventType)) {
return dataSources.cargoBikeAPI.unlockBikeEventType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateBikeEventType: (_: any, { bikeEventType }: { bikeEventType: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEventType)) {
return dataSources.cargoBikeAPI.updateBikeEventType(bikeEventType, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
}
}
};

@ -68,6 +68,48 @@ export default {
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockPerson: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WritePerson)) {
return dataSources.contactInformationAPI.lockPerson(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockPerson: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WritePerson)) {
return dataSources.contactInformationAPI.unlockPerson(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updatePerson: (_: any, { person }: { person: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WritePerson)) {
return dataSources.contactInformationAPI.updatePerson(person, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockContactInformation: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WritePerson)) {
return dataSources.contactInformationAPI.lockContactInformation(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockContactInformation: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WritePerson)) {
return dataSources.contactInformationAPI.unlockContactInformation(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateContactInformation: (_: any, { contactInformation }: { contactInformation: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WritePerson)) {
return dataSources.contactInformationAPI.updateContactInformation(contactInformation, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
}
}
};

@ -7,7 +7,7 @@ export default {
Query: {
lendingStationById: (_: any, { id }: { id: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadLendingStation)) {
return dataSources.lendingStationAPI.lendingStationById({ id });
return dataSources.lendingStationAPI.lendingStationById(id);
} else {
return new GraphQLError('Insufficient Permissions');
}
@ -56,11 +56,32 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
contactInformationIntern (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadPerson)) {
return dataSources.contactInformationAPI.contactInternByLendingStationId(parent.id);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
contactInformationExtern (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadPerson)) {
return dataSources.contactInformationAPI.contactExternByLendingStationId(parent.id);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
organisation (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadOrganisation)) {
return dataSources.providerAPI.organisationByLendingStationId(parent.id);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
isLocked: (parent: any, __: any, { dataSources, req }: { dataSources: any; req: any }) => isLocked(parent, { dataSources, req })
},
LoanPeriod: {
loanTimes (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) {
return parent.loanTimes.split(',');
return parent.loanTimes ? parent.loanTimes : [];
}
},
TimeFrame: {
@ -111,7 +132,7 @@ export default {
},
updateLendingStation: (_: any, { lendingStation }:{ lendingStation: LendingStation }, { dataSources, req }:{dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteLendingStation)) {
return dataSources.lendingStationAPI.updateLendingStation({ lendingStation });
return dataSources.lendingStationAPI.updateLendingStation(lendingStation, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
@ -129,6 +150,20 @@ export default {
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockTimeFrame: (_: any, { id }:{ id: number }, { dataSources, req }:{dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteTimeFrame)) {
return dataSources.lendingStationAPI.unlockTimeFrame(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateTimeFrame: (_: any, { timeFrame }:{ timeFrame: LendingStation }, { dataSources, req }:{dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteTimeFrame)) {
return dataSources.lendingStationAPI.updateTimeFrame(timeFrame, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
}
}
};

@ -103,6 +103,27 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
lockParticipant: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteParticipant)) {
return dataSources.participantAPI.lockeParticipant(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockParticipant: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteParticipant)) {
return dataSources.participantAPI.unlockeParticipant(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateParticipant: (_: any, { participant }: { participant: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteParticipant)) {
return dataSources.participantAPI.updateParticipant(participant, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
createEngagement: (_: any, { engagement }: { engagement: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagement)) {
return dataSources.participantAPI.createEngagement(engagement);
@ -110,6 +131,48 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
lockEngagement: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagement)) {
return dataSources.participantAPI.lockEngagement(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockEngagement: (_: any, { id }: {id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagement)) {
return dataSources.participantAPI.unlockngagement(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateEngagement: (_: any, { engagement }: { engagement: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagement)) {
return dataSources.participantAPI.updateEngagement(engagement, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockEngagementType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagementType)) {
return dataSources.participantAPI.lockEngagementType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockEngagementType: (_: any, { id }: {id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagementType)) {
return dataSources.participantAPI.unlockngagementType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateEngagementType: (_: any, { engagementType }: { engagementType: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagementType)) {
return dataSources.participantAPI.updateEngagementType(engagementType, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
createEngagementType: (_: any, { engagementType }: { engagementType: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteEngagementType)) {
return dataSources.participantAPI.createEngagementType(engagementType);

@ -72,22 +72,71 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
lendingStations: (parent: any, __: any, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadLendingStation)) {
return dataSources.providerAPI.lendingStationByOrganisationId(parent.id);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
isLocked: (parent: any, __: any, { dataSources, req }: { dataSources: any; req: any }) => isLocked(parent, { dataSources, req })
},
Mutation: {
createProvider: (_: any, { provider }: { provider: number }, { dataSources, req }: { dataSources: any, req: any }) => {
createProvider: (_: any, { provider }: { provider: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteProvider)) {
return dataSources.providerAPI.createProvider(provider);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockProvider: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteProvider)) {
return dataSources.providerAPI.lockProvider(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockProvider: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteProvider)) {
return dataSources.providerAPI.unlockProvider(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateProvider: (_: any, { provider }: { provider: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteProvider)) {
return dataSources.providerAPI.updateProvider(provider, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
createOrganisation: (_: any, { organisation }: { organisation: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteOrganisation)) {
return dataSources.providerAPI.createOrganisation(organisation);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockOrganisation: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteOrganisation)) {
return dataSources.providerAPI.lockOrganisation(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockOrganisation: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteOrganisation)) {
return dataSources.providerAPI.unlockOrganisation(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateOrganisation: (_: any, { organisation }: { organisation: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteOrganisation)) {
return dataSources.providerAPI.updateOrganisation(organisation, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
}
}
};

@ -61,12 +61,54 @@ export default {
return new GraphQLError('Insufficient Permissions');
}
},
createWorkshopType: (_: any, { workshopType }: { workshopType: number }, { dataSources, req }: { dataSources: any, req: any }) => {
lockWorkshop: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshop)) {
return dataSources.workshopAPI.lockWorkshop(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockWorkshop: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshop)) {
return dataSources.workshopAPI.unlockWorkshop(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateWorkshop: (_: any, { workshop }: { workshop: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshop)) {
return dataSources.workshopAPI.updateWorkshop(workshop, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
createWorkshopType: (_: any, { workshopType }: { workshopType: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshopType)) {
return dataSources.workshopAPI.createWorkshopType(workshopType);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
lockWorkshopType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshopType)) {
return dataSources.workshopAPI.lockWorkshopType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
unlockWorkshopType: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshopType)) {
return dataSources.workshopAPI.unlockWorkshopType(id, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
updateWorkshopType: (_: any, { workshopType }: { workshopType: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteWorkshopType)) {
return dataSources.workshopAPI.updateWorkshopType(workshopType, req.userId);
} else {
return new GraphQLError('Insufficient Permissions');
}
}
}
};

@ -344,6 +344,23 @@ input ParticipantCreateInput {
memberCoreTeam: Boolean
}
input ParticipantUpdateInput {
id: ID!
"if not set, CURRENT_DATE will be used"
start: Date
end: Date
"must create contactinformation first, if you want to use new"
contactInformationId: ID
usernamefLotte: String
usernameSlack: String
"default: false"
memberADFC: Boolean
locationZIPs: [String]
"default: false"
memberCoreTeam: Boolean
keepLock: Boolean
}
type Workshop {
id: ID!
title: String!
@ -367,6 +384,17 @@ input WorkshopCreateInput {
trainer2Id: ID
}
input WorkshopUpdateInput {
id: ID!
title: String
description: String
date: Date!
workshopTypeId: ID
trainer1Id: ID
trainer2Id: ID
keepLock: Boolean
}
type WorkshopType {
id: ID!
name: String!
@ -380,6 +408,11 @@ input WorkshopTypeCreateInput {
name: String!
}
input WorkshopTypeUpdateInput {
id: ID!
name: String
}
type EngagementType {
id: ID!
name: String!
@ -395,6 +428,13 @@ input EngagementTypeCreateInput {
description: String
}
input EngagementTypeUpdateInput {
id: ID!
name: String
description: String
keepLock: Boolean
}
type Engagement {
id: ID!
engagementType: EngagementType!
@ -402,14 +442,6 @@ type Engagement {
to: Date
participant: Participant!
cargoBike: CargoBike!
roleCoordinator: Boolean!
roleEmployeeADFC: Boolean!
"""
Wahr, wenn die Person Pate ist.
"""
roleMentor: Boolean!
roleAmbulance: Boolean!
roleBringer: Boolean!
isLocked: Boolean!
"null if not locked by other user"
lockedBy: ID
@ -424,19 +456,15 @@ input EngagementCreateInput {
to: Date
participantId: ID!
cargoBikeId: ID!
"default: false"
roleCoordinator: Boolean
"default: false"
roleEmployeeADFC: Boolean
"""
Wahr, wenn die Person Pate ist.
default: false
"""
roleMentor: Boolean
"default: false"
roleAmbulance: Boolean
"default: false"
roleBringer: Boolean
}
input EngagementUpdateInput {
id: ID!
engagementTypeId: ID
from: Date
to: Date
participantId: ID
cargoBikeId: ID
keepLock: Boolean
}
type Taxes {
@ -511,6 +539,7 @@ input EquipmentTypeUpdateInput {
id: ID!
name: String
description: String
keepLock: Boolean
}
"An Event is a point in time, when the state of the bike somehow changed."
@ -547,6 +576,22 @@ input BikeEventCreateInput {
remark: String
}
input BikeEventUpdateInput {
id: ID!
bikeEventTypeId: ID
cargoBikeId: ID
responsibleId: ID
relatedId: ID
date: Date
description: String
"""
Path to documents
"""
documents: [String]
remark: String
keepLock: Boolean
}
type BikeEventType {
id: ID!
name: String!
@ -554,9 +599,10 @@ type BikeEventType {
lockedUntil: Date
}
input BikeEventTypeInput {
input BikeEventTypeUpdateInput {
id: ID!
name: String
keepLock: Boolean
}
"(dt. Anbieter) bezieht sich auf die Beziehung einer Person oder Organisation zum Lastenrad"
@ -580,6 +626,16 @@ input ProviderCreateInput {
cargoBikeIds: [ID]
}
input ProviderUpdateInput {
id: ID!
formName: String
privatePersonId: ID
organisationId: ID
"cargoBikes are added, you can not take existing relations away. use update cargoBike or add bike to another provider instead"
cargoBikeIds: [ID]
keepLock: Boolean
}
"""
A Person can have several instances of contact information.
The reason for this is, that some people have info for interns and externals that are different.
@ -600,6 +656,13 @@ input PersonCreateInput {
firstName: String!
}
input PersonUpdateInput {
id: ID!
name: String
firstName: String
keepLock: Boolean
}
type ContactInformation {
id: ID!
person: Person!
@ -631,6 +694,7 @@ input ContactInformationUpdateInput {
email: String
email2: String
note: String
keepLock: Boolean
}
type Organisation {
@ -655,17 +719,27 @@ type Organisation {
input OrganisationCreateInput {
address: AddressCreateInput!
name: String!
"(dt. Ausleihstation)"
lendingStationIds: [ID]
"registration number of association"
associationNo: String!
"If Club, at what court registered"
registeredAt: String
providerId: ID
contactInformationId: ID
otherData: String
}
input OrganisationUpdateInput {
id: ID!
address: AddressCreateInput
name: String
"registration number of association"
associationNo: String
"If Club, at what court registered"
registeredAt: String
contactInformationId: ID
otherData: String
keepLock: Boolean
}
"(dt. Standort)"
type LendingStation {
id: ID!
@ -678,6 +752,7 @@ type LendingStation {
cargoBikes: [CargoBike]
"Total amount of cargoBikes currently assigned to the lending station"
numCargoBikes: Int!
organisation: Organisation
isLocked: Boolean!
"null if not locked by other user"
lockedBy: ID
@ -693,6 +768,7 @@ input LendingStationCreateInput {
contactInformationExternId: ID
address: AddressCreateInput!
loanPeriod: LoanPeriodInput
organisationId: ID
}
"""
@ -701,9 +777,12 @@ If you want to create LendingStation with cargoBikes, use createTimeFrame and se
input LendingStationUpdateInput {
id: ID!
name: String
contactInformation: [ContactInformationUpdateInput]
contactInformationInternId: ID
contactInformationExternId: ID
address: AddressUpdateInput
loanPeriod: LoanPeriodInput
organisationId: ID
keepLock: Boolean
}
"""
@ -763,8 +842,8 @@ input TimeFrameUpdateInput {
from: Date
to: Date
note: String
lendingStation: ID
cargoBike: ID
lendingStationId: ID
cargoBikeId: ID
keepLock: Boolean
}
@ -824,7 +903,7 @@ type Query {
bikeEventTypes(offset: Int!, limit: Int!): [BikeEventType]
bikeEventTypeByd(id: ID!): BikeEventType
bikeEvents(offset: Int!, limit: Int!): [BikeEvent]!
bikeEventById(id:ID!): BikeEvent!
bikeEventById(id:ID!): BikeEvent
}
type Mutation {
@ -851,6 +930,9 @@ type Mutation {
"update Equipment, returns updated equipment. CargoBike will be null, if cargoBikeId is not set. Pass null for cargoBikeIs to delete the relation"
updateEquipment(equipment: EquipmentUpdateInput!): Equipment!
createEquipmentType(equipmentType: EquipmentTypeCreateInput!): EquipmentType!
lockEquipmentType(id: ID!): EquipmentType!
unlockEquipmentType(id: ID!): Boolean!
updateEquipmentType(equipmentType: EquipmentTypeUpdateInput!): EquipmentType!
"""
LENDINGSTATION
creates new lendingStation and returns lendingStation with new ID
@ -861,29 +943,62 @@ type Mutation {
"updates lendingStation of given ID with supplied fields and returns updated lendingStation"
updateLendingStation(lendingStation: LendingStationUpdateInput!): LendingStation!
createTimeFrame(timeFrame: TimeFrameCreateInput!): TimeFrame!
lockTimeFrame(id: ID!): TimeFrame
lockTimeFrame(id: ID!): TimeFrame!
unlockTimeFrame(id: ID!): Boolean!
updateTimeFrame(timeFrame: TimeFrameUpdateInput!): TimeFrame!
"""
BIKEEVENT
"""
createBikeEventType(name: String!): BikeEventType!
lockBikeEventType(id: ID!): BikeEventType!
unlockBikeEventType(id:ID!): Boolean!
updateBikeEventType(bikeEventType: BikeEventTypeUpdateInput!): BikeEventType!
"creates new BikeEvent"
createBikeEvent(bikeEvent: BikeEventCreateInput!): BikeEvent!
lockBikeEventById(id: ID!): BikeEvent
unlockBikeEventById(id: ID!): Boolean!
updateBikeEvent(bikeEvent: BikeEventUpdateInput!): BikeEvent
"""
PARTICIPANTS
"""
createParticipant(participant: ParticipantCreateInput!): Participant!
lockParticipant(id: ID!): Participant!
unlockParticipant(id: ID!): Boolean
updateParticipant(participant: ParticipantUpdateInput!): Participant!
createWorkshopType(workshopType: WorkshopTypeCreateInput!): WorkshopType!
lockWorkshopType(id: ID!): WorkshopType!
unlockWorkshopType(id: ID!): Boolean!
updateWorkshopType(workshopType: WorkshopTypeUpdateInput!): WorkshopType!
createWorkshop(workshop: WorkshopCreateInput!): Workshop!
lockWorkshop(id: ID!): Workshop!
unlockWorkshop(id: ID!): Boolean!
updateWorkshop(workshop: WorkshopUpdateInput!): Workshop!
"create new contactInfo"
createContactInformation(contactInformation: ContactInformationCreateInput!): ContactInformation!
lockContactInformation(id: ID!): ContactInformation!
unlockContactInformation(id: ID!): Boolean!
updateContactInformation(contactInformation: ContactInformationUpdateInput!): ContactInformation!
createPerson(person: PersonCreateInput!): Person!
lockPerson(id: ID!): Person!
unlockPerson(id: ID!): Person!
updatePerson(person: PersonUpdateInput!): Person!
lockEngagement(id: ID!): Engagement!
unlockEngagement(id: ID!): Boolean!
updateEngagement(engagement: EngagementUpdateInput!): Engagement!
createEngagementType(engagementType: EngagementTypeCreateInput!): EngagementType!
lockEngagementType(id: ID!): EngagementType!
unlockEngagementType(id: ID!): Boolean!
updateEngagementType(engagementType: EngagementTypeUpdateInput!): EngagementType!
"create Engagement"
createEngagement(engagement: EngagementCreateInput): Engagement!
createProvider(provider: ProviderCreateInput!): Provider!
lockProvider(id: ID!): Provider!
unlockProvider(id: ID!): Boolean!
updateProvider(provider: ProviderUpdateInput!): Provider!
createOrganisation(organisation: OrganisationCreateInput!): Organisation!
lockOrganisation(id: ID!): Organisation!
unlockOrganisation(id: ID!): Boolean!
updateOrganisation(organisation: OrganisationUpdateInput!): Organisation!
}
`;

Loading…
Cancel
Save