From 14b9d3445a4f1725decc6bae69ecdf964716041b Mon Sep 17 00:00:00 2001 From: leonnicolas Date: Fri, 25 Sep 2020 15:58:52 +0200 Subject: [PATCH] generic methods to lock and unlock Entities --- src/datasources/db/cargobikeAPI.ts | 96 ++-------------------- src/datasources/db/lendingstationAPI.ts | 52 ++++++++++++ src/datasources/db/utils.ts | 101 ++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 91 deletions(-) create mode 100644 src/datasources/db/utils.ts diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index 8206de8..2dc7288 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -7,6 +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'; /** * extended datasource to feed resolvers with data about cargoBikes @@ -66,7 +67,7 @@ export class CargoBikeAPI extends DataSource { } async lockCargoBike (id: number, req: any, dataSources: any) { - if (await this.lockEntity(CargoBike, 'cargobike', id, req, dataSources)) { + if (await LockUtils.lockEntity(this.connection, CargoBike, 'cargobike', id, req, dataSources)) { return this.findCargoBikeById(id); } else { return new GraphQLError('CargoBike is locked by other user'); @@ -77,103 +78,16 @@ export class CargoBikeAPI extends DataSource { return this.unlockEntity(CargoBike, 'cargobike', id, req, dataSources); } - /** - * locks any entity that implemts Lockable - */ async lockEntity (target: ObjectType, alias: string, id: number, req: any, dataSources: any) { - const token = req.headers.authorization?.replace('Bearer ', ''); - const userId = await dataSources.userAPI.getUserId(token); - const lock = await this.connection.getRepository(target) - .createQueryBuilder(alias) - .select([ - alias + '.lockedUntil', - alias + '.lockedBy' - ]) - .where('id = :id', { - id: id - }) - .andWhere(alias + '.lockedUntil > CURRENT_TIMESTAMP') - .getOne(); - // eslint-disable-next-line eqeqeq - if (!lock?.lockedUntil || lock?.lockedBy == userId) { - // no lock -> set lock - await this.connection.getRepository(target) - .createQueryBuilder(alias) - .update() - .set({ - lockedUntil: () => 'CURRENT_TIMESTAMP + INTERVAL \'10 MINUTE\'', - lockedBy: userId - }) - .where('id = :id', { id: id }) - .execute(); - return true; - } else { - // lock was set - return false; - } + return LockUtils.lockEntity(this.connection, target, alias, id, req, dataSources); } async unlockEntity (target: ObjectType, alias: string, id: number, req: any, dataSources: any) { - const token = req.headers.authorization?.replace('Bearer ', ''); - const userId = await dataSources.userAPI.getUserId(token); - const lock = await this.connection.getRepository(target) - .createQueryBuilder(alias) - .select([ - alias + '.lockedUntil', - alias + '.lockedBy' - ]) - .where('id = :id', { - id: id - }) - .andWhere(alias + '.lockedUntil > CURRENT_TIMESTAMP') - .getOne(); - if (!lock?.lockedUntil) { - // no lock - return true; - // eslint-disable-next-line eqeqeq - } else if (lock?.lockedBy == userId) { - // user can unlock - await this.connection.getRepository(target) - .createQueryBuilder(alias) - .update() - .set({ - lockedUntil: null, - lockedBy: null - }) - .where('id = :id', { id: id }) - .execute(); - return true; - } else { - // enity is locked by other user - return false; - } + return LockUtils.unlockEntity(this.connection, target, alias, id, req, dataSources); } async isLocked (id: number, req: any, dataSources: any) { - const token = req.headers.authorization?.replace('Bearer ', ''); - const userId = await dataSources.userAPI.getUserId(token); - const lock = await this.connection.getRepository(CargoBike) - .createQueryBuilder('cargobike') - .select([ - 'cargobike' + '.lockedUntil', - 'cargobike' + '.lockedBy' - ]) - .where('id = :id', { - id: id - }) - .andWhere('cargobike' + '.lockedUntil > CURRENT_TIMESTAMP') - .getOne(); - if (!lock?.lockedUntil) { - // no lock - return false; - // eslint-disable-next-line eqeqeq - } else if (lock?.lockedBy == userId) { - // user has locked - return false; - } else { - // enity is locked by other user - return true; - } + return LockUtils.isLocked(this.connection, CargoBike, 'cargobike', id, req, dataSources); } /** diff --git a/src/datasources/db/lendingstationAPI.ts b/src/datasources/db/lendingstationAPI.ts index 708a5f5..bafc0d0 100644 --- a/src/datasources/db/lendingstationAPI.ts +++ b/src/datasources/db/lendingstationAPI.ts @@ -209,4 +209,56 @@ export class LendingStationAPI extends DataSource { inserts.generatedMaps[0].id = inserts.identifiers[0].id; 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; + } + return new ApolloError('Transaction could not be completed'); + } + inserts.generatedMaps[0].id = inserts.identifiers[0].id; + return inserts.generatedMaps[0]; + }*/ } diff --git a/src/datasources/db/utils.ts b/src/datasources/db/utils.ts new file mode 100644 index 0000000..688f8b1 --- /dev/null +++ b/src/datasources/db/utils.ts @@ -0,0 +1,101 @@ +import { Connection, ObjectType } from 'typeorm'; +import { CargoBike, Lockable } from '../../model/CargoBike'; + +export class LockUtils { + static getToken (req: any) : string { + return req.headers.authorization?.replace('Bearer ', ''); + } + + /** + * locks any entity that implements Lockable + */ + static async lockEntity (connection: Connection, target: ObjectType, alias: string, id: number, req: any, dataSources: any) { + const token = this.getToken(req); + const userId = await dataSources.userAPI.getUserId(token); + const lock = await connection.getRepository(target) + .createQueryBuilder(alias) + .select([ + alias + '.lockedUntil', + alias + '.lockedBy' + ]) + .where('id = :id', { + id: id + }) + .andWhere(alias + '.lockedUntil > CURRENT_TIMESTAMP') + .getOne(); + // eslint-disable-next-line eqeqeq + if (!lock?.lockedUntil || lock?.lockedBy == userId) { + // no lock -> set lock + await connection.getRepository(target) + .createQueryBuilder(alias) + .update() + .set({ + lockedUntil: () => 'CURRENT_TIMESTAMP + INTERVAL \'10 MINUTE\'', + lockedBy: userId + }) + .where('id = :id', { id: id }) + .execute(); + return true; + } else { + // lock was set + return false; + } + } + + static async unlockEntity (connection: Connection, target: ObjectType, alias: string, id: number, req: any, dataSources: any) { + const token = this.getToken(req); + const userId = await dataSources.userAPI.getUserId(token); + const lock = await connection.getRepository(target) + .createQueryBuilder(alias) + .select([ + alias + '.lockedUntil', + alias + '.lockedBy' + ]) + .where('id = :id', { + id: id + }) + .andWhere(alias + '.lockedUntil > CURRENT_TIMESTAMP') + .getOne(); + if (!lock?.lockedUntil) { + // no lock + return true; + // eslint-disable-next-line eqeqeq + } else if (lock?.lockedBy == userId) { + // user can unlock + await connection.getRepository(target) + .createQueryBuilder(alias) + .update() + .set({ + lockedUntil: null, + lockedBy: null + }) + .where('id = :id', { id: id }) + .execute(); + return true; + } else { + // entity is locked by other user + return false; + } + } + + static async isLocked (connection: Connection, target: ObjectType, alias: string, id: number, req: any, dataSources: any) { + const token = this.getToken(req); + const userId = await dataSources.userAPI.getUserId(token); + const lock = await connection.getRepository(CargoBike) + .createQueryBuilder(alias) + .select([ + alias + '.lockedUntil', + alias + '.lockedBy' + ]) + .where('id = :id', { + id: id + }) + .andWhere(alias + '.lockedUntil > CURRENT_TIMESTAMP') + .getOne(); + if (!lock?.lockedUntil) { + // no lock + return false; + // eslint-disable-next-line eqeqeq + } else return lock?.lockedBy != userId; + } +}