Skip to content

Commit

Permalink
feat(event): allow editing event organizers together with the event. …
Browse files Browse the repository at this point in the history
…Fixes MEMB-710
  • Loading branch information
serge1peshcoff committed Nov 19, 2019
1 parent b37a8b5 commit d299d83
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 525 deletions.
31 changes: 31 additions & 0 deletions lib/core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const request = require('request-promise-native');

const config = require('../config');

module.exports.fetchUser = async (user, token) => {
const userRequest = await request({
url: config.core.url + ':' + config.core.port + '/members/' + user.user_id,
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Auth-Token': token,
},
simple: false,
json: true
});

if (typeof userRequest !== 'object') {
throw new Error('Malformed response when fetching user: ' + userRequest);
}

if (!userRequest.success) {
throw new Error('Error fetching user: ' + JSON.stringify(userRequest));
}

return {
user_id: user.user_id,
comment: user.comment,
first_name: userRequest.data.first_name,
last_name: userRequest.data.last_name
};
};
136 changes: 12 additions & 124 deletions lib/events.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
const request = require('request-promise-native');

const errors = require('./errors');
const config = require('../config');
const merge = require('./merge');
const helpers = require('./helpers');
const { Event, Application } = require('../models');
const { Sequelize } = require('./sequelize');
const core = require('./core');

exports.listEvents = async (req, res) => {
// Get default query obj.
Expand Down Expand Up @@ -124,26 +122,22 @@ exports.addEvent = async (req, res) => {
const data = req.body;
delete data.id;
delete data.status;
delete data.organizers;
delete data.deleted;

const newEvent = new Event(data);
const event = new Event(data);

// Creating user automatically becomes organizer
newEvent.organizers = [
{
user_id: req.user.id,
first_name: req.user.first_name,
last_name: req.user.last_name
},
];
if (!event.organizers.some(org => org.user_id === req.user.id)) {
return errors.makeForbiddenError(res, 'User creating the event should be the organizers.');
}

event.organizers = await Promise.all(event.organizers.map(organizer => core.fetchUser(organizer, req.headers['x-auth-token'])));

await newEvent.save();
await event.save();

return res.status(201).json({
success: true,
message: 'Event successfully created',
data: newEvent,
data: event,
});
};

Expand All @@ -166,24 +160,20 @@ exports.editEvent = async (req, res) => {
const data = req.body;
const event = req.event;

// Disallow changing applications and organizers, use separate requests for that
delete data.organizers;
delete data.status;
delete event.deleted;

if (Object.keys(data).length === 0) {
return errors.makeValidationError(res, 'No valid field changes requested');
}

await event.update(data);
event.organizers = await Promise.all(event.organizers.map(organizer => core.fetchUser(organizer, req.headers['x-auth-token'])));

const retval = event.toJSON();
delete retval.applications;
delete retval.organizers;
await event.update(data);

return res.json({
success: true,
data: retval
data: event.toJSON()
});
};

Expand Down Expand Up @@ -213,105 +203,3 @@ exports.setApprovalStatus = async (req, res) => {
message: 'Successfully changed approval status',
});
};

exports.addOrganizer = async (req, res) => {
if (!req.permissions.edit_event) {
return errors.makeForbiddenError(res, 'You are not allowed to edit organizers.');
}

const organizer = req.event.organizers.find((org) => org.user_id === req.body.user_id);
if (organizer) {
return errors.makeBadRequestError(res, 'User with id ' + req.body.user_id + ' is already an organizer.');
}

// Fetching the organizer from core.
const user = await request({
url: config.core.url + ':' + config.core.port + '/members/' + req.body.user_id,
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Auth-Token': req.headers['x-auth-token'],
},
simple: false,
json: true
});

if (typeof user !== 'object') {
throw new Error('Malformed response when fetching user: ' + user);
}

if (!user.success) {
throw new Error('Error fetching user: ' + JSON.stringify(user));
}

const organizers = req.event.organizers;
organizers.push({
user_id: req.body.user_id,
comment: req.body.comment,
first_name: user.data.first_name,
last_name: user.data.last_name
});

await req.event.update({
organizers
});

return res.json({
success: true,
message: 'Organizer is added.'
});
};

exports.editOrganizer = async (req, res) => {
if (!req.permissions.edit_event) {
return errors.makeForbiddenError(res, 'You are not allowed to edit organizers.');
}

const userId = parseInt(req.params.user_id, 10);
if (Number.isNaN(userId)) {
return errors.makeBadRequestError(res, 'userId is not a number.');
}

const organizer = req.event.organizers.find((org) => org.user_id === userId);
if (!organizer) {
return errors.makeNotFoundError(res, 'Organizer with id ' + userId + ' is not found.');
}
organizer.comment = req.body.comment;

await req.event.update({
organizers: req.event.organizers
});

return res.json({
success: true,
message: 'Organizer is updated.'
});
};

