Merge pull request #28 from fLotte-meets-HWR-DB/develop
Improved Error Handling And tests for graphql queriespull/31/head
commit
d898a1e9aa
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,167 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
import { ApolloServer } from 'apollo-server-express';
|
||||
import bikeResolver from './resolvers/cargoBikeResolver';
|
||||
import { CargoBikeAPI } from './datasources/db/cargobikeAPI';
|
||||
import typeDefs from './schema/type-defs';
|
||||
import 'reflect-metadata';
|
||||
import { ConnectionOptions, createConnection } from 'typeorm';
|
||||
import { UserServerAPI } from './datasources/userserver/userserviceAPI';
|
||||
import express from 'express';
|
||||
import { requiredPermissions } from './datasources/userserver/permission';
|
||||
import { CargoBike } from './model/CargoBike';
|
||||
import { BikeEvent } from './model/BikeEvent';
|
||||
import { ContactInformation } from './model/ContactInformation';
|
||||
import { Equipment } from './model/Equipment';
|
||||
import { LendingStation } from './model/LendingStation';
|
||||
import { TimeFrame } from './model/TimeFrame';
|
||||
import { Participant } from './model/Participant';
|
||||
import { Organisation } from './model/Organisation';
|
||||
import { Provider } from './model/Provider';
|
||||
import { Engagement } from './model/Engagement';
|
||||
import { Workshop } from './model/Workshop';
|
||||
import { LendingStationAPI } from './datasources/db/lendingstationAPI';
|
||||
import lendingStationResolvers from './resolvers/lendingStationResolvers';
|
||||
import { ParticipantAPI } from './datasources/db/participantAPI';
|
||||
import participantResolvers from './resolvers/participantResolvers';
|
||||
import { ContactInformationAPI } from './datasources/db/contactinformationAPI';
|
||||
import providerResolvers from './resolvers/providerResolvers';
|
||||
import { ProviderAPI } from './datasources/db/providerAPI';
|
||||
import contactInformationResolvers from './resolvers/contactInformationResolvers';
|
||||
import { Person } from './model/Person';
|
||||
import { WorkshopType } from './model/WorkshopType';
|
||||
import { EngagementType } from './model/EngagementType';
|
||||
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';
|
||||
import actionLogResolvers from './resolvers/actionLogResolvers';
|
||||
import { ActionLogAPI } from './datasources/db/actionLogAPI';
|
||||
import bodyParser from 'body-parser';
|
||||
const cors = require('cors');
|
||||
require('dotenv').config();
|
||||
|
||||
export const userAPI = new UserServerAPI(process.env.RPC_HOST);
|
||||
|
||||
export function getConnectionOptions (): ConnectionOptions {
|
||||
return {
|
||||
// @ts-ignore
|
||||
type: process.env.DATABASE_TYPE,
|
||||
url: process.env.DATABASE_URL,
|
||||
database: process.env.DATABASE_NAME,
|
||||
entities: [
|
||||
CargoBike,
|
||||
BikeEvent,
|
||||
BikeEventType,
|
||||
ContactInformation,
|
||||
Equipment,
|
||||
EquipmentType,
|
||||
LendingStation,
|
||||
TimeFrame,
|
||||
Organisation,
|
||||
Participant,
|
||||
Provider,
|
||||
Engagement,
|
||||
EngagementType,
|
||||
Workshop,
|
||||
Person,
|
||||
WorkshopType,
|
||||
ActionLog
|
||||
],
|
||||
synchronize: true,
|
||||
logging: false
|
||||
};
|
||||
}
|
||||
|
||||
export async function getApp (connOptions: ConnectionOptions) {
|
||||
/**
|
||||
* Function that is called to authenticate a user by using the user rpc server
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
async function authenticate (req: any, res: any, next: any) {
|
||||
if (req.body.operationName === 'IntrospectionQuery') {
|
||||
next();
|
||||
} else if (process.env.NODE_ENV === 'develop') {
|
||||
req.permissions = requiredPermissions.map((e) => e.name);
|
||||
req.userId = await userAPI.getUserId(req.headers.authorization?.replace('Bearer ', ''));
|
||||
next();
|
||||
} else {
|
||||
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||
if (token) {
|
||||
if (await userAPI.validateToken(token)) {
|
||||
req.permissions = await userAPI.getUserPermissions(token);
|
||||
req.userId = await userAPI.getUserId(req.headers.authorization?.replace('Bearer ', ''));
|
||||
next();
|
||||
} else {
|
||||
res.status(401);
|
||||
res.send('Unauthorized');
|
||||
}
|
||||
} else {
|
||||
res.status(401);
|
||||
res.send('Unauthorized');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await createConnection(connOptions);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
const server = new ApolloServer({
|
||||
resolvers: [
|
||||
bikeResolver,
|
||||
lendingStationResolvers,
|
||||
participantResolvers,
|
||||
providerResolvers,
|
||||
contactInformationResolvers,
|
||||
workshopResolvers,
|
||||
actionLogResolvers
|
||||
],
|
||||
typeDefs,
|
||||
dataSources: () => ({
|
||||
cargoBikeAPI: new CargoBikeAPI(),
|
||||
lendingStationAPI: new LendingStationAPI(),
|
||||
participantAPI: new ParticipantAPI(),
|
||||
contactInformationAPI: new ContactInformationAPI(),
|
||||
providerAPI: new ProviderAPI(),
|
||||
workshopAPI: new WorkshopAPI(),
|
||||
actionLogAPI: new ActionLogAPI(),
|
||||
userAPI
|
||||
}),
|
||||
context: (req: any) => {
|
||||
return req;
|
||||
}
|
||||
});
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
app.post('/graphql', authenticate);
|
||||
app.get(/\/graphql?&.*query=/, authenticate);
|
||||
|
||||
server.applyMiddleware({ app });
|
||||
|
||||
return app;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { ApolloError } from 'apollo-server-express';
|
||||
|
||||
export class PermissionError extends ApolloError {
|
||||
constructor () {
|
||||
super('Insufficient permissions.', 'INSUFFICIENT_PERMISSIONS');
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
function gql (strings: TemplateStringsArray) {
|
||||
}
|
||||
|
||||
export const CREATE_CARGO_BIKE = gql`
|
||||
mutation {
|
||||
createCargoBike(
|
||||
cargoBike: {
|
||||
group: KL
|
||||
name: "Test"
|
||||
modelName: "cool"
|
||||
numberOfWheels: 1
|
||||
forCargo: true
|
||||
forChildren: false
|
||||
numberOfChildren: 2
|
||||
technicalEquipment: {
|
||||
bicycleShift: "shift"
|
||||
isEBike: false
|
||||
hasLightSystem:false
|
||||
}
|
||||
dimensionsAndLoad: {
|
||||
hasCoverBox: true
|
||||
lockable:false
|
||||
boxLength: 0.1
|
||||
boxWidth: 0.2
|
||||
boxHeight:0.3
|
||||
maxWeightBox: 1.1
|
||||
maxWeightLuggageRack: 1.2
|
||||
maxWeightTotal: 1.3
|
||||
bikeLength:2.1
|
||||
}
|
||||
security: {frameNumber: "bla"}
|
||||
insuranceData: {name:"in"
|
||||
benefactor: "ben"
|
||||
billing: "bill"
|
||||
noPnP: "noP"
|
||||
|
||||
maintenanceResponsible: "someone"
|
||||
maintenanceBenefactor: "mben"
|
||||
hasFixedRate: true}
|
||||
taxes: {costCenter:"cost"}
|
||||
}
|
||||
) {
|
||||
id
|
||||
insuranceData{
|
||||
maintenanceResponsible
|
||||
}
|
||||
equipmentType {
|
||||
id
|
||||
name
|
||||
}
|
||||
provider {
|
||||
id
|
||||
organisation{
|
||||
id
|
||||
}
|
||||
}
|
||||
lendingStation {
|
||||
id
|
||||
name
|
||||
cargoBikes {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
export const GET_CARGO_BIKE = gql`{
|
||||
cargoBikes(offset: 0, limit: 1) {
|
||||
id
|
||||
}
|
||||
}`;
|
@ -0,0 +1,65 @@
|
||||
/* eslint no-unused-expressions: 0 */
|
||||
import * as chai from 'chai';
|
||||
import { expect } from 'chai';
|
||||
import { describe, it, before, after } from 'mocha';
|
||||
import { step } from 'mocha-steps';
|
||||
|
||||
import chaiHttp from 'chai-http';
|
||||
// @ts-ignore
|
||||
import * as queries from './testQueries';
|
||||
import { getApp, getConnectionOptions } from '../src/app';
|
||||
import { getConnection } from 'typeorm';
|
||||
|
||||
chai.use(chaiHttp);
|
||||
const chaiLib = <any>chai;
|
||||
const request = chaiLib.default.request;
|
||||
// @ts-ignore
|
||||
chai.request = request;
|
||||
|
||||
process.env.NODE_ENV = 'develop';
|
||||
|
||||
function getAppServer () {
|
||||
return getApp(getConnectionOptions());
|
||||
}
|
||||
|
||||
describe('cargo bike resolver', () => {
|
||||
let agent: any = null;
|
||||
|
||||
before(async () => {
|
||||
const app = await getAppServer();
|
||||
const connection = getConnection();
|
||||
await connection.dropDatabase();
|
||||
await connection.synchronize();
|
||||
agent = chai.request.agent(app).post('/graphql').type('json');
|
||||
});
|
||||
|
||||
step('creates cargo bikes', (done) => {
|
||||
agent.send({
|
||||
query: queries.CREATE_CARGO_BIKE
|
||||
}).end((err: any, res: any) => {
|
||||
debugger;
|
||||
expect(err).to.be.null;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
expect(res.body.errors).to.be.undefined;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
step('returns cargo bike data', (done) => {
|
||||
agent.send({
|
||||
query: queries.GET_CARGO_BIKE
|
||||
}).end((err: any, res: any) => {
|
||||
expect(err).to.be.null;
|
||||
expect(res).to.have.status(200);
|
||||
expect(res).to.be.json;
|
||||
expect(res.body.errors).to.be.undefined;
|
||||
expect(res.body.data.cargoBikes).not.to.be.empty;
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await getConnection().dropDatabase();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue