UNPKG

h54s

Version:

HTML5 Data Adapter for SAS

178 lines (153 loc) 7.53 kB
const h54sError = require('../error.js'); const logs = require('../logs.js'); /* * Convert table object to Sas readable object * * @param {object} inObject - Object to convert * */ module.exports.convertTableObject = function(inObject, chunkThreshold) { const self = this; if(chunkThreshold > 30000) { console.warn('You should not set threshold larger than 30kb because of the SAS limitations'); } // first check that the object is an array if (typeof (inObject) !== 'object') { throw new h54sError('argumentError', 'The parameter passed to checkAndGetTypeObject is not an object'); } const arrayLength = inObject.length; if (typeof (arrayLength) !== 'number') { throw new h54sError('argumentError', 'The parameter passed to checkAndGetTypeObject does not have a valid length and is most likely not an array'); } const existingCols = {}; // this is just to make lookup easier rather than traversing array each time. Will transform after // function checkAndSetArray - this will check an inObject current key against the existing typeArray and either return -1 if there // is a type mismatch or add an element and update/increment the length if needed function checkAndIncrement(colSpec) { if (typeof (existingCols[colSpec.colName]) === 'undefined') { existingCols[colSpec.colName] = {}; existingCols[colSpec.colName].colName = colSpec.colName; existingCols[colSpec.colName].colType = colSpec.colType; existingCols[colSpec.colName].colLength = colSpec.colLength > 0 ? colSpec.colLength : 1; return 0; // all ok } // check type match if (existingCols[colSpec.colName].colType !== colSpec.colType) { return -1; // there is a fudge in the typing } if (existingCols[colSpec.colName].colLength < colSpec.colLength) { existingCols[colSpec.colName].colLength = colSpec.colLength > 0 ? colSpec.colLength : 1; // increment the max length of this column return 0; } } let chunkArrayCount = 0; // this is for keeping tabs on how long the current array string would be const targetArray = []; // this is the array of target arrays let currentTarget = 0; targetArray[currentTarget] = []; let j = 0; for (let i = 0; i < inObject.length; i++) { targetArray[currentTarget][j] = {}; let chunkRowCount = 0; for (let key in inObject[i]) { const thisSpec = {}; const thisValue = inObject[i][key]; //skip undefined values if(thisValue === undefined || thisValue === null) { continue; } //throw an error if there's NaN value if(typeof thisValue === 'number' && isNaN(thisValue)) { throw new h54sError('typeError', 'NaN value in one of the values (columns) is not allowed'); } if(thisValue === -Infinity || thisValue === Infinity) { throw new h54sError('typeError', thisValue.toString() + ' value in one of the values (columns) is not allowed'); } if(thisValue === true || thisValue === false) { throw new h54sError('typeError', 'Boolean value in one of the values (columns) is not allowed'); } // get type... if it is an object then convert it to json and store as a string const thisType = typeof (thisValue); if (thisType === 'number') { // straightforward number if(thisValue < Number.MIN_SAFE_INTEGER || thisValue > Number.MAX_SAFE_INTEGER) { logs.addApplicationLog('Object[' + i + '].' + key + ' - This value exceeds expected numeric precision.'); } thisSpec.colName = key; thisSpec.colType = 'num'; thisSpec.colLength = 8; thisSpec.encodedLength = thisValue.toString().length; targetArray[currentTarget][j][key] = thisValue; } else if (thisType === 'string') { thisSpec.colName = key; thisSpec.colType = 'string'; thisSpec.colLength = thisValue.length; if (thisValue === "") { targetArray[currentTarget][j][key] = " "; } else { targetArray[currentTarget][j][key] = encodeURIComponent(thisValue).replace(/'/g, '%27'); } thisSpec.encodedLength = targetArray[currentTarget][j][key].length; } else if(thisValue instanceof Date) { console.log("ERROR VALUE ", thisValue) console.log("TYPEOF VALUE ", typeof thisValue) throw new h54sError('typeError', 'Date type not supported. Please use h54s.toSasDateTime function to convert it'); } else if (thisType == 'object') { thisSpec.colName = key; thisSpec.colType = 'json'; thisSpec.colLength = JSON.stringify(thisValue).length; targetArray[currentTarget][j][key] = encodeURIComponent(JSON.stringify(thisValue)).replace(/'/g, '%27'); thisSpec.encodedLength = targetArray[currentTarget][j][key].length; } chunkRowCount = chunkRowCount + 6 + key.length + thisSpec.encodedLength; if (checkAndIncrement(thisSpec) == -1) { throw new h54sError('typeError', 'There is a type mismatch in the array between values (columns) of the same name.'); } } //remove last added row if it's empty if(Object.keys(targetArray[currentTarget][j]).length === 0) { targetArray[currentTarget].splice(j, 1); continue; } if (chunkRowCount > chunkThreshold) { throw new h54sError('argumentError', 'Row ' + j + ' exceeds size limit of 32kb'); } else if(chunkArrayCount + chunkRowCount > chunkThreshold) { //create new array if this one is full and move the last item to the new array const lastRow = targetArray[currentTarget].pop(); // get rid of that last row currentTarget++; // move onto the next array targetArray[currentTarget] = [lastRow]; // make it an array j = 0; // initialise new row counter for new array - it will be incremented at the end of the function chunkArrayCount = chunkRowCount; // this is the new chunk max size } else { chunkArrayCount = chunkArrayCount + chunkRowCount; } j++; } // reformat existingCols into an array so sas can parse it; const specArray = []; for (let k in existingCols) { specArray.push(existingCols[k]); } return { spec: specArray, data: targetArray, jsonLength: chunkArrayCount }; // the spec will be the macro[0], with the data split into arrays of macro[1-n] // means in terms of dojo xhr object at least they need to go into the same array }; /* * Convert javascript date to sas time * * @param {object} jsDate - javascript Date object * */ module.exports.toSasDateTime = function (jsDate) { const basedate = new Date("January 1, 1960 00:00:00"); const currdate = jsDate; // offsets for UTC and timezones and BST const baseOffset = basedate.getTimezoneOffset(); // in minutes const currOffset = currdate.getTimezoneOffset(); // in minutes // convert currdate to a sas datetime const offsetSecs = (currOffset - baseOffset) * 60; // offsetDiff is in minutes to start with const baseDateSecs = basedate.getTime() / 1000; // get rid of ms const currdateSecs = currdate.getTime() / 1000; // get rid of ms const sasDatetime = Math.round(currdateSecs - baseDateSecs - offsetSecs); // adjust return sasDatetime; };