Skip to content

Commit

Permalink
Remove function nesting
Browse files Browse the repository at this point in the history
  • Loading branch information
kennsippell committed Dec 9, 2024
1 parent fe27a5a commit 28be7fb
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 111 deletions.
2 changes: 1 addition & 1 deletion src/lib/hierarchy-operations/hierarchy-data-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ async function getAncestorsOf(db, contactDoc) {
}

module.exports = {
HIERARCHY_ROOT,
BATCH_SIZE,
HIERARCHY_ROOT,
getAncestorsOf,
getContactWithDescendants,
getContact,
Expand Down
177 changes: 91 additions & 86 deletions src/lib/hierarchy-operations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,61 @@ const { trace, info } = require('../log');
const JsDocs = require('./jsdocFolder');
const DataSource = require('./hierarchy-data-source');

function moveHierarchy(db, options) {
return async function (sourceIds, destinationId) {
JsDocs.prepareFolder(options);
trace(`Fetching contact details: ${destinationId}`);
const constraints = await LineageConstraints(db, options);
const destinationDoc = await DataSource.getContact(db, destinationId);
const sourceDocs = await DataSource.getContactsByIds(db, sourceIds);
constraints.assertNoHierarchyErrors(Object.values(sourceDocs), destinationDoc);

let affectedContactCount = 0;
let affectedReportCount = 0;
const replacementLineage = lineageManipulation.createLineageFromDoc(destinationDoc);
for (const sourceId of sourceIds) {
const sourceDoc = sourceDocs[sourceId];
const descendantsAndSelf = await DataSource.getContactWithDescendants(db, sourceId);
const moveContext = {
sourceId,
destinationId,
descendantsAndSelf,
replacementLineage,
};

if (options.merge) {
JsDocs.writeDoc(options, {
_id: sourceDoc._id,
_rev: sourceDoc._rev,
_deleted: true,
});
}

const prettyPrintDocument = doc => `'${doc.name}' (${doc._id})`;
await constraints.assertNoPrimaryContactViolations(sourceDoc, destinationDoc, descendantsAndSelf);

trace(`Considering lineage updates to ${descendantsAndSelf.length} descendant(s) of contact ${prettyPrintDocument(sourceDoc)}.`);
const updatedDescendants = replaceLineageInContacts(options, moveContext);

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

minifyLineageAndWriteToDisk(options, [...updatedDescendants, ...updatedAncestors]);

const movedReportsCount = await moveReports(db, options, moveContext);
trace(`${movedReportsCount} report(s) created by these affected contact(s) will be updated`);

affectedContactCount += updatedDescendants.length + updatedAncestors.length;
affectedReportCount += movedReportsCount;

info(`Staged updates to ${prettyPrintDocument(sourceDoc)}. ${updatedDescendants.length} contact(s) and ${movedReportsCount} report(s).`);
async function moveHierarchy(db, options, sourceIds, destinationId) {
JsDocs.prepareFolder(options);
trace(`Fetching contact details: ${destinationId}`);
const constraints = await LineageConstraints(db, options);
const destinationDoc = await DataSource.getContact(db, destinationId);
const sourceDocs = await DataSource.getContactsByIds(db, sourceIds);
constraints.assertNoHierarchyErrors(Object.values(sourceDocs), destinationDoc);

let affectedContactCount = 0;
let affectedReportCount = 0;
const replacementLineage = lineageManipulation.createLineageFromDoc(destinationDoc);
for (const sourceId of sourceIds) {
const sourceDoc = sourceDocs[sourceId];
const descendantsAndSelf = await DataSource.getContactWithDescendants(db, sourceId);
const moveContext = {
sourceId,
destinationId,
descendantsAndSelf,
replacementLineage,
merge: !!options.merge,
};

if (options.merge) {
JsDocs.writeDoc(options, {
_id: sourceDoc._id,
_rev: sourceDoc._rev,
_deleted: true,
});
}

info(`Staged changes to lineage information for ${affectedContactCount} contact(s) and ${affectedReportCount} report(s).`);
};
const prettyPrintDocument = doc => `'${doc.name}' (${doc._id})`;
await constraints.assertNoPrimaryContactViolations(sourceDoc, destinationDoc, descendantsAndSelf);

trace(`Considering lineage updates to ${descendantsAndSelf.length} descendant(s) of contact ${prettyPrintDocument(sourceDoc)}.`);
const updatedDescendants = replaceLineageInContacts(options, moveContext);

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

minifyLineageAndWriteToDisk(options, [...updatedDescendants, ...updatedAncestors]);

const movedReportsCount = await updateReports(db, options, moveContext);
trace(`${movedReportsCount} report(s) created by these affected contact(s) will be updated`);

affectedContactCount += updatedDescendants.length + updatedAncestors.length;
affectedReportCount += movedReportsCount;

info(`Staged updates to ${prettyPrintDocument(sourceDoc)}. ${updatedDescendants.length} contact(s) and ${movedReportsCount} report(s).`);
}

info(`Staged changes to lineage information for ${affectedContactCount} contact(s) and ${affectedReportCount} report(s).`);
}

async function moveReports(db, options, moveContext) {
async function updateReports(db, options, moveContext) {
const descendantIds = moveContext.descendantsAndSelf.map(contact => contact._id);

let skip = 0;
Expand All @@ -70,8 +69,8 @@ async function moveReports(db, options, moveContext) {
const createdAtId = options.merge && moveContext.sourceId;
reportDocsBatch = await DataSource.getReportsForContacts(db, descendantIds, createdAtId, skip);

const lineageUpdates = replaceLineageInReports(options, reportDocsBatch, moveContext);
const reassignUpdates = reassignReports(options, reportDocsBatch, moveContext);
const lineageUpdates = replaceCreatorLineageInReports(reportDocsBatch, moveContext);
const reassignUpdates = reassignReports(reportDocsBatch, moveContext);
const updatedReports = reportDocsBatch.filter(doc => lineageUpdates.has(doc._id) || reassignUpdates.has(doc._id));

minifyLineageAndWriteToDisk(options, updatedReports);
Expand All @@ -82,28 +81,34 @@ async function moveReports(db, options, moveContext) {
return skip;
}

function reassignReports(options, reports, { sourceId, destinationId }) {
function reassignReportWithSubject(report, subjectId) {
function reassignReportSubjects(report, { sourceId, destinationId }) {
const SUBJECT_IDS = ['patient_id', 'patient_uuid', 'place_id', 'place_uuid'];
let updated = false;
for (const subjectId of SUBJECT_IDS) {
if (report[subjectId] === sourceId) {
report[subjectId] = destinationId;
updated.add(report._id);
updated = true;
}

if (report.fields[subjectId] === sourceId) {
report.fields[subjectId] = destinationId;
updated.add(report._id);
updated = true;
}
}

return updated;
}

function reassignReports(reports, moveContext) {
const updated = new Set();
if (!options.merge) {
if (!moveContext.merge) {
return updated;
}

for (const report of reports) {
const subjectIds = ['patient_id', 'patient_uuid', 'place_id', 'place_uuid'];
for (const subjectId of subjectIds) {
reassignReportWithSubject(report, subjectId);
const isUpdated = reassignReportSubjects(report, moveContext);
if (isUpdated) {
updated.add(report._id);
}
}

Expand All @@ -117,16 +122,16 @@ function minifyLineageAndWriteToDisk(options, docs) {
});
}

function replaceLineageInReports(options, reports, moveContext) {
const replaceLineageOptions = {
function replaceCreatorLineageInReports(reports, moveContext) {
const replaceContactLineage = doc => lineageManipulation.replaceContactLineage(doc, {
replaceWith: moveContext.replacementLineage,
startingFromId: moveContext.sourceId,
merge: options.merge,
};
merge: moveContext.merge,
});

const updates = new Set();
reports.forEach(doc => {
if (lineageManipulation.replaceContactLineage(doc, replaceLineageOptions)) {
if (replaceContactLineage(doc)) {
updates.add(doc._id);
}
});
Expand All @@ -147,31 +152,31 @@ function replaceLineageInAncestors(descendantsAndSelf, ancestors) {
return updatedAncestors;
}

function replaceLineageInContacts(options, moveContext) {
function replaceForSingleContact(doc, moveContext) {
const { sourceId } = moveContext;
function replaceForSingleContact(doc) {
const docIsDestination = doc._id === sourceId;
const startingFromId = options.merge || !docIsDestination ? sourceId : undefined;
const replaceLineageOptions = {
replaceWith: moveContext.replacementLineage,
startingFromId,
merge: options.merge,
};
const parentWasUpdated = lineageManipulation.replaceParentLineage(doc, replaceLineageOptions);
const docIsDestination = doc._id === sourceId;
const startingFromId = moveContext.merge || !docIsDestination ? sourceId : undefined;
const replaceLineageOptions = {
replaceWith: moveContext.replacementLineage,
startingFromId,
merge: moveContext.merge,
};
const parentWasUpdated = lineageManipulation.replaceParentLineage(doc, replaceLineageOptions);

replaceLineageOptions.startingFromId = sourceId;
const contactWasUpdated = lineageManipulation.replaceContactLineage(doc, replaceLineageOptions);
if (parentWasUpdated || contactWasUpdated) {
return doc;
}
replaceLineageOptions.startingFromId = sourceId;
const contactWasUpdated = lineageManipulation.replaceContactLineage(doc, replaceLineageOptions);
if (parentWasUpdated || contactWasUpdated) {
return doc;
}
}

function replaceLineageInContacts(options, moveContext) {
function sonarQubeComplexityFiveIsTooLow(doc) {
const docIsSource = doc._id === sourceId;
const docIsSource = doc._id === moveContext.sourceId;

// skip source because it will be deleted
if (!options.merge || !docIsSource) {
return replaceForSingleContact(doc);
return replaceForSingleContact(doc, moveContext);
}
}

Expand All @@ -189,8 +194,8 @@ function replaceLineageInContacts(options, moveContext) {
module.exports = (db, options) => {
return {
HIERARCHY_ROOT: DataSource.HIERARCHY_ROOT,
move: moveHierarchy(db, { ...options, merge: false }),
merge: moveHierarchy(db, { ...options, merge: true }),
move: (sourceIds, destinationId) => moveHierarchy(db, { ...options, merge: false }, sourceIds, destinationId),
merge: (sourceIds, destinationId) => moveHierarchy(db, { ...options, merge: true }, sourceIds, destinationId),
};
};

48 changes: 24 additions & 24 deletions src/lib/hierarchy-operations/lineage-constraints.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,36 +51,36 @@ Enforce the list of allowed parents for each contact type
Ensure we are not creating a circular hierarchy
*/
const getMovingViolations = (mapTypeToAllowedParents, sourceDoc, destinationDoc) => {
function getContactTypeError() {
const sourceContactType = getContactType(sourceDoc);
const destinationType = getContactType(destinationDoc);
const rulesForContact = mapTypeToAllowedParents[sourceContactType];
if (!rulesForContact) {
return `cannot move contact with unknown type '${sourceContactType}'`;
}
const commonViolations = getCommonViolations(sourceDoc, destinationDoc);
const contactTypeError = getMovingContactTypeError(mapTypeToAllowedParents, sourceDoc, destinationDoc);
const circularHierarchyError = findCircularHierarchyErrors(sourceDoc, destinationDoc);
return commonViolations || contactTypeError || circularHierarchyError;
};

const isPermittedMoveToRoot = !destinationDoc && rulesForContact.length === 0;
if (!isPermittedMoveToRoot && !rulesForContact.includes(destinationType)) {
return `contacts of type '${sourceContactType}' cannot have parent of type '${destinationType}'`;
}
function getMovingContactTypeError(mapTypeToAllowedParents, sourceDoc, destinationDoc) {
const sourceContactType = getContactType(sourceDoc);
const destinationType = getContactType(destinationDoc);
const rulesForContact = mapTypeToAllowedParents[sourceContactType];
if (!rulesForContact) {
return `cannot move contact with unknown type '${sourceContactType}'`;
}

function findCircularHierarchyErrors() {
if (!destinationDoc || !sourceDoc._id) {
return;
}
const isPermittedMoveToRoot = !destinationDoc && rulesForContact.length === 0;
if (!isPermittedMoveToRoot && !rulesForContact.includes(destinationType)) {
return `contacts of type '${sourceContactType}' cannot have parent of type '${destinationType}'`;
}
}

const parentAncestry = [destinationDoc._id, ...lineageManipulation.pluckIdsFromLineage(destinationDoc.parent)];
if (parentAncestry.includes(sourceDoc._id)) {
return `Circular hierarchy: Cannot set parent of contact '${sourceDoc._id}' as it would create a circular hierarchy.`;
}
function findCircularHierarchyErrors(sourceDoc, destinationDoc) {
if (!destinationDoc || !sourceDoc._id) {
return;
}

const commonViolations = getCommonViolations(sourceDoc, destinationDoc);
const contactTypeError = getContactTypeError();
const circularHierarchyError = findCircularHierarchyErrors();
return commonViolations || contactTypeError || circularHierarchyError;
};
const parentAncestry = [destinationDoc._id, ...lineageManipulation.pluckIdsFromLineage(destinationDoc.parent)];
if (parentAncestry.includes(sourceDoc._id)) {
return `Circular hierarchy: Cannot set parent of contact '${sourceDoc._id}' as it would create a circular hierarchy.`;
}
}

const getCommonViolations = (sourceDoc, destinationDoc) => {
const sourceContactType = getContactType(sourceDoc);
Expand Down

0 comments on commit 28be7fb

Please sign in to comment.