Skip to content

Commit

Permalink
feat(general): mail EQAC on new events approved. Fixes MEMB-760
Browse files Browse the repository at this point in the history
  • Loading branch information
serge1peshcoff committed Jan 12, 2020
1 parent cf9626b commit d2bff3a
Show file tree
Hide file tree
Showing 6 changed files with 889 additions and 26 deletions.
6 changes: 5 additions & 1 deletion config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ const config = {
},
core: {
url: 'http://oms-core-elixir',
port: 4000
port: 4000,
user: {
login: process.env.CORE_LOGIN || '[email protected]',
password: process.env.CORE_PASSWORD || '5ecr3t'
}
},
mailer: {
url: 'http://oms-mailer',
Expand Down
98 changes: 83 additions & 15 deletions lib/core.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
const request = require('request-promise-native');

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

module.exports.fetchUser = async (user, token) => {
const userRequest = await request({
url: config.core.url + ':' + config.core.port + '/members/' + user.user_id,
method: 'GET',

const makeRequest = (options) => {
const requestOptions = {
url: options.url,
method: options.method || 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Auth-Token': token,
'X-Auth-Token': options.token,
},
simple: false,
json: true
json: true,
resolveWithFullResponse: options.resolveWithFullResponse || false
};

return request(requestOptions);
};

const fetchUser = async (user, token) => {
const userRequest = await makeRequest({
url: config.core.url + ':' + config.core.port + '/members/' + user.user_id,
token
});

if (typeof userRequest !== 'object') {
Expand All @@ -31,21 +43,15 @@ module.exports.fetchUser = async (user, token) => {
};
};

module.exports.fetchBody = async (body, token) => {
const fetchBody = async (body, token) => {
// return invalid body as it is, will catch it in Event validation.
if (typeof body !== 'object' || typeof body.body_id !== 'number') {
return body;
}

const bodyRequest = await request({
const bodyRequest = await makeRequest({
url: config.core.url + ':' + config.core.port + '/bodies/' + body.body_id,
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-Auth-Token': token,
},
simple: false,
json: true
token
});

if (typeof bodyRequest !== 'object') {
Expand All @@ -61,3 +67,65 @@ module.exports.fetchBody = async (body, token) => {
body_name: bodyRequest.data.name
};
};

const fetchUsersWithPermission = async (permission) => {
// Getting access and refresh token.
const authRequest = await makeRequest({
url: config.core.url + ':' + config.core.port + '/login',
method: 'POST',
body: {
username: config.core.user.login,
password: config.core.user.password
}
});

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

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

// Fetching permissions.
const permissionsResponse = await makeRequest({
url: config.core.url + ':' + config.core.port + '/permissions',
token: authRequest.access_token
});

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

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

// Finding a permission.
const permissionToFind = permissionsResponse.data.find(elt => elt.combined.includes(permission));
if (!permissionToFind) {
throw new Error(`No permission found: "${permission}".`);
}

// Fetching permissions users.
const permissionsMembersResponse = await makeRequest({
url: config.core.url + ':' + config.core.port + '/permissions/' + permissionToFind.id + '/members',
token: authRequest.access_token
});

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

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

return permissionsMembersResponse.data;
};

module.exports = {
fetchUser,
fetchBody,
fetchUsersWithPermission
};
45 changes: 35 additions & 10 deletions lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,17 +255,42 @@ exports.setApprovalStatus = async (req, res) => {

const oldStatus = req.event.status;

await req.event.update({ status: req.body.status });

// Send email to all organizers.
await mailer.sendMail({
to: req.event.organizers.map((organizer) => organizer.email),
subject: 'Your event\'s status was changed',
template: 'events_status_changed.html',
parameters: {
event: req.event,
old_status: oldStatus
await sequelize.transaction(async (t) => {
await req.event.update({ status: req.body.status }, { transaction: t });

// Send email to all organizers.
await mailer.sendMail({
to: req.event.organizers.map((organizer) => organizer.email),
subject: 'Your event\'s status was changed',
template: 'events_status_changed.html',
parameters: {
event: req.event,
old_status: oldStatus
}
});

// If the new status is submitted and the old status is draft, send a mail
// to those who have permissions (EQAC/CD)
if (oldStatus !== 'draft' || req.event.status !== 'submitted') {
return;
}

// GET /permissions/:id/members can return the same user multiple times (I suppose
// if a user is a member of multiple bodies which have this permission), so we need
// to filter it so emails list won't contain duplicates.
const membersWthPermissions = await core.fetchUsersWithPermission('approve_event:' + req.event.type);
const emails = membersWthPermissions
.map(member => member.user.email)
.filter((elt, index, array) => array.indexOf(elt) === index);

await mailer.sendMail({
to: emails,
subject: 'A new event was submitted.',
template: 'events_submitted.html',
parameters: {
event: req.event
}
});
});

return res.json({
Expand Down
172 changes: 172 additions & 0 deletions test/api/events-approval.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,4 +496,176 @@ describe('Events status change', () => {
expect(res.body).toHaveProperty('message');
});
});

describe('if core fails', () => {
it('should return 500 if core login returns net error', async () => {
mock.mockAll({ login: { netError: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core login returns bad response', async () => {
mock.mockAll({ login: { badResponse: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core login returns unsuccessful response', async () => {
mock.mockAll({ login: { unsuccessfulResponse: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permissions returns net error', async () => {
mock.mockAll({ permissions: { netError: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permissions returns bad response', async () => {
mock.mockAll({ permissions: { badResponse: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permissions returns unsuccessful response', async () => {
mock.mockAll({ permissions: { unsuccessfulResponse: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permissions returns empty response', async () => {
mock.mockAll({ permissions: { noPermissions: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permission members returns net error', async () => {
mock.mockAll({ permissionMembers: { netError: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permission members returns bad response', async () => {
mock.mockAll({ permissionMembers: { badResponse: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

it('should return 500 if core permission members returns unsuccessful response', async () => {
mock.mockAll({ permissionMembers: { unsuccessfulResponse: true } });

const event = await generator.createEvent({ status: 'draft' });

const res = await request({
uri: '/single/' + event.id + '/status',
headers: { 'X-Auth-Token': 'foobar' },
method: 'PUT',
body: { status: 'submitted' }
});

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

0 comments on commit d2bff3a

Please sign in to comment.