Skip to content

Commit

Permalink
feat: Added support of replyToList in the library, #339: (#1303)
Browse files Browse the repository at this point in the history
* reply_to_list support is added
  • Loading branch information
subinoy7 authored Oct 15, 2021
1 parent beccbd8 commit 6a7fb30
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 7 deletions.
24 changes: 24 additions & 0 deletions docs/use-cases/multiple-reply-to-email.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Multiple emails in replyTo

An array of recipients who will receive replies and/or bounces. Each object in this array must contain the recipient's email address. Each object in the array may optionally contain the recipient's name. You can either choose to use “reply_to” field or “reply_to_list” but not both. [API specification](https://docs.sendgrid.com/api-reference/mail-send/mail-send#multiple-reply-to-emails)

```js
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: '[email protected]',
from: '[email protected]',
subject: 'Multiple mail in replyTo',
html: '<p>Here’s an example of multiple replyTo email for you!</p>',
replyToList: [
{
'name': 'Test User1',
'email': '[email protected]'
},
{
'email': '[email protected]'
}
],
};
```

8 changes: 8 additions & 0 deletions packages/helpers/classes/mail.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ export interface MailData {
dynamicTemplateData?: { [key: string]: any },

hideWarnings?: boolean,

replyToList?: EmailJSON | EmailJSON[],
}

export type MailDataRequired = MailData & (
Expand All @@ -179,6 +181,7 @@ export interface MailJSON {
batch_id?: string;
template_id?: string;
ip_pool_name?: string;
reply_to_list?: EmailJSON[];
}

export default class Mail {
Expand Down Expand Up @@ -353,4 +356,9 @@ export default class Mail {
* Create a Mail instance from given data
*/
static create(data: MailData[]): Mail[];

/**
* Set reply_to_list header from given data
*/
setReplyToList(replyToList: EmailJSON[]): void;
}
20 changes: 18 additions & 2 deletions packages/helpers/classes/mail.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class Mail {
templateId, personalizations, attachments, ipPoolName, batchId,
sections, headers, categories, category, customArgs, asm, mailSettings,
trackingSettings, substitutions, substitutionWrappers, dynamicTemplateData, isMultiple,
hideWarnings,
hideWarnings, replyToList,
} = data;

//Set data
Expand All @@ -90,6 +90,7 @@ class Mail {
this.setMailSettings(mailSettings);
this.setTrackingSettings(trackingSettings);
this.setHideWarnings(hideWarnings);
this.setReplyToList(replyToList);

if (this.isDynamic) {
this.setDynamicTemplateData(dynamicTemplateData);
Expand Down Expand Up @@ -504,7 +505,7 @@ class Mail {
from, replyTo, sendAt, subject, content, templateId,
personalizations, attachments, ipPoolName, batchId, asm,
sections, headers, categories, customArgs, mailSettings,
trackingSettings,
trackingSettings, replyToList,
} = this;

//Initialize with mandatory values
Expand Down Expand Up @@ -560,6 +561,9 @@ class Mail {
if (typeof ipPoolName !== 'undefined') {
json.ipPoolName = ipPoolName;
}
if(typeof replyToList !== 'undefined') {
json.replyToList = replyToList;
}

//Return as snake cased object
return toSnakeCase(json, ['substitutions', 'dynamicTemplateData', 'customArgs', 'headers', 'sections']);
Expand Down Expand Up @@ -667,6 +671,18 @@ class Mail {
value,
[this._checkUndefined, this._createCheckThatThrows(Array.isArray, 'Array expected for`' + propertyName + '`')]);
}

/**
* Set the replyToList from email body
*/
setReplyToList(replyToList) {
if (this._doArrayCheck('replyToList', replyToList) && replyToList.length) {
if (!replyToList.every(replyTo => replyTo && typeof replyTo.email === 'string')) {
throw new Error('Expected each replyTo to contain an `email` string');
}
this.replyToList = replyToList;
}
}
}

//Export class
Expand Down
58 changes: 58 additions & 0 deletions packages/helpers/classes/mail.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,4 +246,62 @@ describe('Mail', function() {
expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true);
});
});

describe('set replyToList to set multiple reply-to', () => {
let data;

this.beforeEach(() => {
data = {
to: '[email protected]',
from: '[email protected]',
subject: 'test replyToList',
category: 'test',
text: 'Testing replyToList settings',
html: '<p>Testing replyToList settings</p>',
};
});

it('should set the replyToList', () => {
let replyToList = [
{
'name': 'Test User1',
'email': '[email protected]'
},
{
'email': '[email protected]'
}
];
data.replyToList = replyToList;

const mail = new Mail(data);

expect(mail.replyToList)
.to.be.deep.equal(replyToList);
});

it('should throw error for incorrect replyToList format', () => {
let replyToList = [
{
'name': 'Test User1'
},
{
'email_data': '[email protected]'
}
];
data.replyToList = replyToList;

expect(() => new Mail(data))
.to.throw('Expected each replyTo to contain an `email` string');
});

it('should throw error for as replyToList is not an array', () => {
let replyToList = {
'name': 'Test User1',
'email': '[email protected]'
};
data.replyToList = replyToList;
expect(() => new Mail(data))
.to.throw('Array expected for`replyToList`');
});
});
});
75 changes: 70 additions & 5 deletions packages/mail/src/mail.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ before(() => {
* Default mock header
*/
beforeEach(() => {
sgClient.setDefaultHeader('X-Mock', 200);
sgClient.setDefaultHeader('X-Mock', 202);
});

/**
Expand All @@ -39,11 +39,11 @@ describe('sgMail.send()', () => {
});

it('should send a basic email', () => {
sgClient.setDefaultHeader('X-Mock', 201);
sgClient.setDefaultHeader('X-Mock', 202);
return sgMail
.send(data)
.then(([response, body]) => {
expect(response.statusCode).to.equal(201);
expect(response.statusCode).to.equal(202);
});
});

Expand All @@ -54,18 +54,83 @@ describe('sgMail.send()', () => {
});

it('should include custom headers to the request', () => {
sgClient.setDefaultHeader('X-Mock', 201);
sgClient.setDefaultHeader('X-Mock', 202);
const clientSpy = sinon.spy(sgClient, "request")
return sgMail
.send(Object.assign(data, { headers: { customHeader: "Custom Header Content" } }))
.then(([response, body]) => {
expect(response.statusCode).to.equal(201);
expect(response.statusCode).to.equal(202);
expect(clientSpy).to.have.been.calledWith(sinon.match({
url: "/v3/mail/send",
method: "POST",
headers: { customHeader: "Custom Header Content" }
}));
});
});

it('should send email with correct replyToList format', () => {
sgClient.setDefaultHeader('X-Mock', 202);
data["replyToList"] = [
{
"name": "Test Team",
"email": "[email protected]"
},
{
"name": "Support Test Team",
"email": "[email protected]"
}
];
return sgMail
.send(data)
.then(([response, body]) => {
expect(response.statusCode).to.equal(202);
});
});

it('should throw error with wrong replyToList format', () => {
sgClient.setDefaultHeader('X-Mock', 202);
data["replyToList"] = {
"name": "Support Test Team",
"email": "[email protected]"
};
return expect(function() {
sgMail.send(data, false, {});
}).to.throw(Error);
});

it('should throw error if any record in replyToList is without email', () => {
data["replyToList"] = [
{
"name": "Test Team",
"email": "[email protected]"
},
{
"name": "Support Test Team"
}
];
return expect(function() {
sgMail.send(data, false, {});
}).to.throw(Error);
});

it('should throw error if both replyTo and replyToList are mentioned', () => {
data["replyTo"] = {
"name": "Manual Tester",
"email": "[email protected]"
};
data["replyToList"] = [
{
"name": "Test Team",
"email": "[email protected]"
},
{
"name": "Support Test Team",
"email": "[email protected]"
}
];
return expect(function() {
sgMail.send(data, false, {});
}).to.throw(Error);
});
});

0 comments on commit 6a7fb30

Please sign in to comment.