exports.deleteOrganizer = async (req, res) => {
if (!req.permissions.edit_event) {
return errors.makeForbiddenError(res, 'You are not allowed to edit organizers.');
}

const userId = parseInt(req.params.user_id, 10);
if (Number.isNaN(userId)) {
return errors.makeBadRequestError(res, 'userId is not a number.');
}

const organizerIndex = req.event.organizers.findIndex((org) => org.user_id === userId);
if (organizerIndex === -1) {
return errors.makeNotFoundError(res, 'Organizer with id ' + userId + ' is not found.');
}

const organizers = JSON.parse(JSON.stringify(req.event.organizers));
organizers.splice(organizerIndex, 1);

await req.event.update({
organizers
});

return res.json({
success: true,
message: 'Organizer is deleted.'
});
};
4 changes: 0 additions & 4 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ EventsRouter.put('/applications/:application_id', middlewares.fetchSingleApplica
EventsRouter.put('/applications/:application_id/status/', middlewares.fetchSingleApplication, applications.setApplicationStatus);
EventsRouter.put('/applications/:application_id/comment/', middlewares.fetchSingleApplication, applications.setApplicationComment);

EventsRouter.post('/organizers', events.addOrganizer);
EventsRouter.put('/organizers/:user_id', events.editOrganizer);
EventsRouter.delete('/organizers/:user_id', events.deleteOrganizer);

server.use(endpointsMetrics.addEndpointMetrics);
server.use('/', GeneralRouter);
server.use('/single/:event_id', EventsRouter);
Expand Down
94 changes: 75 additions & 19 deletions test/api/events-creation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ describe('Events creation', () => {
starts: '2017-12-11 15:00',
ends: '2017-12-14 12:00',
type: 'es',
organizing_bodies: [{ body_id: user.bodies[0].id }]
organizing_bodies: [{ body_id: user.bodies[0].id }],
organizers: [{ user_id: user.id }]
}
});

Expand Down Expand Up @@ -87,6 +88,7 @@ describe('Events creation', () => {
required: false
},
],
organizers: [{ user_id: user.id }],
organizing_bodies: [{ body_id: user.bodies[0].id }]
}
});
Expand Down Expand Up @@ -126,29 +128,15 @@ describe('Events creation', () => {
starts: '2017-12-11 15:00',
ends: '2017-12-14 12:00',
type: 'es',
organizers: [
{
user_id: 3,
first_name: 'test',
last_name: 'test',
role: 'full',
},
],
applications: [
{
user_id: 5,
body_id: 10,
status: 'accepted',
},
],
organizing_bodies: [{ body_id: user.bodies[0].id }]
organizers: [{ user_id: user.id }],
organizing_bodies: [{ body_id: user.bodies[0].id }],
status: 'published'
}
});

expect(res.statusCode).toEqual(201);

expect(res.body.data.organizers.length).toEqual(1);
expect(res.body.data.organizers[0].user_id).not.toEqual(3);
expect(res.body.data.status).not.toEqual('published');
});

it('should return validation errors on malformed / POST', async () => {
Expand All @@ -160,6 +148,7 @@ describe('Events creation', () => {
starts: '2015-12-11 15:00',
ends: 'sometime, dunno yet',
type: 'non-statutory',
organizers: [{ user_id: user.id }],
organizing_bodies: [{ body_id: user.bodies[0].id }],
fee: -150
}
Expand Down Expand Up @@ -189,6 +178,73 @@ describe('Events creation', () => {
expect(res.body.errors).toHaveProperty('locations');
});

it('should fail is the event creator is not an organizer', async () => {
const event = generator.generateEvent({ organizers: [{ user_id: 1337 }] });
event.locations = false;

const res = await request({
uri: '/',
headers: { 'X-Auth-Token': 'foobar' },
method: 'POST',
body: event
});

expect(res.statusCode).toEqual(403);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
});

it('should fail is core request returns net error', async () => {
mock.mockAll({ member: { netError: true } });
const event = generator.generateEvent({ organizers: [{ user_id: user.id }] });
event.locations = false;

const res = await request({
uri: '/',
headers: { 'X-Auth-Token': 'foobar' },
method: 'POST',
body: event
});

expect(res.statusCode).toEqual(500);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
});

it('should fail is core request returns garbage', async () => {
mock.mockAll({ member: { badResponse: true } });
const event = generator.generateEvent({ organizers: [{ user_id: user.id }] });
event.locations = false;

const res = await request({
uri: '/',
headers: { 'X-Auth-Token': 'foobar' },
method: 'POST',
body: event
});

expect(res.statusCode).toEqual(500);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
});

it('should fail is core request returns unsuccessful response', async () => {
mock.mockAll({ member: { unsuccessfulResponse: true } });
const event = generator.generateEvent({ organizers: [{ user_id: user.id }] });
event.locations = false;

const res = await request({
uri: '/',
headers: { 'X-Auth-Token': 'foobar' },
method: 'POST',
body: event
});

expect(res.statusCode).toEqual(500);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
});

it('should return 422 if the location is not an object', async () => {
const event = generator.generateEvent({ body_id: user.bodies[0].id });
event.locations = [false];
Expand Down
16 changes: 5 additions & 11 deletions test/api/events-editing.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,24 +105,18 @@ describe('Events editing', () => {
expect(res.body.errors).toHaveProperty('ends');
});

it('should not update the organizers list with /single/<eventid> PUT', async () => {
await request({
it('should fail if no organizers are set', async () => {
const res = await request({
uri: '/single/' + event.id,
method: 'PUT',
headers: { 'X-Auth-Token': 'blablabla' },
body: {
organizers: [
{
first_name: 'new',
last_name: 'new',
user_id: 1337
},
],
organizers: []
}
});

const newEvent = await Event.findByPk(event.id);
expect(newEvent.organizers.map((org) => org.user_id)).not.toContain(1337);
expect(res.body).toHaveProperty('errors');
expect(res.body.errors).toHaveProperty('organizers');
});

it('should disallow event deleting if the user doesn\'t have rights', async () => {
Expand Down
Loading

0 comments on commit d299d83

Please sign in to comment.