bugs and clean up and retry when db connection failed

pull/14/head
leonnicolas 4 years ago
parent 0098d9b3bf
commit 2e02920d75
No known key found for this signature in database
GPG Key ID: 088D0743E2B65C07

@ -11,7 +11,6 @@ Userserver and postgres are running e.g. with Julius' Docker Compose.
docker build -t <image name> . docker build -t <image name> .
docker run --rm -p 4000:4000 <image name> docker run --rm -p 4000:4000 <image name>
``` ```
The Dockerfile is pretty stupid and could produce a smaller image, e.g. with multistage build.
### Compile and run ### Compile and run
Install gulp if not installed Install gulp if not installed
```bash ```bash
@ -23,7 +22,7 @@ gulp
npm start npm start
``` ```
### For Development ### For Development
Install node_modules and gulp Install node\_modules and gulp
```bash ```bash
npm -g gulp npm -g gulp
npm install npm install

@ -13,14 +13,6 @@ export class ContactInformationAPI extends DataSource {
this.connection = getConnection(); this.connection = getConnection();
} }
async numContactInformationById (id: number) {
return await this.connection.getRepository(ContactInformation)
.createQueryBuilder('contactInformation')
.select()
.where('"contactInformation".id = :id', { id: id })
.getCount();
}
async contactInformation (offset: number, limit: number) { async contactInformation (offset: number, limit: number) {
return await this.connection.getRepository(ContactInformation) return await this.connection.getRepository(ContactInformation)
.createQueryBuilder('ci') .createQueryBuilder('ci')

@ -146,6 +146,14 @@ export class ParticipantAPI extends DataSource {
.loadOne(); .loadOne();
} }
async workshopsByParticipantId (id: number) {
return await this.connection.getRepository(Participant)
.createQueryBuilder('p')
.relation(Participant, 'workshopIds')
.of(id)
.loadMany();
}
/** /**
* creates participant and creates relation to given contactInformation * creates participant and creates relation to given contactInformation
* @param participant to be created * @param participant to be created
@ -160,6 +168,11 @@ export class ParticipantAPI extends DataSource {
.values([participant]) .values([participant])
.returning('*') .returning('*')
.execute(); .execute();
await entityManager.getRepository(Participant)
.createQueryBuilder('w')
.relation(Participant, 'workshopIds')
.of(participant.id)
.add(participant.workshopIds);
}); });
return this.participantById(inserts?.identifiers[0].id); return this.participantById(inserts?.identifiers[0].id);
} }
@ -179,6 +192,8 @@ export class ParticipantAPI extends DataSource {
if (await LockUtils.isLocked(entityManager, Participant, 'p', participant.id, userId)) { if (await LockUtils.isLocked(entityManager, Participant, 'p', participant.id, userId)) {
throw new GraphQLError('Participant is locked by another user'); throw new GraphQLError('Participant is locked by another user');
} }
const workshops = participant.workshopIds;
delete participant.workshopIds;
await ActionLogger.log(entityManager, Participant, 'p', participant, userId); await ActionLogger.log(entityManager, Participant, 'p', participant, userId);
await entityManager.getRepository(Participant) await entityManager.getRepository(Participant)
.createQueryBuilder('p') .createQueryBuilder('p')
@ -186,6 +201,11 @@ export class ParticipantAPI extends DataSource {
.set({ ...participant }) .set({ ...participant })
.where('id = :id', { id: participant.id }) .where('id = :id', { id: participant.id })
.execute().then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } }); .execute().then(value => { if (value.affected !== 1) { throw new GraphQLError('ID not found'); } });
await entityManager.getRepository(Participant)
.createQueryBuilder('w')
.relation(Participant, 'workshopIds')
.of(participant.id)
.add(workshops);
}); });
!keepLock && await this.unlockParticipant(participant.id, userId); !keepLock && await this.unlockParticipant(participant.id, userId);
return await this.participantById(participant.id); return await this.participantById(participant.id);

@ -5,6 +5,7 @@ import { Workshop } from '../../model/Workshop';
import { ActionLogger, deleteEntity, LockUtils } from './utils'; import { ActionLogger, deleteEntity, LockUtils } from './utils';
import { UserInputError } from 'apollo-server-express'; import { UserInputError } from 'apollo-server-express';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { Participant } from '../../model/Participant';
export class WorkshopAPI extends DataSource { export class WorkshopAPI extends DataSource {
connection: Connection connection: Connection
@ -152,4 +153,12 @@ export class WorkshopAPI extends DataSource {
.of(id) .of(id)
.loadOne(); .loadOne();
} }
async participantsByWorkshopId (id: number): Promise<Participant[]> {
return await this.connection.getRepository(Workshop)
.createQueryBuilder('w')
.relation(Workshop, 'participantIds')
.of(id)
.loadMany();
}
} }

@ -1,9 +1,9 @@
import { ApolloServer } from 'apollo-server-express'; import { ApolloServer } from 'apollo-server-express';
import bikeresolver from './resolvers/cargobikeResolver'; import bikeResolver from './resolvers/cargoBikeResolver';
import { CargoBikeAPI } from './datasources/db/cargobikeAPI'; import { CargoBikeAPI } from './datasources/db/cargobikeAPI';
import typeDefs from './schema/type-defs'; import typeDefs from './schema/type-defs';
import 'reflect-metadata'; import 'reflect-metadata';
import { createConnection } from 'typeorm'; import { ConnectionOptions, createConnection } from 'typeorm';
import { UserServerAPI } from './datasources/userserver/userserviceAPI'; import { UserServerAPI } from './datasources/userserver/userserviceAPI';
import express from 'express'; import express from 'express';
import { requiredPermissions } from './datasources/userserver/permission'; import { requiredPermissions } from './datasources/userserver/permission';
@ -19,13 +19,13 @@ import { Provider } from './model/Provider';
import { Engagement } from './model/Engagement'; import { Engagement } from './model/Engagement';
import { Workshop } from './model/Workshop'; import { Workshop } from './model/Workshop';
import { LendingStationAPI } from './datasources/db/lendingstationAPI'; import { LendingStationAPI } from './datasources/db/lendingstationAPI';
import lendingstationResolvers from './resolvers/lendingstationResolvers'; import lendingStationResolvers from './resolvers/lendingStationResolvers';
import { ParticipantAPI } from './datasources/db/participantAPI'; import { ParticipantAPI } from './datasources/db/participantAPI';
import participantResolvers from './resolvers/participantResolvers'; import participantResolvers from './resolvers/participantResolvers';
import { ContactInformationAPI } from './datasources/db/contactinformationAPI'; import { ContactInformationAPI } from './datasources/db/contactinformationAPI';
import providerResolvers from './resolvers/providerResolvers'; import providerResolvers from './resolvers/providerResolvers';
import { ProviderAPI } from './datasources/db/providerAPI'; import { ProviderAPI } from './datasources/db/providerAPI';
import contactinformationResolvers from './resolvers/contactinformationResolvers'; import contactInformationResolvers from './resolvers/contactInformationResolvers';
import { Person } from './model/Person'; import { Person } from './model/Person';
import { WorkshopType } from './model/WorkshopType'; import { WorkshopType } from './model/WorkshopType';
import { EngagementType } from './model/EngagementType'; import { EngagementType } from './model/EngagementType';
@ -34,11 +34,36 @@ import { BikeEventType } from './model/BikeEventType';
import { WorkshopAPI } from './datasources/db/workshopAPI'; import { WorkshopAPI } from './datasources/db/workshopAPI';
import workshopResolvers from './resolvers/workshopResolvers'; import workshopResolvers from './resolvers/workshopResolvers';
import { ActionLog } from './model/ActionLog'; 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';
require('dotenv').config(); require('dotenv').config();
const connOptions: ConnectionOptions = {
type: 'postgres',
url: process.env.POSTGRES_CONNECTION_URL,
entities: [
CargoBike,
BikeEvent,
BikeEventType,
ContactInformation,
Equipment,
EquipmentType,
LendingStation,
TimeFrame,
Organisation,
Participant,
Provider,
Engagement,
EngagementType,
Workshop,
Person,
WorkshopType,
ActionLog
],
synchronize: true,
logging: false
};
/** /**
* Function that is called to authenticate a user by using the user rpc server * Function that is called to authenticate a user by using the user rpc server
* @param req * @param req
@ -68,45 +93,26 @@ async function authenticate (req: any, res: any, next: any) {
} }
} }
createConnection({ function retryConnect (): any {
type: 'postgres', createConnection(connOptions).catch(error => {
url: process.env.POSTGRES_CONNECTION_URL, console.log(error);
entities: [ setTimeout(retryConnect, 3000);
CargoBike, });
BikeEvent, }
BikeEventType,
ContactInformation, retryConnect();
Equipment,
EquipmentType,
LendingStation,
TimeFrame,
Organisation,
Participant,
Provider,
Engagement,
EngagementType,
Workshop,
Person,
WorkshopType,
ActionLog
],
synchronize: true,
logging: false
}).then(async () => {
console.log('connected to db');
}).catch(error => console.log(error));
const userAPI = new UserServerAPI(process.env.RPC_HOST); const userAPI = new UserServerAPI(process.env.RPC_HOST);
const server = new ApolloServer({ const server = new ApolloServer({
resolvers: [ resolvers: [
bikeresolver, bikeResolver,
lendingstationResolvers, lendingStationResolvers,
participantResolvers, participantResolvers,
providerResolvers, providerResolvers,
contactinformationResolvers, contactInformationResolvers,
workshopResolvers, workshopResolvers,
actionlogResolvers actionLogResolvers
], ],
typeDefs, typeDefs,
dataSources: () => ({ dataSources: () => ({
@ -130,8 +136,7 @@ app.post('/graphql', authenticate);
app.get(/\/graphql?&.*query=/, authenticate); app.get(/\/graphql?&.*query=/, authenticate);
server.applyMiddleware({ app }); server.applyMiddleware({ app });
console.log(__dirname);
app.listen(4000, async () => { app.listen(4000, async () => {
console.log('Server listening on port 4000'); await userAPI.createDefinedPermissions().catch(
await userAPI.createDefinedPermissions(); err => console.log(err));
}); });

@ -1,4 +1,4 @@
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, OneToMany, ManyToMany } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn, OneToMany, ManyToMany, JoinTable } from 'typeorm';
import { ContactInformation } from './ContactInformation'; import { ContactInformation } from './ContactInformation';
import { Engagement } from './Engagement'; import { Engagement } from './Engagement';
import { Workshop } from './Workshop'; import { Workshop } from './Workshop';
@ -47,10 +47,11 @@ export class Participant implements Lockable {
@OneToMany(type => Engagement, engagement => engagement.participantId) @OneToMany(type => Engagement, engagement => engagement.participantId)
engagement: Engagement[]; engagement: Engagement[];
@ManyToMany(type => Workshop, workshop => workshop.participants, { @ManyToMany(type => Workshop, workshop => workshop.participantIds, {
nullable: true nullable: true
}) })
workshops: Workshop[]; @JoinTable()
workshopIds: number[];
@Column({ @Column({
nullable: false, nullable: false,

@ -22,10 +22,10 @@ export class Workshop implements Lockable {
}) })
date: Date; date: Date;
@ManyToMany(type => Participant, participant => participant.workshops, { @ManyToMany(type => Participant, participant => participant.workshopIds, {
nullable: true nullable: true
}) })
participants: Participant[]; participantIds: number[];
@ManyToOne(type => Participant, { @ManyToOne(type => Participant, {
nullable: false nullable: false

@ -62,6 +62,13 @@ export default {
return new GraphQLError('Insufficient Permissions'); return new GraphQLError('Insufficient Permissions');
} }
}, },
workshops (parent: any, _: any, { dataSources, req }: { dataSources: any, req: any }) {
if (req.permissions.includes(Permission.ReadWorkshop)) {
return (dataSources.participantAPI.workshopsByParticipantId(parent.id));
} else {
return new GraphQLError('Insufficient Permissions');
}
},
isLocked: (parent: any, __: any, { dataSources, req }: { dataSources: any; req: any }) => isLocked(parent, { dataSources, req }) isLocked: (parent: any, __: any, { dataSources, req }: { dataSources: any; req: any }) => isLocked(parent, { dataSources, req })
}, },
Engagement: { Engagement: {

@ -1,6 +1,7 @@
import { Permission } from '../datasources/userserver/permission'; import { Permission } from '../datasources/userserver/permission';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { isLocked } from '../datasources/db/utils'; import { isLocked } from '../datasources/db/utils';
import { Participant } from '../model/Participant';
export default { export default {
Query: { Query: {
@ -48,6 +49,13 @@ export default {
return new GraphQLError('Insufficient Permissions'); return new GraphQLError('Insufficient Permissions');
} }
}, },
participants (parent: any, _: any, { dataSources, req }: { dataSources: any; req: any }): Promise<Participant[]> | GraphQLError {
if (req.permissions.includes(Permission.ReadParticipant)) {
return dataSources.workshopAPI.participantsByWorkshopId(parent.id);
} else {
return new GraphQLError('Insufficient Permissions');
}
},
isLocked: (parent: any, __: any, { dataSources, req }: { dataSources: any; req: any }) => isLocked(parent, { dataSources, req }) isLocked: (parent: any, __: any, { dataSources, req }: { dataSources: any; req: any }) => isLocked(parent, { dataSources, req })
}, },
WorkshopType: { WorkshopType: {

@ -325,6 +325,7 @@ type Participant {
""" """
distributedActiveBikeParte: Boolean! distributedActiveBikeParte: Boolean!
engagement: [Engagement] engagement: [Engagement]
workshops: [Workshop]
isLocked: Boolean! isLocked: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID
@ -344,6 +345,7 @@ input ParticipantCreateInput {
locationZIPs: [String]! locationZIPs: [String]!
"default: false" "default: false"
memberCoreTeam: Boolean memberCoreTeam: Boolean
workshopIds: [ID]
} }
input ParticipantUpdateInput { input ParticipantUpdateInput {
@ -360,6 +362,7 @@ input ParticipantUpdateInput {
locationZIPs: [String] locationZIPs: [String]
"default: false" "default: false"
memberCoreTeam: Boolean memberCoreTeam: Boolean
workshopIds: [ID]
keepLock: Boolean keepLock: Boolean
} }
@ -371,6 +374,7 @@ type Workshop {
workshopType: WorkshopType! workshopType: WorkshopType!
trainer1: Participant! trainer1: Participant!
trainer2: Participant trainer2: Participant
participants: [Participant]
isLocked: Boolean! isLocked: Boolean!
"null if not locked by other user" "null if not locked by other user"
lockedBy: ID lockedBy: ID

Loading…
Cancel
Save