",
17 | "license": "MIT"
18 | }
19 |
--------------------------------------------------------------------------------
/docs/examples/webhooks-docker/routes/events.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express.Router()
3 | const bodyParser = require('body-parser')
4 |
5 | router.use(bodyParser.json())
6 |
7 | router.post('/', (req, res) => {
8 |
9 | // Modify and extend the business logic applied to the req.body
10 | // array. You could send this to a file, database, or other system
11 | // for storage and analysis.
12 | console.log(req.body)
13 |
14 | let events = req.body
15 |
16 | events.forEach(element => {
17 | console.log()
18 | console.log('---------- EVENT START ----------')
19 | console.log('EMAIL:', element.email)
20 | console.log('TIMESTAMP:', element.timestamp)
21 | // TODO: invalid JSON key name
22 | // console.log('EMAIL:', element.smtp-id)
23 | console.log('EVENT:', element.event)
24 | console.log('CATEGORY:', element.category)
25 | console.log('SG_EVENT_ID:', element.sg_event_id)
26 | console.log('SG_MESSAGE_ID:', element.sg_message_id)
27 | console.log('USERAGENT:', element.useragent)
28 | console.log('IP:', element.ip)
29 | console.log('URL:', element.url)
30 | console.log('ASM_GROUP_ID:', element.asm_group_id)
31 | console.log('---------- EVENT END ----------')
32 | console.log()
33 | });
34 |
35 | res.sendStatus(200)
36 | })
37 |
38 | module.exports = router
--------------------------------------------------------------------------------
/docs/examples/webhooks-docker/routes/inbound-parse.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const router = express.Router()
3 | const formidable = require('express-formidable')
4 |
5 | router.use(formidable())
6 |
7 | router.post('/', (req, res) => {
8 | console.log('---------- START RECEIVED WEBHOOK DATA ----------')
9 | // Email data comes in as a form. Using express-formidable to
10 | // handle the form data. Form fields are available in req.fields
11 | // Below, extracting the from and text.
12 | // You can take this data and do something more interesting with it
13 | // such as sending it to a database.
14 | // Attachments are stored in /tmp based on the default configuration
15 | // of the formidable middleware.
16 | // console.log(req.fields)
17 | console.log()
18 | console.log('HEADERS: ' + req.fields.headers)
19 | console.log()
20 | console.log('DKIM: ' + req.fields.dkim)
21 | console.log()
22 | console.log('CONTENT-IDS: ' + req.fields["content-ids"])
23 | console.log()
24 | console.log('TO: ' + req.fields.to)
25 | console.log()
26 | console.log('HTML: ' + req.fields.html)
27 | console.log()
28 | console.log('FROM: ' + req.fields.from)
29 | console.log()
30 | console.log('SENDER-IP: ' + req.fields.sender_ip)
31 | console.log()
32 | console.log('SPAM-REPORT: ' + req.fields.spam_report)
33 | console.log()
34 | console.log('ENVELOPE: ' + req.fields.envelope)
35 | console.log()
36 | console.log('ATTACHMENTS: ' + req.fields.attachments)
37 | console.log()
38 | console.log('SPAM-SCORE: ' + req.fields.spam_score)
39 | console.log()
40 | console.log('ATTACHMENT-INFO: ' + req.fields["attachment-info"])
41 | console.log()
42 | console.log('CHARSETS: ' + req.fields.charsets)
43 | console.log()
44 | console.log('SPF: ' + req.fields.SPF)
45 | console.log()
46 | console.log('MESSAGE TEXT: ' + req.fields.text)
47 | console.log()
48 | console.log('---------- END RECEIVED WEBHOOK DATA ----------')
49 |
50 | res.sendStatus(200)
51 | })
52 |
53 | module.exports = router
--------------------------------------------------------------------------------
/docs/migration-guides/migrating-from-version-6-to-7.md:
--------------------------------------------------------------------------------
1 | This guide relates to changes implemented in [PR 1058](https://github.com/sendgrid/sendgrid-nodejs/pull/1058).
2 |
3 | The [request](https://github.com/request/request#deprecated) HTTP client was deprecated as of Feb 11th 2020. We have updated this helper library with the [axios](https://www.npmjs.com/package/axios) HTTP client.
4 |
5 | In version 6.X.X, our `ClientRequest` and `ClientResponse` types exposed the raw interfaces from the `request` module directly. In version 7.X.X, the [`ClientRequest`](../../packages/helpers/classes/request.d.ts) and [`ClientResponse`](../../packages/helpers/classes/response.d.ts) objects untethers from a specific HTTP client implementation. We have maintained the core request and response interfaces exposed in test cases and usage examples, but mask everything else.
6 |
7 | If you are passing in request options, outside of what’s documented, to the HTTP request client, this may be a breaking change for you. Please check the [`RequestOptions` interface](../../packages/helpers/classes/request.d.ts) to inspect the updated options. If you rely on HTTP response data [previously available](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/HEAD/types/request/index.d.ts#L319), this may also be a breaking change for you. Please see the [`ClientResponse`](../../packages/helpers/classes/response.d.ts) object for details about what data is now exposed.
8 |
9 | ## Action steps:
10 |
11 | 1. Locate any areas in your code that utilizes request and response properties that are no longer exposed in the new interfaces.
12 | 1. Update the response values to use the new [`Response`](../../packages/helpers/classes/response.d.ts) object.
13 | 1. Update the request values to use the new options exposed in the [`RequestOptions`](../../packages/helpers/classes/request.d.ts) object.
14 |
--------------------------------------------------------------------------------
/docs/use-cases/README.md:
--------------------------------------------------------------------------------
1 | This documentation provides examples for specific Twilio SendGrid v3 API use cases. Please [open an issue](https://github.com/sendgrid/sendgrid-nodejs/issues) or make a pull request for any email use cases you would like us to document here. Thank you!
2 |
3 | # Email Use Cases
4 | * [Send a Single Email to a Single Recipient](single-email-single-recipient.md)
5 | * [Send a Single Email to Multiple Recipients](single-email-multiple-recipients.md)
6 | * [Send Multiple Emails to Multiple Recipients](multiple-emails-multiple-recipients.md)
7 | * [Send Multiple Emails with Personalizations](multiple-emails-personalizations.md)
8 | * [Send Multiple Emails with Personalizations and Substitutions](multiple-emails-personalizations-with-substitutions.md)
9 | * [CC, BCC and Reply To](cc-bcc-reply-to.md)
10 | * [Flexible Email Address Fields](flexible-address-fields.md)
11 | * [Handling Success/Failure/Errors](success-failure-errors.md)
12 | * [Show Email Activity](email-activity.md)
13 | * [Advanced Usage](advanced.md)
14 | * [Transactional Templates](transactional-templates.md)
15 | * [Legacy Transactional Templates](transactional-legacy-templates.md)
16 | * [Hide Warnings](hide-warnings.md)
17 | * [Attachments](attachments.md)
18 | * [Customization Per Recipient](customization.md)
19 | * [Manually Providing Content](manual-content.md)
20 | * [Scheduled Send](scheduled-send.md)
21 | * [Specifying Custom Headers](custom-headers.md)
22 | * [Specifying Categories](categories.md)
23 | * [Timeout](timeout.md)
24 | * [Kitchen Sink - an example with all settings used](kitchen-sink.md)
25 |
26 | # Twilio Use Cases
27 | * [Twilio Setup](twilio-setup.md)
28 | * [Send an Email With Twilio Email (Pilot)](twilio-email.md)
29 | * [Send an SMS Message](sms.md)
30 |
31 | # Non-Email Use Cases
32 | * [How to Set up a Domain Authentication](domain-authentication.md)
33 | * [How to View Email Statistics](email-stats.md)
34 |
--------------------------------------------------------------------------------
/docs/use-cases/advanced.md:
--------------------------------------------------------------------------------
1 | # Advanced Usage
2 |
3 | All other advanced settings are supported and can be passed in through the msg object according to the expected format as per the [API v3 documentation](https://sendgrid.com/docs/api-reference/). Note that you can use either `camelCase` or `snake_case` for property names.
4 |
--------------------------------------------------------------------------------
/docs/use-cases/attachments.md:
--------------------------------------------------------------------------------
1 | # Attachments
2 |
3 | Attachments can be sent by providing an array of `attachments` as per the [API specification](https://sendgrid.com/docs/api-reference/):
4 |
5 | ```js
6 | const sgMail = require('@sendgrid/mail');
7 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
8 | const msg = {
9 | to: 'recipient@example.org',
10 | from: 'sender@example.org',
11 | subject: 'Hello attachment',
12 | html: 'Here’s an attachment for you!
',
13 | attachments: [
14 | {
15 | content: 'Some base 64 encoded attachment content',
16 | filename: 'some-attachment.txt',
17 | type: 'plain/text',
18 | disposition: 'attachment',
19 | content_id: 'mytext'
20 | },
21 | ],
22 | };
23 | ```
24 |
25 | Reading and converting a local PDF file.
26 |
27 | ```js
28 | import fs from 'fs';
29 |
30 | fs.readFile(('Document.pdf'), (err, data) => {
31 | if (err) {
32 | // do something with the error
33 | }
34 | if (data) {
35 | const msg = {
36 | to: 'recipient@test.org',
37 | from: 'sender@test.org',
38 | subject: 'Attachment',
39 | html: 'Here’s an attachment for you!
',
40 | attachments: [
41 | {
42 | content: data.toString('base64'),
43 | filename: 'some-attachment.pdf',
44 | type: 'application/pdf',
45 | disposition: 'attachment',
46 | content_id: 'mytext',
47 | },
48 | ],
49 | };
50 | }
51 | });
52 | ```
53 |
54 | If you are using a PDF URL:
55 |
56 | ```js
57 | import request from 'request';
58 |
59 | request(fileURl, { encoding: null }, (err, res, body) => {
60 | if (err) { return err; }
61 | if (body) {
62 | const textBuffered = Buffer.from(body);
63 |
64 | const msg = {
65 | to: 'recipient@test.org',
66 | from: 'sender@test.org',
67 | subject: 'Attachment',
68 | html: 'Here’s an attachment for you!
',
69 | attachments: [
70 | {
71 | content: textBuffered.toString('base64'),
72 | filename: 'some-attachment.pdf',
73 | type: 'application/pdf',
74 | disposition: 'attachment',
75 | content_id: 'mytext',
76 | },
77 | ],
78 | };
79 | // send msg here
80 | }
81 | });
82 | ```
83 |
--------------------------------------------------------------------------------
/docs/use-cases/categories.md:
--------------------------------------------------------------------------------
1 | # Specifying Categories
2 |
3 | Use the `categories` property to provide an array of categories for your email:
4 |
5 | ```js
6 | const msg = {
7 | to: 'recipient@example.org',
8 | from: 'sender@example.org',
9 | subject: 'Hello email with categories',
10 | html: 'Some email content
',
11 | categories: [
12 | 'transactional', 'customer', 'weekly',
13 | ],
14 | };
15 | ```
16 |
17 | Specifying a single `category` is also supported:
18 |
19 | ```js
20 | const msg = {
21 | to: 'recipient@example.org',
22 | from: 'sender@example.org',
23 | subject: 'Hello email with categories',
24 | html: 'Some email content
',
25 | category: 'transactional',
26 | };
27 | ```
28 |
--------------------------------------------------------------------------------
/docs/use-cases/cc-bcc-reply-to.md:
--------------------------------------------------------------------------------
1 | # CC, BCC and Reply To
2 |
3 | You can specify the `cc`, `bcc`, and `replyTo` fields for more control over who you send the email to and where people will reply to:
4 |
5 | ```js
6 | const msg = {
7 | to: 'recipient@example.org',
8 | cc: 'someone@example.org',
9 | bcc: ['me@example.org', 'you@example.org'],
10 | from: 'sender@example.org',
11 | replyTo: 'othersender@example.org',
12 | subject: 'Hello world',
13 | text: 'Hello plain world!',
14 | html: 'Hello HTML world!
',
15 | };
16 | ```
17 |
--------------------------------------------------------------------------------
/docs/use-cases/custom-headers.md:
--------------------------------------------------------------------------------
1 | # Specifying Custom Headers
2 |
3 | Use the `headers` property to specify any custom headers (note that these can also be set globally per the [API specification](https://sendgrid.com/docs/api-reference/):
4 |
5 | ```js
6 | const msg = {
7 | to: 'recipient@example.org',
8 | from: 'sender@example.org',
9 | subject: 'Hello custom header',
10 | html: 'Some email content
',
11 | headers: {
12 | 'X-CustomHeader': 'Custom header value',
13 | },
14 | };
15 | ```
16 |
--------------------------------------------------------------------------------
/docs/use-cases/customization.md:
--------------------------------------------------------------------------------
1 | # Customization Per Recipient
2 |
3 | To send multiple individual emails to multiple recipients with additional customization (like a different subject), use the `personalizations` field as per the [API definition](https://sendgrid.com/docs/api-reference/) instead of `to`, leveraging all customization options:
4 |
5 | ```js
6 | const msg = {
7 | personalizations: [
8 | {
9 | to: 'recipient1@example.org',
10 | subject: 'Hello recipient 1',
11 | dynamicTemplateData: {
12 | name: 'Recipient 1',
13 | id: '123',
14 | },
15 | headers: {
16 | 'X-Custom-Header': 'Recipient 1',
17 | },
18 | customArgs: {
19 | myArg: 'Recipient 1', // must be a string
20 | },
21 | },
22 | {
23 | to: 'recipient2@example.org',
24 | subject: 'Hello recipient 2',
25 | dynamicTemplateData: {
26 | name: 'Recipient 2',
27 | id: '456',
28 | },
29 | headers: {
30 | 'X-Custom-Header': 'Recipient 2',
31 | },
32 | customArgs: {
33 | myArg: 'Recipient 1', // must be a string
34 | },
35 | sendAt: 1500077141,
36 | }
37 | ],
38 | from: 'sender@example.org',
39 | templateId: 'd-12345678901234567890123456789012',
40 | };
41 | ```
42 |
43 | If the `dynamicTemplateData` field is provided globally as well, these substitutions will be merged with any custom `dynamicTemplateData` you provide in the `personalizations`.
44 |
--------------------------------------------------------------------------------
/docs/use-cases/data-residency-set-hostname.md:
--------------------------------------------------------------------------------
1 | # Choosing a data-residency to send messages to
2 |
3 | Use the `setDataResidency` setter to specify which host to send to:
4 |
5 | Send to EU (data-residency: `https://api.eu.sendgrid.com/`)
6 | ```js
7 | const client = require('@sendgrid/client');
8 | const sgMail = require('@sendgrid/mail');
9 | client.setDataResidency('eu');
10 | const msg = {
11 | to: 'recipient@example.org',
12 | from: 'sender@example.org',
13 | subject: 'Hello world',
14 | text: 'Hello plain world!',
15 | html: 'Hello HTML world!
',
16 | };
17 | sgMail.setClient(client);
18 | sgMail.send(msg);
19 | ```
20 | Send to Global region, this is also the default host, if the setter is not used
21 | (data-residency: `https://api.sendgrid.com/`)
22 | ```js
23 | const client = require('@sendgrid/client');
24 | const sgMail = require('@sendgrid/mail');
25 | client.setDataResidency('global');
26 | const msg = {
27 | to: 'recipient@example.org',
28 | from: 'sender@example.org',
29 | subject: 'Hello world',
30 | text: 'Hello plain world!',
31 | html: 'Hello HTML world!
',
32 | };
33 | sgMail.setClient(client);
34 | sgMail.send(msg);
35 | ```
36 |
37 | ## Limitations
38 |
39 | 1. Emails can only be sent to two hosts for now; 'eu' (https://api.eu.sendgrid.com/) and 'global' (https://api.eu.sendgrid.com/)
40 | 2. The default data-residency is https://api.sendgrid.com/
41 | 3. The valid values for `region` in `client.setDataResidency(region)` are only `eu` and `global`. Case-sensitive.
42 |
--------------------------------------------------------------------------------
/docs/use-cases/domain-authentication.md:
--------------------------------------------------------------------------------
1 | # How to Setup a Domain Authentication
2 |
3 | You can find documentation for how to setup a domain authentication via the UI [here](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) and via API [here](../../packages/client/USAGE.md#sender-authentication).
4 |
5 | Find more information about all of Twilio SendGrid's authentication related documentation [here](https://sendgrid.com/docs/ui/account-and-settings/).
6 |
--------------------------------------------------------------------------------
/docs/use-cases/email-activity.md:
--------------------------------------------------------------------------------
1 | # How to View Email Activity
2 |
3 | You can find documentation for how to use the Email Activity Feed via the UI [here](https://sendgrid.com/docs/ui/analytics-and-reporting/email-activity-feed/) and via API [here](../../packages/client/USAGE.md#messages).
4 |
5 | Find more information about getting started with the Email Activity Feed API [here](https://sendgrid.com/docs/API_Reference/Web_API_v3/Tutorials/getting_started_email_activity_api.html).
6 |
--------------------------------------------------------------------------------
/docs/use-cases/email-stats.md:
--------------------------------------------------------------------------------
1 | # How to View Email Statistics
2 |
3 | You can find documentation for how to view your email statistics via the UI [here](https://app.sendgrid.com/statistics) and via API [here](../../packages/client/USAGE.md#stats).
4 |
5 | Alternatively, we can post events to a URL of your choice via our [Event Webhook](https://docs.sendgrid.com/for-developers/tracking-events/event) about events that occur as Twilio SendGrid processes your email.
6 |
--------------------------------------------------------------------------------
/docs/use-cases/event-webhook.md:
--------------------------------------------------------------------------------
1 | First, follow the guide to [set up signature verification for event webhooks](https://sendgrid.com/docs/for-developers/tracking-events/getting-started-event-webhook-security-features/)
2 |
3 | **Note:** It is important that the body of the request is verified raw (as a `Buffer` or `string`), not after it was parsed as `json`.
4 | If you are using `express.json()` or `bodyParser.json()` you will need to exclude the webhook path from it (one way to do it is using [express-unless](https://www.npmjs.com/package/express-unless)).
5 |
6 | An example of a server to process incoming event webhooks:
7 | ```javascript
8 | const bodyParser = require("body-parser"); // With Express 4.16+ you do not need this and can use express.json() and express.raw() instead
9 | const unless = require('express-unless');
10 | const express = require('express');
11 | const functions = require("firebase-functions");
12 | const app = express();
13 |
14 | const {EventWebhook, EventWebhookHeader} = require('@sendgrid/eventwebhook');
15 |
16 | const verifyRequest = function (publicKey, payload, signature, timestamp) {
17 | const eventWebhook = new EventWebhook();
18 | const ecPublicKey = eventWebhook.convertPublicKeyToECDSA(publicKey);
19 | return eventWebhook.verifySignature(ecPublicKey, payload, signature, timestamp);
20 | }
21 |
22 | // Exclude the webhook path from any json parsing
23 | const json_parser = bodyParser.json();
24 | json_parser.unless = unless;
25 | app.use(json_parser.unless({ path: ["/sendgrid/webhook"]}));
26 |
27 | app.post("/sendgrid/webhook",
28 | // parse req.body as a Buffer
29 | bodyParser.raw({ type: 'application/json' }),
30 | async (req, resp) => {
31 | try {
32 | const key = '';
33 | // Alternatively, you can get your key from your firebase function cloud config
34 | // const key = getConfig().sendgrid.webhook_verification_key;
35 |
36 | const signature = req.get(EventWebhookHeader.SIGNATURE());
37 | const timestamp = req.get(EventWebhookHeader.TIMESTAMP());
38 |
39 | // Be sure to _not_ remove any leading/trailing whitespace characters (e.g., '\r\n').
40 | const requestBody = req.body;
41 | // Alternatively, if using firebase cloud functions, remove the middleware and use:
42 | // const requestBody = (req as functions.https.Request).rawBody;
43 |
44 | if (verifyRequest(key, requestBody, signature, timestamp)) {
45 | resp.sendStatus(204);
46 | } else {
47 | resp.sendStatus(403);
48 | }
49 | } catch (error) {
50 | resp.status(500).send(error);
51 | }
52 | })
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/use-cases/flexible-address-fields.md:
--------------------------------------------------------------------------------
1 | ## Flexible email address fields
2 | The email address fields (`to`, `from`, `cc`, `bcc`, `replyTo`) are flexible and can be any of the following:
3 |
4 | ```js
5 | const msg = {
6 |
7 | //Simple email address string
8 | to: 'someone@example.org',
9 |
10 | //Email address with name
11 | to: 'Some One ',
12 |
13 | //Object with name/email
14 | to: {
15 | name: 'Some One',
16 | email: 'someone@example.org',
17 | },
18 |
19 | //Arrays are supported for to, cc and bcc
20 | to: [
21 | 'someone@example.org',
22 | 'Some One ',
23 | {
24 | name: 'Some One',
25 | email: 'someone@example.org',
26 | },
27 | ],
28 | };
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/use-cases/hide-warnings.md:
--------------------------------------------------------------------------------
1 | # Hide Warnings
2 |
3 | When using dynamic templates, if one of the values in the template data contains a single quote, a double quote or an ampersand, a warning will be logged.
4 |
5 | To hide this warning, set `hideWarnings` to `true` in the message.
6 |
7 | ```js
8 | const sgMail = require('@sendgrid/mail');
9 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
10 | const msg = {
11 | to: 'recipient@example.org',
12 | from: 'sender@example.org',
13 | templateId: 'd-f43daeeaef504760851f727007e0b5d0',
14 | dynamic_template_data: {
15 | subject: 'Testing Templates',
16 | name: 'Some One',
17 | city: 'Denver',
18 | company: 'Recipient & Sender'
19 | },
20 | hideWarnings: true // now the warning won't be logged
21 | };
22 | sgMail.send(msg);
23 | ```
--------------------------------------------------------------------------------
/docs/use-cases/kitchen-sink.md:
--------------------------------------------------------------------------------
1 | # Kitchen Sink - an example with all settings used
2 |
3 | All other options from the [API definition](https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html) are supported (note that some settings can be used in multiple ways, see above for full details for each setting):
4 |
5 | ```js
6 | const sgMail = require('@sendgrid/mail');
7 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
8 | const msg = {
9 | to: 'recipient@example.org',
10 | cc: 'someone@example.org',
11 | bcc: ['me@example.org', 'you@example.org'],
12 | from: 'sender@example.org',
13 | replyTo: 'othersender@example.org',
14 | subject: 'Hello world',
15 | text: 'Hello plain world!',
16 | html: 'Hello HTML world!
',
17 | templateId: 'd-12345678901234567890123456789012',
18 | substitutionWrappers: ['{{', '}}'],
19 | dynamicTemplateData: {
20 | name: 'Some One',
21 | id: '123',
22 | },
23 | attachments: [
24 | {
25 | content: 'Some attachment content',
26 | filename: 'some-attachment.txt',
27 | },
28 | ],
29 | categories: ['Transactional', 'My category'],
30 | sendAt: 1500077141,
31 | headers: {
32 | 'X-CustomHeader': 'Custom header value',
33 | },
34 | sections: {},
35 | customArgs: {
36 | myCustomArg: 'some string', // must be a string
37 | },
38 | batchId: 'sendgrid-batch-id',
39 | asm: {
40 | groupId: 1
41 | },
42 | ipPoolName: 'sendgrid-ip-pool-name',
43 | mailSettings: {
44 | sandboxMode: {
45 | enable: true,
46 | },
47 | },
48 | trackingSettings: {},
49 | };
50 | sgMail
51 | .send(msg)
52 | .then(() => console.log('Mail sent successfully'))
53 | .catch(error => {
54 | console.error(error);
55 |
56 | if (error.response) {
57 | console.error(error.response.body)
58 | }
59 | });
60 | ```
61 |
62 | ### Caveats:
63 |
64 | As per [issue #288](https://github.com/sendgrid/sendgrid-nodejs/issues/288), please note that the `customArgs` field *must* have a string value.
65 |
--------------------------------------------------------------------------------
/docs/use-cases/manual-content.md:
--------------------------------------------------------------------------------
1 | # Manually Providing Content
2 |
3 | Instead of using the `text` and `html` shorthand properties, you can manually use the `content` property:
4 |
5 | ```js
6 | const msg = {
7 | to: 'recipient@example.org',
8 | from: 'sender@example.org',
9 | subject: 'Hello manual content',
10 | content: [
11 | {
12 | type: 'text/html',
13 | value: 'Hello HTML world!
',
14 | },
15 | {
16 | type: 'text/plain',
17 | value: 'Hello plain world!',
18 | },
19 | ],
20 | };
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/use-cases/multiple-emails-multiple-recipients.md:
--------------------------------------------------------------------------------
1 | # Send Multiple Emails to Multiple Recipients
2 |
3 | The `send` method also accepts an array of email msg if you want to send multiple different single emails with for example different content and sender values. This will send multiple requests (in parallel), so be aware of any API rate restrictions:
4 |
5 | ```js
6 | const emails = [
7 | {
8 | to: 'recipient1@example.org',
9 | from: 'sender@example.org',
10 | subject: 'Hello recipient 1',
11 | text: 'Hello plain world!',
12 | html: 'Hello HTML world!
',
13 | },
14 | {
15 | to: 'recipient2@example.org',
16 | from: 'other-sender@example.org',
17 | subject: 'Hello recipient 2',
18 | text: 'Hello other plain world!',
19 | html: 'Hello other HTML world!
',
20 | },
21 | ];
22 | sgMail.send(emails);
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/use-cases/multiple-emails-personalizations-with-substitutions.md:
--------------------------------------------------------------------------------
1 | # Send Multiple Emails with Personalizations and Substitutions
2 |
3 | Personalizations are an array of objects, each representing a separate email, that allow you to customize the metadata of each email sent within a request. With substitutions you can also have the email template dynamically be updated through [substitution tags](https://docs.sendgrid.com/for-developers/sending-email/substitution-tags).
4 |
5 | The below example shows how multiple emails, each with varying metadata and substitutions, are sent with personalizations.
6 |
7 | ```js
8 | const sgMail = require('@sendgrid/mail');
9 |
10 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
11 |
12 | const msg = {
13 | from: 'sender1@example.org',
14 | subject: 'Ahoy!',
15 | text: 'Ahoy {{name}}!',
16 | html: 'Ahoy {{name}}!
',
17 | personalizations: [
18 | {
19 | to: 'recipient1@example.org',
20 | substitutions: {
21 | name: 'Jon'
22 | }
23 | },
24 | {
25 | to: 'recipient2@example.org',
26 | substitutions: {
27 | name: 'Jane'
28 | }
29 | },
30 | {
31 | to: 'recipient3@example.org',
32 | substitutions: {
33 | name: 'Jack'
34 | }
35 | }
36 | ],
37 | };
38 |
39 | sgMail.send(msg);
40 | ```
41 |
42 | The default `substitutionWrappers` are `{{` and `}}` in the node.js library, but you can change it using the `substitutionWrappers` property.
43 |
44 | You can also use the SendGrid helper classes to achieve the same outcome:
45 |
46 | ```js
47 | const sgMail = require('@sendgrid/mail');
48 | const sgHelpers = require('@sendgrid/helpers');
49 | const Mail = sgHelpers.classes.Mail;
50 | const Personalization = sgHelpers.classes.Personalization;
51 |
52 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
53 | const mail = new Mail();
54 | mail.setFrom('sender1@example.org');
55 | mail.setSubject('Ahoy');
56 | mail.addTextContent('Ahoy {{name}}!');
57 | mail.addHtmlContent('Ahoy {{name}}!
');
58 |
59 | const personalization1 = new Personalization();
60 | personalization1.setTo('recipient1@example.org');
61 | personalization1.addSubstitution('name', 'Jon');
62 | mail.addPersonalization(personalization1);
63 |
64 | const personalization2 = new Personalization();
65 | personalization1.setTo('recipient2@example.org');
66 | personalization1.addSubstitution('name', 'Jane');
67 | mail.addPersonalization(personalization2);
68 |
69 | const personalization3 = new Personalization();
70 | personalization1.setTo('recipient3@example.org');
71 | personalization1.addSubstitution('name', 'Jack');
72 | mail.addPersonalization(personalization3);
73 |
74 | sgMail.send(mail);
75 | ```
76 |
77 | Refer to [the SendGrid documentation](https://docs.sendgrid.com/for-developers/sending-email/personalizations) for more details about personalizations.
78 |
--------------------------------------------------------------------------------
/docs/use-cases/multiple-emails-personalizations.md:
--------------------------------------------------------------------------------
1 | # Send Multiple Emails with Personalizations
2 |
3 | Personalizations are an array of objects, each representing a separate email, that allow you to customize the metadata of each email sent within a request. The below example shows how multiple emails, each with varying metadata, are sent with personalizations.
4 |
5 | Refer to [the Sendgrid documentation](https://docs.sendgrid.com/for-developers/sending-email/personalizations) for more details about personalizations.
6 | ```js
7 | const sgMail = require('@sendgrid/mail');
8 | const sgHelpers = require('@sendgrid/helpers');
9 | const Personalization = sgHelpers.classes.Personalization;
10 |
11 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
12 | const msg = {
13 | from: 'sender1@example.org',
14 | subject: 'Hello world',
15 | text: 'Hello plain world!',
16 | html: 'Hello HTML world!
',
17 | personalizations: []
18 | };
19 |
20 | const personalization1 = new Personalization();
21 | personalization1.setTo(['recipient2@example.org', 'recipient3@example.org']);
22 | personalization1.setCc('recipient4@example.org');
23 | msg.personalizations.push(personalization1);
24 |
25 | const personalization2 = new Personalization();
26 | personalization2.setTo(['recipient5@example.org', 'recipient6@example.org', 'recipient7@example.org']);
27 | personalization2.setFrom('sender2@example.org');
28 | personalization2.setCc('recipient8@example.org');
29 | msg.personalizations.push(personalization2);
30 |
31 | const personalization3 = new Personalization();
32 | personalization3.setTo('recipient9@example.org');
33 | personalization3.setFrom('sender3@example.org');
34 | personalization3.setCc('recipient10@example.org');
35 | personalization3.setSubject('Greetings world');
36 | msg.personalizations.push(personalization3);
37 |
38 | sgMail.send(msg);
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/use-cases/multiple-reply-to-email.md:
--------------------------------------------------------------------------------
1 | # Multiple emails in replyTo
2 |
3 | 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)
4 |
5 | ```js
6 | const sgMail = require('@sendgrid/mail');
7 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
8 | const msg = {
9 | to: 'recipient@example.org',
10 | from: 'sender@example.org',
11 | subject: 'Multiple mail in replyTo',
12 | html: 'Here’s an example of multiple replyTo email for you!
',
13 | replyToList: [
14 | {
15 | 'name': 'Test User1',
16 | 'email': 'test_user1@example.org'
17 | },
18 | {
19 | 'email': 'test_user2@example.org'
20 | }
21 | ],
22 | };
23 | ```
24 |
25 |
--------------------------------------------------------------------------------
/docs/use-cases/scheduled-send.md:
--------------------------------------------------------------------------------
1 | # Creating a Scheduled Send
2 |
3 | Use the `sendAt` property to specify when to send the emails (in [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time) seconds, not milliseconds):
4 |
5 | ```js
6 | const msg = {
7 | to: 'recipient@example.org',
8 | from: 'sender@example.org',
9 | subject: 'Hello delayed email',
10 | html: 'Some email content
',
11 | sendAt: 1500077141,
12 | };
13 |
14 | await sgMail.send(msg);
15 | ```
16 |
17 | ## Limitations
18 |
19 | 1. Emails can only be scheduled, at most, 72 hours in advance.
20 | 2. If successful, without a `batchId` set, the call to `sgMail.send()` returns a 202 status code with an empty response body. Currently, cancelling a scheduled email without a `batchId` set [requires a change of password or contacting our support team](https://sendgrid.com/docs/for-developers/sending-email/stopping-an-in-progress-send/#stopping-transactional-email).
21 |
22 | ## [To Cancel or Pause Your Scheduled Send](https://sendgrid.com/docs/for-developers/sending-email/stopping-a-scheduled-send/#canceling-transactional-email):
23 |
24 | 1. Create a [Batch ID](../../packages/client/USAGE.md#create-a-batch-id).
25 | 2. Assign Batch ID to a `msg` object:
26 | ```js
27 | const msg = {
28 | to: 'recipient@example.org',
29 | from: 'sender@example.org',
30 | subject: 'Hello delayed email',
31 | html: 'Some email content
',
32 | sendAt: 1500077141,
33 | batchId: 'YOUR_BATCH_ID'
34 | };
35 |
36 | await sgMail.send(msg);
37 | ```
38 | 3. [Update your Batch ID](../../packages/client/USAGE.md#post-userscheduled_sends) with a `cancel` or `pause` status.
39 |
--------------------------------------------------------------------------------
/docs/use-cases/single-email-multiple-recipients.md:
--------------------------------------------------------------------------------
1 | # Send a Single Email to Multiple Recipients
2 |
3 | The `to` field can contain an array of recipients, which will send a single email with all of the recipients in the `to` field. The recipients will be able to see each other:
4 |
5 | ```js
6 | const sgMail = require('@sendgrid/mail');
7 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
8 | const msg = {
9 | to: ['recipient1@example.org', 'recipient2@example.org'],
10 | from: 'sender@example.org',
11 | subject: 'Hello world',
12 | text: 'Hello plain world!',
13 | html: 'Hello HTML world!
',
14 | };
15 | sgMail.send(msg);
16 | ```
17 |
18 | If you want to send multiple _individual_ emails to multiple recipient where they don't see each other's email addresses, use `sendMultiple` instead:
19 |
20 | ```js
21 | const sgMail = require('@sendgrid/mail');
22 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
23 | const msg = {
24 | to: ['recipient1@example.org', 'recipient2@example.org'],
25 | from: 'sender@example.org',
26 | subject: 'Hello world',
27 | text: 'Hello plain world!',
28 | html: 'Hello HTML world!
',
29 | };
30 | sgMail.sendMultiple(msg);
31 | ```
32 |
33 | Note that `sendMultiple(msg)` is a convenience shortcut for `send(msg, true)`, and alternatively, you can also set the `isMultiple` flag to `true` on your `msg` object.
34 |
--------------------------------------------------------------------------------
/docs/use-cases/single-email-single-recipient.md:
--------------------------------------------------------------------------------
1 | # Send a Single Email to a Single Recipient
2 |
3 | ```js
4 | const sgMail = require('@sendgrid/mail');
5 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
6 | const msg = {
7 | to: 'recipient@example.org',
8 | from: 'sender@example.org',
9 | subject: 'Hello world',
10 | text: 'Hello plain world!',
11 | html: 'Hello HTML world!
',
12 | };
13 | sgMail.send(msg);
14 | ```
15 |
--------------------------------------------------------------------------------
/docs/use-cases/sms.md:
--------------------------------------------------------------------------------
1 | First, follow the [Twilio Setup](twilio-setup.md) guide for creating a Twilio account and setting up environment variables with the proper credentials.
2 |
3 | Then, install the Twilio Helper Library.
4 |
5 | ```bash
6 | npm install twilio
7 | ```
8 |
9 | Finally, send a message.
10 |
11 | ```js
12 | var accountSid = process.env.TWILIO_ACCOUNT_SID;
13 | var authToken = process.env.TWILIO_AUTH_TOKEN;
14 |
15 | var twilio = require('twilio');
16 | var client = new twilio(accountSid, authToken);
17 |
18 | client.messages.create({
19 | body: 'Hello from Node',
20 | to: '+12345678901', // Text this number
21 | from: '+12345678901' // From a valid Twilio number
22 | })
23 | .then((message) => console.log(message.sid));
24 | ```
25 |
26 | For more information, please visit the [Twilio SMS Node.js documentation](https://www.twilio.com/docs/sms/quickstart/node).
27 |
--------------------------------------------------------------------------------
/docs/use-cases/success-failure-errors.md:
--------------------------------------------------------------------------------
1 | # Handling Success/Failure/Errors
2 |
3 | The `send` and `sendMultiple` methods return a `Promise`, so you can handle success and capture errors:
4 |
5 | ```js
6 | sgMail
7 | .send(msg)
8 | .then(() => {
9 | // Celebrate
10 | })
11 | .catch(error => {
12 | // Log friendly error
13 | console.error(error);
14 |
15 | if (error.response) {
16 | // Extract error msg
17 | const {message, code, response} = error;
18 |
19 | // Extract response msg
20 | const {headers, body} = response;
21 |
22 | console.error(body);
23 | }
24 | });
25 | ```
26 |
27 | Alternatively, pass a callback function as the last parameter:
28 |
29 | ```js
30 | sgMail
31 | .send(msg, (error, result) => {
32 | if (error) {
33 | // Do something with the error
34 | }
35 | else {
36 | // Celebrate
37 | }
38 | });
39 | ```
40 |
--------------------------------------------------------------------------------
/docs/use-cases/timeout.md:
--------------------------------------------------------------------------------
1 | # Timeout
2 |
3 | The `setTimeout` method allows you to set a timeout on a request. The request will be aborted after the timeout. Note that the timeout is in milliseconds. Details [here](https://github.com/axios/axios#request-config).
4 |
5 | ```js
6 | const sgMail = require('@sendgrid/mail');
7 | sgMail.setTimeout(3000);
8 | ```
9 |
--------------------------------------------------------------------------------
/docs/use-cases/transactional-legacy-templates.md:
--------------------------------------------------------------------------------
1 | # (LEGACY) Transactional Templates
2 |
3 | For this example, we assume you have created a [legacy transactional template](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html) in the UI or via the API. Following is the template content we used for testing.
4 |
5 | Template ID (replace with your own):
6 |
7 | ```text
8 | 13b8f94f-bcae-4ec6-b752-70d6cb59f932
9 | ```
10 |
11 | Email Subject:
12 |
13 | ```text
14 | <%subject%>
15 | ```
16 |
17 | Template Body:
18 |
19 | ```html
20 |
21 |
22 |
23 |
24 |
25 | Hello {{name}},
26 |
27 | I'm glad you are trying out the template feature!
28 |
29 | <%body%>
30 |
31 | I hope you are having a great day in {{city}} :)
32 |
33 |
34 |
35 | ```
36 |
37 | ```js
38 | const sgMail = require('@sendgrid/mail');
39 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
40 | sgMail.setSubstitutionWrappers('{{', '}}'); // Configure the substitution tag wrappers globally
41 | const msg = {
42 | to: 'recipient@example.org',
43 | from: 'sender@example.org',
44 | subject: 'Hello world',
45 | text: 'Hello plain world!',
46 | html: 'Hello HTML world!
',
47 | templateId: '13b8f94f-bcae-4ec6-b752-70d6cb59f932',
48 | substitutions: {
49 | name: 'Some One',
50 | city: 'Denver',
51 | },
52 | };
53 | sgMail.send(msg);
54 | ```
55 |
56 | Alternatively, you may specify the substitution wrappers via the msg object as well. This will override any wrappers you may have configured globally.
57 |
58 | ```js
59 | const msg = {
60 | ...
61 | substitutionWrappers: ['{{', '}}'],
62 | ...
63 | };
64 | ```
65 |
--------------------------------------------------------------------------------
/docs/use-cases/transactional-templates.md:
--------------------------------------------------------------------------------
1 | # Transactional Templates
2 |
3 | For this example, we assume you have created a [dynamic transactional template](https://sendgrid.com/docs/ui/sending-email/how-to-send-an-email-with-dynamic-transactional-templates/) in the UI or via the API. Following is the template content we used for testing.
4 |
5 | Email Subject:
6 |
7 | ```text
8 | {{ subject }}
9 | ```
10 |
11 | Template Body:
12 |
13 | ```html
14 |
15 |
16 |
17 |
18 |
19 | Hello {{ name }},
20 |
21 | I'm glad you are trying out the template feature!
22 |
23 | I hope you are having a great day in {{ city }} :)
24 |
25 |
26 |
27 | ```
28 |
29 | ```js
30 | const sgMail = require('@sendgrid/mail');
31 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
32 | const msg = {
33 | to: 'recipient@example.org',
34 | from: 'sender@example.org',
35 | templateId: 'd-f43daeeaef504760851f727007e0b5d0',
36 | dynamicTemplateData: {
37 | subject: 'Testing Templates',
38 | name: 'Some One',
39 | city: 'Denver',
40 | },
41 | };
42 | sgMail.send(msg);
43 | ```
44 |
45 | There's no need to specify the substitution wrappers as it will assume that you're using [Handlebars](https://handlebarsjs.com/) templating by default.
46 |
47 | ## Prevent Escaping Characters
48 |
49 | Per Handlebars' documentation: If you don't want Handlebars to escape a value, use the "triple-stash", {{{
50 |
51 | > If you include the characters ', " or & in a subject line replacement be sure to use three brackets.
52 |
53 | Email Subject:
54 |
55 | ```text
56 | {{{ subject }}}
57 | ```
58 |
59 | Template Body:
60 |
61 | ```html
62 |
63 |
64 |
65 |
66 |
67 | Hello {{{ name }}},
68 |
69 | I'm glad you are trying out the template feature!
70 |
71 | <%body%>
72 |
73 | I hope you are having a great day in {{{ city }}} :)
74 |
75 |
76 |
77 | ```
78 |
79 | ```js
80 | const sgMail = require('@sendgrid/mail');
81 | sgMail.setApiKey(process.env.SENDGRID_API_KEY);
82 | const msg = {
83 | to: 'recipient@example.org',
84 | from: 'sender@example.org',
85 | subject: 'Hello world',
86 | text: 'Hello plain world!',
87 | html: 'Hello HTML world!
',
88 | templateId: 'd-f43daeeaef504760851f727007e0b5d0',
89 | dynamic_template_data: {
90 | subject: 'Testing Templates & Stuff',
91 | name: 'Some "Testing" One',
92 | city: 'Denver',
93 | },
94 | };
95 | sgMail.send(msg);
96 | ```
97 |
--------------------------------------------------------------------------------
/docs/use-cases/twilio-email.md:
--------------------------------------------------------------------------------
1 | First, follow the [Twilio Setup](twilio-setup.md) guide for creating a Twilio account and setting up environment variables with the proper credentials.
2 |
3 | Then, initialize the Twilio Email Client.
4 |
5 | ```js
6 | const client = require('@sendgrid/client');
7 |
8 | client.setTwilioEmailAuth(process.env.TWILIO_API_KEY, process.env.TWILIO_API_SECRET);
9 |
10 | // or
11 |
12 | client.setTwilioEmailAuth(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
13 | ```
14 |
15 | Or similarly using the mail helper.
16 |
17 | ```js
18 | const mail = require('@sendgrid/mail');
19 |
20 | mail.setTwilioEmailAuth(process.env.TWILIO_API_KEY, process.env.TWILIO_API_SECRET);
21 |
22 | // or
23 |
24 | mail.setTwilioEmailAuth(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
25 | ```
26 |
27 | This sets the client to use Twilio Auth and the Twilio Email API.
28 |
--------------------------------------------------------------------------------
/docs/use-cases/twilio-setup.md:
--------------------------------------------------------------------------------
1 | ## 1. Obtain a Free Twilio Account
2 |
3 | Sign up for a free Twilio account [here](https://www.twilio.com/try-twilio?source=sendgrid-nodejs).
4 |
5 | ## 2. Set Up Your Environment Variables
6 |
7 | The Twilio API allows for authentication using with either an API key/secret or your Account SID/Auth Token. You can create an API key [here](https://twil.io/get-api-key) or obtain your Account SID and Auth Token [here](https://twil.io/console).
8 |
9 | Once you have those, follow the steps below based on your operating system.
10 |
11 | ### Linux/Mac
12 |
13 | ```bash
14 | echo "export TWILIO_API_KEY='YOUR_TWILIO_API_KEY'" > twilio.env
15 | echo "export TWILIO_API_SECRET='YOUR_TWILIO_API_SECRET'" >> twilio.env
16 |
17 | # or
18 |
19 | echo "export TWILIO_ACCOUNT_SID='YOUR_TWILIO_ACCOUNT_SID'" > twilio.env
20 | echo "export TWILIO_AUTH_TOKEN='YOUR_TWILIO_AUTH_TOKEN'" >> twilio.env
21 | ```
22 |
23 | Then:
24 |
25 | ```bash
26 | echo "twilio.env" >> .gitignore
27 | source ./twilio.env
28 | ```
29 |
30 | ### Windows
31 |
32 | Temporarily set the environment variable (accessible only during the current CLI session):
33 |
34 | ```bash
35 | set TWILIO_API_KEY=YOUR_TWILIO_API_KEY
36 | set TWILIO_API_SECRET=YOUR_TWILIO_API_SECRET
37 |
38 | : or
39 |
40 | set TWILIO_ACCOUNT_SID=YOUR_TWILIO_ACCOUNT_SID
41 | set TWILIO_AUTH_TOKEN=YOUR_TWILIO_AUTH_TOKEN
42 | ```
43 |
44 | Or permanently set the environment variable (accessible in all subsequent CLI sessions):
45 |
46 | ```bash
47 | setx TWILIO_API_KEY "YOUR_TWILIO_API_KEY"
48 | setx TWILIO_API_SECRET "YOUR_TWILIO_API_SECRET"
49 |
50 | : or
51 |
52 | setx TWILIO_ACCOUNT_SID "YOUR_TWILIO_ACCOUNT_SID"
53 | setx TWILIO_AUTH_TOKEN "YOUR_TWILIO_AUTH_TOKEN"
54 | ```
55 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "npmClient": "yarn",
3 | "packages": [
4 | "packages/*"
5 | ],
6 | "version": "8.1.5",
7 | "tagVersionPrefix": "",
8 | "lerna": "2.0.0"
9 | }
10 |
--------------------------------------------------------------------------------
/license.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const fs = require('fs');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('LICENSE', () => {
12 | it('should contain the current year as the end year of the license', () => {
13 | const license = fs.readFileSync(`${process.cwd()}/LICENSE`, 'utf8');
14 | const currentYear = (new Date()).getFullYear();
15 | return expect(license.indexOf(`${currentYear}`)).to.not.equal(-1);
16 | });
17 | });
18 |
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sendgrid",
3 | "version": "6.0.0",
4 | "private": false,
5 | "license": "SEE LICENSE",
6 | "repository": {
7 | "type": "git",
8 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
9 | },
10 | "devDependencies": {
11 | "@babel/cli": "^7.7.7",
12 | "@babel/core": "^7.7.7",
13 | "@babel/node": "^7.7.7",
14 | "chai": "4.2.0",
15 | "chai-as-promised": "^7.1.1",
16 | "dirty-chai": "^2.0.1",
17 | "eslint": "^6.8.0",
18 | "istanbul": "^1.0.0-alpha.2",
19 | "lerna": "^3.19.0",
20 | "mocha": "^6.2.2",
21 | "mocha-clean": "^1.0.0",
22 | "mocha-sinon": "^2.0.0",
23 | "moment": "^2.19.3",
24 | "sinon": "^2.3.2",
25 | "sinon-chai": "^2.10.0",
26 | "typescript": "^4.0.0"
27 | },
28 | "scripts": {
29 | "lint": "if [ `node --version | cut -d'.' -f1 | cut -c 2` -ge \"8\" ]; then eslint . --fix; else echo \"eslint is not available for node < 8.0\"; fi",
30 | "test:all": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"**/*.spec.js\"",
31 | "test:helpers": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"packages/helpers/**/*.spec.js\"",
32 | "test:client": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"packages/client/**/*.spec.js\"",
33 | "test:mail": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"packages/mail/**/*.spec.js\"",
34 | "test:eventwebhook": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"packages/eventwebhook/**/*.spec.js\"",
35 | "test:inbound": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"packages/inbound-mail-parser/**/*.spec.js\"",
36 | "test:contact": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"packages/contact-importer/**/*.spec.js\"",
37 | "test:files": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha \"test/files.spec.js\"",
38 | "test:license": "babel-node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha license.spec.js",
39 | "test:typescript": "tsc",
40 | "test": "npm run test:all -s",
41 | "coverage": "open -a \"Google Chrome\" ./coverage/lcov-report/index.html"
42 | },
43 | "description": "",
44 | "bugs": {
45 | "url": "https://github.com/sendgrid/sendgrid-nodejs/issues"
46 | },
47 | "homepage": "https://github.com/sendgrid/sendgrid-nodejs#readme",
48 | "main": "index.js",
49 | "directories": {
50 | "test": "test"
51 | },
52 | "author": "Twilio SendGrid"
53 | }
54 |
--------------------------------------------------------------------------------
/packages/client/USE_CASES.md:
--------------------------------------------------------------------------------
1 | This document provides examples for specific Twilio SendGrid v3 API non-mail/send use cases. Please [open an issue](https://github.com/sendgrid/sendgrid-nodejs/issues) or make a pull request for any use cases you would like us to document here. Thank you!
2 |
3 | # Table of Contents
4 |
5 | - [How to Setup a Domain Authentication](#domain-authentication)
6 | - [How to View Email Statistics](#how-to-view-email-statistics)
7 | - [How to use the Email Activity Feed](#how-to-use-the-email-activity-feed)
8 |
9 |
10 |
11 | # How to Setup a Domain Authentication
12 |
13 | You can find documentation for how to setup a domain authentication via the UI [here](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) and via API [here](USAGE.md#sender-authentication).
14 |
15 | Find more information about all of Twilio SendGrid's authentication related documentation [here](https://sendgrid.com/docs/ui/account-and-settings/).
16 |
17 |
18 | # How to View Email Statistics
19 |
20 | You can find documentation for how to view your email statistics via the UI [here](https://app.sendgrid.com/statistics) and via API [here](USAGE.md#stats).
21 |
22 | Alternatively, we can post events to a URL of your choice via our [Event Webhook](https://sendgrid.com/docs/API_Reference/Webhooks/event.html) about events that occur as Twilio SendGrid processes your email.
23 |
24 |
25 | # How to use the Email Activity Feed
26 |
27 | You can find documentation for how to use the Email Activity Feed via the UI [here](https://sendgrid.com/docs/ui/analytics-and-reporting/email-activity-feed/) and via API [here](USAGE.md#messages).
28 |
29 | Find more information about getting started with the Email Activity Feed API [here](https://sendgrid.com/docs/API_Reference/Web_API_v3/Tutorials/getting_started_email_activity_api.html).
30 |
--------------------------------------------------------------------------------
/packages/client/index.d.ts:
--------------------------------------------------------------------------------
1 | import Client = require("@sendgrid/client/src/client");
2 |
3 | export = Client;
--------------------------------------------------------------------------------
/packages/client/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const client = require('./src/client');
4 | const Client = require('./src/classes/client');
5 |
6 | module.exports = client;
7 | module.exports.Client = Client;
8 |
--------------------------------------------------------------------------------
/packages/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/client",
3 | "description": "Twilio SendGrid NodeJS API client",
4 | "version": "8.1.5",
5 | "author": "Twilio SendGrid (sendgrid.com)",
6 | "contributors": [
7 | "Kyle Partridge ",
8 | "David Tomberlin ",
9 | "Swift ",
10 | "Brandon West ",
11 | "Scott Motte ",
12 | "Robert Acosta ",
13 | "Elmer Thomas ",
14 | "Adam Reis "
15 | ],
16 | "license": "MIT",
17 | "homepage": "https://sendgrid.com",
18 | "repository": {
19 | "type": "git",
20 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
21 | },
22 | "publishConfig": {
23 | "access": "public"
24 | },
25 | "main": "index.js",
26 | "engines": {
27 | "node": ">=12.*"
28 | },
29 | "dependencies": {
30 | "@sendgrid/helpers": "^8.0.0",
31 | "axios": "^1.8.2"
32 | },
33 | "devDependencies": {
34 | "chai": "4.2.0",
35 | "nock": "^10.0.6"
36 | },
37 | "resolutions": {
38 | "chai": "4.2.0"
39 | },
40 | "tags": [
41 | "http",
42 | "rest",
43 | "api",
44 | "mail",
45 | "sendgrid"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/packages/client/src/client.d.ts:
--------------------------------------------------------------------------------
1 | import {ResponseError} from "@sendgrid/helpers/classes";
2 | import {ClientRequest} from "@sendgrid/client/src/request";
3 | import {ClientResponse} from "@sendgrid/client/src/response";
4 |
5 | declare class Client {
6 | constructor();
7 |
8 | /**
9 | * Set the SendGrid API key.
10 | */
11 | setApiKey(apiKey: string): void;
12 |
13 | /**
14 | * Set the Twilio Email credentials.
15 | */
16 | setTwilioEmailAuth(username: string, password: string): void;
17 |
18 | /**
19 | * Set client requests to impersonate a subuser
20 | */
21 | setImpersonateSubuser(subuser: string): void;
22 |
23 | /**
24 | * Set default header
25 | */
26 | setDefaultHeader(key: string | { [s: string]: string }, value ?: string): this;
27 |
28 | /**
29 | * Set default request
30 | */
31 | setDefaultRequest(key: K | ClientRequest, value ?: ClientRequest[K]): this;
32 |
33 | /**
34 | * Sets the data residency as per region provided
35 | */
36 | setDataResidency(region: string): this;
37 |
38 | /**
39 | * Create headers for request
40 | */
41 | createHeaders(data: { [key: string]: string }): { [key: string]: string };
42 |
43 | /**
44 | * Create request
45 | */
46 | createRequest(data: ClientRequest): ClientRequest;
47 |
48 | /**
49 | * Do a request
50 | */
51 | request(data: ClientRequest, cb?: (err: ResponseError, response: [ClientResponse, any]) => void): Promise<[ClientResponse, any]>;
52 | }
53 |
54 | declare const client: Client;
55 | // @ts-ignore
56 | export = client
57 |
58 | export {Client};
59 |
--------------------------------------------------------------------------------
/packages/client/src/client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Client = require('./classes/client');
7 |
8 | //Export singleton instance
9 | module.exports = new Client();
10 |
--------------------------------------------------------------------------------
/packages/client/src/request.d.ts:
--------------------------------------------------------------------------------
1 | import RequestOptions from "@sendgrid/helpers/classes/request";
2 |
3 | export type ClientRequest = RequestOptions;
4 |
--------------------------------------------------------------------------------
/packages/client/src/response.d.ts:
--------------------------------------------------------------------------------
1 | import Response from "@sendgrid/helpers/classes/response";
2 |
3 | export type ClientResponse = Response;
4 |
--------------------------------------------------------------------------------
/packages/contact-importer/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/sendgrid/sendgrid-nodejs/actions/workflows/test-and-deploy.yml)
2 | [](https://www.npmjs.com/org/sendgrid)
3 |
4 | **This package is part of a monorepo, please see [this README](../../README.md) for details.**
5 |
6 |
7 | # How to Contribute
8 |
9 | We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING](../../CONTRIBUTING.md) guide for details.
10 |
11 | * [Feature Request](../../CONTRIBUTING.md#feature-request)
12 | * [Bug Reports](../../CONTRIBUTING.md#submit-a-bug-report)
13 | * [Improvements to the Codebase](../../CONTRIBUTING.md#improvements-to-the-codebase)
14 |
15 |
16 | # Troubleshooting
17 |
18 | Please see our [troubleshooting guide](https://github.com/sendgrid/sendgrid-nodejs/blob/main/TROUBLESHOOTING.md) for common library issues.
19 |
20 |
21 | # About
22 |
23 | @sendgrid/contact-importer is maintained and funded by Twilio SendGrid, Inc. The names and logos for @sendgrid/contact-importer are trademarks of Twilio SendGrid, Inc.
24 |
25 | If you need help installing or using the library, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com).
26 |
27 | If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo!
28 |
29 | 
30 |
--------------------------------------------------------------------------------
/packages/contact-importer/index.d.ts:
--------------------------------------------------------------------------------
1 | import Importer = require("@sendgrid/contact-importer/src/importer");
2 |
3 | export = Importer;
--------------------------------------------------------------------------------
/packages/contact-importer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/contact-importer",
3 | "description": "Twilio SendGrid NodeJS contact importer",
4 | "version": "8.1.5",
5 | "author": "Twilio SendGrid (sendgrid.com)",
6 | "contributors": [
7 | "Kyle Partridge ",
8 | "David Tomberlin ",
9 | "Swift ",
10 | "Brandon West ",
11 | "Scott Motte ",
12 | "Robert Acosta ",
13 | "Elmer Thomas "
14 | ],
15 | "license": "MIT",
16 | "homepage": "https://sendgrid.com",
17 | "repository": {
18 | "type": "git",
19 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
20 | },
21 | "main": "src/importer.js",
22 | "engines": {
23 | "node": ">=12.*"
24 | },
25 | "publishConfig": {
26 | "access": "public"
27 | },
28 | "dependencies": {
29 | "@sendgrid/client": "^8.1.5",
30 | "async.ensureasync": "^0.5.2",
31 | "async.queue": "^0.5.2",
32 | "bottleneck": "^1.12.0",
33 | "debug": "^4.0.1",
34 | "lodash.chunk": "^4.2.0"
35 | },
36 | "tags": [
37 | "sendgrid"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/packages/contact-importer/src/importer.d.ts:
--------------------------------------------------------------------------------
1 | declare interface ContactImporterOptions {
2 | batchSize?: number;
3 | rateLimitLimit?: number;
4 | rateLimitPeriod?: number;
5 | }
6 |
7 | declare interface Contact {
8 | email: string;
9 | first_name?: string;
10 | last_name?: string;
11 | age?: number;
12 | }
13 |
14 | declare class ContactImporter {
15 | constructor(sg, options?: ContactImporterOptions);
16 |
17 | push(data: Contact|Contact[])
18 |
19 | on(event: "success", cb: (result: any, batch: Contact[]) => void): void;
20 | on(event: "error", cb: (err: Error, batch?: Contact[]) => void): void;
21 | on(event: "drain", cb: () => void): void;
22 | on(event: string, cb: (data: any) => void): void;
23 |
24 | once(event: "success", cb: (result: any, batch: Contact[]) => void): void;
25 | once(event: "error", cb: (err: Error, batch?: Contact[]) => void): void;
26 | once(event: "drain", cb: () => void): void;
27 | once(event: string, cb: (data: any) => void): void;
28 | }
29 |
30 | export = ContactImporter;
--------------------------------------------------------------------------------
/packages/contact-importer/src/importer.spec.js:
--------------------------------------------------------------------------------
1 | const sendgrid = require('@sendgrid/client');
2 | const ContactImporter = require('./importer');
3 |
4 | describe('test_contact_importer', function() {
5 | beforeEach(function() {
6 | // Create a new Twilio SendGrid instance.
7 | const API_KEY = process.env.API_KEY;
8 | sendgrid.setApiKey(API_KEY);
9 |
10 | // Create a new importer with a batch size of 2.
11 | this.contactImporter = new ContactImporter(sendgrid, {
12 | batchSize: 2,
13 | });
14 | // this.spy = sinon.spy(ContactImporter.prototype, '_sendBatch')
15 | this.sinon.spy(ContactImporter.prototype, '_sendBatch');
16 |
17 | // Generate some test data.
18 | const data = [];
19 | for (i = 0; i < 5; i++) {
20 | const item = {
21 | email: 'example' + i + '@example.com',
22 | first_name: 'Test',
23 | last_name: 'User',
24 | };
25 | // Lets make the first user produce an error.
26 | if (i === 1) {
27 | item.invalid_field = 'some value';
28 | }
29 | data.push(item);
30 | }
31 | this.contactImporter.push(data);
32 | });
33 |
34 | it('test_contact_importer sends items in batches', function(done) {
35 | const self = this;
36 | this.timeout(30000);
37 | this.contactImporter.on('success', function(result, batch) {
38 | console.log('SUCCESS result', result);
39 | console.log('SUCCESS batch', batch);
40 | });
41 | this.contactImporter.on('error', function(error, batch) {
42 | console.log('ERROR error', error);
43 | console.log('ERROR batch', batch);
44 | });
45 | this.contactImporter.on('drain', function() {
46 | expect(self.contactImporter._sendBatch).to.have.callCount(3);
47 | done();
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/packages/eventwebhook/index.d.ts:
--------------------------------------------------------------------------------
1 | import EventWebhook = require('./src/eventwebhook');
2 |
3 | export = EventWebhook;
4 |
--------------------------------------------------------------------------------
/packages/eventwebhook/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EventWebhook = require('./src/eventwebhook');
4 |
5 | module.exports = EventWebhook;
6 |
--------------------------------------------------------------------------------
/packages/eventwebhook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/eventwebhook",
3 | "description": "Twilio SendGrid NodeJS Event Webhook",
4 | "version": "8.0.0",
5 | "author": "Twilio SendGrid (sendgrid.com)",
6 | "contributors": [
7 | "Elise Shanholtz "
8 | ],
9 | "license": "MIT",
10 | "homepage": "https://sendgrid.com",
11 | "repository": {
12 | "type": "git",
13 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
14 | },
15 | "publishConfig": {
16 | "access": "public"
17 | },
18 | "main": "src/eventwebhook.js",
19 | "engines": {
20 | "node": ">=12.*"
21 | },
22 | "dependencies": {
23 | "starkbank-ecdsa": "^1.1.1"
24 | },
25 | "tags": [
26 | "sendgrid"
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/packages/eventwebhook/src/eventwebhook.d.ts:
--------------------------------------------------------------------------------
1 | // From starkbank-ecdsa, minimal interface of PublicKey
2 | declare class PublicKey {
3 | toString(encode?: boolean): string;
4 | static fromPem(pem: string): PublicKey;
5 | static fromDer(der: string): PublicKey;
6 | static fromString(key: string): PublicKey;
7 | }
8 |
9 | declare class EventWebhook {
10 | /**
11 | *
12 | * @param {string} publicKey verification key under Mail Settings
13 | * @return {PublicKey} A public key using the ECDSA algorithm
14 | */
15 | convertPublicKeyToECDSA(publicKey: string): PublicKey;
16 |
17 | /**
18 | *
19 | * @param {PublicKey} publicKey elliptic curve public key
20 | * @param {string|Buffer} payload event payload in the request body
21 | * @param {string} signature value obtained from the 'X-Twilio-Email-Event-Webhook-Signature' header
22 | * @param {string} timestamp value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header
23 | * @return {Boolean} true or false if signature is valid
24 | */
25 | verifySignature(publicKey: PublicKey, payload: string|Buffer, signature: string, timestamp: string): boolean;
26 | }
27 |
28 | /*
29 | * This class lists headers that get posted to the webhook. Read the docs for
30 | * more details: https://sendgrid.com/docs/for-developers/tracking-events/event
31 | */
32 | declare class EventWebhookHeader {
33 | static SIGNATURE(): string;
34 | static TIMESTAMP(): string
35 | }
36 |
37 | export {
38 | EventWebhook,
39 | EventWebhookHeader,
40 | };
41 |
--------------------------------------------------------------------------------
/packages/eventwebhook/src/eventwebhook.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const ecdsa = require('starkbank-ecdsa');
4 | const Ecdsa = ecdsa.Ecdsa;
5 | const Signature = ecdsa.Signature;
6 | const PublicKey = ecdsa.PublicKey;
7 |
8 | /*
9 | * This class allows you to use the Event Webhook feature. Read the docs for
10 | * more details: https://sendgrid.com/docs/for-developers/tracking-events/event
11 | */
12 | class EventWebhook {
13 | /**
14 | * Convert the public key string to a ECPublicKey.
15 | *
16 | * @param {string} publicKey verification key under Mail Settings
17 | * @return {PublicKey} A public key using the ECDSA algorithm
18 | */
19 | convertPublicKeyToECDSA(publicKey) {
20 | return PublicKey.fromPem(publicKey);
21 | }
22 |
23 | /**
24 | * Verify signed event webhook requests.
25 | *
26 | * @param {PublicKey} publicKey elliptic curve public key
27 | * @param {string|Buffer} payload event payload in the request body
28 | * @param {string} signature value obtained from the 'X-Twilio-Email-Event-Webhook-Signature' header
29 | * @param {string} timestamp value obtained from the 'X-Twilio-Email-Event-Webhook-Timestamp' header
30 | * @return {Boolean} true or false if signature is valid
31 | */
32 | verifySignature(publicKey, payload, signature, timestamp) {
33 | let timestampPayload = Buffer.isBuffer(payload) ? payload.toString() : payload;
34 | timestampPayload = timestamp + timestampPayload;
35 | const decodedSignature = Signature.fromBase64(signature);
36 |
37 | return Ecdsa.verify(timestampPayload, decodedSignature, publicKey);
38 | }
39 | }
40 |
41 | /*
42 | * This class lists headers that get posted to the webhook. Read the docs for
43 | * more details: https://sendgrid.com/docs/for-developers/tracking-events/event
44 | */
45 | class EventWebhookHeader {
46 | static SIGNATURE() {
47 | return 'X-Twilio-Email-Event-Webhook-Signature';
48 | }
49 |
50 | static TIMESTAMP() {
51 | return 'X-Twilio-Email-Event-Webhook-Timestamp';
52 | }
53 | }
54 |
55 | module.exports = {
56 | EventWebhook,
57 | EventWebhookHeader,
58 | };
59 |
--------------------------------------------------------------------------------
/packages/helpers/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.com/sendgrid/sendgrid-nodejs)
2 | [](https://www.npmjs.com/org/sendgrid)
3 |
4 | **This package is part of a monorepo, please see [this README](../../README.md) for details.**
5 |
6 | # Support classes and helpers for the SendGrid NodeJS libraries
7 | This is a collection of classes and helpers used internally by the
8 | [SendGrid NodeJS libraries](https://www.npmjs.com/org/sendgrid).
9 |
10 | 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).
11 |
12 | ## Mail class
13 | 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/). 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.
14 |
15 | ## Attachment class
16 | Used by the inbound mail parser to compose `Attachment` objects.
17 |
18 | ## Personalization class
19 | Used by the Mail class to compose `Personalization` objects.
20 |
21 | ## Email address
22 | `Helper` class to represent an email address with name/email. Used by both the `Mail` and `Personalization` classes to deal with email addresses of various formats.
23 |
24 | ## Helpers
25 | Internal helpers that mostly speak for themselves.
26 |
27 |
28 | # How to Contribute
29 |
30 | We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING](https://github.com/sendgrid/sendgrid-nodejs/blob/HEAD/CONTRIBUTING.md) guide for details.
31 |
32 | * [Feature Request](../../CONTRIBUTING.md#feature-request)
33 | * [Bug Reports](../../CONTRIBUTING.md#submit-a-bug-report)
34 | * [Improvements to the Codebase](../../CONTRIBUTING.md#improvements-to-the-codebase)
35 |
36 |
37 | # About
38 |
39 | @sendgrid/helpers are maintained and funded by Twilio SendGrid, Inc. The names and logos for @sendgrid/helpers are trademarks of Twilio SendGrid, Inc.
40 |
41 | If you need help installing or using the library, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com).
42 |
43 | If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo!
44 |
45 | 
46 |
--------------------------------------------------------------------------------
/packages/helpers/attachment.txt:
--------------------------------------------------------------------------------
1 | Just a little file for testing attachments.
--------------------------------------------------------------------------------
/packages/helpers/classes/attachment.d.ts:
--------------------------------------------------------------------------------
1 | export interface AttachmentData {
2 | content: string;
3 | filename: string;
4 | type?: string;
5 | disposition?: string;
6 | contentId?: string;
7 | }
8 |
9 | export interface AttachmentJSON {
10 | content: string;
11 | filename: string;
12 | type?: string;
13 | disposition?: string;
14 | content_id?: string;
15 | }
16 |
17 | export default class Attachment implements AttachmentData {
18 | content: string;
19 | filename: string;
20 | type?: string;
21 | disposition?: string;
22 | contentId?: string;
23 |
24 | constructor(data?: AttachmentData);
25 |
26 | fromData(data: AttachmentData): void;
27 | setContent(content: string): void;
28 | setFilename(filename: string): void;
29 | setType(type: string): void;
30 | setDisposition(disposition: string): void;
31 | setContentId(contentId: string): void;
32 | toJSON(): AttachmentJSON;
33 | }
34 |
--------------------------------------------------------------------------------
/packages/helpers/classes/attachment.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const path = require('path');
5 |
6 | /**
7 | * Dependencies
8 | */
9 | const Attachment = require('./attachment');
10 |
11 | /**
12 | * Tests
13 | */
14 | describe('Attachment', function() {
15 | let attachment;
16 | beforeEach(function() {
17 | attachment = new Attachment();
18 | });
19 |
20 | //Set content as string
21 | describe('setContent(), string', function() {
22 | it('should set string as content', function() {
23 | attachment.setContent('Just a string.');
24 | expect(attachment.content).to.equal('Just a string.');
25 | });
26 | });
27 |
28 | //Set content as stream
29 | describe('setContent(), stream', function() {
30 | it('should convert stream to string and set as content', function() {
31 | const fileData = fs.readFileSync('./packages/helpers/attachment.txt');
32 | attachment.setContent(fileData);
33 | expect(attachment.content).to.equal('Just a little file for testing attachments.');
34 | });
35 | });
36 |
37 | //Set content as wrong type
38 | describe('setContent(), wrong type', function() {
39 | it('should not allow setting content of wrong type', function() {
40 | expect(() => attachment.setContent(null)).to.throw('`content` expected to be either Buffer or string');
41 | });
42 | });
43 |
44 | //Constructor
45 | describe('constructor(data)', function() {
46 | it('should not accept both content and filePath', function() {
47 | expect(function() {
48 | attachment = new Attachment({
49 | filename: 'attachment.txt',
50 | type: 'plain/text',
51 | disposition: 'attachment',
52 | contentId: 'myattachment',
53 | content: '',
54 | filePath: '',
55 | });
56 | }).to.throw(Error);
57 | });
58 | });
59 | });
60 |
61 | //Set content
62 | describe('setContent()', function() {
63 | let attachment;
64 |
65 | beforeEach(function() {
66 | attachment = new Attachment({
67 | filename: 'attachment.txt',
68 | type: 'plain/text',
69 | disposition: 'attachment',
70 | contentId: 'myattachment',
71 | content: 'SGVsbG8gV29ybGQK',
72 | });
73 | });
74 |
75 | it('should set the given value', function() {
76 | expect(attachment.content).to.equal('SGVsbG8gV29ybGQK');
77 | });
78 |
79 | it('should accept a buffer', function() {
80 | attachment.setContent(new Buffer('Hello World\n'));
81 | expect(attachment.content).to.equal('SGVsbG8gV29ybGQK');
82 | });
83 |
84 | it('should accept a raw file', function() {
85 | attachment = new Attachment({
86 | filename: 'attachment.txt',
87 | type: 'plain/text',
88 | disposition: 'attachment',
89 | contentId: 'myattachment',
90 | filePath: path.join(__dirname, '/attachment.js'),
91 | });
92 |
93 | expect(attachment.content).to.be.a('string');
94 | });
95 | });
96 |
--------------------------------------------------------------------------------
/packages/helpers/classes/email-address.d.ts:
--------------------------------------------------------------------------------
1 | export type EmailData = string|{ name?: string; email: string; }
2 |
3 | export type EmailJSON = { name?: string; email: string }
4 |
5 | export default class EmailAddress {
6 | constructor(data?: EmailData);
7 |
8 | /**
9 | * From data
10 | */
11 | fromData(data: EmailData): void;
12 |
13 | /**
14 | * Set name
15 | */
16 | setName(name: string): void;
17 |
18 | /**
19 | * Set email (mandatory)
20 | */
21 | setEmail(email: string): void;
22 |
23 | toJSON(): EmailJSON;
24 | }
--------------------------------------------------------------------------------
/packages/helpers/classes/email-address.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const splitNameEmail = require('../helpers/split-name-email');
7 |
8 | /**
9 | * Email address class
10 | */
11 | class EmailAddress {
12 |
13 | /**
14 | * Constructor
15 | */
16 | constructor(data) {
17 |
18 | //Construct from data
19 | if (data) {
20 | this.fromData(data);
21 | }
22 | }
23 |
24 | /**
25 | * From data
26 | */
27 | fromData(data) {
28 |
29 | //String given
30 | if (typeof data === 'string') {
31 | const [name, email] = splitNameEmail(data);
32 | data = {name, email};
33 | }
34 |
35 | //Expecting object
36 | if (typeof data !== 'object') {
37 | throw new Error('Expecting object or string for EmailAddress data');
38 | }
39 |
40 | //Extract name and email
41 | const {name, email} = data;
42 |
43 | //Set
44 | this.setEmail(email);
45 | this.setName(name);
46 | }
47 |
48 | /**
49 | * Set name
50 | */
51 | setName(name) {
52 | if (typeof name === 'undefined') {
53 | return;
54 | }
55 | if (typeof name !== 'string') {
56 | throw new Error('String expected for `name`');
57 | }
58 | this.name = name;
59 | }
60 |
61 | /**
62 | * Set email (mandatory)
63 | */
64 | setEmail(email) {
65 | if (typeof email === 'undefined') {
66 | throw new Error('Must provide `email`');
67 | }
68 | if (typeof email !== 'string') {
69 | throw new Error('String expected for `email`');
70 | }
71 | this.email = email;
72 | }
73 |
74 | /**
75 | * To JSON
76 | */
77 | toJSON() {
78 |
79 | //Get properties
80 | const {email, name} = this;
81 |
82 | //Initialize with mandatory properties
83 | const json = {email};
84 |
85 | //Add name if present
86 | if (name !== '') {
87 | json.name = name;
88 | }
89 |
90 | //Return
91 | return json;
92 | }
93 |
94 | /**************************************************************************
95 | * Static helpers
96 | ***/
97 |
98 | /**
99 | * Create an EmailAddress instance from given data
100 | */
101 | static create(data) {
102 |
103 | //Array?
104 | if (Array.isArray(data)) {
105 | return data
106 | .filter(item => !!item)
107 | .map(item => this.create(item));
108 | }
109 |
110 | //Already instance of EmailAddress class?
111 | if (data instanceof EmailAddress) {
112 | return data;
113 | }
114 |
115 | //Create instance
116 | return new EmailAddress(data);
117 | }
118 | }
119 |
120 | //Export class
121 | module.exports = EmailAddress;
122 |
--------------------------------------------------------------------------------
/packages/helpers/classes/index.d.ts:
--------------------------------------------------------------------------------
1 | import Attachment from "@sendgrid/helpers/classes/attachment";
2 | import EmailAddress from "@sendgrid/helpers/classes/email-address";
3 | import Mail from "@sendgrid/helpers/classes/mail"
4 | import Personalization from "@sendgrid/helpers/classes/personalization";
5 | import Response from "@sendgrid/helpers/classes/response";
6 | import ResponseError from "@sendgrid/helpers/classes/response-error";
7 |
8 | export {
9 | Attachment,
10 | EmailAddress,
11 | Mail,
12 | Personalization,
13 | Response,
14 | ResponseError,
15 | }
16 |
--------------------------------------------------------------------------------
/packages/helpers/classes/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Expose classes
5 | */
6 | const Attachment = require('./attachment');
7 | const EmailAddress = require('./email-address');
8 | const Mail = require('./mail');
9 | const Personalization = require('./personalization');
10 | const Response = require('./response');
11 | const ResponseError = require('./response-error');
12 | const Statistics = require('./statistics');
13 |
14 | /**
15 | * Export
16 | */
17 | module.exports = {
18 | Attachment,
19 | EmailAddress,
20 | Mail,
21 | Personalization,
22 | Response,
23 | ResponseError,
24 | Statistics,
25 | };
26 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization.d.ts:
--------------------------------------------------------------------------------
1 | import { EmailData, EmailJSON } from "./email-address";
2 |
3 | export interface PersonalizationData {
4 | to: EmailData | EmailData[],
5 | from?: EmailData,
6 | cc?: EmailData | EmailData[],
7 | bcc?: EmailData | EmailData[],
8 | subject?: string;
9 | headers?: { [key: string]: string };
10 | substitutions?: { [key: string]: string };
11 | dynamicTemplateData?: { [key: string]: any; };
12 | customArgs?: { [key: string]: string };
13 | sendAt?: number;
14 | }
15 |
16 | export interface PersonalizationJSON {
17 | to: EmailJSON | EmailJSON[];
18 | from?: EmailJSON;
19 | cc?: EmailJSON[];
20 | bcc?: EmailJSON[];
21 | headers?: { [key: string]: string; };
22 | substitutions?: { [key: string]: string; };
23 | dynamic_template_data?: { [key: string]: string; };
24 | custom_args?: { [key: string]: string; };
25 | subject?: string;
26 | send_at?: number;
27 | }
28 |
29 | export default class Personalization {
30 | constructor(data?: PersonalizationData);
31 |
32 | fromData(data: PersonalizationData): void;
33 |
34 | /**
35 | * Set subject
36 | */
37 | setSubject(subject: string): void;
38 |
39 | /**
40 | * Set send at
41 | */
42 | setSendAt(sendAt: number): void;
43 |
44 | /**
45 | * Set to
46 | */
47 | setTo(to: EmailData | EmailData[]): void;
48 |
49 | /**
50 | * Set from
51 | */
52 | setFrom(from: EmailData): void;
53 |
54 | /**
55 | * Add a single to
56 | */
57 | addTo(to: EmailData): void;
58 |
59 | /**
60 | * Set cc
61 | */
62 | setCc(cc: EmailData | EmailData[]): void;
63 |
64 | /**
65 | * Add a single cc
66 | */
67 | addCc(cc: EmailData): void;
68 |
69 | /**
70 | * Set bcc
71 | */
72 | setBcc(bcc: EmailData | EmailData[]): void;
73 |
74 | /**
75 | * Add a single bcc
76 | */
77 | addBcc(bcc: EmailData): void;
78 |
79 | /**
80 | * Set headers
81 | */
82 | setHeaders(headers: { [key: string]: string }): void;
83 |
84 | /**
85 | * Add a header
86 | */
87 | addHeader(key: string, value: string): void;
88 |
89 | /**
90 | * Set custom args
91 | */
92 | setCustomArgs(customArgs: { [key: string]: string }): void;
93 |
94 | /**
95 | * Add a custom arg
96 | */
97 | addCustomArg(key: string, value: string): void;
98 |
99 | /**
100 | * Set substitutions
101 | */
102 | setSubstitutions(substitutions: { [key: string]: string }): void;
103 |
104 | /**
105 | * Add a substitution
106 | */
107 | addSubstitution(key: string, value: string): void;
108 |
109 | /**
110 | * Reverse merge substitutions, preserving existing ones
111 | */
112 | reverseMergeSubstitutions(substitutions: { [key: string]: string }): void;
113 |
114 | /**
115 | * Set substitution wrappers
116 | */
117 | setSubstitutionWrappers(wrappers: string[]): void;
118 |
119 | /**
120 | * Set dynamic template data
121 | */
122 | setDynamicTemplateData(dynamicTemplateData: { [key: string]: any }): void;
123 |
124 | /**
125 | * To JSON
126 | */
127 | toJSON(): PersonalizationJSON;
128 | }
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/527-camel-case-headers.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | // camel case headers test
21 | describe('#527', function() {
22 | it('shouldn\'t convert the headers to camel/snake case', function() {
23 | const p = new Personalization({
24 | to: 'test@example.com',
25 | headers: {
26 | 'List-Unsubscribe': '',
27 | },
28 | });
29 |
30 | expect(p.headers['List-Unsubscribe']).to.equal('');
31 |
32 | expect(p.toJSON().headers['List-Unsubscribe']).to
33 | .equal('');
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/PERSONALIZATION-SPECS-README.md:
--------------------------------------------------------------------------------
1 | #### Personalization helper specs
2 |
3 | - setSubject() - set-subject.spec.js
4 | - setSendAt() - set-send-at.spec.js
5 | - setTo() - set-to.spec.js
6 | - setFrom() - set-from.spec.js
7 | - addTo() - add-to.spec.js
8 | - setCc() - set-cc.spec.js
9 | - addCc() - add-cc.spec.js
10 | - setBcc() - set-bcc.spec.js
11 | - addBcc() - add-bcc.spec.js
12 | - setHeaders() - set-headers.spec.js
13 | - addHeader() - add-headers.spec.js
14 | - setCustomArgs() - set-custom-args.spec.js
15 | - addCustomArg() - add-custom-args.spec.js
16 | - setSubstitutions() - set-substitutions.spec.js
17 | - addSubstitution() - add-substitutions.spec.js
18 | - reverseMergeSubstitutions() - reverse-merge-substitutions.spec.js
19 | - setSubstitutionWrappers() - set-substitutions-wrappers.spec.js
20 | - deepMergeDynamicTemplateData() - reverse-merge-dynamic_template_data.spec.js
21 | - toJSON() - to-json.spec.js
22 | - fromData() - from-data.spec.js
23 | - #527 - 527-camel-case-headers.spec.js
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/add-bcc.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Add bcc
21 | describe('addBcc()', function() {
22 | it('should add the item', function() {
23 | p.addBcc('test@example.org');
24 | expect(p.bcc).to.be.an.instanceof(Array);
25 | expect(p.bcc).to.have.a.lengthOf(1);
26 | expect(p.bcc[0]).to.be.an.instanceof(EmailAddress);
27 | expect(p.bcc[0].email).to.equal('test@example.org');
28 | });
29 | it('should handle multiple values', function() {
30 | p.addBcc('test1@example.org');
31 | p.addBcc('test2@example.org');
32 | expect(p.bcc).to.be.an.instanceof(Array);
33 | expect(p.bcc).to.have.a.lengthOf(2);
34 | expect(p.bcc[0]).to.be.an.instanceof(EmailAddress);
35 | expect(p.bcc[0].email).to.equal('test1@example.org');
36 | expect(p.bcc[1]).to.be.an.instanceof(EmailAddress);
37 | expect(p.bcc[1].email).to.equal('test2@example.org');
38 | });
39 | it('should accept no input', function() {
40 | expect(function() {
41 | p.addBcc();
42 | }).not.to.throw(Error);
43 | });
44 | it('should not overwrite with no input', function() {
45 | p.addBcc('test@example.org');
46 | p.addBcc();
47 | expect(p.bcc).to.be.an.instanceof(Array);
48 | expect(p.bcc).to.have.a.lengthOf(1);
49 | expect(p.bcc[0]).to.be.an.instanceof(EmailAddress);
50 | expect(p.bcc[0].email).to.equal('test@example.org');
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/add-cc.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Add cc
21 | describe('addCc()', function() {
22 | it('should add the item', function() {
23 | p.addCc('test@example.org');
24 | expect(p.cc).to.be.an.instanceof(Array);
25 | expect(p.cc).to.have.a.lengthOf(1);
26 | expect(p.cc[0]).to.be.an.instanceof(EmailAddress);
27 | expect(p.cc[0].email).to.equal('test@example.org');
28 | });
29 | it('should handle multiple values', function() {
30 | p.addCc('test1@example.org');
31 | p.addCc('test2@example.org');
32 | expect(p.cc).to.be.an.instanceof(Array);
33 | expect(p.cc).to.have.a.lengthOf(2);
34 | expect(p.cc[0]).to.be.an.instanceof(EmailAddress);
35 | expect(p.cc[0].email).to.equal('test1@example.org');
36 | expect(p.cc[1]).to.be.an.instanceof(EmailAddress);
37 | expect(p.cc[1].email).to.equal('test2@example.org');
38 | });
39 | it('should accept no input', function() {
40 | expect(function() {
41 | p.addCc();
42 | }).not.to.throw(Error);
43 | });
44 | it('should not overwrite with no input', function() {
45 | p.addCc('test@example.org');
46 | p.addCc();
47 | expect(p.cc).to.be.an.instanceof(Array);
48 | expect(p.cc).to.have.a.lengthOf(1);
49 | expect(p.cc[0]).to.be.an.instanceof(EmailAddress);
50 | expect(p.cc[0].email).to.equal('test@example.org');
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/add-custom-args.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Add custom arg
21 | describe('addCustomArg()', function() {
22 | it('should set the given value', function() {
23 | p.addCustomArg('test', 'Test');
24 | expect(p.customArgs).to.be.an.instanceof(Object);
25 | expect(p.customArgs).to.have.a.property('test');
26 | expect(p.customArgs.test).to.equal('Test');
27 | });
28 | it('should add multiple values', function() {
29 | p.addCustomArg('test1', 'Test1');
30 | p.addCustomArg('test2', 'Test2');
31 | expect(p.customArgs).to.have.a.property('test1');
32 | expect(p.customArgs).to.have.a.property('test2');
33 | expect(p.customArgs.test1).to.equal('Test1');
34 | expect(p.customArgs.test2).to.equal('Test2');
35 | });
36 | it('should throw an error for invalid input', function() {
37 | expect(function() {
38 | p.addCustomArg('test');
39 | }).to.throw(Error);
40 | expect(function() {
41 | p.addCustomArg(null, 'test');
42 | }).to.throw(Error);
43 | expect(function() {
44 | p.addCustomArg(3, 5);
45 | }).to.throw(Error);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/add-headers.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Add header
21 | describe('addHeader()', function() {
22 | it('should set the given value', function() {
23 | p.addHeader('test', 'Test');
24 | expect(p.headers).to.be.an.instanceof(Object);
25 | expect(p.headers).to.have.a.property('test');
26 | expect(p.headers.test).to.equal('Test');
27 | });
28 | it('should add multiple values', function() {
29 | p.addHeader('test1', 'Test1');
30 | p.addHeader('test2', 'Test2');
31 | expect(p.headers).to.have.a.property('test1');
32 | expect(p.headers).to.have.a.property('test2');
33 | expect(p.headers.test1).to.equal('Test1');
34 | expect(p.headers.test2).to.equal('Test2');
35 | });
36 | it('should throw an error for invalid input', function() {
37 | expect(function() {
38 | p.addHeader('test');
39 | }).to.throw(Error);
40 | expect(function() {
41 | p.addHeader(null, 'test');
42 | }).to.throw(Error);
43 | expect(function() {
44 | p.addHeader(3, 5);
45 | }).to.throw(Error);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/add-substitutions.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Add substitution
21 | describe('addSubstitution()', function() {
22 | it('should set the given value', function() {
23 | p.addSubstitution('test', 'Test');
24 | expect(p.substitutions).to.be.an.instanceof(Object);
25 | expect(p.substitutions).to.have.a.property('test');
26 | expect(p.substitutions.test).to.equal('Test');
27 | });
28 | it('should add multiple values', function() {
29 | p.addSubstitution('test1', 'Test1');
30 | p.addSubstitution('test2', 2);
31 | expect(p.substitutions).to.have.a.property('test1');
32 | expect(p.substitutions).to.have.a.property('test2');
33 | expect(p.substitutions.test1).to.equal('Test1');
34 | expect(p.substitutions.test2).to.equal(2);
35 | });
36 | it('should throw an error for invalid input', function() {
37 | expect(function() {
38 | p.addSubstitution('test');
39 | }).to.throw(Error);
40 | expect(function() {
41 | p.addSubstitution(null, 'test');
42 | }).to.throw(Error);
43 | expect(function() {
44 | p.addSubstitution(3, false);
45 | }).to.throw(Error);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/from-data.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //From data
21 | describe('fromData()', function() {
22 |
23 | //Data
24 | const data = {
25 | to: 'to@example.org',
26 | from: 'from@example.org',
27 | cc: ['cc1@example.org', 'cc2@example.org'],
28 | bcc: ['bcc1@example.org', 'bcc2@example.org'],
29 | subject: 'Test',
30 | sendAt: 1000,
31 | headers: {test: 'Test'},
32 | customArgs: {snake_case: 'Test', T_EST: 'Test', camelCase: 'Test'},
33 | substitutions: {snake_case: 'Test', T_EST: 'Test', camelCase: 'Test'},
34 | substitutionWrappers: ['[', ']'],
35 | };
36 |
37 | //Tests
38 | it('should call fromData() from the constructor', () => {
39 | p = new Personalization(data);
40 | expect(p.to[0].email).to.equal('to@example.org');
41 | expect(p.subject).to.equal('Test');
42 | });
43 | it('should throw an error for invalid input', () => {
44 | expect(function() {
45 | p.fromData(5);
46 | }).to.throw(Error);
47 | });
48 | it('should have set all properties', () => {
49 | p.fromData(data);
50 | expect(p.to[0].email).to.equal('to@example.org');
51 | expect(p.from.email).to.equal('from@example.org');
52 | expect(p.cc[0].email).to.equal('cc1@example.org');
53 | expect(p.cc[1].email).to.equal('cc2@example.org');
54 | expect(p.bcc[0].email).to.equal('bcc1@example.org');
55 | expect(p.bcc[1].email).to.equal('bcc2@example.org');
56 | expect(p.subject).to.equal('Test');
57 | expect(p.sendAt).to.equal(1000);
58 | expect(p.headers.test).to.equal('Test');
59 | expect(p.customArgs.snake_case).to.equal('Test');
60 | expect(p.substitutions.snake_case).to.equal('Test');
61 | expect(p.substitutionWrappers).to.have.members(['[', ']']);
62 | });
63 | it('should not modify the keys of substitutions and custom args', () => {
64 | p.fromData(data);
65 | expect(p.substitutions.T_EST).to.equal('Test');
66 | expect(p.substitutions.camelCase).to.equal('Test');
67 | expect(p.substitutions.snake_case).to.equal('Test');
68 | expect(p.customArgs.T_EST).to.equal('Test');
69 | expect(p.customArgs.camelCase).to.equal('Test');
70 | expect(p.customArgs.snake_case).to.equal('Test');
71 | });
72 | });
73 |
74 | describe('#527', function() {
75 | it('shouldn\'t convert the headers to camel/snake case', function() {
76 | const p = new Personalization({
77 | to: 'test@example.com',
78 | headers: {
79 | 'List-Unsubscribe': '',
80 | },
81 | });
82 |
83 | expect(p.headers['List-Unsubscribe']).to.equal('');
84 |
85 | expect(p.toJSON().headers['List-Unsubscribe']).to
86 | .equal('');
87 | });
88 | });
89 | });
90 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/reverse-merge-dynamic_teplate_data.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Reverse merge substitutions
21 | describe('deepMergeDynamicTemplateData()', function() {
22 | it('should reverse merge dynamicTemplateData', function() {
23 | p.setDynamicTemplateData({ test1: 'Test1' });
24 | p.deepMergeDynamicTemplateData({ test2: 'Test2' });
25 | expect(p.dynamicTemplateData).to.have.a.property('test1');
26 | expect(p.dynamicTemplateData).to.have.a.property('test2');
27 | expect(p.dynamicTemplateData.test1).to.equal('Test1');
28 | expect(p.dynamicTemplateData.test2).to.equal('Test2');
29 | });
30 | it('should not overwrite existing keys', function() {
31 | p.setDynamicTemplateData({ test1: 'Test1' });
32 | p.deepMergeDynamicTemplateData({ test1: 'Test3', test2: 'Test2' });
33 | expect(p.dynamicTemplateData).to.have.a.property('test1');
34 | expect(p.dynamicTemplateData).to.have.a.property('test2');
35 | expect(p.dynamicTemplateData.test1).to.equal('Test1');
36 | expect(p.dynamicTemplateData.test2).to.equal('Test2');
37 | });
38 | it('should work without prior dynamicTemplateData', function() {
39 | p.deepMergeDynamicTemplateData({ test2: 'Test2' });
40 | expect(p.dynamicTemplateData).to.have.a.property('test2');
41 | expect(p.dynamicTemplateData.test2).to.equal('Test2');
42 | });
43 | it('should throw an error for invalid input', function() {
44 | expect(function() {
45 | p.deepMergeDynamicTemplateData(3);
46 | }).to.throw(Error);
47 | });
48 | it('should accept no input', function() {
49 | expect(function() {
50 | p.deepMergeDynamicTemplateData();
51 | }).not.to.throw(Error);
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/reverse-merge-substitutions.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Reverse merge substitutions
21 | describe('reverseMergeSubstitutions()', function() {
22 | it('should reverse merge substitutions', function() {
23 | p.setSubstitutions({test1: 'Test1'});
24 | p.reverseMergeSubstitutions({test2: 'Test2'});
25 | expect(p.substitutions).to.have.a.property('test1');
26 | expect(p.substitutions).to.have.a.property('test2');
27 | expect(p.substitutions.test1).to.equal('Test1');
28 | expect(p.substitutions.test2).to.equal('Test2');
29 | });
30 | it('should not overwrite existing keys', function() {
31 | p.setSubstitutions({test1: 'Test1'});
32 | p.reverseMergeSubstitutions({test1: 'Test3', test2: 'Test2'});
33 | expect(p.substitutions).to.have.a.property('test1');
34 | expect(p.substitutions).to.have.a.property('test2');
35 | expect(p.substitutions.test1).to.equal('Test1');
36 | expect(p.substitutions.test2).to.equal('Test2');
37 | });
38 | it('should work without prior substitutions', function() {
39 | p.reverseMergeSubstitutions({test2: 'Test2'});
40 | expect(p.substitutions).to.have.a.property('test2');
41 | expect(p.substitutions.test2).to.equal('Test2');
42 | });
43 | it('should throw an error for invalid input', function() {
44 | expect(function() {
45 | p.reverseMergeSubstitutions(3);
46 | }).to.throw(Error);
47 | });
48 | it('should accept no input', function() {
49 | expect(function() {
50 | p.reverseMergeSubstitutions();
51 | }).not.to.throw(Error);
52 | });
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-add-to.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Add to
21 | describe('addTo()', function() {
22 | it('should add the item', function() {
23 | p.addTo('test@example.org');
24 | expect(p.to).to.be.an.instanceof(Array);
25 | expect(p.to).to.have.a.lengthOf(1);
26 | expect(p.to[0]).to.be.an.instanceof(EmailAddress);
27 | expect(p.to[0].email).to.equal('test@example.org');
28 | });
29 | it('should handle multiple values', function() {
30 | p.addTo('test1@example.org');
31 | p.addTo('test2@example.org');
32 | expect(p.to).to.be.an.instanceof(Array);
33 | expect(p.to).to.have.a.lengthOf(2);
34 | expect(p.to[0]).to.be.an.instanceof(EmailAddress);
35 | expect(p.to[0].email).to.equal('test1@example.org');
36 | expect(p.to[1]).to.be.an.instanceof(EmailAddress);
37 | expect(p.to[1].email).to.equal('test2@example.org');
38 | });
39 | it('should accept no input', function() {
40 | expect(function() {
41 | p.addTo();
42 | }).not.to.throw(Error);
43 | });
44 | it('should not overwrite with no input', function() {
45 | p.addTo('test@example.org');
46 | p.addTo();
47 | expect(p.to).to.be.an.instanceof(Array);
48 | expect(p.to).to.have.a.lengthOf(1);
49 | expect(p.to[0]).to.be.an.instanceof(EmailAddress);
50 | expect(p.to[0].email).to.equal('test@example.org');
51 | });
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-bcc.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set bcc
21 | describe('setBcc()', function() {
22 | it('should handle array values', function() {
23 | p.setBcc(['test@example.org']);
24 | expect(p.bcc).to.be.an.instanceof(Array);
25 | expect(p.bcc).to.have.a.lengthOf(1);
26 | expect(p.bcc[0]).to.be.an.instanceof(EmailAddress);
27 | expect(p.bcc[0].email).to.equal('test@example.org');
28 | });
29 | it('should handle string values', function() {
30 | p.setBcc('test@example.org');
31 | expect(p.bcc).to.be.an.instanceof(Array);
32 | expect(p.bcc).to.have.a.lengthOf(1);
33 | expect(p.bcc[0]).to.be.an.instanceof(EmailAddress);
34 | expect(p.bcc[0].email).to.equal('test@example.org');
35 | });
36 | it('should handle multiple values', function() {
37 | p.setBcc(['test1@example.org', 'test2@example.org']);
38 | expect(p.bcc).to.be.an.instanceof(Array);
39 | expect(p.bcc).to.have.a.lengthOf(2);
40 | expect(p.bcc[0]).to.be.an.instanceof(EmailAddress);
41 | expect(p.bcc[0].email).to.equal('test1@example.org');
42 | expect(p.bcc[1]).to.be.an.instanceof(EmailAddress);
43 | expect(p.bcc[1].email).to.equal('test2@example.org');
44 | });
45 | it('should accept no input', function() {
46 | expect(function() {
47 | p.setBcc();
48 | }).not.to.throw(Error);
49 | });
50 | it('should not overwrite with no input', function() {
51 | p.setBcc('test@example.org');
52 | p.setBcc();
53 | expect(p.bcc[0].email).to.equal('test@example.org');
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-cc.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set cc
21 | describe('setCc()', function() {
22 | it('should handle array values', function() {
23 | p.setCc(['test@example.org']);
24 | expect(p.cc).to.be.an.instanceof(Array);
25 | expect(p.cc).to.have.a.lengthOf(1);
26 | expect(p.cc[0]).to.be.an.instanceof(EmailAddress);
27 | expect(p.cc[0].email).to.equal('test@example.org');
28 | });
29 | it('should handle string values', function() {
30 | p.setCc('test@example.org');
31 | expect(p.cc).to.be.an.instanceof(Array);
32 | expect(p.cc).to.have.a.lengthOf(1);
33 | expect(p.cc[0]).to.be.an.instanceof(EmailAddress);
34 | expect(p.cc[0].email).to.equal('test@example.org');
35 | });
36 | it('should handle multiple values', function() {
37 | p.setCc(['test1@example.org', 'test2@example.org']);
38 | expect(p.cc).to.be.an.instanceof(Array);
39 | expect(p.cc).to.have.a.lengthOf(2);
40 | expect(p.cc[0]).to.be.an.instanceof(EmailAddress);
41 | expect(p.cc[0].email).to.equal('test1@example.org');
42 | expect(p.cc[1]).to.be.an.instanceof(EmailAddress);
43 | expect(p.cc[1].email).to.equal('test2@example.org');
44 | });
45 | it('should accept no input', function() {
46 | expect(function() {
47 | p.setCc();
48 | }).not.to.throw(Error);
49 | });
50 | it('should not overwrite with no input', function() {
51 | p.setCc('test@example.org');
52 | p.setCc();
53 | expect(p.cc[0].email).to.equal('test@example.org');
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-custom-args.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set custom args
21 | describe('setCustomArgs()', function() {
22 | it('should set the given value', function() {
23 | p.setCustomArgs({test: 'Test'});
24 | expect(p.customArgs).to.be.an.instanceof(Object);
25 | expect(p.customArgs).to.have.a.property('test');
26 | expect(p.customArgs.test).to.equal('Test');
27 | });
28 | it('should throw an error for invalid input', function() {
29 | expect(function() {
30 | p.setCustomArgs('Invalid');
31 | }).to.throw(Error);
32 | expect(function() {
33 | p.setCustomArgs(null);
34 | }).to.throw(Error);
35 | });
36 | it('should accept no input', function() {
37 | expect(function() {
38 | p.setCustomArgs();
39 | }).not.to.throw(Error);
40 | });
41 | it('should not overwrite with no input', function() {
42 | p.setCustomArgs({test: 'Test'});
43 | p.setCustomArgs();
44 | expect(p.customArgs.test).to.equal('Test');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-from.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set from
21 | describe('setFrom()', function() {
22 | it('should accept string values', function() {
23 | p.setFrom('test@example.org');
24 | expect(p.from).to.be.an.instanceof(EmailAddress);
25 | expect(p.from.email).to.equal('test@example.org');
26 | });
27 | it('should properly update from value', function() {
28 | p.setFrom('test1@example.com');
29 | p.setFrom('test2@example.com');
30 | p.setFrom('test3@example.com');
31 | p.setFrom('test4@example.com');
32 | expect(p.from.email).to.equal('test4@example.com');
33 | });
34 | it('should accept no input', function() {
35 | expect(function() {
36 | p.setFrom();
37 | }).not.to.throw(Error);
38 | });
39 | it('should not overwrite value with no input', function() {
40 | p.setFrom('test@example.org');
41 | p.setFrom();
42 | expect(p.from.email).to.equal('test@example.org');
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-headers.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set headers
21 | describe('setHeaders()', function() {
22 | it('should set the given value', function() {
23 | p.setHeaders({test: 'Test'});
24 | expect(p.headers).to.be.an.instanceof(Object);
25 | expect(p.headers).to.have.a.property('test');
26 | expect(p.headers.test).to.equal('Test');
27 | });
28 | it('should throw an error for invalid input', function() {
29 | expect(function() {
30 | p.setHeaders('Invalid');
31 | }).to.throw(Error);
32 | expect(function() {
33 | p.setHeaders(null);
34 | }).to.throw(Error);
35 | });
36 | it('should accept no input', function() {
37 | expect(function() {
38 | p.setHeaders();
39 | }).not.to.throw(Error);
40 | });
41 | it('should not overwrite with no input', function() {
42 | p.setHeaders({test: 'Test'});
43 | p.setHeaders();
44 | expect(p.headers.test).to.equal('Test');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-send-at.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set send at
21 | describe('setSendAt()', function() {
22 | it('should set the given value', function() {
23 | p.setSendAt(1500077141);
24 | expect(p.sendAt).to.equal(1500077141);
25 | });
26 | it('should throw an error for invalid input', function() {
27 | expect(function() {
28 | p.setSendAt('Invalid');
29 | }).to.throw(Error);
30 | expect(function() {
31 | p.setSendAt(null);
32 | }).to.throw(Error);
33 | });
34 | it('should accept no input', function() {
35 | expect(function() {
36 | p.setSendAt();
37 | }).not.to.throw(Error);
38 | });
39 | it('should not overwrite with no input', function() {
40 | p.setSendAt(1500077141);
41 | p.setSendAt();
42 | expect(p.sendAt).to.equal(1500077141);
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-subject.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set subject
21 | describe('setSubject()', function() {
22 | it('should set the given value', function() {
23 | p.setSubject('Test');
24 | expect(p.subject).to.equal('Test');
25 | });
26 | it('should throw an error for invalid input', function() {
27 | expect(function() {
28 | p.setSubject(5);
29 | }).to.throw(Error);
30 | expect(function() {
31 | p.setSubject(null);
32 | }).to.throw(Error);
33 | });
34 | it('should accept no input', function() {
35 | expect(function() {
36 | p.setSubject();
37 | }).not.to.throw(Error);
38 | });
39 | it('should not overwrite with no input', function() {
40 | p.setSubject('Test');
41 | p.setSubject();
42 | expect(p.subject).to.equal('Test');
43 | });
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-substitutions.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set substitutions
21 | describe('setSubstitutions()', function() {
22 | it('should set the given value', function() {
23 | p.setSubstitutions({test: 'Test'});
24 | expect(p.substitutions).to.be.an.instanceof(Object);
25 | expect(p.substitutions).to.have.a.property('test');
26 | expect(p.substitutions.test).to.equal('Test');
27 | });
28 | it('should throw an error for invalid input', function() {
29 | expect(function() {
30 | p.setSubstitutions('Invalid');
31 | }).to.throw(Error);
32 | expect(function() {
33 | p.setSubstitutions(3);
34 | }).to.throw(Error);
35 | });
36 | it('should accept no input', function() {
37 | expect(function() {
38 | p.setSubstitutions();
39 | }).not.to.throw(Error);
40 | });
41 | it('should not overwrite with no input', function() {
42 | p.setSubstitutions({test: 'Test'});
43 | p.setSubstitutions();
44 | expect(p.substitutions.test).to.equal('Test');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set-to.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set to
21 | describe('setTo()', function() {
22 | it('should handle array values', function() {
23 | p.setTo(['test@example.org']);
24 | expect(p.to).to.be.an.instanceof(Array);
25 | expect(p.to).to.have.a.lengthOf(1);
26 | expect(p.to[0]).to.be.an.instanceof(EmailAddress);
27 | expect(p.to[0].email).to.equal('test@example.org');
28 | });
29 | it('should handle string values', function() {
30 | p.setTo('test@example.org');
31 | expect(p.to).to.be.an.instanceof(Array);
32 | expect(p.to).to.have.a.lengthOf(1);
33 | expect(p.to[0]).to.be.an.instanceof(EmailAddress);
34 | expect(p.to[0].email).to.equal('test@example.org');
35 | });
36 | it('should handle multiple values', function() {
37 | p.setTo(['test1@example.org', 'test2@example.org']);
38 | expect(p.to).to.be.an.instanceof(Array);
39 | expect(p.to).to.have.a.lengthOf(2);
40 | expect(p.to[0]).to.be.an.instanceof(EmailAddress);
41 | expect(p.to[0].email).to.equal('test1@example.org');
42 | expect(p.to[1]).to.be.an.instanceof(EmailAddress);
43 | expect(p.to[1].email).to.equal('test2@example.org');
44 | });
45 | it('should accept no input', function() {
46 | expect(function() {
47 | p.setTo();
48 | }).not.to.throw(Error);
49 | });
50 | it('should not overwrite with no input', function() {
51 | p.setTo('test@example.org');
52 | p.setTo();
53 | expect(p.to[0].email).to.equal('test@example.org');
54 | });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/set_dynamic_template_data.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set substitutions
21 | describe('setDynamicTemplateData()', function() {
22 | it('should set the given value', function() {
23 | p.setDynamicTemplateData({ test: 'Test' });
24 | expect(p.dynamicTemplateData).to.be.an.instanceof(Object);
25 | expect(p.dynamicTemplateData).to.have.a.property('test');
26 | expect(p.dynamicTemplateData.test).to.equal('Test');
27 | });
28 | it('should throw an error for invalid input', function() {
29 | expect(function() {
30 | p.setDynamicTemplateData('Invalid');
31 | }).to.throw(Error);
32 | expect(function() {
33 | p.setDynamicTemplateData(3);
34 | }).to.throw(Error);
35 | });
36 | it('should accept no input', function() {
37 | expect(function() {
38 | p.setDynamicTemplateData();
39 | }).not.to.throw(Error);
40 | });
41 | it('should not overwrite with no input', function() {
42 | p.setDynamicTemplateData({ test: 'Test' });
43 | p.setDynamicTemplateData();
44 | expect(p.dynamicTemplateData.test).to.equal('Test');
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/helpers/classes/personalization_specs/substitutions-wrappers.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const Personalization = require('../personalization');
7 | const EmailAddress = require('../email-address');
8 |
9 | /**
10 | * Tests
11 | */
12 | describe('Personalization', function() {
13 |
14 | //Create new personalization before each test
15 | let p;
16 | beforeEach(function() {
17 | p = new Personalization();
18 | });
19 |
20 | //Set substitutions wrappers
21 | describe('setSubstitutionWrappers()', function() {
22 | it('should have defaults', function() {
23 | expect(p.substitutionWrappers).to.be.an.instanceof(Array);
24 | expect(p.substitutionWrappers).to.have.a.lengthOf(2);
25 | expect(p.substitutionWrappers[0]).to.equal('{{');
26 | expect(p.substitutionWrappers[1]).to.equal('}}');
27 | });
28 | it('should set the given value', function() {
29 | p.setSubstitutionWrappers(['a', 'b']);
30 | expect(p.substitutionWrappers).to.be.an.instanceof(Array);
31 | expect(p.substitutionWrappers).to.have.a.lengthOf(2);
32 | expect(p.substitutionWrappers[0]).to.equal('a');
33 | expect(p.substitutionWrappers[1]).to.equal('b');
34 | });
35 | it('should throw an error for invalid input', function() {
36 | expect(function() {
37 | p.setSubstitutionWrappers('Invalid');
38 | }).to.throw(Error);
39 | expect(function() {
40 | p.setSubstitutionWrappers(['a']);
41 | }).to.throw(Error);
42 | expect(function() {
43 | p.setSubstitutionWrappers(['a', 'b', 'c']);
44 | }).to.throw(Error);
45 | });
46 | it('should accept no input', function() {
47 | expect(function() {
48 | p.setSubstitutionWrappers();
49 | }).not.to.throw(Error);
50 | });
51 | it('should not overwrite with no input', function() {
52 | p.setSubstitutionWrappers(['a', 'b']);
53 | p.setSubstitutionWrappers();
54 | expect(p.substitutionWrappers[0]).to.equal('a');
55 | expect(p.substitutionWrappers[1]).to.equal('b');
56 | });
57 | });
58 | });
59 |
--------------------------------------------------------------------------------
/packages/helpers/classes/request.d.ts:
--------------------------------------------------------------------------------
1 | import * as https from 'https';
2 |
3 | type HttpMethod = 'get'|'GET'|'post'|'POST'|'put'|'PUT'|'patch'|'PATCH'|'delete'|'DELETE';
4 |
5 | export default interface RequestOptions {
6 | url: string;
7 | method?: HttpMethod;
8 | baseUrl?: string;
9 | qs?: TParams;
10 | body?: TData;
11 | headers?: object;
12 | httpsAgent?: https.Agent;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/helpers/classes/response-error.d.ts:
--------------------------------------------------------------------------------
1 | export default class ResponseError extends Error {
2 | code: number;
3 | message: string;
4 | response: {
5 | headers: { [key: string]: string; };
6 | body: string;
7 | };
8 | }
--------------------------------------------------------------------------------
/packages/helpers/classes/response-error.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Response error class
5 | */
6 | class ResponseError extends Error {
7 |
8 | /**
9 | * Constructor
10 | */
11 | constructor(response) {
12 |
13 | //Super
14 | super();
15 |
16 | //Extract data from response
17 | const { headers, status, statusText, data } = response;
18 |
19 | //Set data
20 | this.code = status;
21 | this.message = statusText;
22 | this.response = { headers, body: data };
23 |
24 | //Capture stack trace
25 | if (!this.stack) {
26 | Error.captureStackTrace(this, this.constructor);
27 | }
28 |
29 | //Clean up stack trace
30 | const regex = new RegExp(process.cwd() + '/', 'gi');
31 | this.stack = this.stack.replace(regex, '');
32 | }
33 |
34 | /**
35 | * Convert to string
36 | */
37 | toString() {
38 | const { body } = this.response;
39 | let err = `${this.message} (${this.code})`;
40 | if (body && Array.isArray(body.errors)) {
41 | body.errors.forEach(error => {
42 | const message = error.message;
43 | const field = error.field;
44 | const help = error.help;
45 | err += `\n ${message}\n ${field}\n ${help}`;
46 | });
47 | }
48 | return err;
49 | }
50 |
51 | /**
52 | * Convert to simple object for JSON responses
53 | */
54 | toJSON() {
55 | const { message, code, response } = this;
56 | return { message, code, response };
57 | }
58 | }
59 |
60 | //Export
61 | module.exports = ResponseError;
62 |
--------------------------------------------------------------------------------
/packages/helpers/classes/response.d.ts:
--------------------------------------------------------------------------------
1 | export default class Response {
2 | statusCode: number;
3 | body: TPayload;
4 | headers: any;
5 | constructor(statusCode: number, body: TPayload, headers?: any);
6 | toString(): string;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/helpers/classes/response.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class Response {
4 | constructor(statusCode, body, headers) {
5 | this.statusCode = statusCode;
6 | this.body = body;
7 | this.headers = headers;
8 | }
9 |
10 | toString() {
11 | return 'HTTP ' + this.statusCode + ' ' + this.body;
12 | }
13 | }
14 |
15 | module.exports = Response;
16 |
--------------------------------------------------------------------------------
/packages/helpers/classes/statistics.d.ts:
--------------------------------------------------------------------------------
1 | export class Stats {
2 | startDate: Date;
3 | endDate?: Date;
4 | aggregatedBy?: string;
5 | }
6 |
7 | export default class Statistics {
8 | constructor(data?: Stats);
9 |
10 | fromData(data: Stats): void;
11 |
12 | /**
13 | * To JSON
14 | */
15 | toJSON(): Stats;
16 |
17 | /**
18 | * Get Advanced Statistics
19 | */
20 | getAdvanced();
21 |
22 | /**
23 | * Get Category Statistics
24 | */
25 | getCategory();
26 |
27 | /**
28 | * Get Global Statistics
29 | */
30 | getGlobal();
31 |
32 | /**
33 | * Get Parse Statistics
34 | */
35 | getParse();
36 |
37 | /**
38 | * Get Subuser Statistics
39 | */
40 | getSubuser();
41 |
42 | /**
43 | * Set StartDate
44 | */
45 | setStartDate(startDate: Date): void;
46 |
47 | /**
48 | * Set EndDate
49 | */
50 | setEndDate(endDate: Date): void;
51 |
52 | /**
53 | * Set AggregatedBy
54 | */
55 | setAggregatedBy(aggregatedBy: string): void;
56 | }
--------------------------------------------------------------------------------
/packages/helpers/constants/index.js:
--------------------------------------------------------------------------------
1 | const DYNAMIC_TEMPLATE_CHAR_WARNING = `
2 | Content with characters ', " or & may need to be escaped with three brackets
3 | {{{ content }}}
4 | See https://sendgrid.com/docs/for-developers/sending-email/using-handlebars/ for more information.`;
5 |
6 | module.exports = {
7 | DYNAMIC_TEMPLATE_CHAR_WARNING,
8 | };
9 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/array-to-json.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Helper to convert an array of objects to JSON
3 | */
4 | declare function arrayToJSON(arr: any[]): any[];
5 |
6 | export = arrayToJSON;
--------------------------------------------------------------------------------
/packages/helpers/helpers/array-to-json.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Helper to convert an array of objects to JSON
5 | */
6 | module.exports = function arrayToJSON(arr) {
7 | return arr.map(item => {
8 | if (typeof item === 'object' && item !== null && typeof item.toJSON === 'function') {
9 | return item.toJSON();
10 | }
11 | return item;
12 | });
13 | };
14 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/array-to-json.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const arrayToJSON = require('./array-to-json');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('arrayToJSON', function() {
12 |
13 | //Test object with toJSON function
14 | const obj1 = {
15 | toJSON() {
16 | return {a: 1, b: 2};
17 | },
18 | };
19 |
20 | //Test plain object
21 | const obj2 = {c: 3, d: 4};
22 |
23 | //Create mixed array
24 | const test = [obj1, obj2, null, obj2, obj1, 2, 'test'];
25 | const json = arrayToJSON(test);
26 |
27 | //Tests
28 | it('should leave non object values as is', function() {
29 | expect(json[2]).to.be.null();
30 | expect(json[5]).to.equal(2);
31 | expect(json[6]).to.equal('test');
32 | });
33 | it('should leave plain objects as they are', function() {
34 | expect(json[1]).to.have.property('c');
35 | expect(json[3]).to.have.property('d');
36 | });
37 | it('should use the toJSON() handler if specified', function() {
38 | expect(json[0]).to.have.property('a');
39 | expect(json[4]).to.have.property('b');
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/convert-keys.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Helper to convert an object's keys
3 | */
4 | declare function convertKeys(obj: T, converter: (key: string) => string, ignored?: string[]): S;
5 |
6 | export = convertKeys;
--------------------------------------------------------------------------------
/packages/helpers/helpers/convert-keys.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Helper to convert an object's keys
5 | */
6 | module.exports = function convertKeys(obj, converter, ignored) {
7 |
8 | //Validate
9 | if (typeof obj !== 'object' || obj === null) {
10 | throw new Error('Non object passed to convertKeys: ' + obj);
11 | }
12 |
13 | //Ignore arrays
14 | if (Array.isArray(obj)) {
15 | return obj;
16 | }
17 |
18 | //Ensure array for ignored values
19 | if (!Array.isArray(ignored)) {
20 | ignored = [];
21 | }
22 |
23 | //Process all properties
24 | for (const key in obj) {
25 | //istanbul ignore else
26 | if (obj.hasOwnProperty(key)) {
27 |
28 | //Convert key to snake case
29 | const converted = converter(key);
30 |
31 | //Recursive for child objects, unless ignored
32 | //The ignored check checks both variants of the key
33 | if (typeof obj[key] === 'object' && obj[key] !== null) {
34 | if (!ignored.includes(key) && !ignored.includes(converted)) {
35 | obj[key] = convertKeys(obj[key], converter, ignored);
36 | }
37 | }
38 |
39 | //Convert key to snake case and set if needed
40 | if (converted !== key) {
41 | obj[converted] = obj[key];
42 | delete obj[key];
43 | }
44 | }
45 | }
46 |
47 | //Return object
48 | return obj;
49 | };
50 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/convert-keys.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const convertKeys = require('./convert-keys');
7 | const deepClone = require('./deep-clone');
8 | const strToCamelCase = require('./str-to-camel-case');
9 |
10 | /**
11 | * Tests
12 | */
13 | describe('convertKeys', function() {
14 |
15 | //Test object
16 | const obj = {
17 | a: 1,
18 | snake_case: 2,
19 | camelCase: 3,
20 | nested_snake_case: {
21 | a: 1,
22 | snake_case: 2,
23 | camelCase: 3,
24 | },
25 | nestedCamelCase: {
26 | a: 1,
27 | snake_case: 2,
28 | camelCase: 3,
29 | },
30 | arr: ['a', 'b'],
31 | };
32 |
33 | //Create copy of the object
34 | const objCopy = deepClone(obj);
35 |
36 | //Convert keys
37 | convertKeys(obj, strToCamelCase);
38 |
39 | //Tests
40 | it('should convert top level keys properly', function() {
41 | expect(obj).to.have.property('a');
42 | expect(obj).to.have.property('snakeCase');
43 | expect(obj).to.have.property('camelCase');
44 | expect(obj).to.have.property('nestedSnakeCase');
45 | expect(obj).to.have.property('nestedCamelCase');
46 | expect(obj).not.to.have.property('snake_case');
47 | expect(obj).not.to.have.property('nested_snake_case');
48 | });
49 | it('should convert nested keys properly', function() {
50 | expect(obj.nestedSnakeCase).to.have.property('a');
51 | expect(obj.nestedSnakeCase).to.have.property('snakeCase');
52 | expect(obj.nestedSnakeCase).to.have.property('camelCase');
53 | expect(obj.nestedSnakeCase).not.to.have.property('snake_case');
54 | expect(obj.nestedCamelCase).to.have.property('a');
55 | expect(obj.nestedCamelCase).to.have.property('snakeCase');
56 | expect(obj.nestedCamelCase).to.have.property('camelCase');
57 | expect(obj.nestedCamelCase).not.to.have.property('snake_case');
58 | });
59 | it('should handle arrays properly', function() {
60 | expect(obj.arr).to.be.an.instanceof(Array);
61 | expect(obj.arr).to.have.lengthOf(2);
62 | expect(obj.arr).to.have.members(['a', 'b']);
63 | });
64 | it('should not converted nested objects if ignored', function() {
65 | convertKeys(objCopy, strToCamelCase, ['nestedSnakeCase']);
66 | expect(objCopy.nestedCamelCase).to.have.property('a');
67 | expect(objCopy.nestedCamelCase).to.have.property('snakeCase');
68 | expect(objCopy.nestedCamelCase).to.have.property('camelCase');
69 | expect(objCopy.nestedCamelCase).not.to.have.property('snake_case');
70 | expect(objCopy.nestedSnakeCase).to.have.property('a');
71 | expect(objCopy.nestedSnakeCase).to.have.property('camelCase');
72 | expect(objCopy.nestedSnakeCase).to.have.property('snake_case');
73 | expect(objCopy.nestedSnakeCase).not.to.have.property('snakeCase');
74 | });
75 | it('should throw an error for non object input', function() {
76 | expect(function() {
77 | convertKeys(null);
78 | }).to.throw(Error);
79 | expect(function() {
80 | convertKeys(5);
81 | }).to.throw(Error);
82 | });
83 | });
84 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/deep-clone.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Deep cloning helper for objects
3 | */
4 | declare function deepClone(obj: T): T;
5 |
6 | export = deepClone;
--------------------------------------------------------------------------------
/packages/helpers/helpers/deep-clone.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Deep cloning helper for objects
5 | */
6 | module.exports = function deepClone(obj) {
7 | return JSON.parse(JSON.stringify(obj));
8 | };
9 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/deep-clone.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const deepClone = require('./deep-clone');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('deepClone', function() {
12 |
13 | //Test object
14 | const obj = {
15 | nested: {a: 1, b: 2, c: {d: 4}},
16 | e: 5,
17 | arr: ['a', 'b'],
18 | };
19 |
20 | //Create clone
21 | const clone = deepClone(obj);
22 |
23 | //Tests
24 | it('should equal the objects to themselves', function() {
25 | expect(obj).to.equal(obj);
26 | expect(clone).to.equal(clone);
27 | });
28 | it('should make a copy of the object', function() {
29 | expect(obj).to.not.equal(clone);
30 | expect(obj).to.deep.equal(clone);
31 | });
32 | it('should make a copy of nested objects', function() {
33 | expect(obj.nested).to.not.equal(clone.nested);
34 | expect(obj.nested).to.deep.equal(clone.nested);
35 | expect(obj.nested.c).to.not.equal(clone.nested.c);
36 | expect(obj.nested.c).to.deep.equal(clone.nested.c);
37 | });
38 | it('should handle arrays properly', function() {
39 | expect(clone.arr).to.be.an.instanceof(Array);
40 | expect(clone.arr).to.have.lengthOf(2);
41 | expect(clone.arr).to.have.members(['a', 'b']);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/html-to-plain-text.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Helper to convert an HTML string to a plain text string
5 | */
6 | module.exports = function convertHTML2PlainString(html) {
7 | let text = html.replace(/(<([^>]+)>)/g, '');
8 | text = text.replace(/\s+/g, ' ');
9 | return text;
10 | };
11 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/html-to-plain-text.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const convertHTML2PlainString = require('./html-to-plain-text');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('convertHTML2PlainString', function() {
12 |
13 | //Test string with one html tag
14 | const html1 = 'Hello world
';
15 |
16 | //Test string with nested html tags
17 | const html2 = '';
18 |
19 | //Test string with html tag with attributes
20 | const html3 = 'Hello World!
';
21 |
22 | //Tests
23 | it('should strip out html tags', function() {
24 | expect(convertHTML2PlainString(html1)).to.be.equal('Hello world');
25 | });
26 | it('should strip out nested html tags', function() {
27 | expect(convertHTML2PlainString(html2)).to.be.equal('Hello World!');
28 | });
29 | it('should strip out html tags with attributes', function() {
30 | expect(convertHTML2PlainString(html3)).to.be.equal('Hello World!');
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/index.d.ts:
--------------------------------------------------------------------------------
1 | import arrayToJSON = require("@sendgrid/helpers/helpers/array-to-json");
2 | import convertKeys = require("@sendgrid/helpers/helpers/convert-keys");
3 | import deepClone = require("@sendgrid/helpers/helpers/deep-clone");
4 | import mergeData = require("@sendgrid/helpers/helpers/merge-data");
5 | import splitNameEmail = require("@sendgrid/helpers/helpers/split-name-email");
6 | import toCamelCase = require("@sendgrid/helpers/helpers/to-camel-case");
7 | import toSnakeCase = require("@sendgrid/helpers/helpers/to-snake-case");
8 | import wrapSubstitutions = require("@sendgrid/helpers/helpers/wrap-substitutions");
9 |
10 | export {
11 | arrayToJSON,
12 | convertKeys,
13 | deepClone,
14 | mergeData,
15 | splitNameEmail,
16 | toCamelCase,
17 | toSnakeCase,
18 | wrapSubstitutions,
19 | }
--------------------------------------------------------------------------------
/packages/helpers/helpers/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Expose helpers
5 | */
6 | const arrayToJSON = require('./array-to-json');
7 | const convertKeys = require('./convert-keys');
8 | const deepClone = require('./deep-clone');
9 | const mergeData = require('./merge-data');
10 | const splitNameEmail = require('./split-name-email');
11 | const toCamelCase = require('./to-camel-case');
12 | const toSnakeCase = require('./to-snake-case');
13 | const wrapSubstitutions = require('./wrap-substitutions');
14 |
15 | /**
16 | * Export
17 | */
18 | module.exports = {
19 | arrayToJSON,
20 | convertKeys,
21 | deepClone,
22 | mergeData,
23 | splitNameEmail,
24 | toCamelCase,
25 | toSnakeCase,
26 | wrapSubstitutions,
27 | };
28 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/merge-data-deep.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Merge data deep helper
3 | */
4 | declare function mergeDataDeep(base: T, data: S): T & S;
5 |
6 | export = mergeDataDeep;
--------------------------------------------------------------------------------
/packages/helpers/helpers/merge-data-deep.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Merge data deep helper
5 | */
6 |
7 | function isObject(item) {
8 | return (item && typeof item === 'object' && !Array.isArray(item));
9 | }
10 |
11 | module.exports = function mergeDeep(base, data) {
12 | //Validate data
13 | if (typeof base !== 'object' || base === null) {
14 | throw new Error('Not an object provided for base');
15 | }
16 | if (typeof data !== 'object' || data === null) {
17 | throw new Error('Not an object provided for data');
18 | }
19 | let output = Object.assign({}, base);
20 | if (isObject(base) && isObject(data)) {
21 | Object.keys(data).forEach(key => {
22 | if (isObject(data[key])) {
23 | if (!(key in base)) {
24 | Object.assign(output, { [key]: data[key] });
25 | } else {
26 | output[key] = mergeDeep(base[key], data[key]);
27 | }
28 | } else {
29 | Object.assign(output, { [key]: data[key] });
30 | }
31 | });
32 | }
33 | return output;
34 | };
35 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/merge-data-deep.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const mergeDataDeep = require('./merge-data-deep');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('mergeDataDeep', function() {
12 |
13 | //Test objects
14 | const obj1 = {
15 | a: 1,
16 | b: 2,
17 | e: { g: 9 },
18 | arr: ['a', 'b'],
19 | };
20 | const obj2 = {
21 | a: 3,
22 | c: 3,
23 | d: 4,
24 | e: { f: 6 },
25 | arr: ['c'],
26 | };
27 |
28 | //Merge
29 | const merged = mergeDataDeep(obj1, obj2);
30 |
31 | //Tests
32 | it('should merge the two objects', function() {
33 | expect(merged).to.have.property('a');
34 | expect(merged).to.have.property('b');
35 | expect(merged).to.have.property('c');
36 | expect(merged).to.have.property('d');
37 | expect(merged).to.have.property('e');
38 | expect(merged.a).to.equal(3);
39 | expect(merged.e).to.have.property('f');
40 | expect(merged.e).to.have.property('g');
41 | });
42 | it('should throw on invalid input', function() {
43 | expect(function() {
44 | mergeDataDeep(null, obj2);
45 | }).to.throw(Error);
46 | expect(function() {
47 | mergeDataDeep(obj1, 4);
48 | }).to.throw(Error);
49 | });
50 | it('should overwrite arrays', function() {
51 | expect(merged).to.have.property('arr');
52 | expect(merged.arr).to.be.an.instanceof(Array);
53 | expect(merged.arr).to.have.lengthOf(1);
54 | expect(merged.arr[0]).to.equal('c');
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/merge-data.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Merge data helper
3 | */
4 | declare function mergeData(base: T, data: S): T&S;
5 |
6 | export = mergeData;
--------------------------------------------------------------------------------
/packages/helpers/helpers/merge-data.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Merge data helper
5 | */
6 | module.exports = function mergeData(base, data) {
7 |
8 | //Validate data
9 | if (typeof base !== 'object' || base === null) {
10 | throw new Error('Not an object provided for base');
11 | }
12 | if (typeof data !== 'object' || data === null) {
13 | throw new Error('Not an object provided for data');
14 | }
15 |
16 | //Copy base
17 | const merged = Object.assign({}, base);
18 |
19 | //Add data
20 | for (const key in data) {
21 | //istanbul ignore else
22 | if (data.hasOwnProperty(key)) {
23 | if (data[key] && Array.isArray(data[key])) {
24 | merged[key] = data[key];
25 | } else if (data[key] && typeof data[key] === 'object') {
26 | merged[key] = Object.assign({}, data[key]);
27 | } else if (data[key]) {
28 | merged[key] = data[key];
29 | }
30 | }
31 | }
32 |
33 | //Return
34 | return merged;
35 | };
36 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/merge-data.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const mergeData = require('./merge-data');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('mergeData', function() {
12 |
13 | //Test objects
14 | const obj1 = {
15 | a: 1,
16 | b: 2,
17 | arr: ['a', 'b'],
18 | };
19 | const obj2 = {
20 | c: 3,
21 | d: 4,
22 | e: {f: 6},
23 | arr: ['c'],
24 | };
25 |
26 | //Merge
27 | const merged = mergeData(obj1, obj2);
28 |
29 | //Tests
30 | it('should merge the two objects', function() {
31 | expect(merged).to.have.property('a');
32 | expect(merged).to.have.property('b');
33 | expect(merged).to.have.property('c');
34 | expect(merged).to.have.property('d');
35 | expect(merged).to.have.property('e');
36 | expect(merged.e).to.have.property('f');
37 | });
38 | it('should throw on invalid input', function() {
39 | expect(function() {
40 | mergeData(null, obj2);
41 | }).to.throw(Error);
42 | expect(function() {
43 | mergeData(obj1, 4);
44 | }).to.throw(Error);
45 | });
46 | it('should overwrite arrays', function() {
47 | expect(merged).to.have.property('arr');
48 | expect(merged.arr).to.be.an.instanceof(Array);
49 | expect(merged.arr).to.have.lengthOf(1);
50 | expect(merged.arr[0]).to.equal('c');
51 | });
52 | });
53 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/split-name-email.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Split name and email address from string
3 | */
4 | declare function splitNameEmail(str: string): string[];
5 |
6 | export = splitNameEmail;
--------------------------------------------------------------------------------
/packages/helpers/helpers/split-name-email.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Split name and email address from string
5 | */
6 | module.exports = function splitNameEmail(str) {
7 |
8 | //If no email bracket present, return as is
9 | if (str.indexOf('<') === -1) {
10 | return ['', str];
11 | }
12 |
13 | //Split into name and email
14 | let [name, email] = str.split('<');
15 |
16 | //Trim and fix up
17 | name = name.trim();
18 | email = email.replace('>', '').trim();
19 |
20 | //Return as array
21 | return [name, email];
22 | };
23 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/split-name-email.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const splitNameEmail = require('./split-name-email');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('splitNameEmail', function() {
12 | it('should not split strings without < symbol', function() {
13 | const [name, email] = splitNameEmail('test@test.com');
14 | expect(name).to.equal('');
15 | expect(email).to.equal('test@test.com');
16 | });
17 | it('should split strings with < symbol', function() {
18 | const [name, email] = splitNameEmail('Tester ');
19 | expect(name).to.equal('Tester');
20 | expect(email).to.equal('test@test.com');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/str-to-camel-case.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Internal conversion helper
5 | */
6 | module.exports = function strToCamelCase(str) {
7 | if (typeof str !== 'string') {
8 | throw new Error('String expected for conversion to snake case');
9 | }
10 | return str
11 | .trim()
12 | .replace(/_+|\-+/g, ' ')
13 | .replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) {
14 | if (Number(match) === 0) {
15 | return '';
16 | }
17 | return (index === 0) ? match.toLowerCase() : match.toUpperCase();
18 | });
19 | };
20 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/str-to-camel-case.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const toCamelCase = require('./str-to-camel-case');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('toCamelCase', function() {
12 | it('should camel case an already camel cased string', function() {
13 | expect(toCamelCase('camelCase')).to.equal('camelCase');
14 | });
15 | it('should camel case a snake cased string', function() {
16 | expect(toCamelCase('camel_case')).to.equal('camelCase');
17 | });
18 | it('should camel case a dasherized string', function() {
19 | expect(toCamelCase('camel-case')).to.equal('camelCase');
20 | });
21 | it('should camel case a string with spaces', function() {
22 | expect(toCamelCase('camel case')).to.equal('camelCase');
23 | });
24 | it('should camel case a string with multiple spaces', function() {
25 | expect(toCamelCase('camel case')).to.equal('camelCase');
26 | expect(toCamelCase('camel ca se')).to.equal('camelCaSe');
27 | });
28 | it('should camel case a mixed string', function() {
29 | expect(toCamelCase('CamelCase With snake_case _and dash-erized -andCamel'))
30 | .to.equal('camelCaseWithSnakeCaseAndDashErizedAndCamel');
31 | expect(toCamelCase('camel_case With vari-ety andCamel'))
32 | .to.equal('camelCaseWithVariEtyAndCamel');
33 | });
34 | it('should lowercase single letters', function() {
35 | expect(toCamelCase('A')).to.equal('a');
36 | expect(toCamelCase('F')).to.equal('f');
37 | expect(toCamelCase('Z')).to.equal('z');
38 | });
39 | it('should trim and camel case properly with leading/trailing spaces', function() {
40 | expect(toCamelCase(' test_me ')).to.equal('testMe');
41 | expect(toCamelCase(' test_me')).to.equal('testMe');
42 | expect(toCamelCase('test_me ')).to.equal('testMe');
43 | expect(toCamelCase(' test_me ')).to.equal('testMe');
44 | });
45 | it('should throw an error for non string input', function() {
46 | expect(function() {
47 | toCamelCase(2);
48 | }).to.throw(Error);
49 | expect(function() {
50 | toCamelCase(null);
51 | }).to.throw(Error);
52 | });
53 | });
54 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/str-to-snake-case.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Internal conversion helper
5 | */
6 | module.exports = function strToSnakeCase(str) {
7 | if (typeof str !== 'string') {
8 | throw new Error('String expected for conversion to snake case');
9 | }
10 | return str.trim().replace(/(\s*\-*\b\w|[A-Z])/g, function($1) {
11 | $1 = $1.trim().toLowerCase().replace('-', '');
12 | return ($1[0] === '_' ? '' : '_') + $1;
13 | }).slice(1);
14 | };
15 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/str-to-snake-case.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const toSnakeCase = require('./str-to-snake-case');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('toSnakeCase', function() {
12 | it('should snake case an already snake cased string', function() {
13 | expect(toSnakeCase('snake_case')).to.equal('snake_case');
14 | });
15 | it('should snake case a camel cased string', function() {
16 | expect(toSnakeCase('snakeCase')).to.equal('snake_case');
17 | expect(toSnakeCase('SnakeCase')).to.equal('snake_case');
18 | expect(toSnakeCase('SnAkeCASe')).to.equal('sn_ake_c_a_se');
19 | });
20 | it('should snake case a dasherized string', function() {
21 | expect(toSnakeCase('snake-case')).to.equal('snake_case');
22 | expect(toSnakeCase('Snake-Case')).to.equal('snake_case');
23 | });
24 | it('should snake case a string with spaces', function() {
25 | expect(toSnakeCase('Snake Case')).to.equal('snake_case');
26 | });
27 | it('should snake case a string with multiple spaces', function() {
28 | expect(toSnakeCase('Snake Case')).to.equal('snake_case');
29 | expect(toSnakeCase('Snake Ca se')).to.equal('snake_ca_se');
30 | });
31 | it('should snake case a mixed string', function() {
32 | expect(toSnakeCase('Snake-Case mixEd Stri_ng te-st'))
33 | .to.equal('snake_case_mix_ed_stri_ng_te_st');
34 | expect(toSnakeCase('CamelCase With snake_case _and dash-erized -andCamel'))
35 | .to.equal('camel_case_with_snake_case_and_dash_erized_and_camel');
36 | });
37 | it('should lowercase single letters', function() {
38 | expect(toSnakeCase('A')).to.equal('a');
39 | expect(toSnakeCase('F')).to.equal('f');
40 | expect(toSnakeCase('Z')).to.equal('z');
41 | });
42 | it('should trim and snake case properly with leading/trailing spaces', function() {
43 | expect(toSnakeCase(' TestMe ')).to.equal('test_me');
44 | expect(toSnakeCase(' TestMe')).to.equal('test_me');
45 | expect(toSnakeCase('TestMe ')).to.equal('test_me');
46 | expect(toSnakeCase(' TestMe ')).to.equal('test_me');
47 | });
48 | it('should throw an error for non string input', function() {
49 | expect(function() {
50 | toSnakeCase(2);
51 | }).to.throw(Error);
52 | expect(function() {
53 | toSnakeCase(null);
54 | }).to.throw(Error);
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/to-camel-case.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Convert object keys to camel case
3 | */
4 | declare function toCamelCase(obj: T, ignored?: string[]): S;
5 |
6 | export = toCamelCase;
--------------------------------------------------------------------------------
/packages/helpers/helpers/to-camel-case.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const convertKeys = require('./convert-keys');
7 | const strToCamelCase = require('./str-to-camel-case');
8 |
9 | /**
10 | * Convert object keys to camel case
11 | */
12 | module.exports = function toCamelCase(obj, ignored) {
13 | return convertKeys(obj, strToCamelCase, ignored);
14 | };
15 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/to-snake-case.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Convert object keys to snake case
3 | */
4 | declare function toSnakeCase(obj: T, ignored?: string[]): S;
5 |
6 | export = toSnakeCase;
--------------------------------------------------------------------------------
/packages/helpers/helpers/to-snake-case.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const convertKeys = require('./convert-keys');
7 | const strToSnakeCase = require('./str-to-snake-case');
8 |
9 | /**
10 | * Convert object keys to snake case
11 | */
12 | module.exports = function toSnakeCase(obj, ignored) {
13 | return convertKeys(obj, strToSnakeCase, ignored);
14 | };
15 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/validate-settings.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const validate = (parent, parentName, childName, childType) => {
4 | if (typeof parent === 'undefined' || typeof parent[childName] === 'undefined') {
5 | return;
6 | }
7 | if (typeof parent[childName] !== childType) {
8 | throw new Error(`${childType} expected for \`${parentName}.${childName}\``)
9 | }
10 | };
11 |
12 | module.exports = {
13 | validateMailSettings(settings) {
14 | if (typeof settings !== 'object') {
15 | throw new Error('Object expected for `mailSettings`');
16 | }
17 | const {
18 | bcc,
19 | bypassListManagement,
20 | bypassSpamManagement,
21 | bypassBounceManagement,
22 | bypassUnsubscribeManagement,
23 | footer,
24 | sandboxMode,
25 | spamCheck,
26 | } = settings;
27 | validate(bcc, 'bcc', 'enable', 'boolean');
28 | validate(bcc, 'bcc', 'email', 'string');
29 | validate(bypassListManagement, 'bypassListManagement', 'enable', 'boolean');
30 | validate(bypassSpamManagement, 'bypassSpamManagement', 'enable', 'boolean');
31 | validate(bypassBounceManagement, 'bypassBounceManagement', 'enable', 'boolean');
32 | validate(bypassUnsubscribeManagement, 'bypassUnsubscribeManagement', 'enable', 'boolean');
33 | validate(footer, 'footer', 'enable', 'boolean');
34 | validate(footer, 'footer', 'text', 'string');
35 | validate(footer, 'footer', 'html', 'string');
36 | validate(sandboxMode, 'sandboxMode', 'enable', 'boolean');
37 | validate(spamCheck, 'spamCheck', 'enable', 'boolean');
38 | validate(spamCheck, 'spamCheck', 'threshold', 'number');
39 | validate(spamCheck, 'spamCheck', 'postToUrl', 'string');
40 | },
41 |
42 | validateTrackingSettings(settings) {
43 | if (typeof settings !== 'object') {
44 | throw new Error('Object expected for `trackingSettings`');
45 | }
46 | const {
47 | clickTracking,
48 | openTracking,
49 | subscriptionTracking,
50 | ganalytics,
51 | } = settings;
52 | validate(clickTracking, 'clickTracking', 'enable', 'boolean');
53 | validate(clickTracking, 'clickTracking', 'enableText', 'boolean');
54 | validate(openTracking, 'openTracking', 'enable', 'boolean');
55 | validate(openTracking, 'openTracking', 'substitutionTag', 'string');
56 | validate(subscriptionTracking, 'subscriptionTracking', 'enable', 'boolean');
57 | validate(subscriptionTracking, 'subscriptionTracking', 'text', 'string');
58 | validate(subscriptionTracking, 'subscriptionTracking', 'html', 'string');
59 | validate(subscriptionTracking, 'subscriptionTracking', 'substitutionTag', 'string');
60 | validate(ganalytics, 'ganalytics', 'enable', 'boolean');
61 | validate(ganalytics, 'ganalytics', 'utm_source', 'string');
62 | validate(ganalytics, 'ganalytics', 'utm_medium', 'string');
63 | validate(ganalytics, 'ganalytics', 'utm_term', 'string');
64 | validate(ganalytics, 'ganalytics', 'utm_content', 'string');
65 | validate(ganalytics, 'ganalytics', 'utm_campaign', 'string');
66 | },
67 | };
68 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/wrap-substitutions.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Wrap substitutions
3 | */
4 | declare function wrapSubstitutions(substitutions: { [key: string]: string }, left?: string, right?: string): { [key: string]: string };
5 |
6 | export = wrapSubstitutions;
7 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/wrap-substitutions.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Wrap substitutions
5 | */
6 | module.exports = function wrap(substitutions, left = '{{', right = '}}') {
7 |
8 | //Process arrays
9 | if (Array.isArray(substitutions)) {
10 | return substitutions.map(subs => wrap(subs, left, right));
11 | }
12 |
13 | //Initialize new wrapped object
14 | const wrapped = {};
15 |
16 | //Map substitutions and ensure string for value
17 | for (const key in substitutions) {
18 | //istanbul ignore else
19 | if (substitutions.hasOwnProperty(key)) {
20 | wrapped[left + key + right] = String(substitutions[key]);
21 | }
22 | }
23 |
24 | //Return wrapped substitutions
25 | return wrapped;
26 | };
27 |
--------------------------------------------------------------------------------
/packages/helpers/helpers/wrap-substitutions.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const wrapSubstitutions = require('./wrap-substitutions');
7 |
8 | /**
9 | * Tests
10 | */
11 | describe('wrapSubstitutions', function() {
12 |
13 | //Test substitutions
14 | const test = {
15 | some: 'string',
16 | someOther: 'string',
17 | 'and-a-number': 3,
18 | };
19 |
20 | //Test array of substitutions
21 | const arr = [test, test];
22 |
23 | //Left/right substitutions
24 | const left1 = '{{';
25 | const right1 = '}}';
26 | const left2 = '[';
27 | const right2 = ']';
28 |
29 | //Wrap
30 | const wrap1 = wrapSubstitutions(test, left1, right1);
31 | const wrap2 = wrapSubstitutions(test, left2, right2);
32 | const wrapArr = wrapSubstitutions(arr, left1, right1);
33 | const wrapDefault = wrapSubstitutions(test);
34 |
35 | //Tests
36 | it('should wrap the substitution keys with the relevant wrapper', function() {
37 | expect(wrap1).to.have.property('{{some}}');
38 | expect(wrap1).to.have.property('{{someOther}}');
39 | expect(wrap1).to.have.property('{{and-a-number}}');
40 | expect(wrap2).to.have.property('[some]');
41 | expect(wrap2).to.have.property('[someOther]');
42 | expect(wrap2).to.have.property('[and-a-number]');
43 | });
44 | it('should not preserve the original keys', function() {
45 | expect(wrap1).not.to.have.property('some');
46 | expect(wrap1).not.to.have.property('someOther');
47 | expect(wrap1).not.to.have.property('and-a-number');
48 | });
49 | it('should convert substitutions to strings', function() {
50 | expect(wrap1['{{some}}']).to.be.a('string');
51 | expect(wrap1['{{and-a-number}}']).to.be.a('string');
52 | });
53 | it('should convert an array of substitutions', function() {
54 | expect(wrapArr).to.be.an('array');
55 | expect(wrapArr[0]).to.have.property('{{some}}');
56 | expect(wrapArr[1]).to.have.property('{{some}}');
57 | });
58 | it('should default to the {{}} wrapper', function() {
59 | expect(wrapDefault).to.have.property('{{some}}');
60 | expect(wrapDefault).to.have.property('{{someOther}}');
61 | expect(wrapDefault).to.have.property('{{and-a-number}}');
62 | });
63 | });
64 |
--------------------------------------------------------------------------------
/packages/helpers/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as helpers from '@sendgrid/helpers/helpers/index';
2 | import * as classes from '@sendgrid/helpers/classes/index';
3 |
4 | export {
5 | helpers,
6 | classes,
7 | };
8 |
--------------------------------------------------------------------------------
/packages/helpers/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Load support assets
5 | */
6 | const classes = require('./classes');
7 | const helpers = require('./helpers');
8 |
9 | /**
10 | * Export
11 | */
12 | module.exports = {classes, helpers};
13 |
--------------------------------------------------------------------------------
/packages/helpers/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/helpers",
3 | "description": "Twilio SendGrid NodeJS internal helpers",
4 | "version": "8.0.0",
5 | "author": "Twilio SendGrid (sendgrid.com)",
6 | "contributors": [
7 | "Kyle Partridge ",
8 | "David Tomberlin ",
9 | "Swift ",
10 | "Brandon West ",
11 | "Scott Motte ",
12 | "Robert Acosta ",
13 | "Elmer Thomas ",
14 | "Adam Reis "
15 | ],
16 | "license": "MIT",
17 | "homepage": "https://sendgrid.com",
18 | "repository": {
19 | "type": "git",
20 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
21 | },
22 | "publishConfig": {
23 | "access": "public"
24 | },
25 | "engines": {
26 | "node": ">= 12.0.0"
27 | },
28 | "tags": [
29 | "sendgrid",
30 | "helpers"
31 | ],
32 | "dependencies": {
33 | "deepmerge": "^4.2.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/inbound-mail-parser/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.com/sendgrid/sendgrid-nodejs)
2 | [](https://www.npmjs.com/org/sendgrid)
3 |
4 | **This package is part of a monorepo, please see [this README](../../README.md) for details.**
5 |
6 | # Inbound Parse Service for the [SendGrid Inbound Parse API](https://sendgrid.com/docs/API_Reference/Parse_Webhook/inbound_email.html)
7 | This package helps get you started consuming and processing [Inbound Parse](https://sendgrid.com/docs/API_Reference/Parse_Webhook/inbound_email.html) data.
8 |
9 | ## Prerequisites
10 |
11 | - Node.js version 6, 8 or >=10
12 | - A Twilio SendGrid account, [sign up for free](https://sendgrid.com/free?source=sendgrid-nodejs) to send up to 40,000 emails for the first 30 days or check out [our pricing](https://sendgrid.com/pricing?source=sendgrid-nodejs).
13 |
14 | ## Obtain an API Key
15 |
16 | Grab your API Key from the [Twilio SendGrid UI](https://app.sendgrid.com/settings/api_keys).
17 |
18 | # Install Package
19 |
20 | The following recommended installation requires [npm](https://npmjs.org/). If you are unfamiliar with npm, see the [npm docs](https://npmjs.org/doc/). Npm comes installed with Node.js since node version 0.8.x, therefore, you likely already have it.
21 |
22 | ```sh
23 | npm install --save @sendgrid/inbound-mail-parser
24 | ```
25 |
26 | You may also use [yarn](https://yarnpkg.com/en/) to install.
27 |
28 | ```sh
29 | yarn add @sendgrid/inbound-mail-parser
30 | ```
31 |
32 |
33 | # How to Contribute
34 |
35 | We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING](https://github.com/sendgrid/sendgrid-nodejs/blob/HEAD/CONTRIBUTING.md) guide for details.
36 |
37 | * [Feature Request](../../CONTRIBUTING.md#feature-request)
38 | * [Bug Reports](../../CONTRIBUTING.md#submit-a-bug-report)
39 | * [Improvements to the Codebase](../../CONTRIBUTING.md#improvements-to-the-codebase)
40 |
41 |
42 | # Troubleshooting
43 |
44 | Please see our [troubleshooting guide](https://github.com/sendgrid/sendgrid-nodejs/blob/main/TROUBLESHOOTING.md) for common library issues.
45 |
46 |
47 | # About
48 |
49 | @sendgrid/inbound-mail-parser is maintained and funded by Twilio SendGrid, Inc. The names and logos for @sendgrid/inbound-mail-parser are trademarks of Twilio SendGrid, Inc.
50 |
51 | If you need help installing or using the library, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com).
52 |
53 | If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo!
54 |
55 | 
56 |
--------------------------------------------------------------------------------
/packages/inbound-mail-parser/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as Parser from "./src/parser";
2 |
3 | export = Parser;
--------------------------------------------------------------------------------
/packages/inbound-mail-parser/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/inbound-mail-parser",
3 | "description": "Twilio SendGrid NodeJS inbound mail parser",
4 | "version": "8.0.0",
5 | "author": "Twilio SendGrid (sendgrid.com)",
6 | "contributors": [
7 | "Kyle Partridge ",
8 | "David Tomberlin ",
9 | "Swift ",
10 | "Brandon West ",
11 | "Scott Motte ",
12 | "Robert Acosta ",
13 | "Elmer Thomas "
14 | ],
15 | "license": "MIT",
16 | "homepage": "https://sendgrid.com",
17 | "repository": {
18 | "type": "git",
19 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
20 | },
21 | "publishConfig": {
22 | "access": "public"
23 | },
24 | "main": "src/parser.js",
25 | "engines": {
26 | "node": ">=12.*"
27 | },
28 | "dependencies": {
29 | "@sendgrid/helpers": "^8.0.0",
30 | "mailparser": "^2.3.4"
31 | },
32 | "tags": [
33 | "sendgrid"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/packages/inbound-mail-parser/src/parser.d.ts:
--------------------------------------------------------------------------------
1 | import {Attachment} from "@sendgrid/helpers/classes"
2 |
3 | declare interface ParseConfig {
4 | keys: string[];
5 | }
6 |
7 | declare interface ParseRequest {
8 | body?: {};
9 | payload?: {};
10 | files?: any[];
11 | }
12 |
13 | declare class Parse {
14 | constructor(config: ParseConfig, request: ParseRequest);
15 |
16 | /**
17 | * Return an object literal of key/values in the payload received from webhook
18 | * @return {Object} Valid key/values in the webhook payload
19 | */
20 | keyValues(): { [key: string]: any };
21 |
22 | /**
23 | * Whether the payload contains the raw email (Only applies to raw payloads)
24 | * @return {Boolean}
25 | */
26 | hasRawEmail(): boolean;
27 |
28 | /**
29 | * Parses the raw email and returns the mail object in a callback (Only applies to raw payloads)
30 | * @param {Function} callback Function which will receive the parsed email object as the sole argument
31 | */
32 | getRawEmail(callback: (mail: any) => void): void;
33 | // TODO: Type information for MailParser result in callback
34 |
35 | /**
36 | * Retrieves all attachments received from the webhook payload
37 | * @param {Function} callback Function which will receive an array, of attachments found, as the sole argument
38 | */
39 | attachments(callback: (attachments: Attachment[]) => void): void;
40 | }
41 |
42 | export = Parse;
--------------------------------------------------------------------------------
/packages/inbound-mail-parser/src/parser.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const { MailParser } = require('mailparser');
5 | const {
6 | classes: {
7 | Attachment,
8 | },
9 | } = require('@sendgrid/helpers');
10 |
11 | /**
12 | * Normalises attachment files retrieved from file system or parsed raw email
13 | *
14 | * @param {Object} file The file object returned by file system or parsed email
15 | * @return {Object} A Twilio SendGrid Attachment object with the file data
16 | */
17 | const createAttachment = (file) => {
18 | const {originalname, fileName, mimetype, contentType, content} = file;
19 | const attachment = new Attachment();
20 |
21 | attachment.setFilename(originalname || fileName);
22 | attachment.setType(mimetype || contentType);
23 | attachment.setContent(content.toString('base64'));
24 |
25 | return attachment;
26 | };
27 |
28 | /**
29 | * Simple class that parses data received from the Twilio SendGrid Inbound Parse Webhook
30 | *
31 | */
32 | class Parse {
33 |
34 | /**
35 | * @constructor
36 | * @param {Object} config inbound configuration object
37 | * @param {Object} request request object of the parse webhook payload
38 | */
39 | constructor(config, request) {
40 | this.keys = config.keys;
41 | this.request = request;
42 | this.payload = request.body || request.payload || {};
43 | this.files = request.files || [];
44 | }
45 |
46 | /**
47 | * Return an object literal of key/values in the payload received from webhook
48 | * @return {Object} Valid key/values in the webhook payload
49 | */
50 | keyValues() {
51 | return this.keys
52 | .filter(key => this.payload[key])
53 | .map(key => ({ [key]: this.payload[key] }))
54 | .reduce((keyValues, keyPayload) => Object.assign(keyValues, keyPayload));
55 | }
56 |
57 | /**
58 | * Whether the payload contains the raw email (Only applies to raw payloads)
59 | * @return {Boolean}
60 | */
61 | hasRawEmail() {
62 | return !!this.payload.email;
63 | }
64 |
65 | /**
66 | * Parses the raw email and returns the mail object in a callback (Only applies to raw payloads)
67 | * @param {Function} callback Function which will receive the parsed email object as the sole argument
68 | */
69 | getRawEmail(callback) {
70 | const mailparser = new MailParser();
71 | const { rawEmail } = this.payload;
72 |
73 | if (!this.hasRawEmail()) {
74 | return callback(null);
75 | }
76 |
77 | mailparser.on('end', callback);
78 | mailparser.write(rawEmail);
79 | mailparser.end();
80 | }
81 |
82 | /**
83 | * Retrieves all attachments received from the webhook payload
84 | * @param {Function} callback Function which will receive an array, of attachments found, as the sole argument
85 | */
86 | attachments(callback) {
87 | return this[`_getAttachments${this.hasRawEmail() ? 'Raw' : ''}`](callback);
88 | }
89 |
90 | /**
91 | * Parses raw email to retrieve any encoded attachments (Only applies to raw payloads)
92 | * @private
93 | * @param {Function} callback Function which will receive an array, of attachments found, as the sole argument
94 | */
95 | _getAttachmentsRaw(callback) {
96 | this.getRawEmail(parsedEmail => {
97 | const attachments = (parsedEmail || {}).attachments || [];
98 | callback(attachments.map(createAttachment));
99 | });
100 | }
101 |
102 | /**
103 | * Retrieves webhook payload files from the file system (Only applies to non raw payloads)
104 | * @private
105 | * @param {Function} callback Function which will receive an array, of attachments found, as the sole argument
106 | */
107 | _getAttachments(callback) {
108 | return callback(this.files
109 | .filter(file => fs.existsSync(file.path))
110 | .map((exists, idx) => [exists, this.files[idx]])
111 | .filter(([exists, _]) => exists)
112 | .map(([_, file]) => {
113 | file.content = fs.readFileSync(file.path);
114 | return createAttachment(file);
115 | })
116 | );
117 | }
118 | }
119 |
120 | module.exports = Parse;
121 |
--------------------------------------------------------------------------------
/packages/inbound-mail-parser/src/parser.spec.js:
--------------------------------------------------------------------------------
1 | const Parse = require('./parser');
2 |
3 | describe('test_parse', () => {
4 | describe('test_parse_key_values', () => {
5 | it('should return the key values specified in the config from the body', () => {
6 | const config = {
7 | keys: ['to', 'from'],
8 | };
9 | const request = {
10 | body: {
11 | to: 'inbound@inbound.example.com',
12 | from: 'Test User ',
13 | subject: 'Test Subject',
14 | },
15 | };
16 |
17 | const parse = new Parse(config, request);
18 | const keyValues = parse.keyValues();
19 | const expectedValues = {
20 | to: 'inbound@inbound.example.com',
21 | from: 'Test User ',
22 | };
23 |
24 | expect(keyValues).to.be.an('object');
25 | expect(keyValues).to.deep.equal(expectedValues);
26 | });
27 |
28 | it('should return the key values specified in the config from the payload', () => {
29 | const config = {
30 | keys: ['to', 'from'],
31 | };
32 | const request = {
33 | payload: {
34 | to: 'inbound@inbound.example.com',
35 | from: 'Test User ',
36 | subject: 'Test Subject',
37 | },
38 | };
39 |
40 | const parse = new Parse(config, request);
41 | const keyValues = parse.keyValues();
42 | const expectedValues = {
43 | to: 'inbound@inbound.example.com',
44 | from: 'Test User ',
45 | };
46 |
47 | expect(keyValues).to.be.an('object');
48 | expect(keyValues).to.deep.equal(expectedValues);
49 | });
50 | });
51 |
52 | describe('test_parse_get_raw_email', () => {
53 | it('should return null if no raw email property in payload', (done) => {
54 | const parse = new Parse({}, {});
55 |
56 | function callback(email) {
57 | expect(email).to.be.null();
58 | done();
59 | }
60 |
61 | parse.getRawEmail(callback);
62 | });
63 |
64 | it('should parse raw email from payload and return a mail object', (done) => {
65 | const request = {
66 | body: {
67 | email: 'MIME-Version: 1.0\r\nReceived: by 0.0.0.0 with HTTP; Wed, 10 Aug 2016 14:44:21 -0700 (PDT)\r\nFrom: Example User \r\nDate: Wed, 10 Aug 2016 14:44:21 -0700\r\nSubject: Inbound Parse Test Raw Data\r\nTo: inbound@inbound.inbound.com\r\nContent-Type: multipart/alternative; boundary=001a113ee97c89842f0539be8e7a\r\n\r\n--001a113ee97c89842f0539be8e7a\r\nContent-Type: text/plain; charset=UTF-8\r\n\r\nHello Twilio SendGrid!\r\n\r\n--001a113ee97c89842f0539be8e7a\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nHello Twilio SendGrid!\r\n\r\n--001a113ee97c89842f0539be8e7a--\r\n',
68 | },
69 | };
70 |
71 | const parse = new Parse({}, request);
72 |
73 | function callback(email) {
74 | expect(email).to.be.an('object');
75 | done();
76 | }
77 |
78 | parse.getRawEmail(callback);
79 | });
80 | });
81 | });
82 |
--------------------------------------------------------------------------------
/packages/mail/USE_CASES.md:
--------------------------------------------------------------------------------
1 | Please see the Email Use Cases [here](../../docs/use-cases/README.md#email-use-cases).
2 |
--------------------------------------------------------------------------------
/packages/mail/index.d.ts:
--------------------------------------------------------------------------------
1 | import MailService = require("@sendgrid/mail/src/mail");
2 |
3 | export = MailService;
--------------------------------------------------------------------------------
/packages/mail/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const mailer = require('./src/mail');
4 | const MailService = require('./src/classes/mail-service');
5 |
6 | module.exports = mailer;
7 | module.exports.MailService = MailService;
8 |
--------------------------------------------------------------------------------
/packages/mail/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/mail",
3 | "description": "Twilio SendGrid NodeJS mail service",
4 | "version": "8.1.5",
5 | "author": "Twilio SendGrid (sendgrid.com)",
6 | "contributors": [
7 | "Kyle Partridge ",
8 | "David Tomberlin ",
9 | "Swift ",
10 | "Brandon West ",
11 | "Scott Motte ",
12 | "Robert Acosta ",
13 | "Elmer Thomas ",
14 | "Adam Reis "
15 | ],
16 | "license": "MIT",
17 | "homepage": "https://sendgrid.com",
18 | "repository": {
19 | "type": "git",
20 | "url": "git://github.com/sendgrid/sendgrid-nodejs.git"
21 | },
22 | "main": "index.js",
23 | "engines": {
24 | "node": ">=12.*"
25 | },
26 | "publishConfig": {
27 | "access": "public"
28 | },
29 | "dependencies": {
30 | "@sendgrid/client": "^8.1.5",
31 | "@sendgrid/helpers": "^8.0.0"
32 | },
33 | "tags": [
34 | "http",
35 | "rest",
36 | "api",
37 | "mail",
38 | "sendgrid"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/packages/mail/src/mail.d.ts:
--------------------------------------------------------------------------------
1 | import {Client} from "@sendgrid/client";
2 | import {ClientResponse} from "@sendgrid/client/src/response";
3 | import {ResponseError} from "@sendgrid/helpers/classes";
4 | import {MailDataRequired} from "@sendgrid/helpers/classes/mail";
5 |
6 | declare class MailService {
7 | /**
8 | * SendGrid API key passthrough for convenience.
9 | */
10 | setApiKey(apiKey: string): void;
11 |
12 | /**
13 | * Client to use for invoking underlying API
14 | */
15 | setClient(client: Client): void;
16 |
17 | /**
18 | * Twilio Email Auth passthrough for convenience.
19 | */
20 | setTwilioEmailAuth(username: string, password: string): void;
21 |
22 | /**
23 | * Set the default request timeout (in milliseconds).
24 | */
25 | setTimeout(timeout: number): void;
26 |
27 | /**
28 | * Set substitution wrappers
29 | */
30 | setSubstitutionWrappers(left: string, right: string): void;
31 |
32 | /**
33 | * Send email
34 | */
35 | send(data: MailDataRequired | MailDataRequired[], isMultiple?: boolean, cb?: (err: Error | ResponseError, result: [ClientResponse, {}]) => void): Promise<[ClientResponse, {}]>;
36 |
37 | /**
38 | * Send multiple emails (shortcut)
39 | */
40 | sendMultiple(data: MailDataRequired, cb?: (error: Error | ResponseError, result: [ClientResponse, {}]) => void): Promise<[ClientResponse, {}]>;
41 | }
42 |
43 | declare const mail: MailService;
44 | // @ts-ignore
45 | export = mail;
46 |
47 | export {MailService};
48 | export {MailDataRequired};
49 | export {ClientResponse};
50 | export {ResponseError};
51 |
--------------------------------------------------------------------------------
/packages/mail/src/mail.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const MailService = require('./classes/mail-service');
7 |
8 | //Export singleton instance
9 | module.exports = new MailService();
10 |
--------------------------------------------------------------------------------
/packages/mail/src/mail.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Dependencies
5 | */
6 | const sgMail = require('./mail');
7 | const sgClient = sgMail.client;
8 |
9 | /**
10 | * Setup client
11 | */
12 | before(() => {
13 | sgClient.setApiKey('SendGrid API Key');
14 | });
15 |
16 | /**
17 | * Default mock header
18 | */
19 | beforeEach(() => {
20 | sgClient.setDefaultHeader('X-Mock', 202);
21 | });
22 |
23 | /**
24 | * Tests
25 | */
26 | describe('sgMail.send()', () => {
27 |
28 | //Create mail data
29 | const data = {
30 | to: 'recipient@example.org',
31 | from: 'sender@example.org',
32 | subject: 'Hello world',
33 | text: 'Hello plain world!',
34 | html: 'Hello HTML world!
',
35 | };
36 |
37 | it('should throw an error when no data provided', () => {
38 | return expect(sgMail.send()).to.eventually.be.rejectedWith(Error);
39 | });
40 |
41 | it('should send a basic email', () => {
42 | sgClient.setDefaultHeader('X-Mock', 202);
43 | return sgMail
44 | .send(data)
45 | .then(([response, body]) => {
46 | expect(response.statusCode).to.equal(202);
47 | });
48 | });
49 |
50 | it('should throw an error if callback is not a function', () => {
51 | return expect(function() {
52 | sgMail.send(data, false, {});
53 | }).to.throw(Error);
54 | });
55 |
56 | it('should include custom headers to the request', () => {
57 | sgClient.setDefaultHeader('X-Mock', 202);
58 | const clientSpy = sinon.spy(sgClient, "request")
59 | return sgMail
60 | .send(Object.assign(data, { headers: { customHeader: "Custom Header Content" } }))
61 | .then(([response, body]) => {
62 | expect(response.statusCode).to.equal(202);
63 | expect(clientSpy).to.have.been.calledWith(sinon.match({
64 | url: "/v3/mail/send",
65 | method: "POST",
66 | headers: { customHeader: "Custom Header Content" }
67 | }));
68 | });
69 | });
70 |
71 | it('should send email with correct replyToList format', () => {
72 | sgClient.setDefaultHeader('X-Mock', 202);
73 | data["replyToList"] = [
74 | {
75 | "name": "Test Team",
76 | "email": "test@example.org"
77 | },
78 | {
79 | "name": "Support Test Team",
80 | "email": "support.test@example.org"
81 | }
82 | ];
83 | return sgMail
84 | .send(data)
85 | .then(([response, body]) => {
86 | expect(response.statusCode).to.equal(202);
87 | });
88 | });
89 |
90 | it('should throw error with wrong replyToList format', () => {
91 | sgClient.setDefaultHeader('X-Mock', 202);
92 | data["replyToList"] = {
93 | "name": "Support Test Team",
94 | "email": "support.test@example.org"
95 | };
96 | return expect(function() {
97 | sgMail.send(data, false, {});
98 | }).to.throw(Error);
99 | });
100 |
101 | it('should throw error if any record in replyToList is without email', () => {
102 | data["replyToList"] = [
103 | {
104 | "name": "Test Team",
105 | "email": "test@example.org"
106 | },
107 | {
108 | "name": "Support Test Team"
109 | }
110 | ];
111 | return expect(function() {
112 | sgMail.send(data, false, {});
113 | }).to.throw(Error);
114 | });
115 |
116 | it('should throw error if both replyTo and replyToList are mentioned', () => {
117 | data["replyTo"] = {
118 | "name": "Manual Tester",
119 | "email": "manual.test@example.org"
120 | };
121 | data["replyToList"] = [
122 | {
123 | "name": "Test Team",
124 | "email": "test@example.org"
125 | },
126 | {
127 | "name": "Support Test Team",
128 | "email": "support.test@example.org"
129 | }
130 | ];
131 | return expect(function() {
132 | sgMail.send(data, false, {});
133 | }).to.throw(Error);
134 | });
135 | });
136 |
137 |
--------------------------------------------------------------------------------
/packages/subscription-widget/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
--------------------------------------------------------------------------------
/packages/subscription-widget/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2012-2017 SendGrid, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/packages/subscription-widget/Procfile:
--------------------------------------------------------------------------------
1 | web: node index.js
--------------------------------------------------------------------------------
/packages/subscription-widget/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Email Double Opt-In",
3 | "description": "Double opt-in email automation",
4 | "repository": "https://github.com/devchas/sendgrid_subscription_widget",
5 | "keywords": ["email", "SendGrid", "Twilio"]
6 | }
--------------------------------------------------------------------------------
/packages/subscription-widget/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const http = require('http');
3 | const bodyParser = require('body-parser');
4 | const morgan = require('morgan');
5 | const app = express();
6 | const router = require('./server/router');
7 |
8 | // App setup
9 | app.use(morgan('combined'));
10 | app.use(bodyParser.json());
11 | app.use(bodyParser.urlencoded({ extended: true }));
12 | router(app);
13 |
14 | // Server setup
15 | const port = process.env.PORT || 3090;
16 | const server = http.createServer(app);
17 | server.listen(port);
18 | console.log('Server listening on:', port);
19 |
--------------------------------------------------------------------------------
/packages/subscription-widget/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@sendgrid/subscription-widget",
3 | "version": "8.0.0",
4 | "description": "Double opt-in email automation",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "dev": "nodemon index.js"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "body-parser": "^1.15.2",
14 | "express": "^4.14.0",
15 | "morgan": "^1.9.1",
16 | "nodemon": "^1.9.2",
17 | "sendgrid": "^4.0.2",
18 | "sendgrid-rest": "^2.5.0"
19 | },
20 | "publishConfig": {
21 | "access": "public"
22 | },
23 | "engines": {
24 | "node": ">=12.*"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/subscription-widget/server/router.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const ContactList = require('./controllers/contact_list_controller');
3 |
4 | module.exports = function(app) {
5 | app.get('/', function(req, res) {
6 | res.sendFile(path.join(__dirname, '/static/index.html'));
7 | });
8 | app.get('/success', function(req, res) {
9 | res.sendFile(path.join(__dirname, '/static/success.html'));
10 | });
11 | app.post('/confirmEmail', ContactList.sendConfirmation);
12 | app.post('/signup', ContactList.addUser);
13 | };
14 |
--------------------------------------------------------------------------------
/packages/subscription-widget/server/static/check-inbox.html:
--------------------------------------------------------------------------------
1 | Please check your inbox for an email containing a link to verify your email address.
--------------------------------------------------------------------------------
/packages/subscription-widget/server/static/error.html:
--------------------------------------------------------------------------------
1 | Looks like something went wrong. Please try again. Please be sure to enter a valid email address.
--------------------------------------------------------------------------------
/packages/subscription-widget/server/static/index.html:
--------------------------------------------------------------------------------
1 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/packages/subscription-widget/server/static/sample-form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/sendgrid-nodejs/2bac86884f71be3fb19f96a10c02a1fb616b81fc/packages/subscription-widget/server/static/sample-form.png
--------------------------------------------------------------------------------
/packages/subscription-widget/server/static/success.html:
--------------------------------------------------------------------------------
1 | Thank you for confirming your email address. You've been added to our mailing list.
--------------------------------------------------------------------------------
/packages/subscription-widget/server/static/template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/sendgrid-nodejs/2bac86884f71be3fb19f96a10c02a1fb616b81fc/packages/subscription-widget/server/static/template.png
--------------------------------------------------------------------------------
/packages/subscription-widget/settings.js:
--------------------------------------------------------------------------------
1 | // Change the url to the domain of your app
2 | exports.url = 'http://localhost:3090';
3 |
4 | exports.senderEmail = 'sender@example.com';
5 | exports.senderName = 'Sender Name';
6 |
7 | // set 'exports.listId = null' to add contact to all contacts, but no specific list
8 | // or a string with the listId to add to a specific list
9 | exports.listId = null;
10 |
11 | // set 'exports.templateId = null' to opt out of using a template
12 | // or a string with the templateId to use a template
13 | exports.templateId = null;
14 |
15 | // receive an email when a new signup is confirmed
16 | exports.sendNotification = true;
17 | exports.notificationEmail = 'admin@example.com';
18 |
--------------------------------------------------------------------------------
/static/img/github-fork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/sendgrid-nodejs/2bac86884f71be3fb19f96a10c02a1fb616b81fc/static/img/github-fork.png
--------------------------------------------------------------------------------
/static/img/github-sign-up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/sendgrid-nodejs/2bac86884f71be3fb19f96a10c02a1fb616b81fc/static/img/github-sign-up.png
--------------------------------------------------------------------------------
/test/files.spec.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var fs = require('fs');
3 |
4 | describe('sendgrid-nodejs repo', function() {
5 | /*
6 | it('should have ./Docker or docker/Docker file', function() {
7 | assert(fileExists('Docker') || fileExists('docker/Docker'));
8 | });
9 |
10 | it('should have ./docker-compose.yml or ./docker/docker-compose.yml file', function() {
11 | assert(fileExists('docker-compose.yml') || fileExists('docker/docker-compose.yml'));
12 | });
13 | */
14 |
15 | it('should have ./.env_sample file', function() {
16 | assert(fileExists('.env_sample'));
17 | });
18 |
19 | it('should have ./.gitignore file', function() {
20 | assert(fileExists('.gitignore'));
21 | });
22 |
23 | it('should have ./.github/workflows/test-and-deploy file', function() {
24 | assert(fileExists('.github/workflows/test-and-deploy.yml'));
25 | });
26 |
27 | it('should have ./CHANGELOG.md file', function() {
28 | assert(fileExists('CHANGELOG.md'));
29 | });
30 |
31 | it('should have ./CODE_OF_CONDUCT.md file', function() {
32 | assert(fileExists('CODE_OF_CONDUCT.md'));
33 | });
34 |
35 | it('should have ./CONTRIBUTING.md file', function() {
36 | assert(fileExists('CONTRIBUTING.md'));
37 | });
38 |
39 | it('should have ./LICENSE file', function() {
40 | assert(fileExists('LICENSE'));
41 | });
42 |
43 | it('should have ./PULL_REQUEST_TEMPLATE.md file', function() {
44 | assert(fileExists('PULL_REQUEST_TEMPLATE.md'));
45 | });
46 |
47 | it('should have ./README.md file', function() {
48 | assert(fileExists('README.md'));
49 | });
50 |
51 | it('should have ./TROUBLESHOOTING.md file', function() {
52 | assert(fileExists('TROUBLESHOOTING.md'));
53 | });
54 |
55 | it('should have ./USAGE.md file', function() {
56 | assert(fileExists('USAGE.md'));
57 | });
58 |
59 | /*
60 | it('should have ./USE_CASES.md file', function() {
61 | assert(fileExists('USE_CASES.md'));
62 | });
63 | */
64 |
65 | function fileExists(filepath) {
66 | try {
67 | return fs.statSync(filepath).isFile();
68 | } catch(e) {
69 | if (e.code === 'ENOENT') {
70 | console.log('' + filepath + ' doesn\'t exist.');
71 | return false;
72 | }
73 | throw e;
74 | }
75 | }
76 | });
77 |
--------------------------------------------------------------------------------
/test/mocha.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Load dependencies
4 | const fs = require('fs');
5 | const chai = require('chai');
6 | const sinon = require('sinon');
7 | const dirtyChai = require('dirty-chai');
8 | const sinonChai = require('sinon-chai');
9 | const chaiAsPromised = require('chai-as-promised');
10 |
11 | //Enable should assertion style for usage with chai-as-promised
12 | chai.should();
13 |
14 | //Extend chai
15 | chai.use(dirtyChai);
16 | chai.use(sinonChai);
17 | chai.use(chaiAsPromised);
18 |
19 | //Load sinon extensions
20 | require('mocha-sinon');
21 |
22 | //Expose globals
23 | global.expect = chai.expect;
24 | global.sinon = sinon;
25 |
--------------------------------------------------------------------------------
/test/mocha.opts:
--------------------------------------------------------------------------------
1 | --recursive
2 | --timeout 50000
3 | --require mocha-clean
4 | ./test/mocha.js
5 |
--------------------------------------------------------------------------------
/test/typescript/client.ts:
--------------------------------------------------------------------------------
1 | import Client = require("@sendgrid/client");
2 | import * as https from 'https';
3 |
4 | // Test setApiKey() method
5 | Client.setApiKey("MY_SENDGRID_API_KEY");
6 |
7 | // Test setDefaultHeader() method
8 | Client.setDefaultHeader({
9 | "X-Testing-Type": "TypeScript",
10 | }).setDefaultHeader("X-Testing", "yes");
11 |
12 | // Test setDefaultRequest() method
13 | Client.setDefaultRequest({
14 | url: "/test",
15 | }).setDefaultRequest("method", "POST")
16 | .setDefaultRequest('httpsAgent', new https.Agent())
17 |
18 | // Test createHeaders() method
19 | Client.createHeaders({
20 | "X-Testing": "yes"
21 | });
22 |
23 | // Test createRequest() method
24 | const req = Client.createRequest({
25 | url: "/test"
26 | });
27 |
28 | // Test request() method
29 | Client.request({
30 | url: "/test"
31 | }).then(res => {
32 | res[0].statusCode;
33 | });
34 |
--------------------------------------------------------------------------------
/test/typescript/eventwebhook.ts:
--------------------------------------------------------------------------------
1 | import { EventWebhook } from '@sendgrid/eventwebhook';
2 |
3 | var ew = new EventWebhook();
4 | const PUBLIC_KEY = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEDr2LjtURuePQzplybdC+u4CwrqDqBaWjcMMsTbhdbcwHBcepxo7yAQGhHPTnlvFYPAZFceEu/1FwCM/QmGUhA==";
5 | const SIGNATURE = "MEUCIQCtIHJeH93Y+qpYeWrySphQgpNGNr/U+UyUlBkU6n7RAwIgJTz2C+8a8xonZGi6BpSzoQsbVRamr2nlxFDWYNH2j/0=";
6 | const TIMESTAMP = "1588788367";
7 | const PAYLOAD = JSON.stringify({
8 | event: 'test_event',
9 | category: 'example_payload',
10 | message_id: 'message_id',
11 | });
12 | var key = ew.convertPublicKeyToECDSA(PUBLIC_KEY);
13 | console.log(ew.verifySignature(key, PAYLOAD, SIGNATURE, TIMESTAMP));
14 |
--------------------------------------------------------------------------------
/test/typescript/helpers.ts:
--------------------------------------------------------------------------------
1 | import * as helpers from "@sendgrid/helpers";
2 | new helpers.classes.Attachment({
3 | content: "",
4 | filename: "test.txt",
5 | type: "text/plain"
6 | });
7 |
8 | new helpers.classes.EmailAddress("someone@example.org");
9 | new helpers.classes.EmailAddress({ name: "Some One", email: "someone@example.org" });
10 |
11 | new helpers.classes.Personalization({
12 | to: "someone@example.org",
13 | from: "somebody@example.org",
14 | subject: "Hello Some One",
15 | dynamicTemplateData: {
16 | translations: {
17 | hello: "Привет!"
18 | },
19 | count: 1
20 | }
21 | });
22 |
23 | new helpers.classes.Mail({
24 | to: "someone@example.org",
25 | from: "someone@example.org",
26 | subject: "Hello Some One",
27 | text: "This is a test message.",
28 | html: "This is a test message.
"
29 | });
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "noEmit": true,
4 | "module": "commonjs",
5 | "target": "es6",
6 | "baseUrl": ".",
7 | "paths": {
8 | "@sendgrid/*": ["packages/*"]
9 | }
10 | },
11 | "include": [
12 | "test/typescript/*.ts"
13 | ]
14 | }
--------------------------------------------------------------------------------
/twilio_sendgrid_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sendgrid/sendgrid-nodejs/2bac86884f71be3fb19f96a10c02a1fb616b81fc/twilio_sendgrid_logo.png
--------------------------------------------------------------------------------