diff --git a/lib/toApiFormat.js b/lib/toApiFormat.js index c9799d0..4f9ba39 100644 --- a/lib/toApiFormat.js +++ b/lib/toApiFormat.js @@ -1,42 +1,59 @@ 'use strict'; -var _ = require('lodash'); +var _ = require('lodash') + , pointer = require('json-pointer'); + +var excludeList = [ + '/substitution_data', + '/tags', + '/metadata', + '/attributes', + '/headers', + '/content/email_rfc822' +]; + +function snakedKeyClone(source) { + + if (!_.isObject(source)) { + return source; + } + + var target = {}; + + if (Array.isArray(source)) { + target = []; + for (var i = 0; i < source.length; i++) { + target.push(snakedKeyClone(source[i])); + } + return target; + } + + Object.keys(source).forEach(function(key) { + target[_.snakeCase(key)] = snakedKeyClone(source[key]); + }); + + return target; +} module.exports = function toApiFormat(source) { - var dest = {}; - // List of property names which we do not want to modify the sub-property names - var excludeList = ['substitution_data', 'tags', 'metadata', 'attributes', 'headers']; - - try{ - // Handle arrays (Only need to handle arrays of objects for our use case.) - if(Array.isArray(source) && _.isPlainObject(source[0])) { - dest = []; - for(var i = 0; i < source.length; i++) { - dest.push(toApiFormat(source[i])); - } - return dest; + var excludedObjects = {}; + + // Look in the source object for the excluded pointers and take a copy of the + // objects pointed to by those keys. Then remove them from the source object. + excludeList.forEach(function(exclusionPointer) { + if (pointer.has(source, exclusionPointer)) { + pointer.set(excludedObjects, exclusionPointer, pointer.get(source, exclusionPointer)); + pointer.remove(source, exclusionPointer); } + }); - // Handle objects - Object.keys(source).forEach(function(key) { - - // Cache snake_cased keys - var snakedKey = _.snakeCase(key); - - // Exclude appropriately - if( -1 === excludeList.indexOf(key)) { - dest[snakedKey] = source[key]; - if( _.isObject (source[key] ) ) { - dest[snakedKey] = toApiFormat(source[key]); - } - } else { - dest[snakedKey] = source[key]; - } - }); - } catch(e) { - // Errors - return e; - } - // No errors return results - return dest; + // Make a clone of the remaining source object but with snaked case keys + var target = snakedKeyClone(source); + + // Reinstated the un-modified objects into the target + pointer.walk(excludedObjects, function(val, ptr) { + pointer.set(target, ptr, val); + }); + + return target; }; diff --git a/package.json b/package.json index 9e8a30e..5461d3a 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "xunit-file": "0.0.5" }, "dependencies": { + "json-pointer": "^0.5.0", "lodash": "^3.9.3", "request": "2.42.0" } diff --git a/test/spec/toApiFormat.spec.js b/test/spec/toApiFormat.spec.js index a0a3ca6..0cd1b4f 100644 --- a/test/spec/toApiFormat.spec.js +++ b/test/spec/toApiFormat.spec.js @@ -24,6 +24,9 @@ describe('toApiFormat', function() { {name: 'John Doe', address: 'j.doe@sparkpost.com'} , {name: 'Sam Jones', address: 's.jones@sparkpost.com'} ] + , content: { + email_rfc822: 'a message' + } , fizzArr: [ { buzzInga: "buzz" @@ -53,6 +56,9 @@ describe('toApiFormat', function() { {name: 'John Doe', address: 'j.doe@sparkpost.com'} , {name: 'Sam Jones', address: 's.jones@sparkpost.com'} ] + , content: { + email_rfc822: 'a message' + } , fizz_arr: [ { buzz_inga: "buzz" diff --git a/test/spec/transmissions.spec.js b/test/spec/transmissions.spec.js index 712d8c7..bbe50e3 100644 --- a/test/spec/transmissions.spec.js +++ b/test/spec/transmissions.spec.js @@ -108,5 +108,20 @@ describe('Transmissions Library', function() { done(); }); }); + + it('should leave email_rfc822 content keys intact', function(done) { + var options = { + transmissionBody: { + content: { + email_rfc822: 'Content-Type: text/plain\nFrom: From Envelope \nSubject: Example Email\n\nHello World' + } + } + }; + + transmission.send(options, function(err, data) { + expect(client.post.firstCall.args[0].json.content).to.have.property('email_rfc822'); + done(); + }); + }); }); });