From 183461c3e40c3dfabf4d94aebc1d6dffd7bfd0f2 Mon Sep 17 00:00:00 2001 From: orval Date: Wed, 23 Mar 2016 17:40:33 +0000 Subject: [PATCH 1/6] Added a test for the case where the email_rfc822 content property of a transmission is modified by toApiFormat() --- test/spec/transmissions.spec.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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(); + }); + }); }); }); From a0c2fc4686143ad7781e8882cb7845ce40e446af Mon Sep 17 00:00:00 2001 From: orval Date: Mon, 4 Apr 2016 12:46:29 +0100 Subject: [PATCH 2/6] Added a test to the toApiFormat spec demonstrating issue #124 --- test/spec/toApiFormat.spec.js | 6 ++++++ 1 file changed, 6 insertions(+) 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" From 442db051e86cd387de5dc33c4e1ef875693c4cb4 Mon Sep 17 00:00:00 2001 From: orval Date: Mon, 4 Apr 2016 12:59:49 +0100 Subject: [PATCH 3/6] Add a new test that uses substitutionData, whose contents should be excluded from case manipulation --- test/spec/toApiFormat.spec.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/spec/toApiFormat.spec.js b/test/spec/toApiFormat.spec.js index 0cd1b4f..834521f 100644 --- a/test/spec/toApiFormat.spec.js +++ b/test/spec/toApiFormat.spec.js @@ -155,4 +155,30 @@ describe('toApiFormat', function() { done(); }); + + it('should ignore allow camelCase exclude list entries', function(done) { + var testObj = { + simpleString: "foo" + , substitutionData: { + key1: 'value1', + key_2: 'value_2', + keyThree: 'valueThree' + } + }; + + var validationObj = { + simple_string: "foo" + , substitution_data: { + key1: 'value1', + key_2: 'value_2', + keyThree: 'valueThree' + } + }; + + var out = toApiFormat(testObj); + + expect(out).to.deep.equal(validationObj); + + done(); + }); }); From d52154d7c8eeb12573cbee37a1f0c2827f4c324c Mon Sep 17 00:00:00 2001 From: orval Date: Mon, 4 Apr 2016 13:02:40 +0100 Subject: [PATCH 4/6] test case typo --- test/spec/toApiFormat.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/toApiFormat.spec.js b/test/spec/toApiFormat.spec.js index 834521f..4aa19e4 100644 --- a/test/spec/toApiFormat.spec.js +++ b/test/spec/toApiFormat.spec.js @@ -156,7 +156,7 @@ describe('toApiFormat', function() { done(); }); - it('should ignore allow camelCase exclude list entries', function(done) { + it('should allow camelCase exclusion list entries', function(done) { var testObj = { simpleString: "foo" , substitutionData: { From 057b3d8e7e94e80619255b323a9686da519b4f4c Mon Sep 17 00:00:00 2001 From: orval Date: Mon, 4 Apr 2016 15:18:24 +0100 Subject: [PATCH 5/6] This fixes the initial email_rfc822 problem and allows for JSON pointers to be used to define exclusions --- lib/toApiFormat.js | 87 +++++++++++++++++++++++++++------------------- package.json | 1 + 2 files changed, 53 insertions(+), 35 deletions(-) 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" } From 7b993ae43afb567c2f59f9ae3192e82e663b4f71 Mon Sep 17 00:00:00 2001 From: orval Date: Mon, 4 Apr 2016 15:22:39 +0100 Subject: [PATCH 6/6] Going to remove this additional test case since it's not actually related to the email_rfc822 problem --- test/spec/toApiFormat.spec.js | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/test/spec/toApiFormat.spec.js b/test/spec/toApiFormat.spec.js index 4aa19e4..0cd1b4f 100644 --- a/test/spec/toApiFormat.spec.js +++ b/test/spec/toApiFormat.spec.js @@ -155,30 +155,4 @@ describe('toApiFormat', function() { done(); }); - - it('should allow camelCase exclusion list entries', function(done) { - var testObj = { - simpleString: "foo" - , substitutionData: { - key1: 'value1', - key_2: 'value_2', - keyThree: 'valueThree' - } - }; - - var validationObj = { - simple_string: "foo" - , substitution_data: { - key1: 'value1', - key_2: 'value_2', - keyThree: 'valueThree' - } - }; - - var out = toApiFormat(testObj); - - expect(out).to.deep.equal(validationObj); - - done(); - }); });