@sendgrid/helpers
Advanced tools
Comparing version 6.3.0 to 6.4.0
@@ -9,2 +9,4 @@ 'use strict'; | ||
const deepClone = require('../helpers/deep-clone'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
@@ -16,5 +18,5 @@ /** | ||
/** | ||
* Constructor | ||
*/ | ||
/** | ||
* Constructor | ||
*/ | ||
constructor(data) { | ||
@@ -44,6 +46,18 @@ | ||
//Extract properties from data | ||
const {content, filename, type, disposition, contentId} = data; | ||
const { | ||
content, | ||
filename, | ||
type, | ||
disposition, | ||
contentId, | ||
filePath, | ||
} = data; | ||
if ((typeof content !== 'undefined') && (typeof filePath !== 'undefined')) { | ||
throw new Error( | ||
`The props 'content' and 'filePath' cannot be used together.` | ||
); | ||
} | ||
//Set data | ||
this.setContent(content); | ||
this.setFilename(filename); | ||
@@ -53,18 +67,47 @@ this.setType(type); | ||
this.setContentId(contentId); | ||
this.setContent(filePath ? this.readFile(filePath) : content); | ||
} | ||
/** | ||
* Read a file and return its content as base64 | ||
*/ | ||
readFile(filePath) { | ||
return fs.readFileSync(path.resolve(filePath)); | ||
} | ||
/** | ||
* Set content | ||
*/ | ||
setContent(content) { | ||
if (typeof content === 'undefined') { | ||
//Duck type check toString on content if it's a Buffer as that's the method that will be called. | ||
if (typeof content === 'string') { | ||
this.content = content; | ||
return; | ||
} | ||
if (typeof content !== 'string') { | ||
throw new Error('String expected for `content`'); | ||
else if (content instanceof Buffer && content.toString !== undefined) { | ||
this.content = content.toString(); | ||
if (this.disposition === 'attachment') { | ||
this.content = content.toString('base64'); | ||
} | ||
return; | ||
} | ||
this.content = content; | ||
throw new Error('`content` expected to be either Buffer or string'); | ||
} | ||
/** | ||
* Set content | ||
*/ | ||
setFileContent(content) { | ||
if (content instanceof Buffer && content.toString !== undefined) { | ||
this.content = content.toString('base64'); | ||
return; | ||
} | ||
throw new Error('`content` expected to be Buffer'); | ||
} | ||
/** | ||
* Set filename | ||
@@ -121,5 +164,5 @@ */ | ||
/** | ||
* To JSON | ||
*/ | ||
/** | ||
* To JSON | ||
*/ | ||
toJSON() { | ||
@@ -126,0 +169,0 @@ |
@@ -13,3 +13,3 @@ 'use strict'; | ||
/** | ||
/** | ||
* Constructor | ||
@@ -59,3 +59,7 @@ */ | ||
} | ||
this.name = name; | ||
// Wrap name in quotes to address API issue | ||
// https://github.com/sendgrid/sendgrid-csharp/issues/268#issuecomment-232177443 | ||
const isQuoted = (name[0] === '\"') && (name[name.length - 1] === '\"'); | ||
const shouldQuote = name.includes(',') && !isQuoted; | ||
this.name = shouldQuote ? `\"${name}\"` : name; | ||
} | ||
@@ -76,3 +80,3 @@ | ||
/** | ||
/** | ||
* To JSON | ||
@@ -79,0 +83,0 @@ */ |
@@ -57,2 +57,12 @@ 'use strict'; | ||
}); | ||
it('should wrap name in quotes if a comma is present', function() { | ||
email.setName('Doe, John'); | ||
expect(email.name).to.equal('\"Doe, John\"'); | ||
}); | ||
it('should not double wrap in quotes', function() { | ||
email.setName('\"Doe, John\"'); | ||
expect(email.name).to.equal('\"Doe, John\"'); | ||
}); | ||
it('should throw an error for invalid input', function() { | ||
@@ -59,0 +69,0 @@ expect(function() { |
@@ -11,2 +11,3 @@ 'use strict'; | ||
const ResponseError = require('./response-error'); | ||
const Statistics = require('./statistics'); | ||
@@ -22,2 +23,3 @@ /** | ||
ResponseError, | ||
Statistics, | ||
}; |
@@ -155,2 +155,3 @@ import {AttachmentData, AttachmentJSON} from "./attachment"; | ||
isMultiple?: boolean, | ||
dynamicTemplateData?: { [key: string]: any }, | ||
} | ||
@@ -346,2 +347,2 @@ | ||
static create(data: MailData[]): Mail[]; | ||
} | ||
} |
@@ -12,2 +12,3 @@ 'use strict'; | ||
const arrayToJSON = require('../helpers/array-to-json'); | ||
const { DYNAMIC_TEMPLATE_CHAR_WARNING } = require('../constants'); | ||
@@ -91,4 +92,5 @@ /** | ||
if (this.isDynamic) { | ||
this.setDynamicTemplateData(dynamicTemplateData) | ||
} else { | ||
this.setDynamicTemplateData(dynamicTemplateData); | ||
} | ||
else { | ||
this.setSubstitutions(substitutions); | ||
@@ -247,3 +249,4 @@ this.setSubstitutionWrappers(substitutionWrappers); | ||
delete personalization.substitutions; | ||
} else if (personalization.dynamicTemplateData) { | ||
} | ||
else if (personalization.dynamicTemplateData) { | ||
delete personalization.dynamicTemplateData; | ||
@@ -260,3 +263,4 @@ } | ||
this.applyDynamicTemplateData(personalization); | ||
} else { | ||
} | ||
else { | ||
this.applySubstitutions(personalization); | ||
@@ -340,2 +344,10 @@ } | ||
} | ||
// Check dynamic template for non-escaped characters and warn if found | ||
Object.values(dynamicTemplateData).forEach(value => { | ||
if (/['"&]/.test(value)) { | ||
console.warn(DYNAMIC_TEMPLATE_CHAR_WARNING); | ||
} | ||
}); | ||
this.dynamicTemplateData = dynamicTemplateData; | ||
@@ -342,0 +354,0 @@ } |
@@ -7,2 +7,3 @@ 'use strict'; | ||
const Mail = require('./mail'); | ||
const { DYNAMIC_TEMPLATE_CHAR_WARNING } = require('../constants'); | ||
@@ -158,2 +159,61 @@ /** | ||
}); | ||
describe('dynamic template handlebars substitutions', () => { | ||
let logSpy, data; | ||
beforeEach(() => { | ||
logSpy = sinon.spy(console, 'warn'); | ||
data = { | ||
to: 'recipient@example.org', | ||
from: 'sender@example.org', | ||
subject: 'Hello world', | ||
text: 'Hello plain world!', | ||
html: '<p>Hello HTML world!</p>', | ||
templateId: 'd-df80613cccc6441ea5cd7c95377bc1ef', | ||
}; | ||
}); | ||
afterEach(() => { | ||
console.warn.restore(); | ||
}); | ||
it('should log an error if template subject line contains improperly escaped "\'" character', () => { | ||
data = Object.assign(data, { | ||
dynamicTemplateData: { | ||
subject: 'Testing Templates and \'Stuff\'', | ||
}, | ||
}); | ||
const mail = new Mail(data); | ||
expect(logSpy.calledOnce).to.equal(true); | ||
expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true); | ||
}); | ||
it('should log an error if template subject line contains improperly escaped """ character', () => { | ||
data = Object.assign(data, { | ||
dynamicTemplateData: { | ||
subject: '"Testing Templates" and Stuff', | ||
}, | ||
}); | ||
const mail = new Mail(data); | ||
expect(logSpy.calledOnce).to.equal(true); | ||
expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true); | ||
}); | ||
it('should log an error if template subject line contains improperly escaped "&" character', () => { | ||
data = Object.assign(data, { | ||
dynamicTemplateData: { | ||
subject: 'Testing Templates & Stuff', | ||
}, | ||
}); | ||
const mail = new Mail(data); | ||
expect(logSpy.calledOnce).to.equal(true); | ||
expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true); | ||
}); | ||
}); | ||
}); |
@@ -12,7 +12,7 @@ 'use strict'; | ||
*/ | ||
describe('Personalization', function () { | ||
describe('Personalization', function() { | ||
//Create new personalization before each test | ||
let p; | ||
beforeEach(function () { | ||
beforeEach(function() { | ||
p = new Personalization(); | ||
@@ -22,4 +22,4 @@ }); | ||
//Reverse merge substitutions | ||
describe('deepMergeDynamicTemplateData()', function () { | ||
it('should reverse merge dynamicTemplateData', function () { | ||
describe('deepMergeDynamicTemplateData()', function() { | ||
it('should reverse merge dynamicTemplateData', function() { | ||
p.setDynamicTemplateData({ test1: 'Test1' }); | ||
@@ -32,3 +32,3 @@ p.deepMergeDynamicTemplateData({ test2: 'Test2' }); | ||
}); | ||
it('should not overwrite existing keys', function () { | ||
it('should not overwrite existing keys', function() { | ||
p.setDynamicTemplateData({ test1: 'Test1' }); | ||
@@ -41,3 +41,3 @@ p.deepMergeDynamicTemplateData({ test1: 'Test3', test2: 'Test2' }); | ||
}); | ||
it('should work without prior dynamicTemplateData', function () { | ||
it('should work without prior dynamicTemplateData', function() { | ||
p.deepMergeDynamicTemplateData({ test2: 'Test2' }); | ||
@@ -47,9 +47,9 @@ expect(p.dynamicTemplateData).to.have.a.property('test2'); | ||
}); | ||
it('should throw an error for invalid input', function () { | ||
expect(function () { | ||
it('should throw an error for invalid input', function() { | ||
expect(function() { | ||
p.deepMergeDynamicTemplateData(3); | ||
}).to.throw(Error); | ||
}); | ||
it('should accept no input', function () { | ||
expect(function () { | ||
it('should accept no input', function() { | ||
expect(function() { | ||
p.deepMergeDynamicTemplateData(); | ||
@@ -56,0 +56,0 @@ }).not.to.throw(Error); |
@@ -12,3 +12,3 @@ 'use strict'; | ||
*/ | ||
describe('Personalization', function() { | ||
describe('Personalization', function() { | ||
@@ -46,2 +46,2 @@ //Create new personalization before each test | ||
}); | ||
}); | ||
}); |
@@ -12,7 +12,7 @@ 'use strict'; | ||
*/ | ||
describe('Personalization', function () { | ||
describe('Personalization', function() { | ||
//Create new personalization before each test | ||
let p; | ||
beforeEach(function () { | ||
beforeEach(function() { | ||
p = new Personalization(); | ||
@@ -22,8 +22,8 @@ }); | ||
//JSON conversion | ||
describe('toJSON()', function () { | ||
beforeEach(function () { | ||
describe('toJSON()', function() { | ||
beforeEach(function() { | ||
p.setTo('test@example.org'); | ||
}); | ||
it('should always have the to field', function () { | ||
it('should always have the to field', function() { | ||
const json = p.toJSON(); | ||
@@ -36,3 +36,3 @@ expect(json).to.have.property('to'); | ||
}); | ||
it('should set the cc field', function () { | ||
it('should set the cc field', function() { | ||
p.setCc('testcc@example.org'); | ||
@@ -46,3 +46,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should set the bcc field', function () { | ||
it('should set the bcc field', function() { | ||
p.setBcc('testbcc@example.org'); | ||
@@ -56,3 +56,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should set the headers field', function () { | ||
it('should set the headers field', function() { | ||
p.setHeaders({ test: 'Test' }); | ||
@@ -64,3 +64,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should set the custom_args field', function () { | ||
it('should set the custom_args field', function() { | ||
p.setCustomArgs({ test: 'Test' }); | ||
@@ -72,3 +72,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should set the substitutions field', function () { | ||
it('should set the substitutions field', function() { | ||
p.setSubstitutions({ test: 'Test' }); | ||
@@ -79,3 +79,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should apply wrappers to the substitutions', function () { | ||
it('should apply wrappers to the substitutions', function() { | ||
p.setSubstitutions({ test: 'Test', otherTest2: 'Test2' }); | ||
@@ -91,3 +91,3 @@ p.setSubstitutionWrappers(['{{', '}}']); | ||
}); | ||
it('should set the dynamicTemplateData field', function () { | ||
it('should set the dynamicTemplateData field', function() { | ||
p.setDynamicTemplateData({ test: 'Test' }); | ||
@@ -98,3 +98,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should set the subject field', function () { | ||
it('should set the subject field', function() { | ||
p.setSubject('Test'); | ||
@@ -105,3 +105,3 @@ const json = p.toJSON(); | ||
}); | ||
it('should set the send_at field', function () { | ||
it('should set the send_at field', function() { | ||
p.setSendAt(555); | ||
@@ -108,0 +108,0 @@ const json = p.toJSON(); |
@@ -7,5 +7,5 @@ 'use strict'; | ||
module.exports = function convertHTML2PlainString(html) { | ||
let text = html.replace(/(<([^>]+)>)/g, ""); | ||
text = text.replace(/\s+/g,' ') | ||
return text | ||
} | ||
let text = html.replace(/(<([^>]+)>)/g, ''); | ||
text = text.replace(/\s+/g, ' '); | ||
return text; | ||
}; |
@@ -13,21 +13,21 @@ 'use strict'; | ||
//Test string with one html tag | ||
const html1 = '<p>Hello world</p>'; | ||
//Test string with nested html tags | ||
const html2 = '<div><p>Hello <b>World!</b></p></div>'; | ||
//Test string with html tag with attributes | ||
const html3 = '<div class="test-class">Hello World!</div>'; | ||
//Tests | ||
it('should strip out html tags', function() { | ||
expect(convertHTML2PlainString(html1)).to.be.equal('Hello world'); | ||
}); | ||
it('should strip out nested html tags', function() { | ||
expect(convertHTML2PlainString(html2)).to.be.equal('Hello World!'); | ||
}); | ||
it('should strip out html tags with attributes', function() { | ||
expect(convertHTML2PlainString(html3)).to.be.equal('Hello World!'); | ||
}); | ||
//Test string with one html tag | ||
const html1 = '<p>Hello world</p>'; | ||
//Test string with nested html tags | ||
const html2 = '<div><p>Hello <b>World!</b></p></div>'; | ||
//Test string with html tag with attributes | ||
const html3 = '<div class="test-class">Hello World!</div>'; | ||
//Tests | ||
it('should strip out html tags', function() { | ||
expect(convertHTML2PlainString(html1)).to.be.equal('Hello world'); | ||
}); | ||
it('should strip out nested html tags', function() { | ||
expect(convertHTML2PlainString(html2)).to.be.equal('Hello World!'); | ||
}); | ||
it('should strip out html tags with attributes', function() { | ||
expect(convertHTML2PlainString(html3)).to.be.equal('Hello World!'); | ||
}); | ||
}); |
@@ -23,7 +23,10 @@ 'use strict'; | ||
if (isObject(data[key])) { | ||
if (!(key in base)) | ||
if (!(key in base)) { | ||
Object.assign(output, { [key]: data[key] }); | ||
else | ||
} | ||
else { | ||
output[key] = mergeDeep(base[key], data[key]); | ||
} else { | ||
} | ||
} | ||
else { | ||
Object.assign(output, { [key]: data[key] }); | ||
@@ -34,2 +37,2 @@ } | ||
return output; | ||
} | ||
}; |
@@ -11,3 +11,3 @@ 'use strict'; | ||
*/ | ||
describe('mergeDataDeep', function () { | ||
describe('mergeDataDeep', function() { | ||
@@ -33,3 +33,3 @@ //Test objects | ||
//Tests | ||
it('should merge the two objects', function () { | ||
it('should merge the two objects', function() { | ||
expect(merged).to.have.property('a'); | ||
@@ -44,11 +44,11 @@ expect(merged).to.have.property('b'); | ||
}); | ||
it('should throw on invalid input', function () { | ||
expect(function () { | ||
it('should throw on invalid input', function() { | ||
expect(function() { | ||
mergeDataDeep(null, obj2); | ||
}).to.throw(Error); | ||
expect(function () { | ||
expect(function() { | ||
mergeDataDeep(obj1, 4); | ||
}).to.throw(Error); | ||
}); | ||
it('should overwrite arrays', function () { | ||
it('should overwrite arrays', function() { | ||
expect(merged).to.have.property('arr'); | ||
@@ -55,0 +55,0 @@ expect(merged.arr).to.be.an.instanceof(Array); |
{ | ||
"name": "@sendgrid/helpers", | ||
"description": "SendGrid NodeJS internal helpers", | ||
"version": "6.3.0", | ||
"author": "SendGrid <dx@sendgrid.com> (sendgrid.com)", | ||
"description": "Twilio SendGrid NodeJS internal helpers", | ||
"version": "6.4.0", | ||
"author": "Twilio SendGrid <dx@sendgrid.com> (sendgrid.com)", | ||
"contributors": [ | ||
@@ -7,0 +7,0 @@ "Kyle Partridge <kyle.partridge@sendgrid.com>", |
@@ -11,3 +11,3 @@ [![BuildStatus](https://travis-ci.org/sendgrid/sendgrid-nodejs.svg?branch=master)](https://travis-ci.org/sendgrid/sendgrid-nodejs) | ||
Note that not all objects represented in the Sendgrid API have helper classes assigned to them, because it is not expected that developers will use these classes themselves. They are primarily for internal use and developers are expected to use the publicly exposed API in the [various endpoint services](https://www.npmjs.com/org/sendgrid). | ||
Note that not all objects represented in the Sendgrid API have helper classes assigned to them because it is not expected that developers will use these classes themselves. They are primarily for internal use and developers are expected to use the publicly exposed API in the [various endpoint services](https://www.npmjs.com/org/sendgrid). | ||
@@ -17,3 +17,3 @@ To be notified when this package is updated, please subscribe to email [notifications](https://dx.sendgrid.com/newsletter/nodejs) for releases and breaking changes. | ||
## Mail class | ||
Used to compose a `Mail` object that converts itself to proper JSON for use with the [Sendgrid v3 API](https://sendgrid.com/docs/API_Reference/api_v3.html). This class supports a slightly different API to make sending emails easier in many cases by not having to deal with personalization arrays, instead offering a simpler interface for composing mails. | ||
Used to compose a `Mail` object that converts itself to proper JSON for use with the [Sendgrid v3 API](https://sendgrid.com/docs/API_Reference/api_v3.html). This class supports a slightly different API to make sending emails easier in many cases by not having to deal with personalization arrays, instead offering a more straightforward interface for composing emails. | ||
@@ -44,6 +44,6 @@ ## Attachment class | ||
@sendgrid/helpers is guided and supported by the SendGrid [Developer Experience Team](mailto:dx@sendgrid.com). | ||
@sendgrid/helpers are guided and supported by the Twilio SendGrid [Developer Experience Team](mailto:dx@sendgrid.com). | ||
@sendgrid/helpers is maintained and funded by SendGrid, Inc. The names and logos for @sendgrid/helpers are trademarks of SendGrid, Inc. | ||
@sendgrid/helpers are maintained and funded by Twilio SendGrid, Inc. The names and logos for @sendgrid/helpers are trademarks of Twilio SendGrid, Inc. | ||
![SendGrid Logo](https://uiux.s3.amazonaws.com/2016-logos/email-logo%402x.png) | ||
![Twilio SendGrid Logo](https://github.com/sendgrid/sendgrid-python/raw/master/twilio_sendgrid_logo.png) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
131961
80
4394
2