Merge pull request #34 from fLotte-meets-HWR-DB/dev

Dev
main
leonnicolas 4 years ago committed by GitHub
commit 111a5d8f6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,4 @@
FROM node:14.14.0-alpine3.10 AS builder FROM node:14.14.0-alpine3.10 AS builder
RUN npm --version
WORKDIR / WORKDIR /
COPY ./src /src COPY ./src /src

@ -56,6 +56,7 @@ import { ActionLog } from './model/ActionLog';
import actionLogResolvers from './resolvers/actionLogResolvers'; import actionLogResolvers from './resolvers/actionLogResolvers';
import { ActionLogAPI } from './datasources/db/actionLogAPI'; import { ActionLogAPI } from './datasources/db/actionLogAPI';
import bodyParser from 'body-parser'; import bodyParser from 'body-parser';
import { CopyConfig } from './model/CopyConfig';
const cors = require('cors'); const cors = require('cors');
require('dotenv').config(); require('dotenv').config();
@ -83,7 +84,8 @@ export function getConnectionOptions (): ConnectionOptions {
Workshop, Workshop,
Person, Person,
WorkshopType, WorkshopType,
ActionLog ActionLog,
CopyConfig
], ],
synchronize: true, synchronize: true,
logging: false logging: false
@ -127,8 +129,12 @@ export async function getApp (connOptions: ConnectionOptions) {
} }
} }
let cargoBikeAPI: CargoBikeAPI;
try { try {
await createConnection(connOptions); await createConnection(connOptions);
// init copy config
cargoBikeAPI = new CargoBikeAPI();
await cargoBikeAPI.populateCopyConfig();
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
@ -145,7 +151,7 @@ export async function getApp (connOptions: ConnectionOptions) {
], ],
typeDefs, typeDefs,
dataSources: () => ({ dataSources: () => ({
cargoBikeAPI: new CargoBikeAPI(), cargoBikeAPI: cargoBikeAPI,
lendingStationAPI: new LendingStationAPI(), lendingStationAPI: new LendingStationAPI(),
participantAPI: new ParticipantAPI(), participantAPI: new ParticipantAPI(),
contactInformationAPI: new ContactInformationAPI(), contactInformationAPI: new ContactInformationAPI(),

@ -18,6 +18,7 @@ This file is part of fLotte-API-Server.
*/ */
import { DataSource } from 'apollo-datasource'; import { DataSource } from 'apollo-datasource';
import { ApolloError } from 'apollo-server-express';
import { Connection, EntityManager, getConnection } from 'typeorm'; import { Connection, EntityManager, getConnection } from 'typeorm';
import { CargoBike } from '../../model/CargoBike'; import { CargoBike } from '../../model/CargoBike';
import { BikeEvent } from '../../model/BikeEvent'; import { BikeEvent } from '../../model/BikeEvent';
@ -31,6 +32,7 @@ import { BikeEventType } from '../../model/BikeEventType';
import { Actions } from '../../model/ActionLog'; import { Actions } from '../../model/ActionLog';
import { ResourceLockedError } from '../../errors/ResourceLockedError'; import { ResourceLockedError } from '../../errors/ResourceLockedError';
import { NotFoundError } from '../../errors/NotFoundError'; import { NotFoundError } from '../../errors/NotFoundError';
import { CopyConfig } from '../../model/CopyConfig';
/** /**
* extended datasource to feed resolvers with data about cargoBikes * extended datasource to feed resolvers with data about cargoBikes
@ -42,6 +44,55 @@ export class CargoBikeAPI extends DataSource {
this.connection = getConnection(); 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) { async getCargoBikes (offset?: number, limit?: number) {
return await DBUtils.getAllEntity(this.connection, CargoBike, 'cb', offset, limit); 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 * Finds cargo bike by id, returns null if id was not found
* @param id * @param id
*/ */
async findCargoBikeById (id: number) { async findCargoBikeById (id: number) : Promise<CargoBike> {
return await this.connection.getRepository(CargoBike) return await this.connection.getRepository(CargoBike)
.createQueryBuilder('cb') .createQueryBuilder('cb')
.select() .select()
.where('id = :id', { id }) .where('id = :id', { id })
.getOne(); .getOne().catch(() => {
throw new NotFoundError('CargoBike', 'id', id);
});
} }
async findCargoBikeByEngagementId (id: number) { async findCargoBikeByEngagementId (id: number) {
@ -498,4 +551,38 @@ export class CargoBikeAPI extends DataSource {
.of(id) .of(id)
.loadMany(); .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<boolean> {
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);
});
}
} }

@ -61,7 +61,8 @@ export enum Permission {
DeleteWorkshopType = 'WORKSHOP_TYPE_DELETE', DeleteWorkshopType = 'WORKSHOP_TYPE_DELETE',
DeleteEventType = 'EVENT_TYPE_DELETE', DeleteEventType = 'EVENT_TYPE_DELETE',
DeleteEquipmentType = 'EQUIPMENT_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 // Permissions where the creation will be requested on startup
@ -237,5 +238,9 @@ export const requiredPermissions = [
{ {
name: Permission.DeleteEngagementType, name: Permission.DeleteEngagementType,
description: 'Allows to delete engagement types' description: 'Allows to delete engagement types'
},
{
name: Permission.EditCopyConfig,
description: 'Allow to edit the copy config for cargo bikes'
} }
]; ];

@ -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 <https://www.gnu.org/licenses/>.
*/
/* 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;
}

@ -44,7 +44,7 @@ export class Participant implements Lockable {
@Column({ @Column({
nullable: true nullable: true
}) })
usernameflotte: string; usernamefLotte: string;
@Column({ @Column({
nullable: true nullable: true

@ -51,7 +51,7 @@ export default {
throw new PermissionError(); throw new PermissionError();
} }
}, },
bikeEventTypeByd: (_:any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => { bikeEventTypeById: (_:any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadBikeEvent)) { if (req.permissions.includes(Permission.ReadBikeEvent)) {
return dataSources.cargoBikeAPI.bikeEventTypeById(id); return dataSources.cargoBikeAPI.bikeEventTypeById(id);
} else { } else {
@ -92,6 +92,13 @@ export default {
} else { } else {
throw new PermissionError(); 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: { CargoBike: {
@ -395,6 +402,13 @@ export default {
} else { } else {
throw new PermissionError(); 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();
}
} }
} }
}; };

@ -25,7 +25,7 @@ export default {
Query: { Query: {
participantById: (_: any, { id }: { id: any }, { dataSources, req }: { dataSources: any, req: any }) => { participantById: (_: any, { id }: { id: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.ReadParticipant)) { if (req.permissions.includes(Permission.ReadParticipant)) {
return dataSources.participantAPI.getParticipantById(id); return dataSources.participantAPI.participantById(id);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }
@ -119,6 +119,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 })
}, },
EngagementType: {
isLockedByMe: (parent: any, __: any, { req }: { req: any }) => isLockedByMe(parent, { req }),
isLocked: (parent: any, __: any, { req }: { req: any }) => isLocked(parent, { req })
},
Mutation: { Mutation: {
createParticipant: (_: any, { participant }: { participant: any }, { dataSources, req }: { dataSources: any, req: any }) => { createParticipant: (_: any, { participant }: { participant: any }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteParticipant)) { if (req.permissions.includes(Permission.WriteParticipant)) {
@ -136,7 +140,7 @@ export default {
}, },
unlockParticipant: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => { unlockParticipant: (_: any, { id }: { id: number }, { dataSources, req }: { dataSources: any, req: any }) => {
if (req.permissions.includes(Permission.WriteParticipant)) { if (req.permissions.includes(Permission.WriteParticipant)) {
return dataSources.participantAPI.unlockeParticipant(id, req.userId); return dataSources.participantAPI.unlockParticipant(id, req.userId);
} else { } else {
throw new PermissionError(); throw new PermissionError();
} }

@ -35,6 +35,7 @@ export default gql`
The kind of currency depends on the database. The kind of currency depends on the database.
""" """
scalar Money scalar Money
scalar Link
"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 {
@ -667,7 +668,7 @@ export default gql`
""" """
Path to documents Path to documents
""" """
documents: [String!]! documents: [Link!]!
remark: String remark: String
isLocked: Boolean! isLocked: Boolean!
isLockedByMe: Boolean! isLockedByMe: Boolean!
@ -686,7 +687,7 @@ export default gql`
""" """
Path to documents Path to documents
""" """
documents: [String] documents: [Link]
remark: String remark: String
} }
@ -701,7 +702,7 @@ export default gql`
""" """
Path to documents Path to documents
""" """
documents: [String] documents: [Link]
remark: String remark: String
keepLock: Boolean keepLock: Boolean
} }
@ -1011,6 +1012,8 @@ 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
"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." "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
@ -1057,7 +1060,7 @@ export default gql`
persons(offset: Int, limit: Int): [Person!] persons(offset: Int, limit: Int): [Person!]
"If offset or limit is not provided, both values are ignored" "If offset or limit is not provided, both values are ignored"
bikeEventTypes(offset: Int, limit: Int): [BikeEventType!] bikeEventTypes(offset: Int, limit: Int): [BikeEventType!]
bikeEventTypeByd(id: ID!): BikeEventType bikeEventTypeById(id: ID!): BikeEventType
"If offset or limit is not provided, both values are ignored" "If offset or limit is not provided, both values are ignored"
bikeEvents(offset: Int, limit: Int): [BikeEvent!]! bikeEvents(offset: Int, limit: Int): [BikeEvent!]!
bikeEventById(id:ID!): BikeEvent bikeEventById(id:ID!): BikeEvent
@ -1083,6 +1086,8 @@ export default gql`
updateCargoBike(cargoBike: CargoBikeUpdateInput!): CargoBike! updateCargoBike(cargoBike: CargoBikeUpdateInput!): CargoBike!
"true on success" "true on success"
deleteCargoBike(id: ID!): Boolean! deleteCargoBike(id: ID!): Boolean!
"edit or add key value pair to copy config for cargo bikes"
editCopyConfig(key: String!, value: Boolean!): Boolean!
""" """
EQUIPMENT EQUIPMENT
creates new peace of unique Equipment creates new peace of unique Equipment

Loading…
Cancel
Save