From da8e2f65ed28b85bbd8d7d936e2632d78db648ca Mon Sep 17 00:00:00 2001 From: leonnicolas Date: Thu, 10 Dec 2020 16:55:46 +0100 Subject: [PATCH] feature: copy bikes with specified fields --- src/app.ts | 10 ++- src/datasources/db/cargobikeAPI.ts | 92 +++++++++++++++++++++++- src/datasources/userserver/permission.ts | 7 +- src/model/CopyConfig.ts | 34 +++++++++ src/resolvers/cargoBikeResolver.ts | 14 ++++ src/schema/type-defs.ts | 4 ++ 6 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 src/model/CopyConfig.ts diff --git a/src/app.ts b/src/app.ts index 9e5d13f..0b5f44c 100644 --- a/src/app.ts +++ b/src/app.ts @@ -56,6 +56,7 @@ import { ActionLog } from './model/ActionLog'; import actionLogResolvers from './resolvers/actionLogResolvers'; import { ActionLogAPI } from './datasources/db/actionLogAPI'; import bodyParser from 'body-parser'; +import { CopyConfig } from './model/CopyConfig'; const cors = require('cors'); require('dotenv').config(); @@ -83,7 +84,8 @@ export function getConnectionOptions (): ConnectionOptions { Workshop, Person, WorkshopType, - ActionLog + ActionLog, + CopyConfig ], synchronize: true, logging: false @@ -127,8 +129,12 @@ export async function getApp (connOptions: ConnectionOptions) { } } + let cargoBikeAPI: CargoBikeAPI; try { await createConnection(connOptions); + // init copy config + cargoBikeAPI = new CargoBikeAPI(); + await cargoBikeAPI.populateCopyConfig(); } catch (err) { console.error(err); } @@ -145,7 +151,7 @@ export async function getApp (connOptions: ConnectionOptions) { ], typeDefs, dataSources: () => ({ - cargoBikeAPI: new CargoBikeAPI(), + cargoBikeAPI: cargoBikeAPI, lendingStationAPI: new LendingStationAPI(), participantAPI: new ParticipantAPI(), contactInformationAPI: new ContactInformationAPI(), diff --git a/src/datasources/db/cargobikeAPI.ts b/src/datasources/db/cargobikeAPI.ts index 40f7093..5b161f4 100644 --- a/src/datasources/db/cargobikeAPI.ts +++ b/src/datasources/db/cargobikeAPI.ts @@ -18,6 +18,7 @@ This file is part of fLotte-API-Server. */ import { DataSource } from 'apollo-datasource'; +import { ApolloError } from 'apollo-server-express'; import { Connection, EntityManager, getConnection } from 'typeorm'; import { CargoBike } from '../../model/CargoBike'; import { BikeEvent } from '../../model/BikeEvent'; @@ -31,6 +32,7 @@ import { BikeEventType } from '../../model/BikeEventType'; import { Actions } from '../../model/ActionLog'; import { ResourceLockedError } from '../../errors/ResourceLockedError'; import { NotFoundError } from '../../errors/NotFoundError'; +import { CopyConfig } from '../../model/CopyConfig'; /** * extended datasource to feed resolvers with data about cargoBikes @@ -42,6 +44,55 @@ export class CargoBikeAPI extends DataSource { this.connection = getConnection(); } + private async addCopyConfig (key: string, value: boolean) { + return await this.connection.getRepository(CopyConfig) + .createQueryBuilder('cc') + .insert() + .values([{ key, value }]) + .execute(); + } + + /** + * populate CopyConfig with default values + */ + async populateCopyConfig () { + if (await this.connection.getRepository(CopyConfig) + .createQueryBuilder('cc') + .select() + .getCount() === 0) { + const config: CopyConfig[] = [ + { key: 'id', value: false }, + { key: 'group', value: true }, + { key: 'name', value: true }, + { key: 'state', value: true }, + { key: 'equipmentIds', value: false }, + { key: 'equipmentTypeIds', value: false }, + { key: 'security', value: false }, + { key: 'stickerBikeNameState', value: true }, + { key: 'note', value: true }, + { key: 'providerId', value: false }, + { key: 'bikeEvents', value: false }, + { key: 'insuranceData', value: true }, + { key: 'timeFrames', value: false }, + { key: 'engagement', value: false }, + { key: 'taxes', value: true }, + { key: 'description', value: true }, + { key: 'modelName', value: true }, + { key: 'numberOfWheels', value: true }, + { key: 'forCargo', value: true }, + { key: 'forChildren', value: true }, + { key: 'numberOfChildren', value: true }, + { key: 'technicalEquipment', value: true }, + { key: 'dimensionsAndLoad', value: true } + ]; + await this.connection.getRepository(CopyConfig) + .createQueryBuilder('cc') + .insert() + .values(config) + .execute(); + } + } + async getCargoBikes (offset?: number, limit?: number) { return await DBUtils.getAllEntity(this.connection, CargoBike, 'cb', offset, limit); } @@ -50,12 +101,14 @@ export class CargoBikeAPI extends DataSource { * Finds cargo bike by id, returns null if id was not found * @param id */ - async findCargoBikeById (id: number) { + async findCargoBikeById (id: number) : Promise { return await this.connection.getRepository(CargoBike) .createQueryBuilder('cb') .select() .where('id = :id', { id }) - .getOne(); + .getOne().catch(() => { + throw new NotFoundError('CargoBike', 'id', id); + }); } async findCargoBikeByEngagementId (id: number) { @@ -498,4 +551,39 @@ export class CargoBikeAPI extends DataSource { .of(id) .loadMany(); } + + async copyCargoBikeById (id: number) { + // load keys + const keys = await this.connection.getRepository(CopyConfig) + .createQueryBuilder('cc') + .select() + .getMany(); + const cargoBike: any = await this.findCargoBikeById(id); + keys.forEach(value => { + if (value.value === false && value.key !== 'id') { + delete cargoBike[value.key]; + } + }); + return cargoBike; + } + + async editCopyConfig (key: string, value: boolean) : Promise { + return await this.connection.getRepository(CopyConfig) + .createQueryBuilder('cc') + .update() + .set({ value: value }) + .where('key = :key', { key: key }) + .returning('*') + .execute().then((v) => { + if (v.affected !== 1) { + throw new NotFoundError('CopyConfig', 'key', key); + } else { + return true; + } + }, + (e) => { + throw new ApolloError(e); + }); + return false; + } } diff --git a/src/datasources/userserver/permission.ts b/src/datasources/userserver/permission.ts index 2506882..cd66efd 100644 --- a/src/datasources/userserver/permission.ts +++ b/src/datasources/userserver/permission.ts @@ -61,7 +61,8 @@ export enum Permission { DeleteWorkshopType = 'WORKSHOP_TYPE_DELETE', DeleteEventType = 'EVENT_TYPE_DELETE', DeleteEquipmentType = 'EQUIPMENT_TYPE_DELETE', - DeleteEngagementType = 'ENGAGEMENT_TYPE_DELETE' + DeleteEngagementType = 'ENGAGEMENT_TYPE_DELETE', + EditCopyConfig = 'EDIT_COPY_CONFIG' } // Permissions where the creation will be requested on startup @@ -237,5 +238,9 @@ export const requiredPermissions = [ { name: Permission.DeleteEngagementType, description: 'Allows to delete engagement types' + }, + { + name: Permission.EditCopyConfig, + description: 'Allow to edit the copy config for cargo bikes' } ]; diff --git a/src/model/CopyConfig.ts b/src/model/CopyConfig.ts new file mode 100644 index 0000000..adb8b51 --- /dev/null +++ b/src/model/CopyConfig.ts @@ -0,0 +1,34 @@ +/* +Copyright (C) 2020 Leon Löchner + +This file is part of fLotte-API-Server. + + fLotte-API-Server is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + fLotte-API-Server is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with fLotte-API-Server. If not, see . +*/ + +/* eslint no-unused-vars: "off" */ + +import { Column, Entity, PrimaryColumn } from 'typeorm'; + +@Entity() +export class CopyConfig { + @PrimaryColumn() + key: string; + + @Column({ + type: 'boolean', + default: true + }) + value: boolean; +} diff --git a/src/resolvers/cargoBikeResolver.ts b/src/resolvers/cargoBikeResolver.ts index ac788aa..da64451 100644 --- a/src/resolvers/cargoBikeResolver.ts +++ b/src/resolvers/cargoBikeResolver.ts @@ -92,6 +92,13 @@ export default { } else { throw new PermissionError(); } + }, + copyCargoBikeById: (_:any, { id }: {id:number}, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.ReadBike)) { + return dataSources.cargoBikeAPI.copyCargoBikeById(id); + } else { + throw new PermissionError(); + } } }, CargoBike: { @@ -395,6 +402,13 @@ export default { } else { throw new PermissionError(); } + }, + editCopyConfig: (_: any, { key, value }: { key: string, value: boolean }, { dataSources, req }: { dataSources: any, req: any }) => { + if (req.permissions.includes(Permission.EditCopyConfig)) { + return dataSources.cargoBikeAPI.editCopyConfig(key, value); + } else { + throw new PermissionError(); + } } } }; diff --git a/src/schema/type-defs.ts b/src/schema/type-defs.ts index 6b25839..a4e005c 100644 --- a/src/schema/type-defs.ts +++ b/src/schema/type-defs.ts @@ -1012,6 +1012,8 @@ export default gql` type Query { "Will (eventually) return all properties of cargo bike" cargoBikeById(id:ID!): CargoBike + "copies cargoBike, the id of the copy needs to be delted by the front end. This function will not create a new entry in the data base" + copyCargoBikeById(id: ID!): CargoBike "Returns cargoBikes ordered by name ascending. If offset or limit is not provided, both values are ignored." cargoBikes(offset: Int, limit: Int): [CargoBike!]! engagementById(id: ID!): Engagement @@ -1084,6 +1086,8 @@ export default gql` updateCargoBike(cargoBike: CargoBikeUpdateInput!): CargoBike! "true on success" deleteCargoBike(id: ID!): Boolean! + "edit or add key value pair to copy config for cargo bikes" + editCopyConfig(key: String!, value: Boolean!): Boolean! """ EQUIPMENT creates new peace of unique Equipment