├── .codeclimate.yml ├── .env_sample ├── .eslintignore ├── .eslintrc.yml ├── .github └── PULL_REQUEST_TEMPLATE ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── USAGE.md ├── examples ├── simple.js └── test.txt ├── package.json ├── src └── sendgrid-transport.js └── test ├── license-test.js ├── sendgrid-transport-test.js └── test.js /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - javascript 8 | eslint: 9 | enabled: true 10 | fixme: 11 | enabled: true 12 | ratings: 13 | paths: 14 | - "**.js" 15 | exclude_paths: 16 | - node_modules/ 17 | - test/ 18 | -------------------------------------------------------------------------------- /.env_sample: -------------------------------------------------------------------------------- 1 | export SENDGRID_API_KEY='' 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | parserOptions: 3 | sourceType: module 4 | ecmaFeatures: 5 | jsx: true 6 | 7 | env: 8 | amd: true 9 | browser: true 10 | es6: true 11 | jquery: true 12 | node: true 13 | 14 | # http://eslint.org/docs/rules/ 15 | rules: 16 | # Possible Errors 17 | no-await-in-loop: off 18 | no-cond-assign: error 19 | no-console: off 20 | no-constant-condition: error 21 | no-control-regex: error 22 | no-debugger: error 23 | no-dupe-args: error 24 | no-dupe-keys: error 25 | no-duplicate-case: error 26 | no-empty-character-class: error 27 | no-empty: error 28 | no-ex-assign: error 29 | no-extra-boolean-cast: error 30 | no-extra-parens: off 31 | no-extra-semi: error 32 | no-func-assign: error 33 | no-inner-declarations: 34 | - error 35 | - functions 36 | no-invalid-regexp: error 37 | no-irregular-whitespace: error 38 | no-negated-in-lhs: error 39 | no-obj-calls: error 40 | no-prototype-builtins: off 41 | no-regex-spaces: error 42 | no-sparse-arrays: error 43 | no-template-curly-in-string: off 44 | no-unexpected-multiline: error 45 | no-unreachable: error 46 | no-unsafe-finally: off 47 | no-unsafe-negation: off 48 | use-isnan: error 49 | valid-jsdoc: off 50 | valid-typeof: error 51 | 52 | # Best Practices 53 | accessor-pairs: error 54 | array-callback-return: off 55 | block-scoped-var: off 56 | class-methods-use-this: off 57 | complexity: 58 | - error 59 | - 6 60 | consistent-return: off 61 | curly: off 62 | default-case: off 63 | dot-location: off 64 | dot-notation: off 65 | eqeqeq: error 66 | guard-for-in: error 67 | no-alert: error 68 | no-caller: error 69 | no-case-declarations: error 70 | no-div-regex: error 71 | no-else-return: off 72 | no-empty-function: off 73 | no-empty-pattern: error 74 | no-eq-null: error 75 | no-eval: error 76 | no-extend-native: error 77 | no-extra-bind: error 78 | no-extra-label: off 79 | no-fallthrough: error 80 | no-floating-decimal: off 81 | no-global-assign: off 82 | no-implicit-coercion: off 83 | no-implied-eval: error 84 | no-invalid-this: off 85 | no-iterator: error 86 | no-labels: 87 | - error 88 | - allowLoop: true 89 | allowSwitch: true 90 | no-lone-blocks: error 91 | no-loop-func: error 92 | no-magic-number: off 93 | no-multi-spaces: off 94 | no-multi-str: off 95 | no-native-reassign: error 96 | no-new-func: error 97 | no-new-wrappers: error 98 | no-new: error 99 | no-octal-escape: error 100 | no-octal: error 101 | no-param-reassign: off 102 | no-proto: error 103 | no-redeclare: error 104 | no-restricted-properties: off 105 | no-return-assign: error 106 | no-return-await: off 107 | no-script-url: error 108 | no-self-assign: off 109 | no-self-compare: error 110 | no-sequences: off 111 | no-throw-literal: off 112 | no-unmodified-loop-condition: off 113 | no-unused-expressions: error 114 | no-unused-labels: off 115 | no-useless-call: error 116 | no-useless-concat: error 117 | no-useless-escape: off 118 | no-useless-return: off 119 | no-void: error 120 | no-warning-comments: off 121 | no-with: error 122 | prefer-promise-reject-errors: off 123 | radix: error 124 | require-await: off 125 | vars-on-top: off 126 | wrap-iife: error 127 | yoda: off 128 | 129 | # Strict 130 | strict: off 131 | 132 | # Variables 133 | init-declarations: off 134 | no-catch-shadow: error 135 | no-delete-var: error 136 | no-label-var: error 137 | no-restricted-globals: off 138 | no-shadow-restricted-names: error 139 | no-shadow: off 140 | no-undef-init: error 141 | no-undef: off 142 | no-undefined: off 143 | no-unused-vars: off 144 | no-use-before-define: off 145 | 146 | # Node.js and CommonJS 147 | callback-return: error 148 | global-require: error 149 | handle-callback-err: error 150 | no-mixed-requires: off 151 | no-new-require: off 152 | no-path-concat: error 153 | no-process-env: off 154 | no-process-exit: error 155 | no-restricted-modules: off 156 | no-sync: off 157 | 158 | # Stylistic Issues 159 | array-bracket-spacing: off 160 | block-spacing: off 161 | brace-style: off 162 | camelcase: off 163 | capitalized-comments: off 164 | comma-dangle: 165 | - error 166 | - never 167 | comma-spacing: off 168 | comma-style: off 169 | computed-property-spacing: off 170 | consistent-this: off 171 | eol-last: off 172 | func-call-spacing: off 173 | func-name-matching: off 174 | func-names: off 175 | func-style: off 176 | id-length: off 177 | id-match: off 178 | indent: off 179 | jsx-quotes: off 180 | key-spacing: off 181 | keyword-spacing: off 182 | line-comment-position: off 183 | linebreak-style: off 184 | lines-around-comment: off 185 | lines-around-directive: off 186 | max-depth: off 187 | max-len: off 188 | max-nested-callbacks: off 189 | max-params: off 190 | max-statements-per-line: off 191 | max-statements: 192 | - error 193 | - 30 194 | multiline-ternary: off 195 | new-cap: off 196 | new-parens: off 197 | newline-after-var: off 198 | newline-before-return: off 199 | newline-per-chained-call: off 200 | no-array-constructor: off 201 | no-bitwise: off 202 | no-continue: off 203 | no-inline-comments: off 204 | no-lonely-if: off 205 | no-mixed-operators: off 206 | no-mixed-spaces-and-tabs: off 207 | no-multi-assign: off 208 | no-multiple-empty-lines: off 209 | no-negated-condition: off 210 | no-nested-ternary: off 211 | no-new-object: off 212 | no-plusplus: off 213 | no-restricted-syntax: off 214 | no-spaced-func: off 215 | no-tabs: off 216 | no-ternary: off 217 | no-trailing-spaces: off 218 | no-underscore-dangle: off 219 | no-unneeded-ternary: off 220 | object-curly-newline: off 221 | object-curly-spacing: off 222 | object-property-newline: off 223 | one-var-declaration-per-line: off 224 | one-var: off 225 | operator-assignment: off 226 | operator-linebreak: off 227 | padded-blocks: off 228 | quote-props: off 229 | quotes: off 230 | require-jsdoc: off 231 | semi-spacing: off 232 | semi: off 233 | sort-keys: off 234 | sort-vars: off 235 | space-before-blocks: off 236 | space-before-function-paren: off 237 | space-in-parens: off 238 | space-infix-ops: off 239 | space-unary-ops: off 240 | spaced-comment: off 241 | template-tag-spacing: off 242 | unicode-bom: off 243 | wrap-regex: off 244 | 245 | # ECMAScript 6 246 | arrow-body-style: off 247 | arrow-parens: off 248 | arrow-spacing: off 249 | constructor-super: off 250 | generator-star-spacing: off 251 | no-class-assign: off 252 | no-confusing-arrow: off 253 | no-const-assign: off 254 | no-dupe-class-members: off 255 | no-duplicate-imports: off 256 | no-new-symbol: off 257 | no-restricted-imports: off 258 | no-this-before-super: off 259 | no-useless-computed-key: off 260 | no-useless-constructor: off 261 | no-useless-rename: off 262 | no-var: off 263 | object-shorthand: off 264 | prefer-arrow-callback: off 265 | prefer-const: off 266 | prefer-destructuring: off 267 | prefer-numeric-literals: off 268 | prefer-rest-params: off 269 | prefer-reflect: off 270 | prefer-spread: off 271 | prefer-template: off 272 | require-yield: off 273 | rest-spread-spacing: off 274 | sort-imports: off 275 | symbol-description: off 276 | template-curly-spacing: off 277 | yield-star-spacing: off 278 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 10 | # Fixes # 11 | 12 | ### Checklist 13 | - [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) 14 | - [ ] I have read the [Contribution Guide] and my PR follows them. 15 | - [ ] I updated my branch with the master branch. 16 | - [ ] I have added tests that prove my fix is effective or that my feature works 17 | - [ ] I have added necessary documentation about the functionality in the appropriate .md file 18 | - [ ] I have added in line documentation to the code I modified 19 | 20 | ### Short description of what this PR does: 21 | - 22 | - 23 | 24 | If you have questions, please send an email to [Sendgrid](mailto:dx@sendgrid.com), or file a Github Issue in this repository. 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Node ### 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 20 | .grunt 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # Commenting this out is preferred by some people, see 27 | # https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # Users Environment Variables 31 | .lock-wscript 32 | 33 | # vim swap files 34 | .*.sw? 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | node_js: 4 | - "6" 5 | - "7" 6 | - "8" 7 | before_install: 8 | - npm install -g npm@next 9 | notifications: 10 | hipchat: 11 | rooms: 12 | secure: wIUsb9E0kurkWR1DgtvkpfOYCsQqrIfPSWh5mnKV604MX9IHwYxCLDTdoFV+zs9jabV2wGA33WjE+he5X5yjYHhqfxVTU0q4R5VG8A1PJqpGwnXk/cYQIUD7m+oAV/CTCzG1ldj9aq9RdHPdClCIk4JInziNW5P30lV3Yeqq2z0= 13 | template: 14 | - '%{repository} 15 | Build %{build_number} on branch %{branch} by %{author}: %{message} 16 | View on GitHub' 17 | format: html 18 | notify: true 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [0.2.0] - 2015-7-06 5 | ### Added 6 | - cc 7 | - bcc 8 | - api key support 9 | - replyTo 10 | 11 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # SendGrid Community Code of Conduct 2 | 3 | The SendGrid open source community is made up of members from around the globe with a diverse set of skills, personalities, and experiences. It is through these differences that our community experiences successes and continued growth. When you're working with members of the community, we encourage you to follow these guidelines, which help steer our interactions and strive to maintain a positive, successful and growing community. 4 | 5 | ## Table of Contents 6 | - [Code of Conduct](#code-of-conduct) 7 | - [Additional Guidance](#additional-guidance) 8 | - [Conduct Concerns](#conduct-concerns) 9 | - [Submission to SendGrid Repositories](#submission) 10 | - [Attribution](#attribution) 11 | 12 | 13 | ### Be Open 14 | Members of the community are open to collaboration, whether it's on pull requests, code reviews, approvals, issues or otherwise. We're receptive to constructive comments and criticism, as the experiences and skill sets of all members contribute to the whole of our efforts. We're accepting of all who wish to take part in our activities, fostering an environment where anyone can participate, and everyone can make a difference. 15 | 16 | ### Be Considerate 17 | Members of the community are considerate of their peers, which include other contributors and users of SendGrid. We're thoughtful when addressing the efforts of others, keeping in mind that often the labor was completed with the intent of the good of the community. We're attentive in our communications, whether in person or online, and we're tactful when approaching differing views. 18 | 19 | ### Be Respectful 20 | Members of the community are respectful. We're respectful of others, their positions, their skills, their commitments and their efforts. We're respectful of the volunteer efforts that permeate the SendGrid community. We're respectful of the processes outlined in the community, and we work within them. When we disagree, we are courteous in raising our issues. Overall, we're good to each other. We contribute to this community not because we have to, but because we want to. If we remember that, these guidelines will come naturally. 21 | 22 | 23 | ## Additional Guidance 24 | 25 | ### Disclose Potential Conflicts of Interest 26 | Community discussions often involve interested parties. We expect participants to be aware when they are conflicted due to employment or other projects they are involved in and disclose those interests to other project members. When in doubt, over-disclose. Perceived conflicts of interest are important to address so that the community’s decisions are credible even when unpopular, difficult or favorable to the interests of one group over another. 27 | 28 | ### Interpretation 29 | This Code is not exhaustive or complete. It is not a rulebook; it serves to distill our common understanding of a collaborative, shared environment and goals. We expect it to be followed in spirit as much as in the letter. When in doubt, try to abide by [SendGrid’s cultural values](https://sendgrid.com/blog/employee-engagement-the-4h-way) defined by our “4H’s”: Happy, Hungry, Humble and Honest. 30 | 31 | ### Enforcement 32 | Most members of the SendGrid community always comply with this Code, not because of the existence of this Code, but because they have long experience participating in open source communities where the conduct described above is normal and expected. However, failure to observe this Code may be grounds for suspension, reporting the user for abuse or changing permissions for outside contributors. 33 | 34 | 35 | ## If you have concerns about someone’s conduct 36 | **Initiate Direct Contact** - It is always appropriate to email a community member (if contact information is available), mention that you think their behavior was out of line, and (if necessary) point them to this Code. 37 | 38 | **Discuss Publicly** - Discussing publicly is always acceptable. Note, though, that approaching the person directly may be better, as it tends to make them less defensive, and it respects the time of other community members, so you probably want to try direct contact first. 39 | 40 | **Contact the Moderators** - You can reach the SendGrid moderators by emailing dx@sendgrid.com. 41 | 42 | 43 | ## Submission to SendGrid Repositories 44 | Finally, just a reminder, changes to the SendGrid repositories will only be accepted upon completion of the [SendGrid Contributor Agreement](https://cla.sendgrid.com). 45 | 46 | 47 | ## Attribution 48 | 49 | SendGrid thanks the following, on which it draws for content and inspiration: 50 | 51 | * [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/) 52 | * [Open Source Initiative General Code of Conduct](https://opensource.org/codeofconduct) 53 | * [Apache Code of Conduct](https://www.apache.org/foundation/policies/conduct.html) 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Hello! Thank you for choosing to help contribute to one of the SendGrid open source libraries. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies. 2 | 3 | - [CLAs and CCLAs](#clas-and-cclas) 4 | - [Roadmap & Milestones](#roadmap-and-milestones) 5 | - [Feature Request](#feature-request) 6 | - [Submit a Bug Report](#submit-a-bug-report) 7 | - [Improvements to the Codebase](#improvements-to-the-codebase) 8 | - [Understanding the Code Base](#understanding-the-codebase) 9 | - [Testing](#testing) 10 | - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) 11 | - [Creating a Pull Request](#creating-a-pull-request) 12 | 13 | 14 | We use [Milestones](https://github.com/sendgrid/nodemailer-sendgrid-transport/milestones) to help define current roadmaps, please feel free to grab an issue from the current milestone. Please indicate that you have begun work on it to avoid collisions. Once a PR is made, community review, comments, suggestions and additional PRs are welcomed and encouraged. 15 | 16 | 17 | ## CLAs and CCLAs 18 | 19 | Before you get started, SendGrid requires that a SendGrid Contributor License Agreement (CLA) be filled out by every contributor to a SendGrid open source project. 20 | 21 | Our goal with the CLA is to clarify the rights of our contributors and reduce other risks arising from inappropriate contributions. The CLA also clarifies the rights SendGrid holds in each contribution and helps to avoid misunderstandings over what rights each contributor is required to grant to SendGrid when making a contribution. In this way the CLA encourages broad participation by our open source community and helps us build strong open source projects, free from any individual contributor withholding or revoking rights to any contribution. 22 | 23 | SendGrid does not merge a pull request made against a SendGrid open source project until that pull request is associated with a signed CLA. Copies of the CLA are available [here](https://gist.github.com/SendGridDX/98b42c0a5d500058357b80278fde3be8#file-sendgrid_cla). 24 | 25 | When you create a Pull Request, after a few seconds, a comment will appear with a link to the CLA. Click the link and fill out the brief form and then click the "I agree" button and you are all set. You will not be asked to re-sign the CLA unless we make a change. 26 | 27 | There are a few ways to contribute, which we'll enumerate below: 28 | 29 | 30 | ## Feature Request 31 | 32 | If you'd like to make a feature request, please read this section. 33 | 34 | The GitHub issue tracker is the preferred channel for library feature requests, but please respect the following restrictions: 35 | 36 | - Please **search for existing issues** in order to ensure we don't have duplicate bugs/feature requests. 37 | - Please be respectful and considerate of others when commenting on issues 38 | 39 | 40 | ## Submit a Bug Report 41 | 42 | Note: DO NOT include your credentials in ANY code examples, descriptions, or media you make public. 43 | 44 | A software bug is a demonstrable issue in the code base. In order for us to diagnose the issue and respond as quickly as possible, please add as much detail as possible into your bug report. 45 | 46 | Before you decide to create a new issue, please try the following: 47 | 48 | 1. Check the Github issues tab if the identified issue has already been reported, if so, please add a +1 to the existing post. 49 | 2. Update to the latest version of this code and check if issue has already been fixed 50 | 3. Copy and fill in the Bug Report Template we have provided below 51 | 52 | ### Please use our Bug Report Template 53 | 54 | In order to make the process easier, we've included a [sample bug report template](https://github.com/sendgrid/nodemailer-sendgrid-transport/.github/ISSUE_TEMPLATE) (borrowed from [Ghost](https://github.com/TryGhost/Ghost/)). The template uses [GitHub flavored markdown](https://help.github.com/articles/github-flavored-markdown/) for formatting. 55 | 56 | 57 | ## Improvements to the Codebase 58 | 59 | We welcome direct contributions to the nodemailer-sendgrid-transport code base. Thank you! 60 | 61 | ### Development Environment ### 62 | 63 | #### Install and Run Locally #### 64 | 65 | ##### Prerequisites ##### 66 | 67 | - Node.js version 0.10, 0.12 or 4 68 | - Please see [package.json](https://github.com/sendgrid/nodemailer-sendgrid-transport/blob/master/package.json) 69 | 70 | ##### Initial setup: ##### 71 | 72 | ```bash 73 | git clone https://github.com/sendgrid/nodemailer-sendgrid-transport.git 74 | cd nodemailer-sendgrid-transport 75 | npm install 76 | ``` 77 | 78 | ##### Execute: ##### 79 | 80 | See the [examples folder](https://github.com/sendgrid/nodemailer-sendgrid-transport/tree/master/examples) to get started quickly. 81 | 82 | You will need to setup the following environment to use the SendGrid example: 83 | 84 | ```bash 85 | echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env 86 | echo "sendgrid.env" >> .gitignore 87 | source ./sendgrid.env 88 | node examples/example.js 89 | ``` 90 | 91 | To run the example: 92 | 93 | ```bash 94 | node ./examples/example.js 95 | ``` 96 | 97 | 98 | ## Understanding the Code Base 99 | 100 | **/examples** 101 | 102 | Working examples that demonstrate usage. 103 | 104 | **client.js** 105 | 106 | There is an object to hold both the request and response to the API server. 107 | 108 | The main function that does the heavy lifting (and external entry point) is `API`. 109 | 110 | 111 | ## Testing 112 | 113 | All PRs require passing tests before the PR will be reviewed. 114 | 115 | All test files are in [`test/test.js`](https://github.com/sendgrid/nodemailer-sendgrid-transport/blob/master/test/test.js). 116 | 117 | For the purposes of contributing to this repo, please update the [`test.js`](https://github.com/sendgrid/nodemailer-sendgrid-transport/blob/master/test/test.js) file with unit tests as you modify the code. 118 | 119 | Run the test: 120 | 121 | `mocha` 122 | 123 | 124 | ## Style Guidelines & Naming Conventions 125 | 126 | Generally, we follow the style guidelines as suggested by the official language. However, we ask that you conform to the styles that already exist in the library. If you wish to deviate, please explain your reasoning. 127 | 128 | - [Unofficial Style Guide](https://github.com/felixge/node-style-guide) 129 | 130 | Please run your code through: 131 | 132 | - [ESLint](http://eslint.org/) with the standard style guide. 133 | 134 | 135 | ## Creating a Pull Request 136 | 137 | 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, 138 | and configure the remotes: 139 | 140 | ```bash 141 | # Clone your fork of the repo into the current directory 142 | git clone https://github.com/sendgrid/nodemailer-sendgrid-transport 143 | # Navigate to the newly cloned directory 144 | cd nodemailer-sendgrid-transport 145 | # Assign the original repo to a remote called "upstream" 146 | git remote add upstream https://github.com/sendgrid/nodemailer-sendgrid-transport 147 | ``` 148 | 149 | 2. If you cloned a while ago, get the latest changes from upstream: 150 | 151 | ```bash 152 | git checkout 153 | git pull upstream 154 | ``` 155 | 156 | 3. Create a new topic branch (off the main project development branch) to 157 | contain your feature, change, or fix: 158 | 159 | ```bash 160 | git checkout -b 161 | ``` 162 | 163 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 164 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 165 | or your code is unlikely to be merged into the main project. Use Git's 166 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 167 | feature to tidy up your commits before making them public. 168 | 169 | 4a. Create tests. 170 | 171 | 4b. Create or update the example code that demonstrates the functionality of this change to the code. 172 | 173 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 174 | 175 | ```bash 176 | git pull [--rebase] upstream master 177 | ``` 178 | 179 | 6. Push your topic branch up to your fork: 180 | 181 | ```bash 182 | git push origin 183 | ``` 184 | 185 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 186 | with a clear title and description against the `master` branch. All tests must be passing before we will review the PR. 187 | 188 | If you have any additional questions, please feel free to [email](mailto:dx@sendgrid.com) us or create an issue in this repo. 189 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 SendGrid 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SendGrid Logo](https://uiux.s3.amazonaws.com/2016-logos/email-logo%402x.png) 2 | 3 | [![BuildStatus](https://travis-ci.org/sendgrid/nodemailer-sendgrid-transport.svg?branch=master)](https://travis-ci.org/sendgrid/nodemailer-sendgrid-transport) 4 | [![npm](https://img.shields.io/npm/l/express.svg)]() 5 | [![NPM version](https://badge.fury.io/js/nodemailer-sendgrid-transport.svg)](http://badge.fury.io/js/nodemailer-sendgrid-transport) 6 | [![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) 7 | [![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/nodemailer-sendgrid-transport.svg)](https://github.com/sendgrid/nodemailer-sendgrid-transport/graphs/contributors) 8 | 9 | # Important Announcement 10 | 11 | **As of June 1, 2016, SendGrid will no longer support this library.** 12 | 13 | Please direct any questions to the [Developer Experience](mailto:dx@sendgrid.com) team. 14 | 15 | # nodemailer-sendgrid-transport 16 | 17 | This module is a transport plugin for [Nodemailer](https://github.com/andris9/Nodemailer) that makes it possible to send through [SendGrid's Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html)! 18 | 19 | ## Table of Contents 20 | - [Usage](#usage) 21 | - [Deploying](#deploying) 22 | - [License](#license) 23 | 24 | 25 | ## Usage 26 | [Library Usage Documentation](USAGE.md) 27 | 28 | 29 | ## License 30 | [The MIT License (MIT)](LICENSE.txt) 31 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | Install via npm. 3 | 4 | npm install nodemailer-sendgrid-transport 5 | 6 | Require the module and initialize it with your SendGrid credentials. 7 | 8 | ```javascript 9 | var nodemailer = require('nodemailer'); 10 | var sgTransport = require('nodemailer-sendgrid-transport'); 11 | 12 | // api key https://sendgrid.com/docs/Classroom/Send/api_keys.html 13 | var options = { 14 | auth: { 15 | api_key: 'SENDGRID_APIKEY' 16 | } 17 | } 18 | 19 | // or 20 | 21 | // username + password 22 | var options = { 23 | auth: { 24 | api_user: 'SENDGRID_USERNAME', 25 | api_key: 'SENDGRID_PASSWORD' 26 | } 27 | } 28 | 29 | var mailer = nodemailer.createTransport(sgTransport(options)); 30 | ``` 31 | 32 | Note: We suggest storing your SendGrid username and password as enviroment variables. 33 | 34 | Create an email and send it off! 35 | 36 | ```javascript 37 | var email = { 38 | to: ['joe@foo.com', 'mike@bar.com'], 39 | from: 'roger@tacos.com', 40 | subject: 'Hi there', 41 | text: 'Awesome sauce', 42 | html: 'Awesome sauce' 43 | }; 44 | 45 | mailer.sendMail(email, function(err, res) { 46 | if (err) { 47 | console.log(err) 48 | } 49 | console.log(res); 50 | }); 51 | ``` 52 | 53 | 54 | ## Deploying 55 | 56 | * Confirm tests pass 57 | * Bump the version in `README.md`, `package.json`, `test/sendgrid-transport-test.js` 58 | * Update `CHANGELOG.md` 59 | * Confirm tests pass 60 | * Commit `Version bump vX.X.X` 61 | * `npm publish` 62 | * Push changes to GitHub 63 | * Release tag on GitHub `vX.X.X` -------------------------------------------------------------------------------- /examples/simple.js: -------------------------------------------------------------------------------- 1 | var nodemailer = require('nodemailer'); 2 | var sgTransport = require('../src/sendgrid-transport.js'); 3 | 4 | var options = { 5 | auth: { 6 | api_user: process.env['SENDGRID_USERNAME'], 7 | api_key: process.env['SENDGRID_PASSWORD'] 8 | } 9 | } 10 | 11 | var mailer = nodemailer.createTransport(sgTransport(options)); 12 | 13 | var email = { 14 | to: ['foo@example.com', 'bar@example.com'], 15 | from: 'baz@example.com', 16 | subject: 'Hi there', 17 | text: 'Awesome sauce', 18 | html: 'Awesome sauce', 19 | attachments: [ 20 | { 21 | filename: 'test.txt', 22 | path: __dirname + '/test.txt' 23 | } 24 | ] 25 | }; 26 | 27 | mailer.sendMail(email, function(err, res) { 28 | if (err) { 29 | console.log(err) 30 | } 31 | console.log(res); 32 | }); 33 | -------------------------------------------------------------------------------- /examples/test.txt: -------------------------------------------------------------------------------- 1 | Hello text 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodemailer-sendgrid-transport", 3 | "version": "0.2.0", 4 | "description": "SendGrid transport for Nodemailer", 5 | "main": "src/sendgrid-transport.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/sendgrid/nodemailer-sendgrid-transport.git" 12 | }, 13 | "keywords": [ 14 | "Nodemailer", 15 | "SendGrid" 16 | ], 17 | "author": "SendGrid (sendgrid.com)", 18 | "contributors": [ 19 | "Eddie Zaneski " 21 | ], 22 | "license": "MIT", 23 | "dependencies": { 24 | "sendgrid": "^1.8.0" 25 | }, 26 | "devDependencies": { 27 | "chai": "^1.9.1", 28 | "mocha": "^1.20.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/sendgrid-transport.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var SendGrid = require('sendgrid'); 4 | var packageData = require('../package.json'); 5 | 6 | module.exports = function(options) { 7 | return new SendGridTransport(options); 8 | }; 9 | 10 | function SendGridTransport(options) { 11 | options = options || {}; 12 | 13 | this.options = options; 14 | this.name = 'SendGrid'; 15 | this.version = packageData.version; 16 | 17 | if (!this.options.auth.api_user) { 18 | // api key 19 | this.sendgrid = SendGrid(this.options.auth.api_key); 20 | } else { 21 | // username + password 22 | this.sendgrid = SendGrid(this.options.auth.api_user, this.options.auth.api_key); 23 | } 24 | } 25 | 26 | // if in "name" format, reformat to just address@example.com 27 | function trimReplyTo(a) { 28 | if (a.indexOf('<') >= 0 && a.indexOf('>') > 0) { 29 | return a.substring(a.indexOf('<')+1, a.indexOf('>')); 30 | } 31 | return a; 32 | } 33 | 34 | SendGridTransport.prototype.send = function(mail, callback) { 35 | var email = mail.data; 36 | 37 | // reformat replyTo to replyto 38 | if (email.replyTo) { 39 | email.replyto = trimReplyTo(email.replyTo); 40 | } 41 | 42 | // fetch envelope data from the message object 43 | var addresses = mail.message.getAddresses(); 44 | var from = [].concat(addresses.from || addresses.sender || addresses['reply-to'] || []).shift(); 45 | var to = [].concat(addresses.to || []); 46 | var cc = [].concat(addresses.cc || []); 47 | var bcc = [].concat(addresses.bcc || []); 48 | 49 | // populate from and fromname 50 | if (from) { 51 | if (from.address) { 52 | email.from = from.address; 53 | } 54 | 55 | if (from.name) { 56 | email.fromname = from.name; 57 | } 58 | } 59 | 60 | // populate to and toname arrays 61 | email.to = to.map(function(rcpt) { 62 | return rcpt.address || ''; 63 | }); 64 | 65 | email.toname = to.map(function(rcpt) { 66 | return rcpt.name || ''; 67 | }); 68 | 69 | // populate cc and bcc arrays 70 | email.cc = cc.map(function(rcpt) { 71 | return rcpt.address || ''; 72 | }); 73 | 74 | email.bcc = bcc.map(function(rcpt) { 75 | return rcpt.address || ''; 76 | }); 77 | 78 | // a list for processing attachments 79 | var contents = []; 80 | 81 | // email.text could be a stream or a file, so store it for processing 82 | if (email.text) { 83 | contents.push({ 84 | obj: email, 85 | key: 'text' 86 | }); 87 | } 88 | 89 | // email.html could be a stream or a file, so store it for processing 90 | if (email.html) { 91 | contents.push({ 92 | obj: email, 93 | key: 'html' 94 | }); 95 | } 96 | 97 | // store attachments for processing, to fetch files, urls and streams 98 | email.files = email.attachments; 99 | [].concat(email.files || []).forEach(function(attachment, i) { 100 | contents.push({ 101 | obj: email.files, 102 | key: i, 103 | isAttachment: true 104 | }); 105 | }); 106 | 107 | // fetch values for text/html/attachments as strings or buffers 108 | // this is an asynchronous action, so we'll handle it with a simple recursion 109 | var _self = this; 110 | var pos = 0; 111 | var resolveContent = function() { 112 | 113 | // if all parts are processed, send out the e-mail 114 | if (pos >= contents.length) { 115 | return _self.sendgrid.send(email, function(err, json) { 116 | callback(err, json); 117 | }); 118 | } 119 | 120 | // get the next element from the processing list 121 | var file = contents[pos++]; 122 | /* 123 | We need to store a pointer to the original attachment object in case 124 | resolveContent replaces it with the Stream value 125 | */ 126 | var prevObj = file.obj[file.key]; 127 | // ensure the object is an actual attachment object, not a string, buffer or a stream 128 | if (prevObj instanceof Buffer ||  typeof prevObj === 'string' || (prevObj && typeof prevObj.pipe === 'function')) { 129 | prevObj = { 130 | content: prevObj 131 | }; 132 | } 133 | 134 | // use the helper function to convert file paths, urls and streams to strings or buffers 135 | mail.resolveContent(file.obj, file.key, function(err, content) { 136 | if (err) { 137 | return callback(err); 138 | } 139 | 140 | if (!file.isAttachment) { 141 | // overwrites email.text and email.html content 142 | file.obj[file.key] = content; 143 | } else { 144 | 145 | // If the object is a String or a Buffer then it is most likely replaces by resolveContent 146 | if (file.obj[file.key] instanceof Buffer ||  typeof file.obj[file.key] === 'string') { 147 | file.obj[file.key] = prevObj; 148 | } 149 | file.obj[file.key].content = content; 150 | if (file.obj[file.key].path) { 151 | if (!file.obj[file.key].filename) { 152 | // try to detect the required filename from the path 153 | file.obj[file.key].filename = file.obj[file.key].path.split(/[\\\/]/).pop(); 154 | } 155 | delete file.obj[file.key].path; 156 | } 157 | // set default filename if filename and content-type are not set (allowed for Nodemailer but not for SendGrid) 158 | if (!file.obj[file.key].filename && !file.obj[file.key].contentType) { 159 | file.obj[file.key].filename = 'attachment-' + pos + '.bin'; 160 | } 161 | } 162 | 163 | resolveContent(); 164 | }); 165 | }; 166 | 167 | // start the recursive function 168 | resolveContent(); 169 | }; 170 | -------------------------------------------------------------------------------- /test/license-test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var assert = require('assert'); 3 | var fs = require('fs'); 4 | 5 | describe('LICENSE', function() { 6 | it('should have correct end year', function(done) { 7 | var licenseFile = fs.readFileSync('LICENSE.txt', 'utf8'); 8 | var yearRange = licenseFile.split('\n')[2].split(' ')[2]; 9 | var endYear = parseInt(yearRange.split('-')[1]); 10 | var currentYear = (new Date()).getFullYear(); 11 | 12 | if (endYear !== currentYear) { 13 | expect(endYear).to.eq(currentYear); 14 | } else { 15 | done(); 16 | } 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/sendgrid-transport-test.js: -------------------------------------------------------------------------------- 1 | var sgTransport = require('../src/sendgrid-transport.js'); 2 | var expect = require('chai').expect; 3 | 4 | var pkg = require('../package.json'); 5 | 6 | var transport = null; 7 | 8 | describe('sendgrid-transport', function() { 9 | it('should take an api_user and api_key', function() { 10 | transport = sgTransport({ 'auth': { api_user: 'test', api_key: 'test' } }) 11 | }); 12 | 13 | it('should take an apikey', function() { 14 | transport = sgTransport({ 'auth': { api_key: 'test' } }) 15 | }); 16 | 17 | it('should have a name and version', function() { 18 | expect(transport.name).to.eq('SendGrid') 19 | expect(transport.version).to.eq(pkg.version) 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | 4 | describe('nodemailer-sendgrid-transport repo', function() { 5 | it('should have ./Dockerfile or docker/Dockerfile', function() { 6 | assert(fileExists('Dockerfile') || fileExists('docker/Dockerfile')); 7 | }); 8 | 9 | it('should have ./docker-compose.yml or ./docker/docker-compose.yml file', function() { 10 | assert(fileExists('docker-compose.yml') || fileExists('docker/docker-compose.yml')); 11 | }); 12 | 13 | it('should have ./.env_sample file', function() { 14 | assert(fileExists('.env_sample')); 15 | }); 16 | 17 | it('should have ./.gitignore file', function() { 18 | assert(fileExists('.gitignore')); 19 | }); 20 | 21 | it('should have ./.travis.yml file', function() { 22 | assert(fileExists('.travis.yml')); 23 | }); 24 | 25 | it('should have ./.codeclimate.yml file', function() { 26 | assert(fileExists('.codeclimate.yml')); 27 | }); 28 | 29 | it('should have ./CHANGELOG.md file', function() { 30 | assert(fileExists('CHANGELOG.md')); 31 | }); 32 | 33 | it('should have ./CODE_OF_CONDUCT.md file', function() { 34 | assert(fileExists('CODE_OF_CONDUCT.md')); 35 | }); 36 | 37 | it('should have ./CONTRIBUTING.md file', function() { 38 | assert(fileExists('CONTRIBUTING.md')); 39 | }); 40 | 41 | it('should have ./.github/ISSUE_TEMPLATE file', function() { 42 | assert(fileExists('.github/ISSUE_TEMPLATE')); 43 | }); 44 | 45 | it('should have ./LICENSE.md file', function() { 46 | assert(fileExists('LICENSE.md') || fileExists('LICENSE.txt')); 47 | }); 48 | 49 | it('should have ./.github/PULL_REQUEST_TEMPLATE file', function() { 50 | assert(fileExists('.github/PULL_REQUEST_TEMPLATE')); 51 | }); 52 | 53 | it('should have ./README.md file', function() { 54 | assert(fileExists('README.md')); 55 | }); 56 | 57 | it('should have ./TROUBLESHOOTING.md file', function() { 58 | assert(fileExists('TROUBLESHOOTING.md')); 59 | }); 60 | 61 | it('should have ./USAGE.md file', function() { 62 | assert(fileExists('USAGE.md')); 63 | }); 64 | 65 | it('should have ./USE_CASES.md file', function() { 66 | assert(fileExists('USE_CASES.md')); 67 | }); 68 | 69 | function fileExists(filepath) { 70 | try { 71 | return fs.statSync(filepath).isFile(); 72 | } catch(e) { 73 | if (e.code === 'ENOENT') { 74 | console.log('' + filepath + ' doesn\'t exist.'); 75 | return false; 76 | } 77 | throw e; 78 | } 79 | } 80 | }); 81 | --------------------------------------------------------------------------------