Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate field types sent to mail helper API #822

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 49 additions & 8 deletions packages/helpers/classes/mail.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const toSnakeCase = require('../helpers/to-snake-case');
const deepClone = require('../helpers/deep-clone');
const arrayToJSON = require('../helpers/array-to-json');
const { DYNAMIC_TEMPLATE_CHAR_WARNING } = require('../constants');
const {validateMailSettings, validateTrackingSettings} = require('../helpers/validate-settings');

/**
* Mail class
Expand Down Expand Up @@ -122,6 +123,10 @@ class Mail {
if (typeof from === 'undefined') {
return;
}
if (typeof from !== 'string' &&
!(typeof from === 'object' && typeof from.email === 'string')) {
throw new Error('String or address object expected for `from`');
}
this.from = EmailAddress.create(from);
}

Expand All @@ -132,6 +137,10 @@ class Mail {
if (typeof replyTo === 'undefined') {
return;
}
if (typeof replyTo !== 'string' &&
!(typeof replyTo === 'object' && typeof replyTo.email === 'string')) {
throw new Error('String or address object expected for `replyTo`');
}
this.replyTo = EmailAddress.create(replyTo);
}

Expand Down Expand Up @@ -215,6 +224,13 @@ class Mail {
if (typeof asm !== 'object') {
throw new Error('Object expected for `asm`');
}
if (typeof asm.groupId !== 'number') {
throw new Error('Expected `asm` to include an integer in its `groupId` field');
}
if (asm.groupsToDisplay &&
(!Array.isArray(asm.groupsToDisplay) || !asm.groupsToDisplay.every(group => typeof group === 'number'))) {
throw new Error('Array of integers expected for `asm.groupsToDisplay`');
}
this.asm = asm;
}

Expand All @@ -225,8 +241,9 @@ class Mail {
if (typeof personalizations === 'undefined') {
return;
}
if (!Array.isArray(personalizations)) {
throw new Error('Array expected for `personalizations`');
if (!Array.isArray(personalizations) ||
!personalizations.every(personalization => typeof personalization === 'object')) {
throw new Error('Array of objects expected for `personalizations`');
}

//Clear and use add helper to add one by one
Expand Down Expand Up @@ -358,6 +375,18 @@ class Mail {
if (!Array.isArray(content)) {
throw new Error('Array expected for `content`');
}
if (!content.every(contentField =>
typeof contentField === 'object')) {
throw new Error('Expected each entry in `content` to be an object');
}
if (!content.every(contentField =>
typeof contentField.type === 'string')) {
throw new Error('Expected each `content` entry to contain a `type` string');
}
if (!content.every(contentField =>
typeof contentField.value === 'string')) {
throw new Error('Expected each `content` entry to contain a `value` string');
}
this.content = content;
}

Expand Down Expand Up @@ -413,6 +442,22 @@ class Mail {
if (!Array.isArray(attachments)) {
throw new Error('Array expected for `attachments`');
}
if (!attachments.every(attachment =>
typeof attachment.content === 'string')) {
throw new Error('Expected each attachment to contain a `content` string');
}
if (!attachments.every(attachment =>
typeof attachment.filename === 'string')) {
throw new Error('Expected each attachment to contain a `filename` string');
}
if (!attachments.every(attachment =>
attachment.type && typeof attachment.type === 'string')) {
throw new Error('Expected the attachment\'s `type` field to be a string');
}
if (!attachments.every(attachment =>
attachment.disposition && typeof attachment.disposition === 'string')) {
throw new Error('Expected the attachment\'s `disposition` field to be a string');
}
this.attachments = attachments;
}

Expand Down Expand Up @@ -512,9 +557,7 @@ class Mail {
if (typeof settings === 'undefined') {
return;
}
if (typeof settings !== 'object') {
throw new Error('Object expected for `trackingSettings`');
}
validateTrackingSettings(settings);
this.trackingSettings = settings;
}

Expand All @@ -525,9 +568,7 @@ class Mail {
if (typeof settings === 'undefined') {
return;
}
if (typeof settings !== 'object') {
throw new Error('Object expected for `mailSettings`');
}
validateMailSettings(settings);
this.mailSettings = settings;
}

Expand Down
61 changes: 61 additions & 0 deletions packages/helpers/helpers/validate-settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict';

const validate = (parent, parentName, childName, childType) => {
if (typeof parent === 'undefined' || typeof parent[childName] === 'undefined') {
return;
}
if (typeof parent[childName] !== childType) {
throw new Error(`${childType} expected for \`${parentName}.${childName}\``)
}
};

module.exports = {
validateMailSettings(settings) {
if (typeof settings !== 'object') {
throw new Error('Object expected for `mailSettings`');
}
const {
bcc,
bypassListManagement,
footer,
sandboxMode,
spamCheck,
} = settings;
validate(bcc, 'bcc', 'enable', 'boolean');
validate(bcc, 'bcc', 'email', 'string');
validate(bypassListManagement, 'bypassListManagement', 'enable', 'boolean');
validate(footer, 'footer', 'enable', 'boolean');
validate(footer, 'footer', 'text', 'string');
validate(footer, 'footer', 'html', 'string');
validate(sandboxMode, 'sandboxMode', 'enable', 'boolean');
validate(spamCheck, 'spamCheck', 'enable', 'boolean');
validate(spamCheck, 'spamCheck', 'threshold', 'number');
validate(spamCheck, 'spamCheck', 'postToUrl', 'string');
},

validateTrackingSettings(settings) {
if (typeof settings !== 'object') {
throw new Error('Object expected for `mailSettings`');
}
const {
clickTracking,
openTracking,
subscriptionTracking,
ganalytics,
} = settings;
validate(clickTracking, 'clickTracking', 'enable', 'boolean');
validate(clickTracking, 'clickTracking', 'enableText', 'boolean');
validate(openTracking, 'openTracking', 'enable', 'boolean');
validate(openTracking, 'openTracking', 'substitutionTag', 'string');
validate(subscriptionTracking, 'subscriptionTracking', 'enable', 'boolean');
validate(subscriptionTracking, 'subscriptionTracking', 'text', 'string');
validate(subscriptionTracking, 'subscriptionTracking', 'html', 'string');
validate(subscriptionTracking, 'subscriptionTracking', 'substitutionTag', 'string');
validate(ganalytics, 'ganalytics', 'enable', 'boolean');
validate(ganalytics, 'ganalytics', 'utm_source', 'string');
validate(ganalytics, 'ganalytics', 'utm_medium', 'string');
validate(ganalytics, 'ganalytics', 'utm_term', 'string');
validate(ganalytics, 'ganalytics', 'utm_content', 'string');
validate(ganalytics, 'ganalytics', 'utm_campaign', 'string');
},
};
5 changes: 2 additions & 3 deletions packages/mail/src/classes/mail-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,8 @@ class MailService {
// test if rule.pattern is a valid regex
rule.pattern.test('');
return rule;
}
catch (err) {
// continue regardless of error
} catch (err) {
// FIXME?
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The build currently fails ESLint due to an empty code block.
I'm mystified as to why that happens, because my PR doesn't change anything else in this entire file.

}
}
});
Expand Down