Skip to content

Commit

Permalink
fix(#8689): only get needed facilities when getting users (#8735)
Browse files Browse the repository at this point in the history
  • Loading branch information
garethbowen authored Dec 13, 2023
1 parent 34edb11 commit 43a1683
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 27 deletions.
22 changes: 22 additions & 0 deletions shared-libs/user-management/src/libs/facility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const db = require('./db');

const list = async (users, settings) => {
const ids = new Set();
for (const user of users) {
ids.add(user?.doc?.facility_id);
}
for (const setting of settings) {
ids.add(setting?.contact_id);
}
ids.delete(undefined);
if (!ids.size) {
return [];
}
const response = await db.medic.allDocs({ keys: Array.from(ids), include_docs: true });
return response.rows.map(row => row?.doc).filter(doc => !!doc);
};

module.exports = {
list,
};

20 changes: 5 additions & 15 deletions shared-libs/user-management/src/users.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const _ = require('lodash');
const passwordTester = require('simple-password-tester');
const db = require('./libs/db');
const facility = require('./libs/facility');
const lineage = require('./libs/lineage');
const couchSettings = require('@medic/settings');
const getRoles = require('./libs/types-and-roles');
Expand Down Expand Up @@ -118,11 +119,6 @@ const getAllUsers = () => {
.then(result => result.rows);
};

const getFacilities = () => {
return db.medic.query('medic-client/contacts_by_type', { include_docs: true })
.then(result => result.rows.map(row => row.doc));
};

const validateContact = (id, placeID) => {
return db.medic.get(id)
.then(doc => {
Expand Down Expand Up @@ -776,16 +772,10 @@ const getUserSettings = async({ name }) => {
*/
module.exports = {
deleteUser: username => deleteUser(createID(username)),
getList: () => {
return Promise
.all([
getAllUsers(),
getAllUserSettings(),
getFacilities()
])
.then(([ users, settings, facilities ]) => {
return mapUsers(users, settings, facilities);
});
getList: async () => {
const [ users, settings ] = await Promise.all([ getAllUsers(), getAllUserSettings() ]);
const facilities = await facility.list(users, settings);
return mapUsers(users, settings, facilities);
},
getUserSettings,
/* eslint-disable max-len */
Expand Down
50 changes: 50 additions & 0 deletions shared-libs/user-management/test/unit/libs/facility.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const { expect } = require('chai');
const sinon = require('sinon');

const { list } = require('../../../src/libs/facility');
const db = require('../../../src/libs/db');

describe('facility', () => {

const userA = { doc: { facility_id: 'a' } };
const userB = { doc: { facility_id: 'b' } };

const settingA = { contact_id: 'a' };
const settingB = { contact_id: 'e' };

const facilityA = { _id: 'a' };
const facilityB = { _id: 'b' };
const facilityNotFound = { error: 'not_found' };

let allDocs;

beforeEach(() => {
allDocs = sinon.stub();
db.init({ medic: { allDocs } });
});

it('handles empty args', async () => {
const result = await list([], []);
expect(result).to.be.empty;
expect(allDocs.callCount).to.equal(0);
});

it('handles incomplete arg docs', async () => {
const result = await list([{}], [{}]);
expect(result).to.be.empty;
expect(allDocs.callCount).to.equal(0);
});

it('finds all facilities', async () => {
allDocs.resolves({ rows: [
{ doc: facilityA },
{ doc: facilityB },
facilityNotFound,
] });
const result = await list([ userA, userB ], [ settingA, settingB ]);
expect(result).to.deep.equal([ facilityA, facilityB ]);
expect(allDocs.callCount).to.equal(1);
expect(allDocs.args[0][0].keys).to.deep.equal([ 'a', 'b', 'e' ]);
});

});
5 changes: 3 additions & 2 deletions shared-libs/user-management/test/unit/users.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const couchSettings = require('@medic/settings');
const tokenLogin = require('../../src/token-login');
const config = require('../../src/libs/config');
const db = require('../../src/libs/db');
const facility = require('../../src/libs/facility');
const lineage = require('../../src/libs/lineage');
const passwords = require('../../src/libs/passwords');
const roles = require('../../src/roles');
Expand Down Expand Up @@ -38,11 +39,11 @@ describe('Users service', () => {
addMessage = sinon.stub();
config.getTransitionsLib.returns({ messages: { addMessage } });
service = rewire('../../src/users');
service.__set__('getFacilities', sinon.stub().returns([
sinon.stub(facility, 'list').resolves([
facilitya,
facilityb,
facilityc,
]));
]);
sinon.stub(couchSettings, 'getCouchConfig').resolves();
sinon.stub(couchSettings, 'updateAdminPassword').resolves();
userData = {
Expand Down
60 changes: 50 additions & 10 deletions tests/integration/api/controllers/users.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ chai.use(require('chai-shallow-deep-equal'));
const sentinelUtils = require('@utils/sentinel');

const getUserId = n => `org.couchdb.user:${n}`;
const password = 'passwordSUP3RS3CR37!';
const parentPlace = {
_id: 'PARENT_PLACE',
type: 'district_hospital',
name: 'Big Parent Hospital'
};


describe('Users API', () => {

Expand Down Expand Up @@ -381,15 +388,6 @@ describe('Users API', () => {
});

describe('/api/v1/users-info', () => {

const password = 'passwordSUP3RS3CR37!';

const parentPlace = {
_id: 'PARENT_PLACE',
type: 'district_hospital',
name: 'Big Parent Hospital'
};

const users = [
{
username: 'offline',
Expand Down Expand Up @@ -623,7 +621,6 @@ describe('Users API', () => {

describe('token-login', () => {
let user;
const password = 'passwordSUP3RS3CR37!';

const getUser = (user) => {
const opts = { path: `/_users/${getUserId(user.username)}` };
Expand Down Expand Up @@ -1619,4 +1616,47 @@ describe('Users API', () => {
});
});
});

describe('POST/GET api/v2/users', () => {
before(async () => {
await utils.saveDoc(parentPlace);
});

after(async () => {
await utils.revertDb([], true);
});

it('should create and get users', async () => {
const users = Array.from({ length: 10 }).map(() => ({
username: uuid(),
password: password,
place: {
type: 'health_center',
name: 'Online place',
parent: 'PARENT_PLACE'
},
contact: {
name: 'OnlineUser'
},
roles: ['district_admin', 'mm-online']
}));

const createUserOpts = { path: '/api/v2/users', method: 'POST' };
for (const user of users) {
await utils.request({ ...createUserOpts, body: user });
}

const savedUsers = await utils.request({ path: '/api/v2/users' });
for (const user of users) {
const savedUser = savedUsers.find(savedUser => savedUser.username === user.username);
expect(savedUser).to.deep.nested.include({
id: `org.couchdb.user:${user.username}`,
'place.type': user.place.type,
'place.name': user.place.name,
'place.parent._id': parentPlace._id,
'contact.name': user.contact.name,
});
}
});
});
});

0 comments on commit 43a1683

Please sign in to comment.