'use strict'; const dotty = require("dotty"); const deepAssign = require("deep-assign"); module.exports = function createPropertyMapper(propertyMap) { let mappings = Object.keys(propertyMap).map((sourceProperty) => { let destinationPath = propertyMap[sourceProperty]; if (typeof destinationPath === "function") { return { sourceProperty: sourceProperty, transformer: destinationPath } } else { let destinationObject, destinationProperty; if (destinationPath.includes(".")) { let destinationPathParts = destinationPath.split("."); destinationObject = destinationPathParts[0]; destinationProperty = destinationPathParts.slice(1).join("."); } else { destinationObject = destinationPath; destinationProperty = sourceProperty; } return { sourceProperty: sourceProperty, destinationObject: destinationObject, destinationProperty: destinationProperty } } }).reduce((mappings, item) => { mappings[item.sourceProperty] = item; return mappings; }, {}); return function mapProperties(properties) { return Object.keys(properties).map((propertyName) => { let propertyValue = properties[propertyName]; if (propertyName.includes(".")) { /* Property names with a dot are attempts to directly access an * object within a composite object; they need to be handled * specially since they essentially bypass the mapping step. * If there is *more* than a single dot, then this is an attempt * to access a *nested* composite object, and therefore we should * keep the entire property name after the first dot intact. */ let propertyNameSegments = propertyName.split("."); let destinationObject = propertyNameSegments[0]; let realPropertyName = propertyNameSegments.slice(1).join("."); return { sourceProperty: propertyName, destinationObject: destinationObject, destinationProperty: realPropertyName, value: propertyValue } } else if (mappings[propertyName] != null) { return Object.assign({ value: propertyValue }, mappings[propertyName]); } else { return { sourceProperty: propertyName, destinationObject: "_", destinationProperty: propertyName, value: propertyValue } } }).filter((item) => { return (item != null); }).reduce((objects, item) => { function ensureObject(object) { if (objects[object] == null) { objects[object] = {}; } } if (item.destinationProperty != null) { ensureObject(item.destinationObject); dotty.put(objects[item.destinationObject], item.destinationProperty, item.value); } else { let results = item.transformer(item.value); Object.keys(results).forEach((object) => { ensureObject(object); deepAssign(objects[object], results[object]); }); } return objects; }, {}) } };