diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index ec3f3c4..0000000 --- a/.eslintrc +++ /dev/null @@ -1,18 +0,0 @@ ---- -env: - es6: true - node: true -extends: 'eslint:recommended' -rules: - indent: - - error - - tab - linebreak-style: - - error - - unix - quotes: - - error - - single - semi: - - error - - always diff --git a/.gitignore b/.gitignore index 81e2efc..a033cca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ + * !/spec @@ -11,3 +12,8 @@ !/package-lock.json !/package.json !/README.md +!/src/** +!/lib/** +!/lib +!/tsconfig.json +!/tslint.json diff --git a/.travis.yml b/.travis.yml index 9b0f74b..cf00cd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,12 @@ branches: - master - qc node_js: - - "0.12.5" - "4" - "6" - "8" - "node" +before_script: + - npm run build script: - npm test after_script: diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ac1cb..299d3e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [0.4.0] - 2019-03-07 +### Added +- Array support +- Composite keys +- TypeScript definitions + +### Changed +- Converted project to TypeScript + ## [0.3.0] - 2016-06-07 ### Added - Changelog diff --git a/NestHydrationJS.js b/NestHydrationJS.js deleted file mode 100644 index 9368cdc..0000000 --- a/NestHydrationJS.js +++ /dev/null @@ -1,432 +0,0 @@ -'use strict'; - -function nestHydrationJS() { - var NestHydrationJS, _; - - var isArray = require('lodash.isarray'); - var isFunction = require('lodash.isfunction'); - var keys = require('lodash.keys'); - var values = require('lodash.values'); - var isPlainObject = require('lodash.isplainobject'); - - NestHydrationJS = {}; - - NestHydrationJS.typeHandlers = { - NUMBER: function (cellValue) { - return parseFloat(cellValue); - }, - BOOLEAN: function (cellValue) { - return cellValue == true; - } - }; - - /* Creates a data structure containing nested objects and/or arrays from - * tabular data based on a structure definition provided by - * structPropToColumnMap. If structPropToColumnMap is not provided but - * the data has column names that follow a particular convention then a - * nested structures can also be created. - */ - NestHydrationJS.nest = function (data, structPropToColumnMap) { - var listOnEmpty, table, meta, struct, i, row, j, _nest, primeIdColumn; - - // VALIDATE PARAMS AND BASIC INITIALIZATION - - listOnEmpty = false; - - if (typeof structPropToColumnMap === 'undefined') { - structPropToColumnMap = null; - } - - if (data === null) { - return null; - } - - if (!isArray(structPropToColumnMap) && !isPlainObject(structPropToColumnMap) && structPropToColumnMap !== null && structPropToColumnMap !== true) { - throw new Error('nest expects param structPropToColumnMap to be an array, plain object, null, or true'); - } - - if (isPlainObject(data)) { - // internal table should be a table format but a plain object - // could be passed as the first (and only) row of that table - table = [data]; - } else if (isArray(data)) { - table = data; - } else { - throw Error('nest expects param data to be in the form of a plain object or an array of plain objects (forming a table)'); - } - - // structPropToColumnMap can be set to true as a tie break between - // returning null (empty structure) or an empty list - if (structPropToColumnMap === true) { - listOnEmpty = true; - structPropToColumnMap = null; - } - - if (structPropToColumnMap === null && table.length > 0) { - // property mapping not specified, determine it from column names - structPropToColumnMap = NestHydrationJS.structPropToColumnMapFromColumnHints(keys(table[0])); - } - - if (structPropToColumnMap === null) { - // properties is empty, can't form structure or determine content - // for a list. Assume a structure unless listOnEmpty - return listOnEmpty ? [] : null; - } else if (table.length === 0) { - // table is empty, return the appropriate empty result based on input definition - return isArray(structPropToColumnMap) ? [] : null; - } - - // COMPLETE VALIDATING PARAMS AND BASIC INITIALIZATION - - meta = NestHydrationJS.buildMeta(structPropToColumnMap); - - // BUILD FROM TABLE - - // defines function that can be called recursively - _nest = function (row, idColumn) { - var value, objMeta, obj, k, containingId, container, cell, cellValue, valueTypeFunction; - - value = row[idColumn]; - - // only really concerned with the meta data for this identity column - objMeta = meta.idMap[idColumn]; - - if (value === null) { - if (objMeta.default !== null && typeof objMeta.default !== 'undefined') { - value = objMeta.default; - } else { - return; - } - } - - if (typeof objMeta.cache[value] !== 'undefined') { - // object already exists in cache - if (objMeta.containingIdUsage === null) { - // at the top level, parent is root - return; - } - - containingId = row[objMeta.containingColumn]; - if (typeof objMeta.containingIdUsage[value] !== 'undefined' - && typeof objMeta.containingIdUsage[value][containingId] !== 'undefined' - ) { - // already placed as to-many relation in container, done - return; - } - - // not already placed as to-many relation in container - obj = objMeta.cache[value]; - } else { - // don't have an object defined for this yet, create it - obj = {}; - objMeta.cache[value] = obj; - - // copy in properties from table data - for (k = 0; k < objMeta.valueList.length; k++) { - cell = objMeta.valueList[k]; - cellValue = row[cell.column]; - if (cellValue !== null) { - if (isFunction(cell.type)) { - valueTypeFunction = cell.type; - } else { - valueTypeFunction = NestHydrationJS.typeHandlers[cell.type]; - } - if (valueTypeFunction) { - cellValue = valueTypeFunction(cellValue, cell.column, row); - } - } else if (typeof cell.default !== 'undefined') { - cellValue = cell.default; - } - - obj[cell.prop] = cellValue; - } - - // initialize empty to-many relations, they will be populated when - // those objects build themselves and find this containing object - for (k = 0; k < objMeta.toManyPropList.length; k++) { - obj[objMeta.toManyPropList[k]] = []; - } - - // initialize null to-one relations and then recursively build them - for (k = 0; k < objMeta.toOneList.length; k++) { - obj[objMeta.toOneList[k].prop] = null; - _nest(row, objMeta.toOneList[k].column); - } - } - - // link from the parent - if (objMeta.containingColumn === null) { - // parent is the top level - if (objMeta.isOneOfMany) { - // it is an array - if (struct === null) { - struct = []; - } - struct.push(obj); - } else { - // it is this object - struct = obj; - } - } else { - containingId = row[objMeta.containingColumn]; - container = meta.idMap[objMeta.containingColumn].cache[containingId]; - - if (container) { - if (objMeta.isOneOfMany) { - // it is an array - container[objMeta.ownProp].push(obj); - } else { - // it is this object - container[objMeta.ownProp] = obj; - } - } - - // record the containing id - if (typeof objMeta.containingIdUsage[value] === 'undefined') { - objMeta.containingIdUsage[value] = {}; - } - objMeta.containingIdUsage[value][containingId] = true; - } - }; - - // struct is populated inside the build function - struct = null; - - for (i = 0; i < table.length; i++) { - // go through each row of the table - row = table[i]; - - for (j = 0; j < meta.primeIdColumnList.length; j++) { - // for each prime id column (corresponding to a to-many relation or - // the top level) attempted to build an object - primeIdColumn = meta.primeIdColumnList[j]; - - _nest(row, primeIdColumn); - } - } - - return struct; - }; - - /* Create a data structure that contains lookups and cache spaces for quick - * reference and action for the workings of the nest method. - */ - NestHydrationJS.buildMeta = function (structPropToColumnMap) { - // internally defines recursive function with extra param. This allows cleaner API - var meta, _buildMeta, primeIdColumn; - - // recursive internal function - _buildMeta = function (structPropToColumnMap, isOneOfMany, containingColumn, ownProp) { - var propList, idProp, idColumn, i, prop, objMeta, subIdColumn; - - - propList = keys(structPropToColumnMap); - if (propList.length === 0) { - throw new Error('invalid structPropToColumnMap format - property \'' + ownProp + '\' can not be an empty array'); - } - - for (i = 0; i < propList.length; i++) { - prop = propList[i]; - if (structPropToColumnMap[prop].id === true) { - idProp = prop; - break; - } - } - - if (idProp === undefined) { - idProp = propList[0]; - } - - idColumn = structPropToColumnMap[idProp].column || structPropToColumnMap[idProp]; - - if (isOneOfMany) { - meta.primeIdColumnList.push(idColumn); - } - - objMeta = { - valueList: [], - toOneList: [], - toManyPropList: [], - containingColumn: containingColumn, - ownProp: ownProp, - isOneOfMany: isOneOfMany === true, - cache: {}, - containingIdUsage: containingColumn === null ? null : {}, - default: typeof structPropToColumnMap[idProp].default === 'undefined' ? null : structPropToColumnMap[idProp].default - }; - - for (i = 0; i < propList.length; i++) { - prop = propList[i]; - if (typeof structPropToColumnMap[prop] === 'string') { - // value property - objMeta.valueList.push({ - prop: prop, - column: structPropToColumnMap[prop], - type: undefined, - default: undefined - }); - } else if (structPropToColumnMap[prop].column) { - // value property - objMeta.valueList.push({ - prop: prop, - column: structPropToColumnMap[prop].column, - type: structPropToColumnMap[prop].type, - default: structPropToColumnMap[prop].default - }); - } else if (isArray(structPropToColumnMap[prop])) { - // list of objects / to-many relation - objMeta.toManyPropList.push(prop); - - _buildMeta(structPropToColumnMap[prop][0], true, idColumn, prop); - } else if (isPlainObject(structPropToColumnMap[prop])) { - // object / to-one relation - - subIdColumn = values(structPropToColumnMap[prop])[0]; - if (typeof subIdColumn === 'undefined') { - throw new Error('invalid structPropToColumnMap format - property \'' + prop + '\' can not be an empty object'); - } - - if (subIdColumn.column) { - subIdColumn = subIdColumn.column; - } - - objMeta.toOneList.push({ - prop: prop, - column: subIdColumn - }); - _buildMeta(structPropToColumnMap[prop], false, idColumn, prop); - } else { - throw new Error('invalid structPropToColumnMap format - property \'' + prop + '\' must be either a string, a plain object or an array'); - } - } - - meta.idMap[idColumn] = objMeta; - }; - - // this data structure is populated by the _buildMeta function - meta = { - primeIdColumnList: [], - idMap: {} - }; - - if (isArray(structPropToColumnMap)) { - if (structPropToColumnMap.length !== 1) { - throw new Error('invalid structPropToColumnMap format - can not have multiple roots for structPropToColumnMap, if an array it must only have one item'); - } - // call with first object, but inform _buildMeta it is an array - _buildMeta(structPropToColumnMap[0], true, null, null); - } else if (isPlainObject(structPropToColumnMap)) { - // register first column as prime id column - primeIdColumn = values(structPropToColumnMap)[0]; - if (typeof primeIdColumn === 'undefined') { - throw new Error('invalid structPropToColumnMap format - the base object can not be an empty object'); - } - - if (typeof primeIdColumn !== 'string') { - primeIdColumn = primeIdColumn.column; - } - - meta.primeIdColumnList.push(primeIdColumn); - - // construct the rest - _buildMeta(structPropToColumnMap, false, null, null); - } - - return meta; - }; - - /* Returns a property mapping data structure based on the names of columns - * in columnList. Used internally by nest when its propertyMapping param - * is not specified. - */ - NestHydrationJS.structPropToColumnMapFromColumnHints = function (columnList, renameMapping) { - var propertyMapping, prop, i, columnType, type, isId, column, pointer, navList, j, nav, renamedColumn, prevKeyList, k; - - if (typeof renameMapping === 'undefined') { - renameMapping = {}; - } - - propertyMapping = {base: null}; - - for (i = 0; i < columnList.length; i++) { - column = columnList[i]; - - columnType = column.split('___'); - - type = null; - isId = false; - for (j = 1; j < columnType.length; j++) { - if (columnType[j] === 'ID') { - isId = true; - } else if (typeof NestHydrationJS.typeHandlers[columnType[j]] !== 'undefined') { - type = columnType[j]; - } - } - - pointer = propertyMapping; // point to base on each new column - prop = 'base'; - - navList = columnType[0].split('_'); - - for (j = 0; j < navList.length; j++) { - nav = navList[j]; - - if (nav === '') { - if (pointer[prop] === null) { - pointer[prop] = [null]; - } - pointer = pointer[prop]; - prop = 0; - } else { - if (pointer[prop] === null) { - pointer[prop] = {}; - } - if (typeof pointer[prop][nav] === 'undefined') { - renamedColumn = typeof renameMapping[column] === 'undefined' - ? column - : renameMapping[column] - ; - if (type !== null || isId) { - // no longer a simple mapping, has need of the type or id properties - renamedColumn = {column: renamedColumn}; - } - if (type !== null) { - // detail the type in the column map if type provided - renamedColumn.type = type; - } - if (isId) { - // set the id property in the column map - renamedColumn.id = true; - // check for any existing id keys that are conflicting - prevKeyList = keys(pointer[prop]); - for (k = 0; k < prevKeyList.length; k++) { - if (pointer[prop][prevKeyList[k]].id === true) { - return 'invalid - multiple id - ' + pointer[prop][prevKeyList[k]].column + ' and ' + renamedColumn.column + ' conflict'; - } - } - } - pointer[prop][nav] = j === (navList.length - 1) - ? renamedColumn // is leaf node, store full column string - : null // iteration will replace with object or array - ; - } - pointer = pointer[prop]; - prop = nav; - } - } - } - - return propertyMapping.base; - }; - - /* Registers a custom type handler */ - NestHydrationJS.registerType = function (name, handler) { - if (NestHydrationJS.typeHandlers[name]) { - throw new Error('Handler with type, ' + name + ', already exists'); - } - - NestHydrationJS.typeHandlers[name] = handler; - }; - return NestHydrationJS; -} -module.exports = nestHydrationJS; diff --git a/README.md b/README.md index 8f5870a..c914662 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ The following example gives same result as above but a column naming convention Nesting is achieved by using a underscore (`_`). A *x to one* relation is defined by a single underscore and a *x to many* relation is defined by preceeding properties of the many object with a 2nd underscore. -If a column alias ends in a triple underscore (`___`) followed by either `NUMBER` or `BOOLEAN` then the values in those columns will be caste to the respective type unless the value is null. Triple underscore with `ID` (`___ID`) can be used to specify a column that is an id propery of that level of object. If an id is not specified the default is for the first column in that object to be the id property. The id specifier can be used in combination with a type caste, so either `___ID___NUMBER`, or `___NUMBER___ID` would be valid appends to a column name. +If a column alias ends in a triple underscore (`___`) followed by either `NUMBER` or `BOOLEAN` then the values in those columns will be caste to the respective type unless the value is null. Triple underscore with `ID` (`___ID`) can be used to specify a column that is an id propery of that level of object. Triple underscore with `ARRAY` (`___ARRAY`) can be used to specify a column that should be treated as an array. If an id is not specified the default is for the first column in that object to be the id property. The id specifier can be used in combination with a type caste, so either `___ID___NUMBER`, or `___NUMBER___ID` would be valid appends to a column name. **Note:** that this means that almost always base level properties will be prefixed with a underscore, as this is actually a *x to many* relation from the variable returned from the `nest` function. @@ -223,10 +223,10 @@ Result Additional Definition Object Capabilities ========================================= -Ids That Aren't First In Definition Properties ----------------------------------------------- +Ids That Aren't First In Definition Properties / Composite Ids +-------------------------------------------------------------- -It is possible to specify an id column for mapping to objects instead of having it default to the first property of each object specified in the definition. If multiple properties for an object are specified to be ids only the first will be used. +It is possible to specify the id columns for mapping to objects instead of having it default to the first property of each object specified in the definition. If multiple id properties for an object are specified, they will be treated as a composite key. ```javascript var NestHydrationJS = require('nesthydrationjs')(); @@ -291,6 +291,41 @@ result = NestHydrationJS.nest(table, definition); */ ``` +Arrays +------ + +You can specify that a column should be joined as an array by setting `array` to true. + +### Example + +```javascript +var NestHydrationJS = require('nesthydrationjs')(); + +var table = [ + { id: 1, foo: 'foo' }, + { id: 1, foo: 'bar' }, + { id: 1, foo: 'baz' }, + { id: 2, foo: 'foo' } +]; +var definition = [{ + id: 'id', + title: {column: 'foo', array: true}, +}]; +result = NestHydrationJS.nest(table, definition); +/* result would be the following: +[ + { + id: 1, + title: ['foo', 'bar', 'baz'] + }, + { + id: 2, + title: ['foo'] + } +] +*/ +``` + Custom Type Definition ---------------------- @@ -351,6 +386,10 @@ result = NestHydrationJS.nest(table, definition); */ ``` +### Typescript Definitions + +Typescript definitions are bundled with this module. + Related Projects ---------------- diff --git a/lib/NestHydrationJS.d.ts b/lib/NestHydrationJS.d.ts new file mode 100644 index 0000000..85eb29d --- /dev/null +++ b/lib/NestHydrationJS.d.ts @@ -0,0 +1,27 @@ +declare namespace NestHydrationJS { + type ITypeHandler = (cellValue: any) => any; + interface IDictionary { + [index: string]: TValue; + } + interface IDefinitionColumn { + column: string; + id?: boolean; + default?: any; + type?: string; + array?: boolean; + } + interface IDefinition { + [index: string]: IDefinitionColumn | string | IDefinition | IDefinition[]; + } + class NestHydrationJS { + private typeHandlers; + private struct; + nest(data: any, structPropToColumnMap: IDefinition | IDefinition[] | null | boolean, verbose?: boolean): any; + structPropToColumnMapFromColumnHints(columnList: string[], renameMapping?: IDictionary): any; + registerType(name: string, handler: ITypeHandler): void; + private computeActualCellValue; + private buildMeta; + } +} +declare const _default: () => NestHydrationJS.NestHydrationJS; +export = _default; diff --git a/lib/NestHydrationJS.js b/lib/NestHydrationJS.js new file mode 100644 index 0000000..06a5494 --- /dev/null +++ b/lib/NestHydrationJS.js @@ -0,0 +1,480 @@ +'use strict'; +var isArray = require("lodash.isarray"); +var isFunction = require("lodash.isfunction"); +var isPlainObject = require("lodash.isplainobject"); +var keys = require("lodash.keys"); +var values = require("lodash.values"); +// tslint:disable-next-line: no-namespace +var NestHydrationJS; +(function (NestHydrationJS_1) { + function createCompositeKey(vals, separator) { + if (separator === void 0) { separator = ', '; } + return vals.join(separator); + } + // tslint:disable-next-line: no-shadowed-variable + var NestHydrationJS = /** @class */ (function () { + function NestHydrationJS() { + this.typeHandlers = { + NUMBER: function (cellValue) { + return parseFloat(cellValue); + }, + BOOLEAN: function (cellValue) { + // tslint:disable-next-line: triple-equals + return cellValue == true; + }, + }; + this.struct = null; + } + /* Creates a data structure containing nested objects and/or arrays from + * tabular data based on a structure definition provided by + * structPropToColumnMap. If structPropToColumnMap is not provided but + * the data has column names that follow a particular convention then a + * nested structures can also be created. + */ + NestHydrationJS.prototype.nest = function (data, structPropToColumnMap, verbose) { + var _this = this; + if (verbose === void 0) { verbose = false; } + var table; + // VALIDATE PARAMS AND BASIC INITIALIZATION + // Determines that on no results, and empty list is used instead of null. // NOTE: fact check this + var listOnEmpty = false; + if (typeof structPropToColumnMap === 'undefined') { + structPropToColumnMap = null; + } + if (data === null) { + return null; + } + if (!isArray(structPropToColumnMap) && !isPlainObject(structPropToColumnMap) && + structPropToColumnMap !== null && structPropToColumnMap !== true) { + throw new Error('nest expects param structPropToColumnMap to be an array, plain object, null, or true'); + } + if (isPlainObject(data)) { + // internal table should be a table format but a plain object + // could be passed as the first (and only) row of that table + table = [data]; + } + else if (isArray(data)) { + table = data; + } + else { + // tslint:disable-next-line: max-line-length + throw Error("nest expects param data to be in the form of a plain object or an array of plain objects (forming a table)"); + } + // structPropToColumnMap can be set to true as a tie break between + // returning null (empty structure) or an empty list + if (structPropToColumnMap === true) { + listOnEmpty = true; + structPropToColumnMap = null; + } + if (structPropToColumnMap === null && table.length > 0) { + // property mapping not specified, determine it from column names + structPropToColumnMap = this.structPropToColumnMapFromColumnHints(keys(table[0])); + } + if (structPropToColumnMap === null) { + // properties is empty, can't form structure or determine content + // for a list. Assume a structure unless listOnEmpty + return listOnEmpty ? [] : null; + } + else if (table.length === 0) { + // table is empty, return the appropriate empty result based on input definition + return isArray(structPropToColumnMap) ? [] : null; + } + // COMPLETE VALIDATING PARAMS AND BASIC INITIALIZATION + var meta = this.buildMeta(structPropToColumnMap); + // BUILD FROM TABLE + // defines function that can be called recursively + var recursiveNest = function (row, idColumns) { + // Obj is the actual object that will end up in the final structure + var obj; + // Get all of the values for each id + var vals = idColumns.map(function (column) { return row[column]; }); + // only really concerned with the meta data for this identity column + var objMeta = meta.idMap[createCompositeKey(idColumns)]; + // If any of the values are null, we'll check and see if we need to set defaults + vals = vals.map(function (value, idx) { + if (value === null) { + if (objMeta.defaults[idColumns[idx]] !== null && typeof objMeta.defaults[idColumns[idx]] !== 'undefined') { + return objMeta.defaults[idColumns[idx]]; + } + } + return value; + }); + if (vals.indexOf(null) !== -1) { + return; + } + // check if object already exists in cache + if (typeof objMeta.cache[createCompositeKey(vals)] !== 'undefined') { + // not already placed as to-many relation in container + obj = objMeta.cache[createCompositeKey(vals)]; + // Add array values if necessary + for (var _i = 0, _a = objMeta.arraysList; _i < _a.length; _i++) { + var prop = _a[_i]; + var cellValue = _this.computeActualCellValue(prop, row[prop.column]); + if (isArray(obj[prop.prop])) { + obj[prop.prop].push(cellValue); + } + else { + obj[prop.prop] = [cellValue]; + } + } + if (objMeta.containingIdUsage === null) { + return; + } + // We know for certain that containing column is set if + // containingIdUsage is not null and can cast it as a string + // check and see if this has already been linked to the parent, + // and if so we don't need to continue + var containingIds = objMeta.containingColumn.map(function (column) { return row[column]; }); + if (typeof objMeta.containingIdUsage[createCompositeKey(vals)] !== 'undefined' + && typeof objMeta.containingIdUsage[createCompositeKey(vals)][createCompositeKey(containingIds)] !== 'undefined') { + return; + } + } + else { + // don't have an object defined for this yet, create it and set the cache + obj = {}; + objMeta.cache[createCompositeKey(vals)] = obj; + // copy in properties from table data + for (var _b = 0, _c = objMeta.valueList; _b < _c.length; _b++) { + var prop = _c[_b]; + var cellValue = _this.computeActualCellValue(prop, row[prop.column]); + obj[prop.prop] = cellValue; + } + // Add array values + for (var _d = 0, _e = objMeta.arraysList; _d < _e.length; _d++) { + var prop = _e[_d]; + var cellValue = _this.computeActualCellValue(prop, row[prop.column]); + if (isArray(obj[prop.prop])) { + obj[prop.prop].push(cellValue); + } + else { + obj[prop.prop] = [cellValue]; + } + } + // initialize empty to-many relations, they will be populated when + // those objects build themselves and find this containing object + for (var _f = 0, _g = objMeta.toManyPropList; _f < _g.length; _f++) { + var prop = _g[_f]; + obj[prop] = []; + } + // initialize null to-one relations and then recursively build them + for (var _h = 0, _j = objMeta.toOneList; _h < _j.length; _h++) { + var prop = _j[_h]; + obj[prop.prop] = null; + recursiveNest(row, Array.isArray(prop.column) ? prop.column : [prop.column]); + } + } + // link from the parent + if (objMeta.containingColumn === null) { + // parent is the top level + if (objMeta.isOneOfMany) { + // it is an array + if (_this.struct === null) { + _this.struct = []; + } + _this.struct.push(obj); + } + else { + // it is this object + _this.struct = obj; + } + } + else { + var containingIds = objMeta.containingColumn.map(function (column) { return row[column]; }); + var container = meta.idMap[createCompositeKey(objMeta.containingColumn)] + .cache[createCompositeKey(containingIds)]; + // If a container exists, it must not be a root, and thus there should + // be an ownProp set + if (container) { + if (objMeta.isOneOfMany) { + // it is an array + container[objMeta.ownProp].push(obj); + } + else { + // it is this object + container[objMeta.ownProp] = obj; + } + } + // record the containing id so we don't do this again (return in earlier + // part of this method) + var containingIdUsage = objMeta.containingIdUsage; + if (typeof (containingIdUsage)[createCompositeKey(vals)] === 'undefined') { + containingIdUsage[createCompositeKey(vals)] = {}; + } + containingIdUsage[createCompositeKey(vals)][createCompositeKey(containingIds)] = true; + } + }; + // struct is populated inside the build function + this.struct = null; + // tslint:disable-next-line: no-console + if (verbose) { + console.log(meta); + } + for (var _i = 0, table_1 = table; _i < table_1.length; _i++) { + var row = table_1[_i]; + for (var _a = 0, _b = meta.primeIdColumnList; _a < _b.length; _a++) { + var primeIdColumn = _b[_a]; + // for each prime id column (corresponding to a to-many relation or + // the top level) attempted to build an object + recursiveNest(row, primeIdColumn); + } + } + return this.struct; + }; + /* Returns a property mapping data structure based on the names of columns + * in columnList. Used internally by nest when its propertyMapping param + * is not specified. + * + */ + NestHydrationJS.prototype.structPropToColumnMapFromColumnHints = function (columnList, renameMapping) { + if (typeof renameMapping === 'undefined') { + renameMapping = {}; + } + var propertyMapping = { base: null }; + for (var _i = 0, columnList_1 = columnList; _i < columnList_1.length; _i++) { + var column = columnList_1[_i]; + var columnType = column.split('___'); + var type = null; + var idFlagSet = false; + var arrayFlagSet = false; + for (var j = 1; j < columnType.length; j++) { + if (columnType[j] === 'ID') { + idFlagSet = true; + } + else if (typeof this.typeHandlers[columnType[j]] !== 'undefined') { + type = columnType[j]; + } + if (columnType[j] === 'ARRAY') { + arrayFlagSet = true; + } + } + var pointer = propertyMapping; // point to base on each new column + var prop = 'base'; + var navList = columnType[0].split('_'); + for (var j = 0; j < navList.length; j++) { + var nav = navList[j]; + if (nav === '') { + if (pointer[prop] === null) { + pointer[prop] = [null]; + } + pointer = pointer[prop]; + prop = 0; + } + else { + if (pointer[prop] === null) { + pointer[prop] = {}; + } + if (typeof pointer[prop][nav] === 'undefined') { + var renamedColumn = typeof renameMapping[column] === 'undefined' + ? column + : renameMapping[column]; + if (type !== null || idFlagSet || arrayFlagSet) { + // no longer a simple mapping, has need of the type or id properties + renamedColumn = { column: renamedColumn }; + } + if (type !== null) { + // detail the type in the column map if type provided + renamedColumn.type = type; + } + if (idFlagSet) { + // set the id property in the column map + renamedColumn.id = true; + } + if (arrayFlagSet) { + renamedColumn.array = true; + } + pointer[prop][nav] = j === (navList.length - 1) + ? renamedColumn // is leaf node, store full column string + : null // iteration will replace with object or array + ; + } + pointer = pointer[prop]; + prop = nav; + } + } + } + return propertyMapping.base; + }; + /* Registers a custom type handler */ + NestHydrationJS.prototype.registerType = function (name, handler) { + if (this.typeHandlers[name]) { + throw new Error('Handler with type, ' + name + ', already exists'); + } + this.typeHandlers[name] = handler; + }; + NestHydrationJS.prototype.computeActualCellValue = function (props, initialValue) { + var cellValue = initialValue; + if (cellValue !== null) { + var valueTypeFunction = void 0; + if (isFunction(props.type)) { + valueTypeFunction = props.type; + } + else if (typeof props.type === 'string') { + valueTypeFunction = this.typeHandlers[props.type]; + } + if (valueTypeFunction) { + cellValue = valueTypeFunction(cellValue); + } + } + else if (typeof props.default !== 'undefined') { + cellValue = props.default; + } + return cellValue; + }; + /* Create a data structure that contains lookups and cache spaces for quick + * reference and action for the workings of the nest method. + */ + NestHydrationJS.prototype.buildMeta = function (structPropToColumnMap) { + var meta; + // internally defines recursive function with extra param. This allows cleaner API + var recursiveBuildMeta = function ( + // tslint:disable-next-line: no-shadowed-variable + structPropToColumnMap, isOneOfMany, containingColumn, ownProp) { + var idProps = []; + var idColumns = []; + var propList = keys(structPropToColumnMap); + if (propList.length === 0) { + throw new Error('invalid structPropToColumnMap format - property \'' + ownProp + '\' can not be an empty array'); + } + // Add all of the columns flagged as id to the array + for (var _i = 0, propList_1 = propList; _i < propList_1.length; _i++) { + var prop = propList_1[_i]; + if (structPropToColumnMap[prop].id === true) { + idProps.push(prop); + } + } + // If no columns are flagged as id, then use the first value in the prop list + if (idProps.length === 0) { + idProps.push(propList[0]); + } + idColumns = idProps.map(function (prop) { + return structPropToColumnMap[prop].column || structPropToColumnMap[prop]; + }); + if (isOneOfMany) { + meta.primeIdColumnList.push(idColumns); + } + var defaults = {}; + idProps.forEach(function (prop) { + defaults[prop] = typeof structPropToColumnMap[prop].default === 'undefined' ? + null : + structPropToColumnMap[prop].default; + }); + var objMeta = { + valueList: [], + toOneList: [], + arraysList: [], + toManyPropList: [], + containingColumn: containingColumn, + ownProp: ownProp, + isOneOfMany: isOneOfMany === true, + cache: {}, + containingIdUsage: containingColumn === null ? null : {}, + defaults: defaults, + }; + for (var _a = 0, propList_2 = propList; _a < propList_2.length; _a++) { + var prop = propList_2[_a]; + if (typeof structPropToColumnMap[prop] === 'string') { + // value property + objMeta.valueList.push({ + prop: prop, + column: structPropToColumnMap[prop], + type: undefined, + default: undefined + }); + } + else if (structPropToColumnMap[prop].column) { + // value property + var definitionColumn = structPropToColumnMap[prop]; + var metaValueProps = { + prop: prop, + column: definitionColumn.column, + type: definitionColumn.type, + default: definitionColumn.default, + }; + // Add this column to our array list if necessary + if (definitionColumn.array === true) { + objMeta.arraysList.push(metaValueProps); + } + else { + objMeta.valueList.push(metaValueProps); + } + } + else if (isArray(structPropToColumnMap[prop])) { + // list of objects / to-many relation + objMeta.toManyPropList.push(prop); + recursiveBuildMeta(structPropToColumnMap[prop][0], true, idColumns, prop); + } + else if (isPlainObject(structPropToColumnMap[prop])) { + // object / to-one relation + var subIdProps = []; + for (var _b = 0, _c = values(structPropToColumnMap[prop]); _b < _c.length; _b++) { + var value = _c[_b]; + if (typeof value === 'object' && value.id === true) { + subIdProps.push(value.column); + } + } + // If no columns are flagged as id, then use the first value in the prop list + if (subIdProps.length === 0) { + var column = values(structPropToColumnMap[prop])[0]; + subIdProps.push(typeof column === 'object' ? column.column : column); + } + objMeta.toOneList.push({ + prop: prop, + column: subIdProps, + }); + recursiveBuildMeta(structPropToColumnMap[prop], false, idColumns, prop); + } + else { + throw new Error('invalid structPropToColumnMap format - property \'' + prop + + '\' must be either a string, a plain object or an array'); + } + } + meta.idMap[createCompositeKey(idColumns)] = objMeta; + }; + // this data structure is populated by the _buildMeta function + meta = { + primeIdColumnList: [], + idMap: {}, + }; + if (isArray(structPropToColumnMap)) { + if (structPropToColumnMap.length !== 1) { + // tslint:disable-next-line: max-line-length + throw new Error("invalid structPropToColumnMap format - can not have multiple roots for structPropToColumnMap, if an array it must only have one item"); + } + // call with first object, but inform _buildMeta it is an array + recursiveBuildMeta(structPropToColumnMap[0], true, null, null); + } + else if (isPlainObject(structPropToColumnMap)) { + // register first column as prime id column + var columns = values(structPropToColumnMap); + if (columns.length === 0) { + throw new Error('invalid structPropToColumnMap format - the base object can not be an empty object'); + } + // First determine if there are any keys set on the columns + var idColumns = columns.reduce(function (accumulator, column) { + if (column.id === true) { + accumulator.push(column.column); + } + return accumulator; + }, []); + // If there were no keys set, then take the first column as the id + if (idColumns.length === 0) { + if (typeof columns[0] === 'string') { + idColumns.push(columns[0]); + } + else if (typeof columns[0].column === 'string') { + idColumns.push(columns[0].column); + } + } + meta.primeIdColumnList.push(idColumns); + // construct the rest + recursiveBuildMeta(structPropToColumnMap, false, null, null); + } + return meta; + }; + return NestHydrationJS; + }()); + NestHydrationJS_1.NestHydrationJS = NestHydrationJS; +})(NestHydrationJS || (NestHydrationJS = {})); +module.exports = function generate() { return new NestHydrationJS.NestHydrationJS(); }; +//# sourceMappingURL=NestHydrationJS.js.map \ No newline at end of file diff --git a/lib/NestHydrationJS.js.map b/lib/NestHydrationJS.js.map new file mode 100644 index 0000000..da1493a --- /dev/null +++ b/lib/NestHydrationJS.js.map @@ -0,0 +1 @@ +{"version":3,"file":"NestHydrationJS.js","sourceRoot":"","sources":["../src/NestHydrationJS.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1491ca2..305ff5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,9 +1,66 @@ { "name": "nesthydrationjs", - "version": "1.0.4", + "version": "1.0.6", "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/lodash": { + "version": "4.14.122", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.122.tgz", + "integrity": "sha512-9IdED8wU93ty8gP06ninox+42SBSJHp2IAamsSYMUY76mshRTeUsid/gtbl8ovnOwy8im41ib4cxTiIYMXGKew==", + "dev": true + }, + "@types/lodash.isarray": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/lodash.isarray/-/lodash.isarray-4.0.6.tgz", + "integrity": "sha512-lXLDKJ4jWhtAd6EVQOGHwX8iKJlITY5J+HAgim6oglepJrPXAv7hdoN7hrJSwpOYZ7oEpMng+bPasl4LZWp4GQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.isfunction": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/lodash.isfunction/-/lodash.isfunction-3.0.6.tgz", + "integrity": "sha512-olhgKmBgzHnA5pxsOI6YHunzTBMSyBw1XjxIKFio8W+XhYiELGTt05FStE0suV0GWtlIMdn7V8M/UbYbSVdGYw==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-8G41YFhmOl8Ck6NrwLK5hhnbz6ADfuDJP+zusDnX3PoYhfC60+H/rQE6zmdO4yFzPCPJPY4oGZK2spbXm6gYEA==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.keys": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@types/lodash.keys/-/lodash.keys-4.2.6.tgz", + "integrity": "sha512-FCtZsHapyH/Xu0New7jTLiNwbDfirIgpmii+vndMxM9Guz+kU+uYgKvSaVN4H+CReT6Ht+sfZTrquq0hnSjgxg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/lodash.values": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/lodash.values/-/lodash.values-4.3.6.tgz", + "integrity": "sha512-iCudwRNYIRO1ERA58WauveyaWFrO3/fisVQZts+acdLZmJTJABLxCG+NeEUqo+/o45LthyAw/gC5wMjssaGpbQ==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/node": { + "version": "11.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.10.4.tgz", + "integrity": "sha512-wa09itaLE8L705aXd8F80jnFpxz3Y1/KRHfKsYL2bPc0XF+wEWu8sR9n5bmeu8Ba1N9z2GRNzm/YdHcghLkLKg==", + "dev": true + }, "acorn": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", @@ -199,6 +256,12 @@ "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==", "dev": true }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -308,6 +371,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -421,6 +490,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -1137,6 +1212,7 @@ "version": "0.1.4", "bundled": true, "dev": true, + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -2319,7 +2395,8 @@ "longest": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "loose-envify": { "version": "1.3.1", @@ -3790,6 +3867,12 @@ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", "dev": true }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -3920,6 +4003,15 @@ "resolve-from": "^1.0.0" } }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, "resolve-from": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", @@ -4146,6 +4238,42 @@ "punycode": "^1.4.1" } }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "dev": true + }, + "tslint": { + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.13.1.tgz", + "integrity": "sha512-fplQqb2miLbcPhyHoMV4FU9PtNRbgmm/zI5d3SZwwmJQM6V0eodju+hplpyfhLWpmwrDNfNYU57uYRb8s0zZoQ==", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4177,6 +4305,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "3.3.3333", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz", + "integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 7fd0f8a..56973fa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nesthydrationjs", - "version": "1.0.6", + "version": "1.1.0", "description": "Provides nested objects from tabular data.", "keywords": [ "Hydration", @@ -11,12 +11,15 @@ "Nested" ], "license": "ISC", - "main": "NestHydrationJS.js", + "main": "lib/NestHydrationJS.js", + "types": "lib/NestHydrationJS.d.ts", "scripts": { "coverage": "nyc jasmine", "coveralls": "nyc report --reporter=text-lcov | coveralls", "test": "jasmine", - "lint": "eslint NestHydrateJS.js spec/**/*.js" + "lint-specs": "eslint spec/**/*.js", + "lint": "tslint -c ./tslint.yaml src/**/*.ts", + "build": "tsc" }, "repository": { "type": "git", @@ -30,10 +33,22 @@ "lodash.isplainobject": "^4.0.6" }, "devDependencies": { + "@types/lodash": "^4.14.122", + "@types/lodash.isarray": "^4.0.6", + "@types/lodash.isfunction": "^3.0.6", + "@types/lodash.isplainobject": "^4.0.6", + "@types/lodash.keys": "^4.2.6", + "@types/lodash.values": "^4.3.6", + "@types/node": "^11.10.4", "coveralls": "^3.0.1", "eslint": "^4.19.1", "jasmine": "^3.1.0", - "nyc": "^11.7.3" + "nyc": "^11.7.3", + "tslint": "^5.13.1", + "typescript": "^3.3.3333" }, + "files": [ + "lib/**/*" + ], "author": "Bluedrop Peformance Learning" } diff --git a/spec/.eslintrc b/spec/.eslintrc index 5a7a8d5..3abd949 100644 --- a/spec/.eslintrc +++ b/spec/.eslintrc @@ -1,3 +1,19 @@ --- env: + es6: true jasmine: true + node: true +extends: 'eslint:recommended' +rules: + indent: + - error + - tab + linebreak-style: + - error + - unix + quotes: + - error + - single + semi: + - error + - always diff --git a/spec/NestHydrationJS/buildMeta.spec.js b/spec/NestHydrationJS/buildMeta.spec.js index 69db8e0..3deabf0 100644 --- a/spec/NestHydrationJS/buildMeta.spec.js +++ b/spec/NestHydrationJS/buildMeta.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var NestHydrationJS = require('../../NestHydrationJS')(); +var NestHydrationJS = require('../../lib/NestHydrationJS')(); describe('NestHydrationJS', function () { describe('buildMeta method', function () { @@ -15,7 +15,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['aColumnName'], + primeIdColumnList: [['aColumnName']], idMap: { aColumnName: { valueList: [ @@ -23,12 +23,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: null + defaults: {a: null} } } }; @@ -47,7 +48,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['a'], + primeIdColumnList: [['a']], idMap: { a: { valueList: [ @@ -55,12 +56,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: null + defaults: {a: null} } } }; @@ -79,7 +81,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['a'], + primeIdColumnList: [['a']], idMap: { a: { valueList: [ @@ -87,12 +89,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: null + defaults: {a: null} } } }; @@ -111,7 +114,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['a'], + primeIdColumnList: [['a']], idMap: { a: { valueList: [ @@ -119,12 +122,46 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: 'a_default' + defaults: {a: 'a_default'} + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('simple mapping with array flag', function () { + var result; + beforeEach(function () { + var mapping = { + a: {column: 'a', array: true} + }; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['a']], + idMap: { + a: { + valueList: [], + toOneList: [], + toManyPropList: [], + arraysList: [ + {prop: 'a', column: 'a', type: undefined, default: undefined} + ], + containingColumn: null, + ownProp: null, + isOneOfMany: false, + cache: {}, + containingIdUsage: null, + defaults: {a: null} } } }; @@ -144,7 +181,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['a'], + primeIdColumnList: [['a']], idMap: { a: { valueList: [ @@ -153,12 +190,84 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], + containingColumn: null, + ownProp: null, + isOneOfMany: false, + cache: {}, + containingIdUsage: null, + defaults: {a: null} + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping with single array flag', function () { + var result; + beforeEach(function () { + var mapping = { + a: {column: 'a', array: true}, + b: 'b' + }; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['a']], + idMap: { + a: { + valueList: [ + {prop: 'b', column: 'b', type: undefined, default: undefined} + ], + toOneList: [], + toManyPropList: [], + arraysList: [ + {prop: 'a', column: 'a', type: undefined, default: undefined} + ], + containingColumn: null, + ownProp: null, + isOneOfMany: false, + cache: {}, + containingIdUsage: null, + defaults: {a: null} + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping with both array flags', function () { + var result; + beforeEach(function () { + var mapping = { + a: {column: 'a', array: true}, + b: {column: 'b', array: true} + }; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['a']], + idMap: { + a: { + valueList: [], + toOneList: [], + toManyPropList: [], + arraysList: [ + {prop: 'a', column: 'a', type: undefined, default: undefined}, + {prop: 'b', column: 'b', type: undefined, default: undefined} + ], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: null + defaults: {a: null} } } }; @@ -178,7 +287,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['a'], + primeIdColumnList: [['a']], idMap: { a: { valueList: [ @@ -187,12 +296,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: 'a_default' + defaults: {a: 'a_default'} } } }; @@ -215,7 +325,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['id'], + primeIdColumnList: [['id']], idMap: { c: { valueList: [ @@ -223,12 +333,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], - containingColumn: 'id', + arraysList: [], + containingColumn: ['id'], ownProp: 'a', isOneOfMany: false, cache: {}, containingIdUsage: {}, - default: 'c_default' + defaults: {c: 'c_default'} }, id: { valueList: [ @@ -237,12 +348,119 @@ describe('NestHydrationJS', function () { ], toOneList: [{prop: 'a', column: 'c'}], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: false, cache: {}, containingIdUsage: null, - default: null + defaults: { id: null } + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping complex with array flag', function () { + var result; + beforeEach(function () { + var mapping = { + id: 'id', + a: { + c: {column: 'c', array: true} + }, + b: 'b' + }; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['id']], + idMap: { + c: { + valueList: [], + toOneList: [], + toManyPropList: [], + arraysList: [ + {prop: 'c', column: 'c', type: undefined, default: undefined} + ], + containingColumn: ['id'], + ownProp: 'a', + isOneOfMany: false, + cache: {}, + containingIdUsage: {}, + defaults: {c: null} + }, + id: { + valueList: [ + {prop: 'id', column: 'id', type: undefined, default: undefined}, + {prop: 'b', column: 'b', type: undefined, default: undefined} + ], + toOneList: [{prop: 'a', column: 'c'}], + toManyPropList: [], + arraysList: [], + containingColumn: null, + ownProp: null, + isOneOfMany: false, + cache: {}, + containingIdUsage: null, + defaults: { id: null } + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping complex with composite id', function () { + var result; + beforeEach(function () { + var mapping = { + id: 'id', + a: { + c: {column: 'c', id: true}, + d: {column: 'd', id: true} + }, + b: 'b' + }; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['id']], + idMap: { + 'c, d': { + valueList: [ + { prop: 'c', column: 'c', type: undefined, default: undefined }, + { prop: 'd', column: 'd', type: undefined, default: undefined } + ], + toOneList: [], + toManyPropList: [], + arraysList: [], + containingColumn: ['id'], + ownProp: 'a', + isOneOfMany: false, + cache: {}, + containingIdUsage: {}, + defaults: {c: null, d: null} + }, + id: { + valueList: [ + {prop: 'id', column: 'id', type: undefined, default: undefined}, + {prop: 'b', column: 'b', type: undefined, default: undefined} + ], + toOneList: [{prop: 'a', column: 'c'}], + toManyPropList: [], + arraysList: [], + containingColumn: null, + ownProp: null, + isOneOfMany: false, + cache: {}, + containingIdUsage: null, + defaults: { id: null } } } }; @@ -262,7 +480,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['_a'], + primeIdColumnList: [['_a']], idMap: { _a: { valueList: [ @@ -271,12 +489,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: true, cache: {}, containingIdUsage: null, - default: null + defaults: { a: null } } } }; @@ -296,7 +515,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['_b'], + primeIdColumnList: [['_b']], idMap: { _b: { valueList: [ @@ -305,12 +524,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: true, cache: {}, containingIdUsage: null, - default: null + defaults: {b: null} } } }; @@ -330,7 +550,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['_a'], + primeIdColumnList: [['_a']], idMap: { _a: { valueList: [ @@ -339,12 +559,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: true, cache: {}, containingIdUsage: null, - default: null + defaults: {a: null} } } }; @@ -364,7 +585,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['_a'], + primeIdColumnList: [['_a']], idMap: { _a: { valueList: [ @@ -373,12 +594,83 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], + arraysList: [], + containingColumn: null, + ownProp: null, + isOneOfMany: true, + cache: {}, + containingIdUsage: null, + defaults: {a: 'a_default'} + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping array with composite id', function () { + var result; + beforeEach(function () { + var mapping = [{ + a: {column: '_a', id: true}, + b: {column: '_b', id: true} + }]; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['_a', '_b']], + idMap: { + '_a, _b': { + valueList: [ + {prop: 'a', column: '_a', type: undefined, default: undefined}, + {prop: 'b', column: '_b', type: undefined, default: undefined} + ], + toOneList: [], + toManyPropList: [], + arraysList: [], + containingColumn: null, + ownProp: null, + isOneOfMany: true, + cache: {}, + containingIdUsage: null, + defaults: {a: null, b: null} + } + } + }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping array with composite id and defaults', function () { + var result; + beforeEach(function () { + var mapping = [{ + a: {column: '_a', id: true, default: 'a_default'}, + b: {column: '_b', id: true, default: 'b_default'} + }]; + result = NestHydrationJS.buildMeta(mapping); + }); + + it('should match expected structure', function () { + var expected = { + primeIdColumnList: [['_a', '_b']], + idMap: { + '_a, _b': { + valueList: [ + {prop: 'a', column: '_a', type: undefined, default: 'a_default'}, + {prop: 'b', column: '_b', type: undefined, default: 'b_default'} + ], + toOneList: [], + toManyPropList: [], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: true, cache: {}, containingIdUsage: null, - default: 'a_default' + defaults: {a: 'a_default', b: 'b_default'} } } }; @@ -405,7 +697,7 @@ describe('NestHydrationJS', function () { it('should match expected structure', function () { var expected = { - primeIdColumnList: ['_a', '_e__f'], + primeIdColumnList: [['_a'], ['_e__f']], idMap: { '_a': { valueList: [ @@ -418,12 +710,13 @@ describe('NestHydrationJS', function () { toManyPropList: [ 'e' ], + arraysList: [], containingColumn: null, ownProp: null, isOneOfMany: true, cache: {}, containingIdUsage: null, - default: null + defaults: { a: null } }, '_c_d': { valueList: [ @@ -431,12 +724,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], - containingColumn: '_a', + arraysList: [], + containingColumn: ['_a'], ownProp: 'c', isOneOfMany: false, cache: {}, containingIdUsage: {}, - default: null + defaults: { d: null } }, '_e__f': { valueList: [ @@ -445,12 +739,13 @@ describe('NestHydrationJS', function () { ], toOneList: [], toManyPropList: [], - containingColumn: '_a', + arraysList: [], + containingColumn: ['_a'], ownProp: 'e', isOneOfMany: true, cache: {}, containingIdUsage: {}, - default: null + defaults: { f: null } } } }; diff --git a/spec/NestHydrationJS/nest.spec.js b/spec/NestHydrationJS/nest.spec.js index e26e2d2..5f49185 100644 --- a/spec/NestHydrationJS/nest.spec.js +++ b/spec/NestHydrationJS/nest.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var NestHydrationJS = require('../../NestHydrationJS')(); +var NestHydrationJS = require('../../lib/NestHydrationJS')(); describe('NestHydrationJS', function () { describe('nest method', function () { @@ -256,6 +256,22 @@ describe('NestHydrationJS', function () { }); }); + describe('simple mapping, array', function() { + var result; + beforeEach(function () { + var mapping = { + arr: { column: 'arr', array: true } + }; + var data = {arr: 'arr1'}; + result = NestHydrationJS.nest(data, mapping); + }); + + it('should match expected structure', function () { + var expected = {arr: ['arr1'] }; + expect(result).toEqual(expected); + }); + }); + describe('multiple mapping', function () { var result; beforeEach(function () { @@ -306,6 +322,76 @@ describe('NestHydrationJS', function () { }); }); + describe('multiple mapping, multiple arrays', function() { + var result; + beforeEach(function () { + var mapping = { + a: 'a', + arr: { column: 'arr', array: true }, + arr2: { column: 'arr2', array :true } + }; + var data = {a: 'value 1', arr: 'arr1', arr2: 'arr2'}; + result = NestHydrationJS.nest(data, mapping); + }); + + it('should match expected structure', function () { + var expected = {a: 'value 1', arr: ['arr1'], arr2: ['arr2'] }; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping array, with array', function() { + var result; + beforeEach(function () { + var mapping = [{ + a: 'a', + arr: { column: 'arr', array: true }, + }]; + var data = [ + {a: 'a1', arr: 'arr1'}, + {a: 'a1', arr: 'arr1-2'}, + {a: 'a2', arr: 'arr2'}, + {a: 'a2', arr: 'arr2-2'}, + {a: 'a3', arr: 'arr3'}, + {a: 'a3', arr: 'arr3-3'} + ]; + result = NestHydrationJS.nest(data, mapping); + }); + + it('should match expected structure', function () { + var expected = [ { a: 'a1', arr: [ 'arr1', 'arr1-2' ] }, + { a: 'a2', arr: [ 'arr2', 'arr2-2' ] }, + { a: 'a3', arr: [ 'arr3', 'arr3-3' ] } ]; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping array with composite id, with array', function() { + var result; + beforeEach(function () { + var mapping = [{ + a: { column: 'a', id: true }, + b: { column: 'b', id: true }, + arr: { column: 'arr', array: true }, + }]; + var data = [ + {a: 'a1', b: 'b1', arr: 'arr1'}, + {a: 'a1', b: 'b1', arr: 'arr1-2'}, + {a: 'a2', b: 'b1', arr: 'arr2'}, + {a: 'a2', b: 'b1', arr: 'arr2-2'} + ]; + result = NestHydrationJS.nest(data, mapping); + }); + + it('should match expected structure', function () { + var expected = [ + { a: 'a1', b: 'b1', arr: [ 'arr1', 'arr1-2' ] }, + { a: 'a2', b: 'b1', arr: [ 'arr2', 'arr2-2' ] } + ]; + expect(result).toEqual(expected); + }); + }); + describe('multiple mapping array', function () { var result; beforeEach(function () { @@ -381,6 +467,61 @@ describe('NestHydrationJS', function () { }); }); + describe('multiple mapping array with composite id', function () { + var result; + beforeEach(function () { + var mapping = [{ + a: {column: 'a', id: true}, + b: {column: 'b', id: true} + }]; + var data = [ + {a: 'a1', b: 'b1'}, + {a: 'a1', b: 'b2'}, + {a: 'a2', b: 'b1'}, + {a: 'a2', b: 'b2'} + ]; + result = NestHydrationJS.nest(data, mapping); + }); + + it('should match expected structure', function () { + var expected = [ + {a: 'a1', b: 'b1'}, + {a: 'a1', b: 'b2'}, + {a: 'a2', b: 'b1'}, + {a: 'a2', b: 'b2'} + ]; + expect(result).toEqual(expected); + }); + }); + + describe('multiple mapping array with composite id and to-many relation', function () { + var result; + beforeEach(function () { + var mapping = [{ + a: {column: 'a', id: true}, + b: {column: 'b', id: true}, + c: [{ + id: 'c_id' + }] + }]; + var data = [ + {a: 'a1', b: 'b1', c_id: 'c1'}, + {a: 'a1', b: 'b1', c_id: 'c2'}, + {a: 'a2', b: 'b1', c_id: 'c1'}, + {a: 'a2', b: 'b1', c_id: 'c2'} + ]; + result = NestHydrationJS.nest(data, mapping); + }); + + it('should match expected structure', function () { + var expected = [ + {a: 'a1', b: 'b1', c: [ { id: 'c1'} , { id: 'c2' } ]}, + {a: 'a2', b: 'b1', c: [ { id: 'c1'} , { id: 'c2' } ]}, + ]; + expect(result).toEqual(expected); + }); + }); + describe('multiple mapping array, hinted mapping', function () { var result; beforeEach(function () { diff --git a/spec/NestHydrationJS/registerType.spec.js b/spec/NestHydrationJS/registerType.spec.js index a312a94..dedd823 100644 --- a/spec/NestHydrationJS/registerType.spec.js +++ b/spec/NestHydrationJS/registerType.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var NestHydrationJS = require('../../NestHydrationJS')(); +var NestHydrationJS = require('../../lib/NestHydrationJS')(); describe('NestHydrationJS', function () { describe('registerType function', function () { diff --git a/spec/NestHydrationJS/structPropToColumnMapFromColumnHints.spec.js b/spec/NestHydrationJS/structPropToColumnMapFromColumnHints.spec.js index 4f98b5f..0f1cee2 100644 --- a/spec/NestHydrationJS/structPropToColumnMapFromColumnHints.spec.js +++ b/spec/NestHydrationJS/structPropToColumnMapFromColumnHints.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var NestHydrationJS = require('../../NestHydrationJS')(); +var NestHydrationJS = require('../../lib/NestHydrationJS')(); describe('NestHydrationJS', function () { describe('structPropToColumnMapFromColumnHints method', function () { @@ -49,6 +49,23 @@ describe('NestHydrationJS', function () { expect(result).toEqual(expected); }); }); + + describe('passed single direct property as columnList, array flag', function () { + var result; + beforeEach(function () { + var columnList = [ + 'a___ARRAY' + ]; + result = NestHydrationJS.structPropToColumnMapFromColumnHints(columnList); + }); + + it('should match expected structure', function () { + var expected = { + a: {column: 'a___ARRAY', array: true} + }; + expect(result).toEqual(expected); + }); + }); describe('passed single direct property as columnList, boolean type', function () { var result; @@ -83,8 +100,8 @@ describe('NestHydrationJS', function () { expect(result).toEqual(expected); }); }); - - describe('passed single direct property as columnList, multiple id columns', function () { + + describe('passed two properties both as id columns', function () { var result; beforeEach(function () { var columnList = [ @@ -95,12 +112,15 @@ describe('NestHydrationJS', function () { }); it('should match expected structure', function () { - var expected = 'invalid - multiple id - a___ID and b___ID conflict'; + var expected = { + a: {column: 'a___ID', id: true}, + b: {column: 'b___ID', id: true} + }; expect(result).toEqual(expected); }); }); - describe('passed single direct property as columnList, id column and typed', function () { + describe('passed single direct property as columnList, id column and type', function () { var result; beforeEach(function () { var columnList = [ @@ -116,6 +136,23 @@ describe('NestHydrationJS', function () { expect(result).toEqual(expected); }); }); + + describe('passed single direct property as columnList, id column, type and array', function () { + var result; + beforeEach(function () { + var columnList = [ + 'a___ID___NUMBER___ARRAY' + ]; + result = NestHydrationJS.structPropToColumnMapFromColumnHints(columnList); + }); + + it('should match expected structure', function () { + var expected = { + a: {column: 'a___ID___NUMBER___ARRAY', type: 'NUMBER', id: true, array: true} + }; + expect(result).toEqual(expected); + }); + }); describe('passed single direct property as columnList, id column and typed, reverse order', function () { var result; diff --git a/spec/NestHydrationJS/typeHandlers.spec.js b/spec/NestHydrationJS/typeHandlers.spec.js index c94533a..7dcb25b 100644 --- a/spec/NestHydrationJS/typeHandlers.spec.js +++ b/spec/NestHydrationJS/typeHandlers.spec.js @@ -1,6 +1,6 @@ 'use strict'; -var NestHydrationJS = require('../../NestHydrationJS')(); +var NestHydrationJS = require('../../lib/NestHydrationJS')(); describe('NestHydrationJS', function () { describe('typeHandlers', function () { diff --git a/src/NestHydrationJS.ts b/src/NestHydrationJS.ts new file mode 100644 index 0000000..c1963f5 --- /dev/null +++ b/src/NestHydrationJS.ts @@ -0,0 +1,575 @@ +'use strict'; + +import isArray = require('lodash.isarray'); +import isFunction = require('lodash.isfunction'); +import isPlainObject = require('lodash.isplainobject'); +import keys = require('lodash.keys'); +import values = require('lodash.values'); + +// tslint:disable-next-line: no-namespace +namespace NestHydrationJS { + + interface ITypeHandlers { + [index: string]: ITypeHandler; + } + + type ITypeHandler = (cellValue: any) => any; + + interface IMetaValueProps { + prop: string; + column: string | string[]; + type?: string | ITypeHandler; + default?: any; + } + + interface IDictionary { + [index: string]: TValue; + } + + interface IMetaColumnData { + valueList: IMetaValueProps[]; + toOneList: IMetaValueProps[]; + arraysList: IMetaValueProps[]; + toManyPropList: string[]; + containingColumn: string[] | null; + ownProp: string | null; + isOneOfMany: boolean; + cache: IDictionary; + containingIdUsage: IDictionary> | null; + defaults: IDictionary; + } + + interface IMetaData { + primeIdColumnList: string[][]; + idMap: { [index: string]: IMetaColumnData }; + } + + interface IDefinitionColumn { + column: string; + id?: boolean; + default?: any; + type?: string; + array?: boolean; + } + + interface IDefinition { + [index: string]: IDefinitionColumn | string | IDefinition | IDefinition[]; + } + + interface IData { + [index: string]: any; + [index: number]: any; + } + + function createCompositeKey(vals: Array, separator = ', '): string { + return vals.join(separator); + } + + // tslint:disable-next-line: no-shadowed-variable + export class NestHydrationJS { + + private typeHandlers = { + NUMBER(cellValue: any) { + return parseFloat(cellValue); + }, + BOOLEAN(cellValue: any) { + // tslint:disable-next-line: triple-equals + return cellValue == true; + }, + } as ITypeHandlers; + + private struct: object | any[] | null = null; + + /* Creates a data structure containing nested objects and/or arrays from + * tabular data based on a structure definition provided by + * structPropToColumnMap. If structPropToColumnMap is not provided but + * the data has column names that follow a particular convention then a + * nested structures can also be created. + */ + public nest(data: any, structPropToColumnMap: IDefinition | IDefinition[] | null | boolean, verbose = false): any { + + let table; + + // VALIDATE PARAMS AND BASIC INITIALIZATION + + // Determines that on no results, and empty list is used instead of null. // NOTE: fact check this + let listOnEmpty = false; + + if (typeof structPropToColumnMap === 'undefined') { + structPropToColumnMap = null; + } + + if (data === null) { + return null; + } + + if (!isArray(structPropToColumnMap) && !isPlainObject(structPropToColumnMap) && + structPropToColumnMap !== null && structPropToColumnMap !== true) { + throw new Error('nest expects param structPropToColumnMap to be an array, plain object, null, or true'); + } + + if (isPlainObject(data)) { + // internal table should be a table format but a plain object + // could be passed as the first (and only) row of that table + table = [data]; + } else if (isArray(data)) { + table = data; + } else { + // tslint:disable-next-line: max-line-length + throw Error(`nest expects param data to be in the form of a plain object or an array of plain objects (forming a table)`); + } + + // structPropToColumnMap can be set to true as a tie break between + // returning null (empty structure) or an empty list + if (structPropToColumnMap === true) { + listOnEmpty = true; + structPropToColumnMap = null; + } + + if (structPropToColumnMap === null && table.length > 0) { + // property mapping not specified, determine it from column names + structPropToColumnMap = this.structPropToColumnMapFromColumnHints(keys(table[0])); + } + + if (structPropToColumnMap === null) { + // properties is empty, can't form structure or determine content + // for a list. Assume a structure unless listOnEmpty + return listOnEmpty ? [] : null; + } else if (table.length === 0) { + // table is empty, return the appropriate empty result based on input definition + return isArray(structPropToColumnMap) ? [] : null; + } + + // COMPLETE VALIDATING PARAMS AND BASIC INITIALIZATION + + const meta = this.buildMeta(structPropToColumnMap as IDefinition | IDefinition[]); + + // BUILD FROM TABLE + + // defines function that can be called recursively + const recursiveNest = (row: IDictionary, idColumns: string[]) => { + + // Obj is the actual object that will end up in the final structure + let obj: IData; + + // Get all of the values for each id + let vals: any[] = idColumns.map((column) => row[column]); + + // only really concerned with the meta data for this identity column + const objMeta = meta.idMap[createCompositeKey(idColumns)]; + + // If any of the values are null, we'll check and see if we need to set defaults + vals = vals.map((value, idx) => { + if (value === null) { + if (objMeta.defaults[idColumns[idx]] !== null && typeof objMeta.defaults[idColumns[idx]] !== 'undefined') { + return objMeta.defaults[idColumns[idx]]; + } + } + return value; + }); + + if (vals.indexOf(null) !== -1) { return; } + + // check if object already exists in cache + if (typeof objMeta.cache[createCompositeKey(vals)] !== 'undefined') { + + // not already placed as to-many relation in container + obj = objMeta.cache[createCompositeKey(vals)]; + + // Add array values if necessary + for (const prop of objMeta.arraysList) { + const cellValue = this.computeActualCellValue(prop, row[prop.column as string]); + if (isArray(obj[prop.prop])) { + obj[prop.prop].push(cellValue); + } else { + obj[prop.prop] = [cellValue]; + } + } + + if (objMeta.containingIdUsage === null) { return; } + + // We know for certain that containing column is set if + // containingIdUsage is not null and can cast it as a string + + // check and see if this has already been linked to the parent, + // and if so we don't need to continue + const containingIds = (objMeta.containingColumn as string[]).map((column) => row[column]); + if (typeof objMeta.containingIdUsage[createCompositeKey(vals)] !== 'undefined' + && typeof objMeta.containingIdUsage[createCompositeKey(vals)][createCompositeKey(containingIds)] !== 'undefined' + ) { return; } + + } else { + // don't have an object defined for this yet, create it and set the cache + obj = {}; + objMeta.cache[createCompositeKey(vals)] = obj; + + // copy in properties from table data + for (const prop of objMeta.valueList) { + const cellValue = this.computeActualCellValue(prop, row[prop.column as string]); + obj[prop.prop] = cellValue; + } + + // Add array values + for (const prop of objMeta.arraysList) { + const cellValue = this.computeActualCellValue(prop, row[prop.column as string]); + if (isArray(obj[prop.prop])) { + obj[prop.prop].push(cellValue); + } else { + obj[prop.prop] = [cellValue]; + } + } + + // initialize empty to-many relations, they will be populated when + // those objects build themselves and find this containing object + for (const prop of objMeta.toManyPropList) { + obj[prop] = []; + } + + // initialize null to-one relations and then recursively build them + for (const prop of objMeta.toOneList) { + obj[prop.prop] = null; + recursiveNest(row, Array.isArray(prop.column) ? prop.column : [prop.column]); + } + } + + // link from the parent + if (objMeta.containingColumn === null) { + // parent is the top level + if (objMeta.isOneOfMany) { + // it is an array + if (this.struct === null) { + this.struct = []; + } + (this.struct as any[]).push(obj); + } else { + // it is this object + this.struct = obj; + } + } else { + const containingIds = objMeta.containingColumn.map((column) => row[column]); + const container = meta.idMap[createCompositeKey(objMeta.containingColumn)] + .cache[createCompositeKey(containingIds)]; + + // If a container exists, it must not be a root, and thus there should + // be an ownProp set + if (container) { + if (objMeta.isOneOfMany) { + // it is an array + container[objMeta.ownProp as string].push(obj); + } else { + // it is this object + container[objMeta.ownProp as string] = obj; + } + } + + // record the containing id so we don't do this again (return in earlier + // part of this method) + const containingIdUsage = objMeta.containingIdUsage as IDictionary>; + if (typeof (containingIdUsage)[createCompositeKey(vals)] === 'undefined') { + containingIdUsage[createCompositeKey(vals)] = {}; + } + containingIdUsage[createCompositeKey(vals)][createCompositeKey(containingIds)] = true; + } + }; + + // struct is populated inside the build function + this.struct = null; + + // tslint:disable-next-line: no-console + if (verbose) { console.log(meta); } + + for (const row of table) { + for (const primeIdColumn of meta.primeIdColumnList) { + // for each prime id column (corresponding to a to-many relation or + // the top level) attempted to build an object + recursiveNest(row, primeIdColumn); + } + } + + return this.struct; + } + /* Returns a property mapping data structure based on the names of columns + * in columnList. Used internally by nest when its propertyMapping param + * is not specified. + * + */ + public structPropToColumnMapFromColumnHints(columnList: string[], renameMapping?: IDictionary) { + + if (typeof renameMapping === 'undefined') { + renameMapping = {}; + } + + const propertyMapping: any = {base: null}; + + for (const column of columnList) { + + const columnType = column.split('___'); + + let type = null; + let idFlagSet = false; + let arrayFlagSet = false; + for (let j = 1; j < columnType.length; j++) { + if (columnType[j] === 'ID') { + idFlagSet = true; + } else if (typeof this.typeHandlers[columnType[j]] !== 'undefined') { + type = columnType[j]; + } + if (columnType[j] === 'ARRAY') { + arrayFlagSet = true; + } + } + + let pointer = propertyMapping; // point to base on each new column + let prop: string | number = 'base'; + + const navList = columnType[0].split('_'); + + for (let j = 0; j < navList.length; j++) { + const nav = navList[j]; + + if (nav === '') { + if (pointer[prop] === null) { + pointer[prop] = [null]; + } + pointer = pointer[prop]; + prop = 0; + } else { + if (pointer[prop] === null) { + pointer[prop] = {}; + } + if (typeof pointer[prop][nav] === 'undefined') { + let renamedColumn: any = typeof renameMapping[column] === 'undefined' + ? column + : renameMapping[column] + ; + if (type !== null || idFlagSet || arrayFlagSet) { + // no longer a simple mapping, has need of the type or id properties + renamedColumn = {column: renamedColumn}; + } + if (type !== null) { + // detail the type in the column map if type provided + renamedColumn.type = type; + } + if (idFlagSet) { + // set the id property in the column map + renamedColumn.id = true; + } + if (arrayFlagSet) { + renamedColumn.array = true; + } + pointer[prop][nav] = j === (navList.length - 1) + ? renamedColumn // is leaf node, store full column string + : null // iteration will replace with object or array + ; + } + pointer = pointer[prop]; + prop = nav; + } + } + } + + return propertyMapping.base; + } + /* Registers a custom type handler */ + public registerType(name: string, handler: ITypeHandler) { + if (this.typeHandlers[name]) { + throw new Error('Handler with type, ' + name + ', already exists'); + } + + this.typeHandlers[name] = handler; + } + private computeActualCellValue(props: IMetaValueProps, initialValue: any) { + let cellValue = initialValue; + if (cellValue !== null) { + let valueTypeFunction: ITypeHandler | undefined; + + if (isFunction(props.type)) { + valueTypeFunction = props.type as ITypeHandler; + } else if (typeof props.type === 'string') { + valueTypeFunction = this.typeHandlers[props.type]; + } + + if (valueTypeFunction) { + cellValue = valueTypeFunction(cellValue); + } + } else if (typeof props.default !== 'undefined') { + cellValue = props.default; + } + return cellValue; + } + + /* Create a data structure that contains lookups and cache spaces for quick + * reference and action for the workings of the nest method. + */ + private buildMeta(structPropToColumnMap: IDefinition | IDefinition[]): IMetaData { + + let meta: IMetaData; + + // internally defines recursive function with extra param. This allows cleaner API + const recursiveBuildMeta = ( + // tslint:disable-next-line: no-shadowed-variable + structPropToColumnMap: IDefinition, + isOneOfMany: boolean, + containingColumn: string[] | null, + ownProp: string | null) => { + + const idProps = []; + let idColumns = []; + + const propList = keys(structPropToColumnMap); + if (propList.length === 0) { + throw new Error('invalid structPropToColumnMap format - property \'' + ownProp + '\' can not be an empty array'); + } + + // Add all of the columns flagged as id to the array + for (const prop of propList) { + if ((structPropToColumnMap[prop] as IDefinitionColumn).id === true) { + idProps.push(prop); + } + } + + // If no columns are flagged as id, then use the first value in the prop list + if (idProps.length === 0) { + idProps.push(propList[0]); + } + + idColumns = idProps.map((prop) => { + return (structPropToColumnMap[prop] as IDefinitionColumn).column || structPropToColumnMap[prop]; + }) as string[]; + + if (isOneOfMany) { + meta.primeIdColumnList.push(idColumns); + } + + const defaults: IDictionary = {}; + + idProps.forEach((prop) => { + defaults[prop] = typeof (structPropToColumnMap[prop] as IDefinitionColumn).default === 'undefined' ? + null : + (structPropToColumnMap[prop] as IDefinitionColumn).default; + }); + + const objMeta: IMetaColumnData = { + valueList: [], + toOneList: [], + arraysList: [], + toManyPropList: [], + containingColumn, + ownProp, + isOneOfMany: isOneOfMany === true, + cache: {}, + containingIdUsage: containingColumn === null ? null : {}, + defaults, + }; + + for (const prop of propList) { + if (typeof structPropToColumnMap[prop] === 'string') { + // value property + objMeta.valueList.push({ + prop, + column: structPropToColumnMap[prop] as string, + type: undefined, + default: undefined }); + } else if ((structPropToColumnMap[prop] as IDefinitionColumn).column) { + // value property + const definitionColumn = structPropToColumnMap[prop] as IDefinitionColumn; + const metaValueProps = { + prop, + column: definitionColumn.column, + type: definitionColumn.type, + default: definitionColumn.default, + }; + + // Add this column to our array list if necessary + if (definitionColumn.array === true) { + objMeta.arraysList.push(metaValueProps); + } else { + objMeta.valueList.push(metaValueProps); + } + } else if (isArray(structPropToColumnMap[prop])) { + // list of objects / to-many relation + objMeta.toManyPropList.push(prop); + + recursiveBuildMeta((structPropToColumnMap[prop] as IDefinition[])[0], true, idColumns, prop); + } else if (isPlainObject(structPropToColumnMap[prop])) { + // object / to-one relation + + const subIdProps = []; + + for (const value of values(structPropToColumnMap[prop])) { + if (typeof value === 'object' && value.id === true) { + subIdProps.push(value.column) + } + } + + // If no columns are flagged as id, then use the first value in the prop list + if (subIdProps.length === 0) { + const column = values(structPropToColumnMap[prop])[0]; + subIdProps.push(typeof column === 'object' ? column.column : column); + } + + objMeta.toOneList.push({ + prop, + column: subIdProps, + }); + recursiveBuildMeta(structPropToColumnMap[prop] as IDefinition, false, idColumns, prop); + } else { + throw new Error('invalid structPropToColumnMap format - property \'' + prop + + '\' must be either a string, a plain object or an array'); + } + } + + meta.idMap[createCompositeKey(idColumns)] = objMeta; + }; + + // this data structure is populated by the _buildMeta function + meta = { + primeIdColumnList: [], + idMap: {}, + } as IMetaData; + + if (isArray(structPropToColumnMap)) { + if (structPropToColumnMap.length !== 1) { + // tslint:disable-next-line: max-line-length + throw new Error(`invalid structPropToColumnMap format - can not have multiple roots for structPropToColumnMap, if an array it must only have one item`); + } + // call with first object, but inform _buildMeta it is an array + recursiveBuildMeta((structPropToColumnMap as IDefinition[])[0], true, null, null); + } else if (isPlainObject(structPropToColumnMap)) { + + // register first column as prime id column + const columns = values(structPropToColumnMap) as any[]; + + if (columns.length === 0) { + throw new Error('invalid structPropToColumnMap format - the base object can not be an empty object'); + } + + // First determine if there are any keys set on the columns + const idColumns = columns.reduce((accumulator: string[], column: any) => { + if (column.id === true) { + accumulator.push(column.column); + } + return accumulator; + }, []); + + // If there were no keys set, then take the first column as the id + if (idColumns.length === 0) { + if (typeof columns[0] === 'string') { + idColumns.push(columns[0]); + } else if (typeof columns[0].column === 'string') { + idColumns.push(columns[0].column); + } + } + + meta.primeIdColumnList.push(idColumns); + + // construct the rest + recursiveBuildMeta(structPropToColumnMap as IDefinition, false, null, null); + } + return meta; + } } + +} + +// We have to wrap this in a function for backwards compatablity +export = function generate(): NestHydrationJS.NestHydrationJS { return new NestHydrationJS.NestHydrationJS(); }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..037ef80 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "declaration": true, + "outDir": "./lib", + "strict": true, + "sourceMap": true + }, + "include": ["src/**/*", "test.ts"], + "exclude": ["node_modules/", "spec"] +}