Skip to content

Commit

Permalink
refactor(ccdaservice): test and refactor populateTimezones function (o…
Browse files Browse the repository at this point in the history
  • Loading branch information
raskolnikov-rodion authored Sep 2, 2023
1 parent c98fb92 commit 408ff7c
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 22 deletions.
23 changes: 1 addition & 22 deletions ccdaservice/serveccda.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const { safeTrim } = require('./utils/safe-trim/safe-trim');
const { headReplace } = require('./utils/head-replace/head-replace');
const { fDate, templateDate } = require('./utils/date/date');
const { countEntities } = require('./utils/count-entities/count-entities');
const { populateTimezones } = require('./utils/timezones/timezones');

var conn = ''; // make our connection scope global to script
var oidFacility = "";
Expand All @@ -32,28 +33,6 @@ var webRoot = "";
var authorDateTime = '';
var documentLocation = '';

// do a recursive descent transformation of the node object populating the timezone offset value if we have
// a precision property (inside a date) with the value of timezone.
function populateTimezones(node, tzOffset, depthCheck) {
if (!node || typeof node !== 'object') {
return node;
}
// we should NEVER go farther than 25 recursive loops down in our heirarchy, if we do it means we have an infinite loop
if (depthCheck > 25) {
console.error("Max depth traversal reached. Potential infinite loop. Breaking out of loop")
return node;
}

if (Object.prototype.hasOwnProperty.call(node, 'precision') && node.precision == 'tz' && !Object.prototype.hasOwnProperty.call(node, 'timezoneOffset')) {
node.timezoneOffset = tzOffset;
} else {
for (const [key, value] of Object.entries(node)) {
node[key] = populateTimezones(value, tzOffset, depthCheck + 1);
}
}
return node;
}

function fetchPreviousAddresses(pd) {
let addressArray = [];
let pa = pd.previous_addresses.address;
Expand Down
40 changes: 40 additions & 0 deletions ccdaservice/utils/timezones/timezones.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const DEPTH_LIMIT = 25;
const TIMEZONE_PRECISION = 'tz';

function isObject(node) {
return node && typeof node === 'object';
}

function isTimezoneDateWithoutOffset(node) {
return (
node.precision == TIMEZONE_PRECISION &&
!Object.prototype.hasOwnProperty.call(node, 'timezoneOffset')
);
}

// do a recursive descent transformation of the node object, populating the timezone offset value
// if we have a precision property (inside a date) with the value of timezone.
function populateTimezones(node, tzOffset, currentDepth) {
if (!isObject(node)) {
return node;
}

if (currentDepth > DEPTH_LIMIT) {
console.error(
'Max depth traversal reached. Potential infinite loop. Breaking out of loop.'
);
} else if (isTimezoneDateWithoutOffset(node)) {
node.timezoneOffset = tzOffset;
} else {
for (const [key, value] of Object.entries(node)) {
node[key] = populateTimezones(value, tzOffset, currentDepth + 1);
}
}
return node;
}

exports.populateTimezones = populateTimezones;
exports.TIMEZONE_PRECISION = TIMEZONE_PRECISION;
exports.DEPTH_LIMIT = DEPTH_LIMIT;
65 changes: 65 additions & 0 deletions ccdaservice/utils/timezones/timezones.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const {
populateTimezones,
TIMEZONE_PRECISION,
DEPTH_LIMIT,
} = require('./timezones');

describe('populateTimezones', () => {
const timezoneOffset = 5;

test.each([null, undefined, 10, false, 'OpenEMR'])(
'should return the node input if the node input is not an object',
(input) => {
expect(populateTimezones(input)).toEqual(input);
}
);

it(`should set timezoneOffset if node input has precision equals ${TIMEZONE_PRECISION}`, () => {
expect(
populateTimezones({ precision: TIMEZONE_PRECISION }, timezoneOffset)
).toEqual({
precision: TIMEZONE_PRECISION,
timezoneOffset,
});
});

it('should set timezoneOffset for nested nodes', () => {
const nodes = {
a: {
b: {
c: {
precision: TIMEZONE_PRECISION,
},
},
},
};
expect(populateTimezones(nodes, timezoneOffset)).toEqual({
a: {
b: {
c: {
precision: TIMEZONE_PRECISION,
timezoneOffset,
},
},
},
});
});

it(`should abort recursion and return node if it hits depth ${DEPTH_LIMIT}`, () => {
// keeping logs clean
jest.spyOn(console, 'error').mockImplementationOnce(() => {});
const node = getDeepNode();
expect(populateTimezones(node, timezoneOffset, 0)).toEqual(node);
expect(console.error).toHaveBeenCalledTimes(1);

function getDeepNode() {
const node = {};
let latest = node;
for (let i = 0; i < DEPTH_LIMIT + 1; i++) {
latest[0] = {};
latest = latest[0];
}
return node;
}
});
});

0 comments on commit 408ff7c

Please sign in to comment.