Skip to content

Commit

Permalink
feat(members): add endpoints for searching and retrieving email (#249)
Browse files Browse the repository at this point in the history
* feat(members): add endpoints for searching and retrieving email

* chore(test): add tests

* chore(email): reduce info returned

* chore(test): uncomment test
  • Loading branch information
WikiRik authored Mar 31, 2021
1 parent 1733251 commit 020ebe1
Show file tree
Hide file tree
Showing 4 changed files with 427 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ GeneralRouter.get('/bodies', bodies.listAllBodies);
GeneralRouter.get('/my_permissions', middlewares.ensureAuthorized, myPermissions.getMyPermissions);
GeneralRouter.post('/my_permissions', middlewares.ensureAuthorized, myPermissions.getMyCirclesWithPermission);
GeneralRouter.get('/members', middlewares.ensureAuthorized, members.listAllUsers);
GeneralRouter.get('/members_search', middlewares.ensureAuthorized, members.searchAllUsers);
GeneralRouter.get('/members_email', middlewares.ensureAuthorized, members.getUsersEmail);
GeneralRouter.post('/bodies', middlewares.ensureAuthorized, bodies.createBody);
GeneralRouter.get('/circles', middlewares.ensureAuthorized, circles.listAllCircles);
GeneralRouter.post('/circles', middlewares.ensureAuthorized, circles.createCircle);
Expand Down
47 changes: 47 additions & 0 deletions middlewares/members.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const moment = require('moment');
const _ = require('lodash');

const { User, Body, MailChange, MailConfirmation } = require('../models');
const constants = require('../lib/constants');
Expand All @@ -25,6 +26,25 @@ exports.listAllUsers = async (req, res) => {
});
};

exports.searchAllUsers = async (req, res) => {
if (!req.permissions.hasPermission('global:search:member')) {
return errors.makeForbiddenError(res, 'Permission global:search:member is required, but not present.');
}

const result = await User.findAndCountAll({
where: helpers.filterBy(req.query.query, constants.FIELDS_TO_QUERY.MEMBER),
...helpers.getPagination(req.query),
attributes: ['id', 'first_name', 'last_name', 'username', 'email', 'gsuite_id'],
order: helpers.getSorting(req.query)
});

return res.json({
success: true,
data: result.rows,
meta: { count: result.count }
});
};

exports.listAllUnconfirmedUsers = async (req, res) => {
if (!req.permissions.hasPermission('global:view_unconfirmed:member')) {
return errors.makeForbiddenError(res, 'Permission global:view_unconfirmed:member is required, but not present.');
Expand Down Expand Up @@ -69,6 +89,33 @@ exports.getUser = async (req, res) => {
});
};

exports.getUsersEmail = async (req, res) => {
if (!req.permissions.hasPermission('global:mail:member')) {
return errors.makeForbiddenError(res, 'Permission global:mail:member is required, but not present.');
}

if (req.query.query && !req.query.query.match(/^\d+(?:,\d+)*$/g)) {
return errors.makeBadRequestError(res, 'Query should be a string of 1 id or multiple ids seperated by commas.');
}

let where = {};

if (typeof req.query.query === 'string' && req.query.query.trim().length > 0) {
where = { id: { [Sequelize.Op.or]: req.query.query.split(',') } };
}

const result = await User.findAndCountAll({
where,
attributes: ['id', 'email', 'gsuite_id', 'primary_email', 'notification_email']
});

return res.json({
success: true,
data: result.rows.map((row) => _.pick(row, ['id', 'notification_email'])),
meta: { count: result.count }
});
};

exports.updateUser = async (req, res) => {
if (!req.permissions.hasPermission('update:member') && req.user.id !== req.currentUser.id) {
return errors.makeForbiddenError(res, 'Permission update:member is required, but not present.');
Expand Down
142 changes: 142 additions & 0 deletions test/api/users-email.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
const { startServer, stopServer } = require('../../lib/server.js');
const { request } = require('../scripts/helpers');
const generator = require('../scripts/generator');

describe('Users list', () => {
beforeAll(async () => {
await startServer();
});

afterAll(async () => {
await stopServer();
});

afterEach(async () => {
await generator.clearAll();
});

test('should fail if no permission', async () => {
const user = await generator.createUser();
const token = await generator.createAccessToken({}, user);

const res = await request({
uri: '/members_email',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

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

test('should succeed when everything is okay', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'mail', object: 'member' });

const res = await request({
uri: '/members_email',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].id).toEqual(user.id);
});

test('should find one by id', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'mail', object: 'member' });

const res = await request({
uri: '/members_email?query=' + user.id,
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(1);
expect(res.body.data[0].id).toEqual(user.id);
});

test('should find multiple by id array', async () => {
const user = await generator.createUser({ superadmin: true });
const secondUser = await generator.createUser();
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'mail', object: 'member' });

const res = await request({
uri: '/members_email?query=' + user.id + ',' + secondUser.id,
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(2);
expect([user.id, secondUser.id]).toContainEqual(res.body.data[0].id);
expect([user.id, secondUser.id]).toContainEqual(res.body.data[1].id);
});

test('should fail when query is not a string of ids seperated by commas', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'mail', object: 'member' });

const res = await request({
uri: '/members_email?query=a,b',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

expect(res.statusCode).toEqual(400);
expect(res.body.success).toEqual(false);
expect(res.body).toHaveProperty('message');
expect(res.body).not.toHaveProperty('data');
});

test('should only return id and notification email', async () => {
const user = await generator.createUser({ superadmin: true });
const token = await generator.createAccessToken({}, user);

await generator.createPermission({ scope: 'global', action: 'mail', object: 'member' });

const res = await request({
uri: '/members_email',
method: 'GET',
headers: { 'X-Auth-Token': token.value }
});

const expectedOutput = {
id: user.id,
notification_email: user.notification_email
};

expect(res.statusCode).toEqual(200);
expect(res.body.success).toEqual(true);
expect(res.body).toHaveProperty('data');
expect(res.body).not.toHaveProperty('errors');

expect(res.body.data.length).toEqual(1);
expect(res.body.data[0]).toEqual(expectedOutput);
});
});
Loading

0 comments on commit 020ebe1

Please sign in to comment.