diff --git a/shared-libs/memdown/src/memdown-medic.js b/shared-libs/memdown/src/memdown-medic.js index d392cf05f68..be5c778df57 100644 --- a/shared-libs/memdown/src/memdown-medic.js +++ b/shared-libs/memdown/src/memdown-medic.js @@ -57,6 +57,16 @@ module.exports = (rootDir='./') => { if (!ddocs) { ddocs = []; filesIn(`${rootDir}/ddocs/medic-db`).forEach(ddoc => loadDdoc(rootDir, 'medic-db', ddoc)); + + // Load offline-only tasks view for tests + const tasksMapPath = `${rootDir}/webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/tasks_by_contact.js`; + if (fs.existsSync(tasksMapPath)) { + const tasksMap = readFile(tasksMapPath).replace(/module.exports.map = /, ''); + ddocs.push({ + _id: '_design/medic-offline-tasks', + views: { tasks_by_contact: { map: tasksMap } } + }); + } } const db = new PouchDB(uuid(), { adapter: 'memory' }); return Promise.all(ddocs.map(ddoc => db.put(ddoc))) diff --git a/shared-libs/rules-engine/src/pouchdb-provider.js b/shared-libs/rules-engine/src/pouchdb-provider.js index 2e3301e01d9..be42c323343 100644 --- a/shared-libs/rules-engine/src/pouchdb-provider.js +++ b/shared-libs/rules-engine/src/pouchdb-provider.js @@ -39,7 +39,7 @@ const medicPouchProvider = db => { // For users with ~1000 contacts it is ~50x faster to provider a start/end key instead of specifying all ids allTasks: prefix => { const options = { startkey: `${prefix}-`, endkey: `${prefix}-\ufff0`, include_docs: true }; - return docsOf(dbQuery('medic-client/tasks_by_contact', options)); + return docsOf(dbQuery('medic-offline-tasks/tasks_by_contact', options)); }, allTaskData: userSettingsDoc => { @@ -108,12 +108,12 @@ const medicPouchProvider = db => { tasksByRelation: (contactIds, prefix) => { const keys = contactIds.map(contactId => `${prefix}-${contactId}`); - return docsOf(dbQuery( 'medic-client/tasks_by_contact', { keys, include_docs: true })); + return docsOf(dbQuery( 'medic-offline-tasks/tasks_by_contact', { keys, include_docs: true })); }, allTaskRowsByOwner: (contactIds) => { const keys = contactIds.map(contactId => (['owner', 'all', contactId])); - return rowsOf(dbQuery( 'medic-client/tasks_by_contact', { keys })); + return rowsOf(dbQuery( 'medic-offline-tasks/tasks_by_contact', { keys })); }, allTaskRows: () => { @@ -122,7 +122,7 @@ const medicPouchProvider = db => { endkey: ['owner', 'all', '\ufff0'], }; - return rowsOf(dbQuery( 'medic-client/tasks_by_contact', options)); + return rowsOf(dbQuery( 'medic-offline-tasks/tasks_by_contact', options)); }, taskDataFor: async (contactIds, userSettingsDoc) => { diff --git a/shared-libs/rules-engine/test/integration.spec.js b/shared-libs/rules-engine/test/integration.spec.js index b6141e28296..30edef084c6 100644 --- a/shared-libs/rules-engine/test/integration.spec.js +++ b/shared-libs/rules-engine/test/integration.spec.js @@ -71,12 +71,12 @@ const reportByPatientIdOnly = { const expectedQueriesForAllFreshData = [ 'medic-client/contacts_by_type', 'medic-client/reports_by_subject', - 'medic-client/tasks_by_contact' + 'medic-offline-tasks/tasks_by_contact' ]; const expectedQueriesForFreshData = [ 'medic-client/reports_by_subject', - 'medic-client/tasks_by_contact', - 'medic-client/tasks_by_contact', + 'medic-offline-tasks/tasks_by_contact', + 'medic-offline-tasks/tasks_by_contact', ]; const fetchTargets = async (interval) => { @@ -458,7 +458,7 @@ describe(`Rules Engine Integration Tests`, () => { const tasksAfterPurge = await rulesEngine.fetchTasksFor(); expect(tasksAfterPurge).to.have.property('length', 0); - const allTasks = await db.query('medic-client/tasks_by_contact'); + const allTasks = await db.query('medic-offline-tasks/tasks_by_contact'); expect(allTasks.total_rows).to.eq(0); }); @@ -487,7 +487,7 @@ describe(`Rules Engine Integration Tests`, () => { expect(secondTasks).to.deep.eq(firstTasks); expect(db.query.args.map(args => args[0])).to.deep.eq([ ...expectedQueriesForAllFreshData, - 'medic-client/tasks_by_contact', + 'medic-offline-tasks/tasks_by_contact', 'medic-client/contacts_by_reference', ...expectedQueriesForFreshData ]); @@ -545,7 +545,7 @@ describe(`Rules Engine Integration Tests`, () => { expect(rulesEmitter.getEmissionsFor.args).excludingEvery(['_rev', 'state', 'stateHistory']) .to.deep.eq([[[], [headlessReport, headlessReport2], [taskEmittedByHeadless2]]]); expect(db.query.args.map(args => args[0])) - .to.deep.eq([...expectedQueriesForAllFreshData, 'medic-client/tasks_by_contact']); + .to.deep.eq([...expectedQueriesForAllFreshData, 'medic-offline-tasks/tasks_by_contact']); expect(firstResult).excludingEvery('_rev').to.deep.eq([taskOwnedByHeadless]); expect(db.bulkDocs.callCount).to.eq(2); // taskEmittedByHeadless2 gets cancelled @@ -773,7 +773,7 @@ const triggerFacilityReminderInReadyState = async (selectBy, docs = [patientCont const tasks = await rulesEngine.fetchTasksFor(selectBy); expect(tasks).to.have.property('length', 1); expect(db.query.args.map(args => args[0])).to.deep.eq( - selectBy ? expectedQueriesForFreshData : [...expectedQueriesForAllFreshData, 'medic-client/tasks_by_contact'] + selectBy ? expectedQueriesForFreshData : [...expectedQueriesForAllFreshData, 'medic-offline-tasks/tasks_by_contact'] ); expect(db.bulkDocs.callCount).to.eq(2); expect(tasks[0]).to.deep.include({ diff --git a/shared-libs/rules-engine/test/pouchdb-provider.spec.js b/shared-libs/rules-engine/test/pouchdb-provider.spec.js index c32acc0998d..2181a60b476 100644 --- a/shared-libs/rules-engine/test/pouchdb-provider.spec.js +++ b/shared-libs/rules-engine/test/pouchdb-provider.spec.js @@ -348,7 +348,10 @@ describe('pouchdb provider', () => { }); expect(db.query.args).to.deep.equal([ ['medic-client/reports_by_subject', { keys: ['abc'], include_docs: true, ...defaultQueryParams }], - ['medic-client/tasks_by_contact', { keys: ['requester-abc'], include_docs: true, ...defaultQueryParams }], + [ + 'medic-offline-tasks/tasks_by_contact', + { keys: ['requester-abc'], include_docs: true, ...defaultQueryParams }, + ], ]); }); it('cht contact yields', async() => { @@ -370,11 +373,19 @@ describe('pouchdb provider', () => { expect(db.query.args).to.deep.equal([ [ 'medic-client/reports_by_subject', - { keys: [chtDocs.contact._id, 'abc', chtDocs.contact.patient_id], include_docs: true, ...defaultQueryParams }, + { + keys: [chtDocs.contact._id, 'abc', chtDocs.contact.patient_id], + include_docs: true, + ...defaultQueryParams, + }, ], [ - 'medic-client/tasks_by_contact', - { keys: [`requester-${chtDocs.contact._id}`, 'requester-abc'], include_docs: true, ...defaultQueryParams } + 'medic-offline-tasks/tasks_by_contact', + { + keys: [`requester-${chtDocs.contact._id}`, 'requester-abc'], + include_docs: true, + ...defaultQueryParams, + }, ], ]); }); @@ -440,11 +451,19 @@ describe('pouchdb provider', () => { expect(db.query.args).to.deep.equal([ [ 'medic-client/reports_by_subject', - { keys: [ ...contactIds, 'place_id', 'patient_id' ], include_docs: true, ...defaultQueryParams }, + { + keys: [ ...contactIds, 'place_id', 'patient_id' ], + include_docs: true, + ...defaultQueryParams, + }, ], [ - 'medic-client/tasks_by_contact', - { keys: contactIds.map(id => `requester-${id}`), include_docs: true, ...defaultQueryParams } + 'medic-offline-tasks/tasks_by_contact', + { + keys: contactIds.map(id => `requester-${id}`), + include_docs: true, + ...defaultQueryParams, + }, ], ]); @@ -482,7 +501,7 @@ describe('pouchdb provider', () => { { include_docs: true, ...defaultQueryParams }, ], [ - 'medic-client/tasks_by_contact', + 'medic-offline-tasks/tasks_by_contact', { include_docs: true, ...defaultQueryParams } ], ]); diff --git a/shared-libs/rules-engine/test/provider-wireup.spec.js b/shared-libs/rules-engine/test/provider-wireup.spec.js index 4eb06dad798..8471f40fd96 100644 --- a/shared-libs/rules-engine/test/provider-wireup.spec.js +++ b/shared-libs/rules-engine/test/provider-wireup.spec.js @@ -404,7 +404,7 @@ describe('provider-wireup integration tests', () => { expect(actual).to.be.empty; expect(rulesEmitter.getEmissionsFor.callCount).to.eq(1); expect(db.query.callCount).to.eq(3); - expect(db.query.args[2][0]).to.eq('medic-client/tasks_by_contact'); + expect(db.query.args[2][0]).to.eq('medic-offline-tasks/tasks_by_contact'); expect(db.query.args[2][1]).to.not.have.property('keys'); }); diff --git a/tests/e2e/default/db/initial-replication.wdio-spec.js b/tests/e2e/default/db/initial-replication.wdio-spec.js index 850b97c6d93..477384d3808 100644 --- a/tests/e2e/default/db/initial-replication.wdio-spec.js +++ b/tests/e2e/default/db/initial-replication.wdio-spec.js @@ -7,7 +7,7 @@ const loginPage = require('@page-objects/default/login/login.wdio.page'); const dataFactory = require('@factories/cht/generate'); const { DOC_IDS, PREFIXES } = require('@medic/constants'); -const LOCAL_ONLY_DOC_IDS = ['_design/medic-offline-freetext']; +const LOCAL_ONLY_DOC_IDS = ['_design/medic-offline-freetext', '_design/medic-offline-tasks']; describe('initial-replication', () => { const LOCAL_LOG = '_local/initial-replication'; diff --git a/webapp/src/js/bootstrapper/offline-ddocs/index.js b/webapp/src/js/bootstrapper/offline-ddocs/index.js index b6cda237df4..38044cc25c1 100644 --- a/webapp/src/js/bootstrapper/offline-ddocs/index.js +++ b/webapp/src/js/bootstrapper/offline-ddocs/index.js @@ -1,4 +1,5 @@ const contactsByFreetext = require('./medic-offline-freetext'); +const tasks = require('./medic-offline-tasks'); const getRev = async (db, id) => db .get(id) @@ -15,4 +16,7 @@ const initDdoc = async (db, ddoc) => db.put({ _rev: await getRev(db, ddoc._id), }); -module.exports.init = async (db) => initDdoc(db, contactsByFreetext); +module.exports.init = async (db) => { + await initDdoc(db, contactsByFreetext); + await initDdoc(db, tasks); +}; diff --git a/webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/index.js b/webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/index.js new file mode 100644 index 00000000000..0bb7a0a85c4 --- /dev/null +++ b/webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/index.js @@ -0,0 +1,10 @@ +const tasksByContact = require('./tasks_by_contact'); + +const packageView = ({ map }) => ({ map: map.toString() }); + +module.exports = { + _id: '_design/medic-offline-tasks', + views: { + tasks_by_contact: packageView(tasksByContact), + } +}; diff --git a/ddocs/medic-db/medic-client/views/tasks_by_contact/map.js b/webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/tasks_by_contact.js similarity index 56% rename from ddocs/medic-db/medic-client/views/tasks_by_contact/map.js rename to webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/tasks_by_contact.js index 533a2eab413..2c803c4a1e2 100644 --- a/ddocs/medic-db/medic-client/views/tasks_by_contact/map.js +++ b/webapp/src/js/bootstrapper/offline-ddocs/medic-offline-tasks/tasks_by_contact.js @@ -1,7 +1,7 @@ -function(doc) { +module.exports.map = function(doc) { if (doc.type === 'task') { - var isTerminalState = ['Cancelled', 'Completed', 'Failed'].indexOf(doc.state) >= 0; - var owner = (doc.owner || '_unassigned'); + const isTerminalState = ['Cancelled', 'Completed', 'Failed'].includes(doc.state); + const owner = (doc.owner || '_unassigned'); if (!isTerminalState) { emit('owner-' + owner); @@ -13,4 +13,4 @@ function(doc) { emit(['owner', 'all', owner], { state: doc.state }); } -} +};