From af9a9aca3dc0790d5acdd4f92381591670b54e8e Mon Sep 17 00:00:00 2001 From: kennsippell Date: Sun, 8 Dec 2024 19:52:40 -0800 Subject: [PATCH] lineage-manipulation refactor --- .../lineage-manipulation.js | 76 +----------------- .../hierarchy-operations/replace-lineage.js | 77 +++++++++++++++++++ .../hierarchy-operations.spec.js | 1 - 3 files changed, 81 insertions(+), 73 deletions(-) create mode 100644 src/lib/hierarchy-operations/replace-lineage.js diff --git a/src/lib/hierarchy-operations/lineage-manipulation.js b/src/lib/hierarchy-operations/lineage-manipulation.js index 0ad9260c..4ce274e8 100644 --- a/src/lib/hierarchy-operations/lineage-manipulation.js +++ b/src/lib/hierarchy-operations/lineage-manipulation.js @@ -1,75 +1,4 @@ - -/** - * Given a doc, replace the lineage information therein with "replaceWith" - * - * @param {Object} doc A CouchDB document containing a hierarchy that needs replacing - * @param {Object} params SonarQube - * @param {Object} params.replaceWith The new hierarchy { parent: { _id: 'parent', parent: { _id: 'grandparent' } } - * @param {string} params.startingFromId Only the part of the lineage "after" this id will be replaced - * @param {boolean} params.merge When true, startingFromId is replaced and when false, startingFromId's parent is replaced - */ -function replaceLineage(doc, lineageAttributeName, params) { - const { replaceWith, startingFromId, merge } = params; - - // Replace the full lineage - if (!startingFromId) { - return replaceEntireLineage(doc, lineageAttributeName, replaceWith); - } - - function getInitialState() { - if (merge) { - return { - element: doc, - attributeName: lineageAttributeName, - }; - } - - return { - element: doc[lineageAttributeName], - attributeName: 'parent', - }; - } - - function traverseOne() { - const compare = merge ? state.element[state.attributeName] : state.element; - if (compare?._id === startingFromId) { - return replaceEntireLineage(state.element, state.attributeName, replaceWith); - } - - state.element = state.element[state.attributeName]; - state.attributeName = 'parent'; - } - - const state = getInitialState(); - while (state.element) { - const result = traverseOne(); - if (result) { - return result; - } - } - - return false; -} - -function replaceParentLineage(doc, params) { - return replaceLineage(doc, 'parent', params); -} - -function replaceContactLineage(doc, params) { - return replaceLineage(doc, 'contact', params); -} - -const replaceEntireLineage = (replaceInDoc, lineageAttributeName, replaceWith) => { - if (!replaceWith) { - const lineageWasDeleted = !!replaceInDoc[lineageAttributeName]; - replaceInDoc[lineageAttributeName] = undefined; - return lineageWasDeleted; - } else { - replaceInDoc[lineageAttributeName] = replaceWith; - } - - return true; -}; +const { replaceContactLineage, replaceParentLineage } = require('./replace-lineage'); /* Function borrowed from shared-lib/lineage @@ -96,6 +25,9 @@ const minifyLineagesInDoc = doc => { if ('contact' in doc) { doc.contact = minifyLineage(doc.contact); + if (doc.contact && !doc.contact.parent) { + delete doc.contact.parent; // for unit test clarity + } } if (doc.type === 'data_record') { diff --git a/src/lib/hierarchy-operations/replace-lineage.js b/src/lib/hierarchy-operations/replace-lineage.js new file mode 100644 index 00000000..7e7cdbec --- /dev/null +++ b/src/lib/hierarchy-operations/replace-lineage.js @@ -0,0 +1,77 @@ +function replaceLineage(doc, lineageAttributeName, params) { + // Replace the full lineage + if (!params.startingFromId) { + return replaceEntireLineage(doc, lineageAttributeName, params.replaceWith); + } + + const selectedFunction = params.merge ? replaceLineageForMerge : replaceLineageForMove; + return selectedFunction(doc, lineageAttributeName, params); +} + +function replaceLineageForMove(doc, lineageAttributeName, params) { + let currentElement = doc[lineageAttributeName]; + while (currentElement) { + if (currentElement?._id === params.startingFromId) { + return replaceEntireLineage(currentElement, 'parent', params.replaceWith); + } + + currentElement = currentElement.parent; + } + + return false; +} + +function replaceLineageForMerge(doc, lineageAttributeName, params) { + let currentElement = doc; + let currentAttributeName = lineageAttributeName; + while (currentElement) { + if (currentElement[currentAttributeName]?._id === params.startingFromId) { + return replaceEntireLineage(currentElement, currentAttributeName, params.replaceWith); + } + + currentElement = currentElement[currentAttributeName]; + currentAttributeName = 'parent'; + } + + return false; +} + +function replaceEntireLineage(replaceInDoc, lineageAttributeName, replaceWith) { + if (!replaceWith) { + const lineageWasDeleted = !!replaceInDoc[lineageAttributeName]; + replaceInDoc[lineageAttributeName] = undefined; + return lineageWasDeleted; + } else { + replaceInDoc[lineageAttributeName] = replaceWith; + } + + return true; +} + +module.exports = { +/** + * Given a doc, replace the lineage information therein with "replaceWith" + * + * @param {Object} doc A CouchDB document containing a hierarchy that needs replacing + * @param {Object} params + * @param {Object} params.replaceWith The new hierarchy { parent: { _id: 'parent', parent: { _id: 'grandparent' } } + * @param {string} params.startingFromId Only the part of the lineage "after" this id will be replaced + * @param {boolean} params.merge When true, startingFromId is replaced and when false, startingFromId's parent is replaced + */ + replaceParentLineage: (doc, params) => { + return replaceLineage(doc, 'parent', params); + }, + +/** + * Given a doc, replace the lineage information therein with "replaceWith" + * + * @param {Object} doc A CouchDB document containing a hierarchy that needs replacing + * @param {Object} params + * @param {Object} params.replaceWith The new hierarchy { parent: { _id: 'parent', parent: { _id: 'grandparent' } } + * @param {string} params.startingFromId Only the part of the lineage "after" this id will be replaced + * @param {boolean} params.merge When true, startingFromId is replaced and when false, startingFromId's parent is replaced + */ + replaceContactLineage: (doc, params) => { + return replaceLineage(doc, 'contact', params); + }, +}; diff --git a/test/lib/hierarchy-operations/hierarchy-operations.spec.js b/test/lib/hierarchy-operations/hierarchy-operations.spec.js index c18cd025..7c47df71 100644 --- a/test/lib/hierarchy-operations/hierarchy-operations.spec.js +++ b/test/lib/hierarchy-operations/hierarchy-operations.spec.js @@ -464,7 +464,6 @@ describe('move-contacts', () => { type: 'data_record', contact: { _id: 'dne', - parent: undefined, }, fields: { patient_uuid: 'district_1'