Skip to content

Commit

Permalink
Backend interface change
Browse files Browse the repository at this point in the history
  • Loading branch information
kennsippell committed Nov 23, 2024
1 parent 2442fcc commit a0a0c84
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 88 deletions.
168 changes: 85 additions & 83 deletions src/lib/hierarchy-operations/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,95 +4,97 @@ const lineageManipulation = require('./lineage-manipulation');
const HIERARCHY_ROOT = 'root';
const BATCH_SIZE = 10000;

const fetch = {
/*
Fetches all of the documents associated with the "contactIds" and confirms they exist.
*/
contactList: async (db, ids) => {
const contactDocs = await db.allDocs({
keys: ids,
include_docs: true,
});

const missingContactErrors = contactDocs.rows.filter(row => !row.doc).map(row => `Contact with id '${row.key}' could not be found.`);
if (missingContactErrors.length > 0) {
throw Error(missingContactErrors);
/*
Fetches all of the documents associated with the "contactIds" and confirms they exist.
*/
async function contactList(db, ids) {
const contactDocs = await db.allDocs({
keys: ids,
include_docs: true,
});

const missingContactErrors = contactDocs.rows.filter(row => !row.doc).map(row => `Contact with id '${row.key}' could not be found.`);
if (missingContactErrors.length > 0) {
throw Error(missingContactErrors);
}

return contactDocs.rows.reduce((agg, curr) => Object.assign(agg, { [curr.doc._id]: curr.doc }), {});
}

async function contact(db, id) {
try {
if (id === HIERARCHY_ROOT) {
return undefined;
}

return contactDocs.rows.reduce((agg, curr) => Object.assign(agg, { [curr.doc._id]: curr.doc }), {});
},

contact: async (db, id) => {
try {
if (id === HIERARCHY_ROOT) {
return undefined;
}

return await db.get(id);
} catch (err) {
if (err.name !== 'not_found') {
throw err;
}

throw Error(`Contact with id '${id}' could not be found`);
}
},

/*
Given a contact's id, obtain the documents of all descendant contacts
*/
descendantsOf: async (db, contactId) => {
const descendantDocs = await db.query('medic/contacts_by_depth', {
key: [contactId],
include_docs: true,
});

return descendantDocs.rows
.map(row => row.doc)
/* We should not move or update tombstone documents */
.filter(doc => doc && doc.type !== 'tombstone');
},

reportsCreatedByOrAt: async (db, createdByIds, createdAtId, skip) => {
const createdByKeys = createdByIds.map(descendantId => [`contact:${descendantId}`]);
const createdAtKeys = createdAtId ? [
[`patient_id:${createdAtId}`],
[`patient_uuid:${createdAtId}`],
[`place_id:${createdAtId}`],
[`place_uuid:${createdAtId}`]
] : [];

const reports = await db.query('medic-client/reports_by_freetext', {
keys: [
...createdByKeys,
...createdAtKeys,
],
include_docs: true,
limit: BATCH_SIZE,
skip,
});

return _.uniqBy(reports.rows.map(row => row.doc), '_id');
},

ancestorsOf: async (db, contactDoc) => {
const ancestorIds = lineageManipulation.pluckIdsFromLineage(contactDoc.parent);
const ancestors = await db.allDocs({
keys: ancestorIds,
include_docs: true,
});

const ancestorIdsNotFound = ancestors.rows.filter(ancestor => !ancestor.doc).map(ancestor => ancestor.key);
if (ancestorIdsNotFound.length > 0) {
throw Error(`Contact '${contactDoc?.name}' (${contactDoc?._id}) has parent id(s) '${ancestorIdsNotFound.join(',')}' which could not be found.`);
return await db.get(id);
} catch (err) {
if (err.name !== 'not_found') {
throw err;
}

return ancestors.rows.map(ancestor => ancestor.doc);
},
};
throw Error(`Contact with id '${id}' could not be found`);
}
}

/*
Given a contact's id, obtain the documents of all descendant contacts
*/
async function descendantsOf(db, contactId) {
const descendantDocs = await db.query('medic/contacts_by_depth', {
key: [contactId],
include_docs: true,
});

return descendantDocs.rows
.map(row => row.doc)
/* We should not move or update tombstone documents */
.filter(doc => doc && doc.type !== 'tombstone');
}

async function reportsCreatedByOrAt(db, createdByIds, createdAtId, skip) {
const createdByKeys = createdByIds.map(descendantId => [`contact:${descendantId}`]);
const createdAtKeys = createdAtId ? [
[`patient_id:${createdAtId}`],
[`patient_uuid:${createdAtId}`],
[`place_id:${createdAtId}`],
[`place_uuid:${createdAtId}`]
] : [];

const reports = await db.query('medic-client/reports_by_freetext', {
keys: [
...createdByKeys,
...createdAtKeys,
],
include_docs: true,
limit: BATCH_SIZE,
skip,
});

return _.uniqBy(reports.rows.map(row => row.doc), '_id');
}

async function ancestorsOf(db, contactDoc) {
const ancestorIds = lineageManipulation.pluckIdsFromLineage(contactDoc.parent);
const ancestors = await db.allDocs({
keys: ancestorIds,
include_docs: true,
});

const ancestorIdsNotFound = ancestors.rows.filter(ancestor => !ancestor.doc).map(ancestor => ancestor.key);
if (ancestorIdsNotFound.length > 0) {
throw Error(`Contact '${contactDoc?.name}' (${contactDoc?._id}) has parent id(s) '${ancestorIdsNotFound.join(',')}' which could not be found.`);
}

return ancestors.rows.map(ancestor => ancestor.doc);
}

module.exports = {
HIERARCHY_ROOT,
BATCH_SIZE,
fetch,
ancestorsOf,
descendantsOf,
contact,
contactList,
reportsCreatedByOrAt,
};
10 changes: 5 additions & 5 deletions src/lib/hierarchy-operations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ const HierarchyOperations = (options) => {
JsDocs.prepareFolder(options);
trace(`Fetching contact details: ${destinationId}`);
const constraints = await LineageConstraints(db, options);
const destinationDoc = await Backend.fetch.contact(db, destinationId);
const sourceDocs = await Backend.fetch.contactList(db, sourceIds);
const destinationDoc = await Backend.contact(db, destinationId);
const sourceDocs = await Backend.contactList(db, sourceIds);
await constraints.assertHierarchyErrors(Object.values(sourceDocs), destinationDoc);

let affectedContactCount = 0, affectedReportCount = 0;
const replacementLineage = lineageManipulation.createLineageFromDoc(destinationDoc);
for (let sourceId of sourceIds) {
const sourceDoc = sourceDocs[sourceId];
const descendantsAndSelf = await Backend.fetch.descendantsOf(db, sourceId);
const descendantsAndSelf = await Backend.descendantsOf(db, sourceId);

if (options.merge) {
const self = descendantsAndSelf.find(d => d._id === sourceId);
Expand All @@ -39,7 +39,7 @@ const HierarchyOperations = (options) => {
trace(`Considering lineage updates to ${descendantsAndSelf.length} descendant(s) of contact ${prettyPrintDocument(sourceDoc)}.`);
const updatedDescendants = replaceLineageInContacts(descendantsAndSelf, replacementLineage, sourceId);

const ancestors = await Backend.fetch.ancestorsOf(db, sourceDoc);
const ancestors = await Backend.ancestorsOf(db, sourceDoc);
trace(`Considering primary contact updates to ${ancestors.length} ancestor(s) of contact ${prettyPrintDocument(sourceDoc)}.`);
const updatedAncestors = replaceLineageInAncestors(descendantsAndSelf, ancestors);

Expand All @@ -66,7 +66,7 @@ const HierarchyOperations = (options) => {
do {
info(`Processing ${skip} to ${skip + Backend.BATCH_SIZE} report docs`);
const createdAtId = options.merge && sourceId;
reportDocsBatch = await Backend.fetch.reportsCreatedByOrAt(db, descendantIds, createdAtId, skip);
reportDocsBatch = await Backend.reportsCreatedByOrAt(db, descendantIds, createdAtId, skip);

const updatedReports = replaceLineageInReports(reportDocsBatch, replacementLineage, sourceId);

Expand Down

0 comments on commit a0a0c84

Please sign in to comment.