From 368b1f0fbb49256f564eafa13d65f2dcb5e0ed6a Mon Sep 17 00:00:00 2001 From: Zadkiel Aslafy-Aharonian Date: Wed, 3 Oct 2018 16:41:01 +0200 Subject: [PATCH 01/29] fix(deps): Update to mailparser==^2.3.4 --- packages/inbound-mail-parser/package.json | 2 +- packages/inbound-mail-parser/yarn.lock | 261 +++++++++++++++++++--- 2 files changed, 230 insertions(+), 33 deletions(-) diff --git a/packages/inbound-mail-parser/package.json b/packages/inbound-mail-parser/package.json index 9f06fa3e3..e544bd461 100644 --- a/packages/inbound-mail-parser/package.json +++ b/packages/inbound-mail-parser/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "@sendgrid/helpers": "^6.3.0", - "mailparser": "^0.6.1" + "mailparser": "^2.3.4" }, "tags": [ "sendgrid" diff --git a/packages/inbound-mail-parser/yarn.lock b/packages/inbound-mail-parser/yarn.lock index b256403d7..a4bcf996a 100644 --- a/packages/inbound-mail-parser/yarn.lock +++ b/packages/inbound-mail-parser/yarn.lock @@ -2,46 +2,243 @@ # yarn lockfile v1 -addressparser@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" +"@sendgrid/helpers@^6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-6.3.0.tgz#1b1798af22aa7a4c98257fab3dd2a6a6afd8b467" + dependencies: + chalk "^2.0.1" + deepmerge "^2.1.1" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +chalk@^2.0.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +deepmerge@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.0.tgz#17087b22e1dccf14310ec892e696269e85374b45" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + dependencies: + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +he@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + +html-to-text@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-4.0.0.tgz#c1f4e100d74e9feab5b152d7b6b3be3c1c6412b0" + dependencies: + he "^1.0.0" + htmlparser2 "^3.9.2" + lodash "^4.17.4" + optimist "^0.6.1" -encoding@^0.1.12, encoding@~0.1.12: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" +htmlparser2@^3.9.2: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" dependencies: - iconv-lite "~0.4.13" + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" -extend@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +inherits@^2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -iconv-lite@~0.4.13: - version "0.4.17" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.17.tgz#4fdaa3b38acbc2c031b045d0edcdfe1ecab18c8d" +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" -mailparser@^0.6.1: - version "0.6.2" - resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-0.6.2.tgz#03c486039bdf4df6cd3b6adcaaac4107dfdbc068" +libbase64@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-1.0.3.tgz#de3023234abeefeb9d49378804c8a94404f5c98c" + +libmime@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-4.0.1.tgz#dc71a19bfa6a97f8249d7205251d862d6fc599fb" dependencies: - encoding "^0.1.12" - mime "^1.3.4" - mimelib "^0.3.0" - uue "^3.1.0" + iconv-lite "0.4.23" + libbase64 "1.0.3" + libqp "1.1.0" -mime@^1.3.4: - version "1.3.6" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" +libqp@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" -mimelib@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mimelib/-/mimelib-0.3.0.tgz#4b16d4b435403daf692bc227890c7165ff3de894" +linkify-it@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" dependencies: - addressparser "~1.0.1" - encoding "~0.1.12" + uc.micro "^1.0.1" + +lodash@^4.17.4: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" -uue@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uue/-/uue-3.1.0.tgz#5d67d37030e66efebbb4b8aac46daf9b55befbf6" +mailparser@^2.3.4: + version "2.3.4" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-2.3.4.tgz#63895f3db56ab21d23713b4d3cfcc18911936497" dependencies: - extend "~3.0.0" + he "1.1.1" + html-to-text "4.0.0" + iconv-lite "0.4.24" + libmime "4.0.1" + linkify-it "2.0.3" + mailsplit "4.2.3" + nodemailer "4.6.8" + tlds "1.203.1" + +mailsplit@4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-4.2.3.tgz#4c8ba5c69322a39625ef424df7c715a51c6271a7" + dependencies: + libbase64 "1.0.3" + libmime "4.0.1" + libqp "1.1.0" + +minimist@~0.0.1: + version "0.0.10" + resolved "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +nodemailer@4.6.8: + version "4.6.8" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-4.6.8.tgz#f82fb407828bf2e76d92acc34b823d83e774f89c" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +readable-stream@^2.0.2: + version "2.3.6" + resolved "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +tlds@1.203.1: + version "1.203.1" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.203.1.tgz#4dc9b02f53de3315bc98b80665e13de3edfc1dfc" + +uc.micro@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" From 8f62a8171fb2705a48fb2cc5628787f823752c16 Mon Sep 17 00:00:00 2001 From: Stuart Reed Date: Tue, 2 Oct 2018 22:14:23 -0600 Subject: [PATCH 02/29] Add documentation for email activity api --- packages/client/USAGE.md | 220 +++++++++++++++++++++++++++++++++++ packages/client/USE_CASES.md | 8 ++ use-cases/README.md | 1 + use-cases/email-activity.md | 5 + 4 files changed, 234 insertions(+) create mode 100644 use-cases/email-activity.md diff --git a/packages/client/USAGE.md b/packages/client/USAGE.md index 852842ac8..d1a428bf2 100644 --- a/packages/client/USAGE.md +++ b/packages/client/USAGE.md @@ -24,6 +24,7 @@ client.setApiKey(process.env.SENDGRID_API_KEY); * [MAIL](#mail) * [MAIL SETTINGS](#mail-settings) * [MAILBOX PROVIDERS](#mailbox-providers) +* [MESSAGES](#messages) * [PARTNER SETTINGS](#partner-settings) * [SCOPES](#scopes) * [SENDERS](#senders) @@ -3089,6 +3090,225 @@ Advanced Stats provide a more in-depth view of your email statistics and the act console.log(response.body); }) ``` + +# MESSAGES + +## Filter all messages +> In order to gain access to the Email Activity Feed API, you must purchase [additional email activity history](https://app.sendgrid.com/settings/billing/addons/email_activity). + +Filter all messages to search your Email Activity. All queries need to be [URL encoded](https://meyerweb.com/eric/tools/dencoder/), and have this format: + +`query={query_type}="{query_content}"` + +encoded, this would look like this: + +`query=type%3D%22query_content%22` + +for example: + +Filter by a specific email - `query=to_email%3D%22example%40example.com%22` + +Filter by subject line - `query=subject%3d%22A%20Great%20Subject%22` + +You can filter by other operators besides `=`. We also accept `!=`, `<`, and `>`. + +For a tutorial on how to get started, check out [Getting Started with the Email Activity API](https://sendgrid.com/docs/API_Reference/Web_API_v3/Tutorials/getting_started_email_activity_api.html). + +**Full list of basic query types and examples:** +(replace the data in quotes with the information you want to query) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
QueryUnencoded example (put this one into the try it out query - it'll automatically encode it for you)Encoded example (use this one in your code)
msg_idmsg_id="filter0307p1las1-16816-5A023E36-1.0"msg_id%3D%22filter0307p1las1-16816-5A023E36-1.0%22
from_emailfrom_email="testing@sendgrid.net"from_email%3D%22testing%40sendgrid.net%22
subjectsubject="This is a subject test"subject%22This%20is%20a%20subject%20test%22
to_emailto_email="example@example.com"to_email%3D%22example%40example.com%22
statusstatus="processed"status%22processed%22
template_idtemplate_id="8f0d27bc-cf8f-42d3-b951-3990af7d0619"template_id%3D%228f0d27bc-cf8f-42d3-b951-3990af7d0619%22
template_nametemplate_name="example_template"template_name%3D%22example_template%22
campaign_namecampaign_name="example_campaign"campaign_name%3D%22example_campaign%22
campaign_idcampaign_id="1453849"campaign_id%3D%221453849%22
api_key_idapi_key_id="-hVjtoFgGUNPq3DPPPkJN3mCIDIwrl3qdFZcqYKnlq94" (everything after the middle dot in the API key)api_key_id%3D%22-hVjtoFgGUNPq3DPPPkJN3mCIDIwrl3qdFZcqYKnlq94%22
api_key_nameapi_key_name="test_name"api_key_name%3D%22test_name%22
eventsstatus="processed"status%3D%22processed%22
originating_ip - this is the IP address of the person sending the messageoriginating_ip="4.77.777.77"originating_ip%3D%224.77.777.77%22
categories - custom tags that you createcategories="category_example"categories="category_example"
unique_args - custom tracking arguments that you can attach to SMTP API callsunique_args="example argument"unique_args%3D%22example%20argument%22
outbound_ip - this is the SendGrid dedicated IP address used to send the emailoutbound_ip="4.77.777.77"outbound_ip%3D%224.77.777.77%22
last_event_timelast_event_time="2017-11-07T23:13:58Z"last_event_time%3D%E2%80%9C2017-11-07T23%3A13%3A58Z%E2%80%9D
clicksclicks="0"clicks%3D%220%22
unsubscribe_group_nameunsubscribe_group_name="Global Unsubscribes"unsubscribe_group_name%3D%22Global%20Unsubscribes%22
unsubscribe_group_idunsubscribe_group_id="1041"unsubscribe_group_id%3D%221041%22
teammate - teamates usernameteammate="my_username"teammate%3D%22my_username%22
+ +For information about building combined queries, see [Building compound Email Activity queries](https://sendgrid.com/docs/API_Reference/Web_API_v3/Tutorials/getting_started_email_activity_api.html#-Creating-compound-queries). + +### GET /messages + + +```javascript + const queryParams = { + 'limit': 10, + 'query': 'from_email%3D%22testing%40sendgrid.net%22' + }; + request.qs = queryParams; + request.method = 'GET'; + request.url = '/v3/messages'; + client.request(request) + .then(([response, body]) => { + console.log(response.statusCode); + console.log(response.body); + }) +``` +## Filter messages by message ID + +> In order to gain access to the Email Activity Feed API, you must purchase [additional email activity history](https://app.sendgrid.com/settings/billing/addons/email_activity). + +Get all of the details about the specified message. + +### GET /messages/{msg_id} + + +```javascript + request.method = 'GET'; + request.url = '/v3/messages/{msg_id}'; + client.request(request) + .then(([response, body]) => { + console.log(response.statusCode); + console.log(response.body); + }) +``` +## Request a CSV + +> In order to gain access to the Email Activity Feed API, you must purchase [additional email activity history](https://app.sendgrid.com/settings/billing/addons/email_activity). + +This request kicks of a process to generate a CSV file. When the file is generated, the email that is listed as the account owner gets an email that links out to the file that is ready for download. The link expires in 3 days. + +The CSV fill contain the last 1 million messages. This endpoint will be rate limited to 1 request every 12 hours. + +### POST /messages/download + + +```javascript + request.method = 'POST'; + request.url = '/v3/messages/download'; + client.request(request) + .then(([response, body]) => { + console.log(response.statusCode); + console.log(response.body); + }) +``` +## Download CSV + +> In order to gain access to the Email Activity Feed API, you must purchase [additional email activity history](https://app.sendgrid.com/settings/billing/addons/email_activity). + +Download the CSV that you requested with the POST Request a CSV. + +When the file is generated, the email that is listed as the account owner gets an email that links out to the file that is ready for download. The link expires in 3 days. + +The CSV fill contain the last 1 million messages. This endpoint will be rate limited to 1 request every 12 hours. + +### GET /messages/download/{download_uuid} + + +```javascript + request.method = 'POST'; + request.url = '/v3/messages/download'; + client.request(request) + .then(([response, body]) => { + console.log(response.statusCode); + console.log(response.body); + }) +``` # PARTNER SETTINGS diff --git a/packages/client/USE_CASES.md b/packages/client/USE_CASES.md index e45ac562b..2acb68612 100644 --- a/packages/client/USE_CASES.md +++ b/packages/client/USE_CASES.md @@ -4,6 +4,7 @@ This documentation provides examples for specific SendGrid v3 API non mail/send * [How to Setup a Domain Whitelabel](#domain-white-label) * [How to View Email Statistics](#email-stats) +* [How to use the Email Activity Feed](#email-activity-feed) @@ -19,3 +20,10 @@ Find more information about all of SendGrid's whitelabeling related documentatio You can find documentation for how to view your email statistics via the UI [here](https://app.sendgrid.com/statistics) and via API [here](https://github.com/sendgrid/sendgrid-nodejs/blob/master/packages/client/USAGE.md#stats). 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 SendGrid processes your email. + + +# How to use the Email Activity Feed + +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](https://github.com/sendgrid/sendgrid-nodejs/blob/master/packages/client/USAGE.md#messages). + +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). diff --git a/use-cases/README.md b/use-cases/README.md index f11dbe798..907178ad3 100644 --- a/use-cases/README.md +++ b/use-cases/README.md @@ -7,6 +7,7 @@ This documentation provides examples for specific SendGrid v3 API use cases. Ple * [CC, BCC and Reply To](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/cc-bcc-reply-to.md) * [Flexible Email Address Fields](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/flexible-address-fields.md) * [Handling Success/Failure/Errors](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/success-failure-errors.md) +* [Show Email Activity](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/email-activity.md) * [Advanced Usage](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/advanced.md) * [Transactional Templates](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/transactional-templates.md) * [Legacy Transactional Templates](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/transactional-legacy-templates.md) diff --git a/use-cases/email-activity.md b/use-cases/email-activity.md new file mode 100644 index 000000000..54999b93d --- /dev/null +++ b/use-cases/email-activity.md @@ -0,0 +1,5 @@ +# How to View Email Activity + +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](https://github.com/sendgrid/sendgrid-nodejs/blob/master/packages/client/USAGE.md#messages). + +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). From d91cf3ae95244c3b073574d4396abe05f4c53aa2 Mon Sep 17 00:00:00 2001 From: Valerian Pereira Date: Thu, 4 Oct 2018 21:04:53 +0530 Subject: [PATCH 03/29] #629 Documentation updated for `from` field #629 Documentation updated for `from` field usage / implementation variations. --- packages/mail/USE_CASES.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/mail/USE_CASES.md b/packages/mail/USE_CASES.md index 55082892a..4813ca6f1 100644 --- a/packages/mail/USE_CASES.md +++ b/packages/mail/USE_CASES.md @@ -149,6 +149,25 @@ const msg = { }; ``` +Another example - for the `from` fields +```js +const msg = { + + //Simple email address string + from: 'someone@example.org', + + //Email address with name + from: 'Some One ', + + //Object with name/email + from: { + name: 'Some One', + email: 'someone@example.org', + }, + +}; +``` + # Handling Success/Failure/Errors From b190590778470b4cc78753d3a9f7d1192ca20137 Mon Sep 17 00:00:00 2001 From: Daksh Date: Fri, 5 Oct 2018 22:47:49 +0530 Subject: [PATCH 04/29] kubernetes secrets "\n" problem. --- TROUBLESHOOTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index aacd7a914..effbf904e 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -86,6 +86,12 @@ becomes In the first case SENDGRID_API_KEY is in reference to the name of the environment variable, while the second case references the actual SendGrid API Key. +If you're using Kubernetes Secrets and passing the API Keys to the Environment using it, You may find that there is a `\n` charachter in the environment variable. You can use the trim function to remove it like this: + +``` +process.env.SENDGRID_API_KEY.trim(); +``` + ## Using the Package Manager From f09d1a3723c4abe6f1b78a4a65d9bd03d65bca22 Mon Sep 17 00:00:00 2001 From: Arshad Kazmi Date: Sat, 6 Oct 2018 21:23:35 +0530 Subject: [PATCH 05/29] Fixes broken link to USE_CASE directory --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 53cbbaf56..a189081a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ npm install First, get your free SendGrid account [here](https://sendgrid.com/free?source=sendgrid-nodejs). -You will need to setup the following environment to use the SendGrid examples in the [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md), [USAGE.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.MD) and [USE_CASES.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USE_CASES.md) files: +You will need to setup the following environment to use the SendGrid examples in the [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md), [USAGE.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.MD) and [USE_CASES](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/README.md) files: ```bash echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env From 9352bca16d7be2896087feb4227fb3a94d65b8a5 Mon Sep 17 00:00:00 2001 From: Arshad Kazmi Date: Sat, 6 Oct 2018 21:29:42 +0530 Subject: [PATCH 06/29] Usage link fix, esdoc link fix --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a189081a5..9326f6758 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ npm install First, get your free SendGrid account [here](https://sendgrid.com/free?source=sendgrid-nodejs). -You will need to setup the following environment to use the SendGrid examples in the [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md), [USAGE.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.MD) and [USE_CASES](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/README.md) files: +You will need to setup the following environment to use the SendGrid examples in the [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md), [USAGE.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.md) and [USE_CASES](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/README.md) files: ```bash echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env @@ -160,7 +160,7 @@ Generally, we follow the style guidelines as suggested by the official language. Please run your code through: - [ESLint](http://eslint.org/) with the standard style guide. -- [esdoc](https://github.com/sendgrid/sendgrid-nodejs/blob/master/.github/USAGE.md) to check the documentation coverage of your added code. +- [esdoc](https://esdoc.org/) to check the documentation coverage of your added code. ## Creating a Pull Request From 5a6e1b5e7029438fce987b002e59c7366daf4004 Mon Sep 17 00:00:00 2001 From: Arshad Kazmi Date: Sat, 6 Oct 2018 21:54:34 +0530 Subject: [PATCH 07/29] v3 send email broken link fixed in TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index aacd7a914..7139ee122 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -59,7 +59,7 @@ Click the "Clone or download" green button in [GitHub](https://github.com/sendgr ## Testing v3 /mail/send Calls Directly -[Here](https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/curl_examples.html) are some cURL examples for common use cases. +[Here](https://sendgrid.com/docs/for-developers/sending-email/curl-examples/) are some cURL examples for common use cases. ## Error Messages From 77d2df5a382464beaa1c343e71189b0ff816222f Mon Sep 17 00:00:00 2001 From: Ishaan Malhi Date: Sun, 7 Oct 2018 17:23:52 +0530 Subject: [PATCH 08/29] docs: specify custom arguments must have string values - Resolves #658 --- use-cases/customization.md | 4 ++-- use-cases/kitchen-sink.md | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/use-cases/customization.md b/use-cases/customization.md index c14013bd2..127c64efd 100644 --- a/use-cases/customization.md +++ b/use-cases/customization.md @@ -16,7 +16,7 @@ const msg = { 'X-Custom-Header': 'Recipient 1', }, customArgs: { - myArg: 'Recipient 1', + myArg: 'Recipient 1', // must be a string }, }, { @@ -30,7 +30,7 @@ const msg = { 'X-Custom-Header': 'Recipient 2', }, customArgs: { - myArg: 'Recipient 1', + myArg: 'Recipient 1', // must be a string }, sendAt: 1500077141, } diff --git a/use-cases/kitchen-sink.md b/use-cases/kitchen-sink.md index b9bba23f7..bec00e71c 100644 --- a/use-cases/kitchen-sink.md +++ b/use-cases/kitchen-sink.md @@ -33,7 +33,7 @@ const msg = { }, sections: {}, customArgs: { - myCustomArg: 123, + myCustomArg: 'some string', // must be a string }, batchId: 'sendgrid-batch-id', asm: { @@ -48,3 +48,7 @@ sgMail .then(() => console.log('Mail sent successfully')) .catch(error => console.error(error.toString())); ``` + +### Error Handling: + +As per [issue #288](https://github.com/sendgrid/sendgrid-nodejs/issues/288), please note that the `customArgs` feild *must* have a string value. From bae6f26e05bce2668805407be655368bcc5d245d Mon Sep 17 00:00:00 2001 From: Siddhant Sharma Date: Sun, 7 Oct 2018 19:39:50 +0530 Subject: [PATCH 09/29] #765 - Updating the troubleshooting.md Added the #692 multi line codes in
 turn to one line after mail send,
to the troubleshooting.md on your request.

Co-Authored-By: Elmer Thomas 
---
 TROUBLESHOOTING.md | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md
index aacd7a914..8a56721d2 100644
--- a/TROUBLESHOOTING.md
+++ b/TROUBLESHOOTING.md
@@ -13,6 +13,7 @@ If you can't find a solution below, please open an [issue](https://github.com/se
 * [Environment Variables and Your SendGrid API Key](#environment)
 * [Using the Package Manager](#package-manager)
 * [Viewing the Request Body](#request-body)
+* [Wrapping Text](#wrapping-text)
 
 
 ## Migrating from v2 to v3
@@ -126,3 +127,45 @@ const mail = Mail.create(data);
 const body = mail.toJSON();
 console.log(body);
 ```
+
+
+## Wrapping Text
+
+You can write blog posts using e-mail with the help of SENDGRID API, like so:
+```javascript
+sgMail.setApiKey(process.env.SENDGRID_API_KEY);
+      let msg = {
+        to: '@blogger.com',
+        from: '@gmail.com',
+        subject: title,
+        html: html,
+      };
+     sgMail.send(msg);
+``` 
+You can also wrap the text in the HTML to make a multi-line blog post:
+```javascript
+
+ int a = 10; + int b = 10; + int d = 10; + +
+``` + + + + + + + + + + + + + + + + + + From f80b1d2b0610ee840674a5f04bedaa18c30eeabd Mon Sep 17 00:00:00 2001 From: PyroclasticMayhem Date: Sun, 7 Oct 2018 16:01:00 -0400 Subject: [PATCH 10/29] Correct link for environment variables setup --- TROUBLESHOOTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index aacd7a914..a9d7e741b 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -74,7 +74,7 @@ We follow the MAJOR.MINOR.PATCH versioning scheme as described by [SemVer.org](h ## Environment Variables and Your SendGrid API Key -All of our examples assume you are using [environment variables](https://github.com/sendgrid/sendgrid-nodejs#setup-environment-variables) to hold your SendGrid API key. +All of our examples assume you are using [environment variables](https://github.com/sendgrid/sendgrid-nodejs/tree/master/packages/client#setup-environment-variables) to hold your SendGrid API key. If you choose to add your SendGrid API key directly (not recommended): From 09f35469f7f696fa8918185bb77dd841eb77472f Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Mon, 8 Oct 2018 16:41:30 -0600 Subject: [PATCH 11/29] Add inbound parse webhook example --- examples/inbound-parse-docker/.dockerignore | 13 + examples/inbound-parse-docker/.gitignore | 1 + examples/inbound-parse-docker/CONTRIBUTING.md | 225 +++++++ examples/inbound-parse-docker/Dockerfile | 8 + examples/inbound-parse-docker/LICENSE | 15 + examples/inbound-parse-docker/README.md | 21 + examples/inbound-parse-docker/app.js | 27 + .../docker-compose.debug.yml | 13 + .../inbound-parse-docker/docker-compose.yml | 10 + .../inbound-parse-docker/package-lock.json | 555 ++++++++++++++++++ examples/inbound-parse-docker/package.json | 17 + 11 files changed, 905 insertions(+) create mode 100644 examples/inbound-parse-docker/.dockerignore create mode 100644 examples/inbound-parse-docker/.gitignore create mode 100644 examples/inbound-parse-docker/CONTRIBUTING.md create mode 100644 examples/inbound-parse-docker/Dockerfile create mode 100644 examples/inbound-parse-docker/LICENSE create mode 100644 examples/inbound-parse-docker/README.md create mode 100644 examples/inbound-parse-docker/app.js create mode 100644 examples/inbound-parse-docker/docker-compose.debug.yml create mode 100644 examples/inbound-parse-docker/docker-compose.yml create mode 100644 examples/inbound-parse-docker/package-lock.json create mode 100644 examples/inbound-parse-docker/package.json diff --git a/examples/inbound-parse-docker/.dockerignore b/examples/inbound-parse-docker/.dockerignore new file mode 100644 index 000000000..3a78c9d4c --- /dev/null +++ b/examples/inbound-parse-docker/.dockerignore @@ -0,0 +1,13 @@ +node_modules +npm-debug.log +Dockerfile* +docker-compose* +.dockerignore +.git +.gitignore +.env +*/bin +*/obj +README.md +LICENSE +.vscode \ No newline at end of file diff --git a/examples/inbound-parse-docker/.gitignore b/examples/inbound-parse-docker/.gitignore new file mode 100644 index 000000000..b512c09d4 --- /dev/null +++ b/examples/inbound-parse-docker/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/examples/inbound-parse-docker/CONTRIBUTING.md b/examples/inbound-parse-docker/CONTRIBUTING.md new file mode 100644 index 000000000..53cbbaf56 --- /dev/null +++ b/examples/inbound-parse-docker/CONTRIBUTING.md @@ -0,0 +1,225 @@ +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. + +- [CLAs and CCLAs](#cla) +- [Roadmap & Milestones](#roadmap) +- [Feature Request](#feature-request) +- [Submit a Bug Report](#submit-a-bug-report) +- [Improvements to the Codebase](#improvements-to-the-codebase) +- [Understanding the Code Base](#understanding-the-codebase) +- [Codebase Overview](#codebase-overview) +- [Testing](#testing) +- [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) +- [Creating a Pull Request](#creating-a-pull-request) +- [Code Reviews](#code-reviews) + + +We use [Milestones](https://github.com/sendgrid/sendgrid-nodejs/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. + + +## CLAs and CCLAs + +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. + +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. + +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). + +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. + +There are a few ways to contribute, which we'll enumerate below: + + +## Feature Request + +If you'd like to make a feature request, please read this section. + +The GitHub [issue tracker](https://github.com/sendgrid/sendgrid-nodejs/issues) is the preferred channel for library feature requests, but please respect the following restrictions: + +- Please [**search for existing issues**](https://github.com/search?utf8=%E2%9C%93&q=repo%3Asendgrid%2Fsendgrid-nodejs&type=Issues) in order to ensure we don't have duplicate bugs/feature requests. +- Please be respectful and considerate of others when commenting on issues + + +## Submit a Bug Report + +Note: DO NOT include your credentials in ANY code examples, descriptions, or media you make public. + +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. + +Before you decide to create a new issue, please try the following: + +1. [Check the Github issues tab](https://github.com/sendgrid/sendgrid-nodejs/issues) if the identified issue has already been reported, if so, please add a +1 to the existing post. +2. Update to the latest version of this code and check if issue has already been fixed +3. Copy and fill in the Bug Report Template we have provided below + +### Please use our Bug Report Template + +In order to make the process easier, we've included a [sample bug report template](https://github.com/sendgrid/sendgrid-nodejs/blob/master/.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. + + +## Improvements to the Codebase + +We welcome direct contributions to the sendgrid-nodejs code base. Thank you! + +### Development Environment ### + +#### Install and Run Locally #### + +##### Prerequisites ##### + +- Node.js version 6, 7 or 8 +- Please see [package.json](https://github.com/sendgrid/sendgrid-nodejs/tree/master/package.json) + +##### Initial setup: ##### + +```bash +git clone https://github.com/sendgrid/sendgrid-nodejs.git +cd sendgrid-nodejs +npm install +``` + +## Environment Variables + +First, get your free SendGrid account [here](https://sendgrid.com/free?source=sendgrid-nodejs). + +You will need to setup the following environment to use the SendGrid examples in the [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md), [USAGE.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.MD) and [USE_CASES.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USE_CASES.md) files: + +```bash +echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env +echo "sendgrid.env" >> .gitignore +source ./sendgrid.env +``` + +##### Execute: ##### + +To run an example: + +```bash +touch example.js +``` + +Copy the desired code into `example.js`. For this example, I'm assuming you create this file in the root of this project. + +Change the path to the Sendgrid library to the relative path, for example: `./packages/mail/mail`. + +```bash +node example.js +``` + + +## Understanding the Code Base + +This repo is organized as a monorepo with the packages residing in the `./packages` directory. Please see the root [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md) for details. + + +## Codebase Overview + +This repo is subdivided in 6 main [packages](https://github.com/sendgrid/sendgrid-nodejs/tree/master/packages). Each package has its own dependencies (internal or external) and its own source code in the `src` folder. Each package also has its isolated ReadME files, use cases and usage.md files. + +To install a particular packages' dependencies. +```bash +cd packages/{NAME} +npm install or yarn install +``` +#### Package List + +**1. Client** +This is a wrapper written on top of the ```request``` module to suite the sendgrid module. All requests made to the sendgrid API are invoked by the `request` function in the `client.js`. + +Type declarations: client.d.ts +Test Cases: client.spec.js + +**2. Mail** +This module exposes the `send` function which send mails via the sdk. This module can be a good starting point to read the source code. + +Type declarations: mail.d.ts +Test Cases: mail.spec.js + +**3. Helpers** +These are a set of utility functions which all the modules use. Some of them are very basic functions and can be an easy starting point for reading the source code. + + +## Testing + +All PRs require passing tests before the PR will be reviewed. + +To run tests, please install Prism first by either running `yarn prism:install` or manually downloading from [the Prism website](https://stoplight.io/platform/prism/). + +Next, start Prism in one console window using `yarn prism`. + +Open a new console window and run `lerna bootstrap`. + +And finally, run `yarn test`, or specific tests e.g. `yarn test:mail` or `yarn test:client`. + + +## Style Guidelines & Naming Conventions + +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. + +- [Unofficial Style Guide](https://github.com/felixge/node-style-guide) + +Please run your code through: + +- [ESLint](http://eslint.org/) with the standard style guide. +- [esdoc](https://github.com/sendgrid/sendgrid-nodejs/blob/master/.github/USAGE.md) to check the documentation coverage of your added code. + +## Creating a Pull Request + +1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com/sendgrid/sendgrid-nodejs + + # Navigate to the newly cloned directory + cd sendgrid-nodejs + + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/sendgrid/sendgrid-nodejs + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout + git pull upstream + ``` + +3. Create a new topic branch (off the main project development branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + + 4a. Create tests. + + 4b. Create or update the example code that demonstrates the functionality of this change to the code. + +5. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream master + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description against the `master` branch. All tests must be passing before we will review the PR. + +If you have any additional questions, please feel free to [email](mailto:dx@sendgrid.com) us or create an issue in this repo. + + +## Code Reviews + +If you can, please look at open PRs and review them. Give feedback and help us merge these PRs much faster! If you don't know how, Github has some [great information on how to review a Pull Request](https://help.github.com/articles/about-pull-request-reviews/). diff --git a/examples/inbound-parse-docker/Dockerfile b/examples/inbound-parse-docker/Dockerfile new file mode 100644 index 000000000..ce85231e0 --- /dev/null +++ b/examples/inbound-parse-docker/Dockerfile @@ -0,0 +1,8 @@ +FROM node:8.9-alpine +ENV NODE_ENV production +WORKDIR /usr/src/app +COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"] +RUN npm install --production --silent && mv node_modules ../ +COPY . . +EXPOSE 3000 +CMD npm start \ No newline at end of file diff --git a/examples/inbound-parse-docker/LICENSE b/examples/inbound-parse-docker/LICENSE new file mode 100644 index 000000000..8fc986624 --- /dev/null +++ b/examples/inbound-parse-docker/LICENSE @@ -0,0 +1,15 @@ +Copyright (c) 2012-2018 SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of +the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md new file mode 100644 index 000000000..ba2cd2019 --- /dev/null +++ b/examples/inbound-parse-docker/README.md @@ -0,0 +1,21 @@ +# Introduction + +This is an example project for using a Docker container as a webhook receiver. + +# Prerequisites + +* Docker CE 18 +* SendGrid Account with inbound parse enabled +* Ngrok or Internet accessible URL + +# Usage + +* `cd ` +* `docker-compose build && docker-compose up` + +At the moment, the `app.js` only prints data to the console. You can extend this project by adding more business logic to the `/parse_webhook` route. + +# License + +See [LICENSE](LICENSE) + diff --git a/examples/inbound-parse-docker/app.js b/examples/inbound-parse-docker/app.js new file mode 100644 index 000000000..5a1d46367 --- /dev/null +++ b/examples/inbound-parse-docker/app.js @@ -0,0 +1,27 @@ +const express = require('express') +const app = express() +const formidable = require('express-formidable') +const port = 3000 + +app.use(formidable()) + + + +app.get('/', (req, res) => res.send('Hello World!')) + +app.post('/parse_webhook', (req, res) => { + console.log('---------- RECEIVED WEBHOOK DATA ----------') + // Email data comes in as a form. Using express-formidable to + // handle the form data. Form fields are available in req.fields + // Below, extracting the from and text. + // You can take this data and do something more interesting with it + // such as sending it to a database. + console.log() + console.log('FROM: ' + req.fields.from) + console.log() + console.log('MESSAGE TEXT: ' + req.fields.text) + + res.sendStatus(200) +}) + +app.listen(port, () => console.log(`SendGrid Inbound Parse webhook listener started on port ${port}!`)) \ No newline at end of file diff --git a/examples/inbound-parse-docker/docker-compose.debug.yml b/examples/inbound-parse-docker/docker-compose.debug.yml new file mode 100644 index 000000000..4e56ea477 --- /dev/null +++ b/examples/inbound-parse-docker/docker-compose.debug.yml @@ -0,0 +1,13 @@ +version: '2.1' + +services: + inbound-parse-docker: + image: inbound-parse-docker + build: . + environment: + NODE_ENV: development + ports: + - 3000:3000 + - 9229:9229 + ## set your startup file here + command: node --inspect index.js \ No newline at end of file diff --git a/examples/inbound-parse-docker/docker-compose.yml b/examples/inbound-parse-docker/docker-compose.yml new file mode 100644 index 000000000..c0a1eb970 --- /dev/null +++ b/examples/inbound-parse-docker/docker-compose.yml @@ -0,0 +1,10 @@ +version: '2.1' + +services: + inbound-parse-docker: + image: inbound-parse-docker + build: . + environment: + NODE_ENV: production + ports: + - 3000:3000 \ No newline at end of file diff --git a/examples/inbound-parse-docker/package-lock.json b/examples/inbound-parse-docker/package-lock.json new file mode 100644 index 000000000..cb49e7cde --- /dev/null +++ b/examples/inbound-parse-docker/package-lock.json @@ -0,0 +1,555 @@ +{ + "name": "inbound-parse-docker", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sendgrid/helpers": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-6.3.0.tgz", + "integrity": "sha512-uTFcmhCDFg/2Uhz+z/cLwyLHH0UsblG49hKwdR7nKbWsGKWv4js7W32FlPdXqy2C/plTJ20vcPLgKM1m3F/MjQ==", + "requires": { + "chalk": "^2.0.1", + "deepmerge": "^2.1.1" + } + }, + "@sendgrid/inbound-mail-parser": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@sendgrid/inbound-mail-parser/-/inbound-mail-parser-6.3.0.tgz", + "integrity": "sha512-4DzcoyHRm9yAYMfeuKpmGpXx7pQHtss865rib6uVCimReSDzjVMslbECWCBb8u2rcfdqvlBZGjqheBU9y95joA==", + "requires": { + "@sendgrid/helpers": "^6.3.0", + "mailparser": "^0.6.1" + } + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "addressparser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", + "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + } + } + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "deepmerge": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", + "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.3", + "resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz", + "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.2", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.3", + "qs": "6.5.1", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.1", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", + "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.1", + "http-errors": "~1.6.2", + "iconv-lite": "0.4.19", + "on-finished": "~2.3.0", + "qs": "6.5.1", + "raw-body": "2.3.2", + "type-is": "~1.6.15" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "raw-body": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", + "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.2", + "iconv-lite": "0.4.19", + "unpipe": "1.0.0" + }, + "dependencies": { + "depd": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" + }, + "http-errors": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", + "requires": { + "depd": "1.1.1", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": ">= 1.3.1 < 2" + } + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + } + } + } + } + }, + "express-formidable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/express-formidable/-/express-formidable-1.0.0.tgz", + "integrity": "sha1-3JIvBFUTIyJFip7BowHYkbP/yo0=", + "requires": { + "formidable": "^1.0.17" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "mailparser": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-0.6.2.tgz", + "integrity": "sha1-A8SGA5vfTfbNO2rcqqxBB9/bwGg=", + "requires": { + "encoding": "^0.1.12", + "mime": "^1.3.4", + "mimelib": "^0.3.0", + "uue": "^3.1.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" + }, + "mime-types": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "requires": { + "mime-db": "~1.36.0" + } + }, + "mimelib": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/mimelib/-/mimelib-0.3.1.tgz", + "integrity": "sha1-eHrdJBXYJ6yzr27EvKHqlZZBiFM=", + "requires": { + "addressparser": "~1.0.1", + "encoding": "~0.1.12" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uue": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/uue/-/uue-3.1.2.tgz", + "integrity": "sha512-axKLXVqwtdI/czrjG0X8hyV1KLgeWx8F4KvSbvVCnS+RUvsQMGRjx0kfuZDXXqj0LYvVJmx3B9kWlKtEdRrJLg==", + "requires": { + "escape-string-regexp": "~1.0.5", + "extend": "~3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/examples/inbound-parse-docker/package.json b/examples/inbound-parse-docker/package.json new file mode 100644 index 000000000..2258b7322 --- /dev/null +++ b/examples/inbound-parse-docker/package.json @@ -0,0 +1,17 @@ +{ + "name": "inbound-parse-docker", + "version": "1.0.0", + "description": "A containter to capture webhook data from SendGrid inbound parse", + "main": "app.js", + "dependencies": { + "express": "^4.16.3", + "express-formidable": "^1.0.0" + }, + "devDependencies": {}, + "scripts": { + "start": "node app.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Ashley Roach ", + "license": "MIT" +} From 771a0ffbf701ef116ca055a9f01124058c85711c Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Mon, 8 Oct 2018 22:05:09 -0600 Subject: [PATCH 12/29] Update console logging with all parsed values --- examples/inbound-parse-docker/README.md | 2 +- examples/inbound-parse-docker/app.js | 35 ++++++++++++++++++- .../docker-compose.debug.yml | 2 +- .../inbound-parse-docker/docker-compose.yml | 4 ++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md index ba2cd2019..09083b57c 100644 --- a/examples/inbound-parse-docker/README.md +++ b/examples/inbound-parse-docker/README.md @@ -5,7 +5,7 @@ This is an example project for using a Docker container as a webhook receiver. # Prerequisites * Docker CE 18 -* SendGrid Account with inbound parse enabled +* SendGrid Account with inbound parse enabled to send form data (default) * Ngrok or Internet accessible URL # Usage diff --git a/examples/inbound-parse-docker/app.js b/examples/inbound-parse-docker/app.js index 5a1d46367..d3c12afb5 100644 --- a/examples/inbound-parse-docker/app.js +++ b/examples/inbound-parse-docker/app.js @@ -10,16 +10,49 @@ app.use(formidable()) app.get('/', (req, res) => res.send('Hello World!')) app.post('/parse_webhook', (req, res) => { - console.log('---------- RECEIVED WEBHOOK DATA ----------') + console.log('---------- START RECEIVED WEBHOOK DATA ----------') // Email data comes in as a form. Using express-formidable to // handle the form data. Form fields are available in req.fields // Below, extracting the from and text. // You can take this data and do something more interesting with it // such as sending it to a database. + // Attachments are stored in /tmp based on the default configuration + // of the formidable middleware. + // console.log(req.fields) + console.log() + console.log('HEADERS: ' + req.fields.headers) + console.log() + console.log('DKIM: ' + req.fields.dkim) + // TODO: The server is sending a non JS compliant key name + // console.log() + // console.log('CONTENT-IDS: ' + req.fields.content-ids) + console.log() + console.log('TO: ' + req.fields.to) + console.log() + console.log('HTML: ' + req.fields.html) console.log() console.log('FROM: ' + req.fields.from) console.log() + console.log('SENDER-IP: ' + req.fields.sender_ip) + console.log() + console.log('SPAM-REPORT: ' + req.fields.spam_report) + console.log() + console.log('ENVELOPE: ' + req.fields.envelope) + console.log() + console.log('ATTACHMENTS: ' + req.fields.attachments) + console.log() + console.log('SPAM-SCORE: ' + req.fields.spam_score) + // TODO: The server is sending a non JS compliant key name + // console.log() + // console.log('ATTACHMENT-INFO: ' + req.fields.attachment-info) + console.log() + console.log('CHARSETS: ' + req.fields.charsets) + console.log() + console.log('SPF: ' + req.fields.SPF) + console.log() console.log('MESSAGE TEXT: ' + req.fields.text) + console.log() + console.log('---------- END RECEIVED WEBHOOK DATA ----------') res.sendStatus(200) }) diff --git a/examples/inbound-parse-docker/docker-compose.debug.yml b/examples/inbound-parse-docker/docker-compose.debug.yml index 4e56ea477..02eebea8f 100644 --- a/examples/inbound-parse-docker/docker-compose.debug.yml +++ b/examples/inbound-parse-docker/docker-compose.debug.yml @@ -10,4 +10,4 @@ services: - 3000:3000 - 9229:9229 ## set your startup file here - command: node --inspect index.js \ No newline at end of file + command: node --inspect app.js \ No newline at end of file diff --git a/examples/inbound-parse-docker/docker-compose.yml b/examples/inbound-parse-docker/docker-compose.yml index c0a1eb970..e823b72be 100644 --- a/examples/inbound-parse-docker/docker-compose.yml +++ b/examples/inbound-parse-docker/docker-compose.yml @@ -7,4 +7,6 @@ services: environment: NODE_ENV: production ports: - - 3000:3000 \ No newline at end of file + - 3000:3000 + # volumes: + # - /tmp:/tmp \ No newline at end of file From 0cb87a6e45d04f6b18134eaea59da9d870accbb0 Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Mon, 8 Oct 2018 22:05:17 -0600 Subject: [PATCH 13/29] Add example payload --- .../example-webhook-payload.txt | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 examples/inbound-parse-docker/example-webhook-payload.txt diff --git a/examples/inbound-parse-docker/example-webhook-payload.txt b/examples/inbound-parse-docker/example-webhook-payload.txt new file mode 100644 index 000000000..697296a8f --- /dev/null +++ b/examples/inbound-parse-docker/example-webhook-payload.txt @@ -0,0 +1,104 @@ +--xYzZY +Content-Disposition: form-data; name="headers" + +Received: by mx0032p1las1.sendgrid.net with SMTP id NRnKey2i6C Tue, 09 Oct 2018 03:16:04 +0000 (UTC) +Received: from mail-yw1-f46.google.com (mail-yw1-f46.google.com [209.85.161.46]) by mx0032p1las1.sendgrid.net (Postfix) with ESMTPS id 1D3BB625D2C for ; Tue, 9 Oct 2018 03:16:04 +0000 (UTC) +Received: by mail-yw1-f46.google.com with SMTP id j75-v6so51762ywj.10 for ; Mon, 08 Oct 2018 20:16:04 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=gaGIowPBiLdos6k2wdsmJawftIRJniyY+QsmcvDWWCA=; b=PnAWpmAy0rbKREK7IOCzDQQeUepqTbv01k8bC72VtSVjRMpYpPsZhR/EUDMVaB+i4/ mng6byw7E62qQykPETggRRaEE8Kh3HDfs6lvQruX+SQZQeNOYiKR1NtrVHw2EL8XQj9C K/64HtprGtCgxA38HFBugUxjNHY0okdaK8tIuxKPC6fyW7YKP8Z50FC9oJO1ZcrZF3y0 nxewJEeyO8dxARX9TABZ5EbAvLqRic3Rjd/5T8j0uMfO+RegC2VUUdhEzQT/98mVzlL/ J7Iibp0PD7vFtLeb3Vm2RhczhrE9cP9SjceuifHbMhBpmBc9AHSqHIcrIGwgwE/Fqqci sn2Q== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=gaGIowPBiLdos6k2wdsmJawftIRJniyY+QsmcvDWWCA=; b=gNDjNwNhOGTB1wXZ7ygXrStMGBVNpK6FrbVq9R06SR2bRPPHoC+lRK1JcMOs5Jz5RK +CQf8PyEIqe3SjqQQl7ThgrwrwM8S3ibQTUzpVnHDcxjmkgrOz8VTFC5TaikM8PhwHUy mw+vYe3Ukc3YclGX1QTrc2VjDJlGED9CYoACDFBjmgG7vn6Qi31n8ZZyR8fi6nvDB6k9 GyQjParwVM565eTX05Gw5SVd97MJ/8Sbpwu2IVM3Xn6CABcCeeMlyER4m5UnfJJAic/5 Ck11za013McUgSp1vPlpRFdFFHTv+z1++C6rYB1qH0Nnml9yxB9bSwE//csBAmMjimR+ fpvg== +X-Gm-Message-State: ABuFfogvHoUAi4aTQkKUQ3MTlDdwo/mh62FXdjJigY5caYWAbiMEYXLz e1OGhMqAJjkCeSW2MeihTbZe70+uShTCQ2DRi+LcyA== +X-Google-Smtp-Source: ACcGV634pgBc3Hl7xI6l2dh2DRIZ89uXLaYuE0zBoFCo3YI9n5F2EyfcVhonIDBVJUwTN+pJ94AhD2zd7++B4tde87w= +X-Received: by 2002:a0d:e445:: with SMTP id n66-v6mr15080932ywe.120.1539054962910; Mon, 08 Oct 2018 20:16:02 -0700 (PDT) +MIME-Version: 1.0 +References: +In-Reply-To: +From: Ashley Roach +Date: Tue, 9 Oct 2018 03:15:50 +0000 +Message-ID: +Subject: Fwd: testing webhook parse +To: test@parse.ashleyroach.com +Content-Type: multipart/alternative; boundary="0000000000005aac2a0577c32893" + +--xYzZY +Content-Disposition: form-data; name="dkim" + +{@gmail.com : pass} +--xYzZY +Content-Disposition: form-data; name="to" + +test@parse.ashleyroach.com +--xYzZY +Content-Disposition: form-data; name="html" + +


---------- Forwarded message ---------
From: Ashley Roach <aroach@gmail.com>
Date: Mon, Oct 8, 2018 at 7:33 PM
Subject: testing webhook parse
To: <test@parse.ashleyroach.com>


this is the body for webhook parse
+
+ +--xYzZY +Content-Disposition: form-data; name="from" + +Ashley Roach +--xYzZY +Content-Disposition: form-data; name="text" + +---------- Forwarded message --------- +From: Ashley Roach +Date: Mon, Oct 8, 2018 at 7:33 PM +Subject: testing webhook parse +To: + + +this is the body for webhook parse + +--xYzZY +Content-Disposition: form-data; name="sender_ip" + +209.85.161.46 +--xYzZY +Content-Disposition: form-data; name="spam_report" + +Spam detection software, running on the system "mx0032p1las1.sendgrid.net", has +identified this incoming email as possible spam. The original message +has been attached to this so you can view it (if it isn't spam) or label +similar future email. If you have any questions, see +@@CONTACT_ADDRESS@@ for details. + +Content preview: Forwarded message --------- From: Ashley Roach Date: Mon, + Oct 8, 2018 at 7:33 PM Subject: testing webhook parse To: this is the body + for webhook parse Forwarded message --------- From: Ashley Roach Date: Mon, + Oct 8, 2018 at 7:33 PM Subject: testing webhook parse To: [...] + +Content analysis details: (0.0 points, 5.0 required) + + pts rule name description +---- ---------------------- -------------------------------------------------- + 0.0 FREEMAIL_FROM Sender email is freemail (aroach[at]gmail.com) + 0.0 HTML_MESSAGE BODY: HTML included in message + 0.0 T_MIME_NO_TEXT No text body parts + 0.0 T_TO_NO_BRKTS_FREEMAIL T_TO_NO_BRKTS_FREEMAIL + + +--xYzZY +Content-Disposition: form-data; name="envelope" + +{"to":["test@parse.ashleyroach.com"],"from":"aroach@gmail.com"} +--xYzZY +Content-Disposition: form-data; name="attachments" + +0 +--xYzZY +Content-Disposition: form-data; name="subject" + +Fwd: testing webhook parse +--xYzZY +Content-Disposition: form-data; name="spam_score" + +0.022 +--xYzZY +Content-Disposition: form-data; name="charsets" + +{"to":"UTF-8","html":"UTF-8","subject":"UTF-8","from":"UTF-8","text":"UTF-8"} +--xYzZY +Content-Disposition: form-data; name="SPF" + +pass +--xYzZY-- \ No newline at end of file From e550efc8f2e744c7648ac76adcdaa3b93707d6b7 Mon Sep 17 00:00:00 2001 From: Stuart Reed Date: Mon, 8 Oct 2018 22:24:00 -0600 Subject: [PATCH 14/29] Warn if dynamic template contains non-escaped character --- packages/helpers/classes/mail.js | 20 +++++++-- packages/helpers/classes/mail.spec.js | 60 +++++++++++++++++++++++++++ packages/helpers/constants/index.js | 8 ++++ use-cases/transactional-templates.md | 51 +++++++++++++++++++++++ 4 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 packages/helpers/constants/index.js diff --git a/packages/helpers/classes/mail.js b/packages/helpers/classes/mail.js index 5bb06d076..b6f83321b 100644 --- a/packages/helpers/classes/mail.js +++ b/packages/helpers/classes/mail.js @@ -9,6 +9,7 @@ const toCamelCase = require('../helpers/to-camel-case'); const toSnakeCase = require('../helpers/to-snake-case'); const deepClone = require('../helpers/deep-clone'); const arrayToJSON = require('../helpers/array-to-json'); +const { DYNAMIC_TEMPLATE_CHAR_WARNING } = require('../constants'); /** * Mail class @@ -87,8 +88,9 @@ class Mail { this.setTrackingSettings(trackingSettings); if (this.isDynamic) { - this.setDynamicTemplateData(dynamicTemplateData) - } else { + this.setDynamicTemplateData(dynamicTemplateData); + } + else { this.setSubstitutions(substitutions); this.setSubstitutionWrappers(substitutionWrappers); } @@ -242,7 +244,8 @@ class Mail { //depending on the templateId if (this.isDynamic && personalization.substitutions) { delete personalization.substitutions; - } else if (personalization.dynamicTemplateData) { + } + else if (personalization.dynamicTemplateData) { delete personalization.dynamicTemplateData; } @@ -254,7 +257,8 @@ class Mail { //If this is dynamic, set dynamicTemplateData, or set substitutions if (this.isDynamic) { this.applyDynamicTemplateData(personalization); - } else { + } + else { this.applySubstitutions(personalization); } @@ -333,6 +337,14 @@ class Mail { if (typeof dynamicTemplateData !== 'object') { throw new Error('Object expected for `dynamicTemplateData`'); } + + // Check dynamic template for non-escaped characters and warn if found + Object.values(dynamicTemplateData).forEach(value => { + if (/['"&]/.test(value)) { + console.warn(DYNAMIC_TEMPLATE_CHAR_WARNING); + } + }); + this.dynamicTemplateData = dynamicTemplateData; } diff --git a/packages/helpers/classes/mail.spec.js b/packages/helpers/classes/mail.spec.js index 9177bde81..c1de9fcbc 100644 --- a/packages/helpers/classes/mail.spec.js +++ b/packages/helpers/classes/mail.spec.js @@ -4,6 +4,7 @@ * Dependencies */ const Mail = require('./mail'); +const { DYNAMIC_TEMPLATE_CHAR_WARNING } = require('../constants'); /** * Tests @@ -154,4 +155,63 @@ describe('Mail', function() { }); }); + + describe('dynamic template handlebars substitutions', () => { + let logSpy, data; + + beforeEach(() => { + logSpy = sinon.spy(console, 'warn'); + data = { + to: 'recipient@example.org', + from: 'sender@example.org', + subject: 'Hello world', + text: 'Hello plain world!', + html: '

Hello HTML world!

', + templateId: 'd-df80613cccc6441ea5cd7c95377bc1ef', + }; + }); + + afterEach(() => { + console.warn.restore(); + }); + + it('should log an error if template subject line contains improperly escaped "\'" character', () => { + data = Object.assign(data, { + dynamicTemplateData: { + subject: 'Testing Templates and \'Stuff\'', + }, + }); + + const mail = new Mail(data); + + expect(logSpy.calledOnce).to.equal(true); + expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true); + }); + + it('should log an error if template subject line contains improperly escaped """ character', () => { + data = Object.assign(data, { + dynamicTemplateData: { + subject: '"Testing Templates" and Stuff', + }, + }); + + const mail = new Mail(data); + + expect(logSpy.calledOnce).to.equal(true); + expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true); + }); + + it('should log an error if template subject line contains improperly escaped "&" character', () => { + data = Object.assign(data, { + dynamicTemplateData: { + subject: 'Testing Templates & Stuff', + }, + }); + + const mail = new Mail(data); + + expect(logSpy.calledOnce).to.equal(true); + expect(logSpy.calledWith(DYNAMIC_TEMPLATE_CHAR_WARNING)).to.equal(true); + }); + }); }); diff --git a/packages/helpers/constants/index.js b/packages/helpers/constants/index.js new file mode 100644 index 000000000..4429da31e --- /dev/null +++ b/packages/helpers/constants/index.js @@ -0,0 +1,8 @@ +const DYNAMIC_TEMPLATE_CHAR_WARNING = ` +Content with characters ', " or & may need to be escaped with three brackets +{{{ content }}} +See https://sendgrid.com/docs/ui/sending-email/using-handlebars/ for more information.`; + +module.exports = { + DYNAMIC_TEMPLATE_CHAR_WARNING, +}; diff --git a/use-cases/transactional-templates.md b/use-cases/transactional-templates.md index 0e8c40077..c0c0f3e1e 100644 --- a/use-cases/transactional-templates.md +++ b/use-cases/transactional-templates.md @@ -48,3 +48,54 @@ sgMail.send(msg); ``` There's no need to specify the substitution wrappers as it will assume that you're using [Handlebars](https://handlebarsjs.com/) templating by default. + +## Prevent Escaping Characters + +Per Handlebars' documentation: If you don't want Handlebars to escape a value, use the "triple-stash", {{{ + +> If you include the characters ', " or & in a subject line replacement be sure to use three brackets. + +Email Subject: + +```text +{{{ subject }}} +``` + +Template Body: + +```html + + + + + +Hello {{{ name }}}, +

+I'm glad you are trying out the template feature! +

+<%body%> +

+I hope you are having a great day in {{{ city }}} :) +

+ + +``` + +```js +const sgMail = require('@sendgrid/mail'); +sgMail.setApiKey(process.env.SENDGRID_API_KEY); +const msg = { + to: 'recipient@example.org', + from: 'sender@example.org', + subject: 'Hello world', + text: 'Hello plain world!', + html: '

Hello HTML world!

', + templateId: 'd-f43daeeaef504760851f727007e0b5d0', + dynamic_template_data: { + subject: 'Testing Templates & Stuff', + name: 'Some "Testing" One', + city: 'Denver', + }, +}; +sgMail.send(msg); +``` From b23949edf2c528bdc7c34ab0e067793c6460e472 Mon Sep 17 00:00:00 2001 From: PyroclasticMayhem Date: Tue, 9 Oct 2018 14:58:50 -0400 Subject: [PATCH 15/29] Use relative link for environment variables setup --- TROUBLESHOOTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index a9d7e741b..f29b11a5c 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -74,7 +74,7 @@ We follow the MAJOR.MINOR.PATCH versioning scheme as described by [SemVer.org](h ## Environment Variables and Your SendGrid API Key -All of our examples assume you are using [environment variables](https://github.com/sendgrid/sendgrid-nodejs/tree/master/packages/client#setup-environment-variables) to hold your SendGrid API key. +All of our examples assume you are using [environment variables](packages/client#setup-environment-variables) to hold your SendGrid API key. If you choose to add your SendGrid API key directly (not recommended): From 37555ba49ddfb1f6b771a6b3d41c76aa09b545fd Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Tue, 9 Oct 2018 13:37:44 -0600 Subject: [PATCH 16/29] Add Kubernetes manifests --- examples/inbound-parse-docker/README.md | 5 +++ .../k8s/inbound-parse.yml | 33 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 examples/inbound-parse-docker/k8s/inbound-parse.yml diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md index 09083b57c..3a66b32bf 100644 --- a/examples/inbound-parse-docker/README.md +++ b/examples/inbound-parse-docker/README.md @@ -15,6 +15,11 @@ This is an example project for using a Docker container as a webhook receiver. At the moment, the `app.js` only prints data to the console. You can extend this project by adding more business logic to the `/parse_webhook` route. +# Deploy to Kubernetes + +* The container must be built and stored in a container registry. In this example, the Kubernetes (k8s) manifest uses the `imagePullPolicy: IfNotPresent` to pull from a local registry on the dev machine running docker-for-desktop. +* Once your k8s installation is running, from the root of the project execute: `kubectl apply -f k8s/inbound-parse.yml` + # License See [LICENSE](LICENSE) diff --git a/examples/inbound-parse-docker/k8s/inbound-parse.yml b/examples/inbound-parse-docker/k8s/inbound-parse.yml new file mode 100644 index 000000000..b33f2d94b --- /dev/null +++ b/examples/inbound-parse-docker/k8s/inbound-parse.yml @@ -0,0 +1,33 @@ +apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 +kind: Deployment +metadata: + name: inbound-parse-deployment +spec: + selector: + matchLabels: + app: inbound-parse + replicas: 2 # tells deployment to run 2 pods matching the template + template: + metadata: + labels: + app: inbound-parse + spec: + containers: + - name: inbound-parse + image: inbound-parse-docker:latest + imagePullPolicy: IfNotPresent # enables pull from local system + ports: + - containerPort: 3000 +--- +apiVersion: v1 +kind: Service +metadata: + name: inbound-parse-svc +spec: + ports: + - protocol: TCP + port: 3000 + targetPort: 3000 + selector: + app: inbound-parse + type: LoadBalancer From ce695f3ba4a896d2c57845772e3391efb2efd8cc Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Tue, 9 Oct 2018 14:53:19 -0600 Subject: [PATCH 17/29] Send back a better status message --- examples/inbound-parse-docker/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/inbound-parse-docker/app.js b/examples/inbound-parse-docker/app.js index d3c12afb5..459a55f88 100644 --- a/examples/inbound-parse-docker/app.js +++ b/examples/inbound-parse-docker/app.js @@ -7,7 +7,7 @@ app.use(formidable()) -app.get('/', (req, res) => res.send('Hello World!')) +app.get('/', (req, res) => res.status(200).json({status: 'ok'})) app.post('/parse_webhook', (req, res) => { console.log('---------- START RECEIVED WEBHOOK DATA ----------') From 89fb8db3da6cca4b5a0dc27bd117b56c6e58a1b0 Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Tue, 9 Oct 2018 14:53:32 -0600 Subject: [PATCH 18/29] Add kubernetes documentation --- examples/inbound-parse-docker/README.md | 33 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md index 3a66b32bf..e5b5c33d7 100644 --- a/examples/inbound-parse-docker/README.md +++ b/examples/inbound-parse-docker/README.md @@ -4,21 +4,46 @@ This is an example project for using a Docker container as a webhook receiver. # Prerequisites -* Docker CE 18 +* [Docker CE 18](https://www.docker.com/get-started) * SendGrid Account with inbound parse enabled to send form data (default) * Ngrok or Internet accessible URL # Usage * `cd ` -* `docker-compose build && docker-compose up` +* Build the container: `docker-compose build` +* Run the container: `docker-compose up` +* Build & Run the container: `docker-compose build && docker-compose up` At the moment, the `app.js` only prints data to the console. You can extend this project by adding more business logic to the `/parse_webhook` route. +# A note on attachments + +This project uses the [express-formidable](https://github.com/utatti/express-formidable) middleware to process the form data sent by SendGrid's Inbound Parse Webhook. As a result, `express-formidable` automatically decodes and stores the images to the `/tmp` directory in the container. This is configurable by passing a configuration object to the middleware: + +```js +app.use(formidable({ + encoding: 'utf-8', + uploadDir: '/my/dir', + multiples: true, // req.files to be arrays of files +}); +``` + +Additionally, the [docker-compose](docker-compose.yml) has a volume mapping commented out if you'd prefer to store them in persistent storage outside of the container. + # Deploy to Kubernetes -* The container must be built and stored in a container registry. In this example, the Kubernetes (k8s) manifest uses the `imagePullPolicy: IfNotPresent` to pull from a local registry on the dev machine running docker-for-desktop. -* Once your k8s installation is running, from the root of the project execute: `kubectl apply -f k8s/inbound-parse.yml` +* In order for Kubernetes to use the container described in this project, the container must be built and stored in a container registry. You can choose to use a private registry in your cloud provider or a public registry (e.g., Docker Hub). You can also run a development environment of Kubernetes via [Docker for Mac or Windows](https://www.docker.com/get-started) +* In this project, the [Kubernetes (k8s) manifest](k8s/inbound-parse.yml) uses the `imagePullPolicy: IfNotPresent` to pull from a local registry on the dev machine running Kubernetes as part of Docker. If you were deploying to Google Cloud, for example, you should disable that option. +* `kubectl` is used to deploy. You should already have a working `kubectl context`. From the root of the project execute: `kubectl apply -f k8s/inbound-parse.yml` + +# Resources + +* [Kubernetes Developer Docs](https://kubernetes.io/docs/user-journeys/users/application-developer/foundational/) +* [Install Kubernetes UI in Docker for Windows/Mac](https://www.ntweekly.com/2018/05/25/deploy-kubernetes-web-ui-dashboard-docker-windows/) +* [Deploy and Expose Apps in Kubernetes](https://www.ntweekly.com/2018/06/10/deploy-expose-applications-kubernetes-docker-windows/) +* [Download Docker](https://www.docker.com/get-started) + # License From a846cd8ec82c5f3deb8b4bd050dc27c496ddcff5 Mon Sep 17 00:00:00 2001 From: Ashley Roach Date: Tue, 9 Oct 2018 15:00:29 -0600 Subject: [PATCH 19/29] Clarify processing events in readme --- examples/inbound-parse-docker/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md index e5b5c33d7..7adeb2351 100644 --- a/examples/inbound-parse-docker/README.md +++ b/examples/inbound-parse-docker/README.md @@ -17,9 +17,13 @@ This is an example project for using a Docker container as a webhook receiver. At the moment, the `app.js` only prints data to the console. You can extend this project by adding more business logic to the `/parse_webhook` route. -# A note on attachments +# A note on processing events -This project uses the [express-formidable](https://github.com/utatti/express-formidable) middleware to process the form data sent by SendGrid's Inbound Parse Webhook. As a result, `express-formidable` automatically decodes and stores the images to the `/tmp` directory in the container. This is configurable by passing a configuration object to the middleware: +This project uses the [express-formidable](https://github.com/utatti/express-formidable) middleware to process the form data sent by SendGrid's Inbound Parse Webhook. + +The events are available in the `/parse_webhook` route in the `req.fields` object. The [app.js](app.js) contains logging statements for the elements that are available to you. It may be useful to review the [example-webhook-payload.txt](example-webhook-payload.txt) for what the form data looks like when extending this application. + +Attachments: `express-formidable` automatically decodes and stores the images to the `/tmp` directory in the container. This is configurable by passing a configuration object to the middleware: ```js app.use(formidable({ From 8c03ff18f81c0a702eab7fbd97eae102887d5cf1 Mon Sep 17 00:00:00 2001 From: Dmitry Danilov Date: Wed, 10 Oct 2018 02:06:05 +0300 Subject: [PATCH 20/29] Add first-timers.md file for newcomers --- first-timers.md | 77 ++++++++++++++++++++++++++++++++++ static/img/github-fork.png | Bin 0 -> 30890 bytes static/img/github-sign-up.png | Bin 0 -> 77240 bytes 3 files changed, 77 insertions(+) create mode 100644 first-timers.md create mode 100644 static/img/github-fork.png create mode 100644 static/img/github-sign-up.png diff --git a/first-timers.md b/first-timers.md new file mode 100644 index 000000000..94bd1e8a2 --- /dev/null +++ b/first-timers.md @@ -0,0 +1,77 @@ +# How To Contribute to SendGrid Repositories via GitHub + Contributing to the SendGrid is easy! All you need to do is find an open issue (see the bottom of this page for a list of repositories containing open issues), fix it and submit a pull request. Once you have submitted your pull request, the team can easily review it before it is merged into the repository. + To make a pull request, follow these steps: + 1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of SendGrid, please use your full name with your GitHub account and enter SendGrid as your company so we can easily identify you. + + + + 2. __[Fork](https://help.github.com/fork-a-repo/)__ the [sendgrid-nodejs](https://github.com/sendgrid/sendgrid-nodejs) repository: + + + + 3. __Clone__ your fork via the following commands: + + ``` + # Clone your fork of the repo into the current directory + git clone https://github.com/your_username/sendgrid-nodejs + # Navigate to the newly cloned directory + cd sendgrid-nodejs + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/sendgrid/sendgrid-nodejs + ``` + + > Don't forget to replace *your_username* in the URL by your real GitHub username. + + 4. __Create a new topic branch__ (off the main project development branch) to + contain your feature, change, or fix: + +``` + git checkout -b +``` + + 5. __Commit your changes__ in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code. + 6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: + + ``` + git pull [--rebase] upstream master + ``` + + 7. __Push__ your topic branch up to your fork: + ``` + git push origin + ``` + 8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ + with a clear title and description against the `master` branch. All tests must be passing before we will review the PR. + + ### Important notice + Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](./CONTRIBUTING.md) file. + ## Repositories with Open, Easy, Help Wanted, Issue Filters + * [Python SDK](https://github.com/sendgrid/sendgrid-python/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [PHP SDK](https://github.com/sendgrid/sendgrid-php/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [C# SDK](https://github.com/sendgrid/sendgrid-csharp/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Ruby SDK](https://github.com/sendgrid/sendgrid-ruby/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Node.js SDK](https://github.com/sendgrid/sendgrid-nodejs/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Java SDK](https://github.com/sendgrid/sendgrid-java/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Go SDK](https://github.com/sendgrid/sendgrid-go/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Python STMPAPI Client](https://github.com/sendgrid/smtpapi-python/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [PHP STMPAPI Client](https://github.com/sendgrid/smtpapi-php/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [C# STMPAPI Client](https://github.com/sendgrid/smtpapi-csharp/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Ruby STMPAPI Client](https://github.com/sendgrid/smtpapi-ruby/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Node.js STMPAPI Client](https://github.com/sendgrid/smtpapi-nodejs/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Java STMPAPI Client](https://github.com/sendgrid/smtpapi-java/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Go STMPAPI Client](https://github.com/sendgrid/smtpapi-go/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Python HTTP Client](https://github.com/sendgrid/python-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [PHP HTTP Client](https://github.com/sendgrid/php-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [C# HTTP Client](https://github.com/sendgrid/csharp-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Java HTTP Client](https://github.com/sendgrid/java-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Ruby HTTP Client](https://github.com/sendgrid/ruby-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Go HTTP Client](https://github.com/sendgrid/rest/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Node.js HTTP Client](https://github.com/sendgrid/nodejs-http-client/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Open Source Data Collector](https://github.com/sendgrid/open-source-library-data-collector/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Open API Definition](https://github.com/sendgrid/sendgrid-oai/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [DX Automator](https://github.com/sendgrid/dx-automator/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) +* [Documentation](https://github.com/sendgrid/docs/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22difficulty%3A+easy%22+label%3A%22status%3A+help+wanted%22) \ No newline at end of file diff --git a/static/img/github-fork.png b/static/img/github-fork.png new file mode 100644 index 0000000000000000000000000000000000000000..c556d312c76a518f6b085d42f3d8d808826be2d4 GIT binary patch literal 30890 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 literal 0 HcmV?d00001 diff --git a/static/img/github-sign-up.png b/static/img/github-sign-up.png new file mode 100644 index 0000000000000000000000000000000000000000..45c028b9add4dccb47c2e922173cd689ef60ada0 GIT binary patch literal 77240 zcma&NbyOU|^XNOc2MGkXAi>?;3GVLh?ydaNd(E6PivAmSkc0DvO(RZJNGpq>B#VhSGe{fe5S8qNC`l#7Uz zDm*;=%BI4``%g4iaZOhh2Xj{sBWE)}#nR2y)y&x>coG2sNB}7@VO7uNlQkb**+VdJ zq0r=l$jTsxEjJ?D*}7O^rLn6_*Rq3$pOY2)jDbj@u1Vb#+vc{0X<+jw86JU@PDNd% zF-@B_2X~p0MRbf?Z|{Ckm8PH;;j!JIL*HiNw@c==@3_mj?>OrOx8$~nxWgN(VCpn+!nC@E=;|I9vRS#Rms+Hhm@6rF2?o?ElUe1Z_nlyy_xBTf!!@|P3qPn`e;>FctB=+YTuDj{ovIYA| zw$4?Ee{v5###ffH*3>Z%Y`QqzOx4}{1oU=E-}BZxba`$xS03KInx1Bf zf-Swt{9;T`U%_2x(Ko1v!#RI?s@nytr?=*0vbMF&Fq+CQDkFmw5{)K8ud~u;wH(DyTG3|4*{@oU}vS#0yx0z?cOW$K!nr zdcMZ@k&uMKkv!?nypCt^W^jAGv^m&;s@l&x8~9zWJom>lm~P-nXO*q2jJpIxJb~N^ z)-?xEL>CXnD(nwhugD! z*Lg;(meyQbIoGq2wl|NAL04D^KY)XeOC-=dv1X#Iq=bG3^>0A^rc`NyXU!U|DhdkJ z>W%3u^1@W#5jl?tBNT5Qe$-GW(xxo$zz$kIOO>?_kZ~(gR{W4|`1pv(yj|@%^_k+EPX4HQsb3;Mq z>bAm~dZiK3+Y9R!(r8_<&`E#&jleRZ_aKK-=Kd!#$`>DR)r_lq5b7NGtL&4@LvhW3 z#P^u29o38+w=(s@Lfde$r=#2B2yrnriq)V7v)Em?`m4>Gmp9KYCE%v1QH&oHjt9OXC(Q>8%?s3I={H6sN zsI{cps#L=sPmj0q?8p5$9nbn{B*#0dbqa-r=B%IV&d%MRU1OvMLNAU`_WVU&{;rLL zIsa%432-X8-Pbh>o%|B})5?m2Z=q`Rfj#|ca}Ro<-$8Px{}=&vmRfA}G9-~2+NeoD zJ~nYckgJti!J^y=u67z)YyYJpjeZ{4rb(?WZ>y58nQ=&ZTd>*XZS(_pQ+F%RPNB|H z@w_uSy7Bmj5qsC4W&&Yp>+R0NmQ_3zi4Vk$G*tP`&H3S_q7;(liz8KUum)PgK3!AE z{A4U?9{CF|ixbBg-B+O%5QB9Pz*pFn9=E#(EFVq8%`y>6R+&PMHn__XRf=-yR zXfImW3!$^V=`C|06s^CQP5KFIe8{VwG$@E6JN5U$*eO(T?dAx-3KS|c_qWf@<{?cO z7I2nIBS|+;(M;94Bkbx;LORp`B3A!dFV<5-q(~<1W<-XveFQERKK*zkg4GOkCPeg+ zYDrWnBG|l|zGV&8)4qW@h#ASmbgYX%OSu))rZIuX89ikB=#*T=(OA9WaVFp&KXXVs zGfdsu+S=iB+wQ+@o?zCBd-?wV+&zua$hL1%YL=|$_R;ATmF^FX6x}7mZA+Ox%61Jy zF^!lAft9`_V)IUmUn?4 zi^AJ5p8`Of^!#M^!~}E^ilQ}PEvPI&?C;d=#;aOLAduhNV^&)d3eJOb4gMH==4H*6+oQ^Z_jIIxE*r|ps3^O z%Pvo_Z*1Mj)x}KH`33Q6m$UhdX4&vqRoO7|F#>7arN`_hKXF6J=PNfkhfkrqv&GLZ z#kilaT1rRGj)Na!-;fPu;j#JPvv?dg&mtEdQO~X&vXIiv3`UaBb(42}-rJj)klqGG z@>cu*ZtyPD#)Im??R53mUWQ|hPM!Obqo1QKv5pp*$Ttw90c@xhT+MIjOjd^m#v zj?;ssPliNEud{1kCO~Zlr{ve>OsG@5Cp?1IMh;D}Ll3+!aQBawgTdc5-gd0EFfc3i z?{0d&y~0)UWMN?s*@TM*-rU|+l+(qlD4T3E7(}#v9``o|b@-I2S1>J!6f@|4*k8H# z11XVt+i4;LfOMS3R2@BcNAHZCD&xv!I?FoIsA$U14-pYEo{m`o&!&r*J{q^x(NG$drzmz{CTy6&ak+Y_)Kia^}4Ony&R$qQ3u{cIZkN&6Z)_`5v zA7yH%79uG8^8s|W-o6WeF5Ax5=)r%?4~mOexj{VIuC^AIQ?IP4>^%-b?s<-;*CNv%oHdSS9vA0KGwR{fxy9ym|Cbi^42eir zkVyU5Tfnsy)L*_w^1F_C|9#e9Y^JS3LMoGI-gZvQJ9cuN>}hz#4bPcbm5_U(mOiUD zp3v5C%k9>wH~M3#?woS2+2r&wfai1FBT)Y1v3Y}M=i*jb^U^tdS~=i!vzCY62O;C1he4+-Fm%iPh`#)?795(o^t&2tDG zV$@rGI1kyWgBC`E{u9c{&urE0LYsV$r8TlkG;J_%(g`Qx?cZS&gufk?K|T!t)H3!F6<6s z|G=B;vC;XH=;YVw^_Ldptj;S$?t}d`e2L?QX`H|*i@CFkZMkWW$JP(Tocn>Z!@RZ= z`KXh76xb7HvHag#Rr9-kZ|2eOa57SAq!vUMegH7VA$%6uR-)`&%)jX;X>`ao>U5G- zhus|f?w`u#i6PUjwt4yu{!4Pc^*d-h6Mabt0vH)v(W*C7u5!rG^=}=+`Gmu$8!4?K2iac^T}Kn z=>GND&tZB>lV9{$y~6v}5ofXs)D7|4_1K#3ol8Gan4Y+M@Aq7sg>5mgi|l$PXqjEt zp?7~3LXhottS%qj0f&k=*`}f3`Ihii{b0gD!(mI4?M%IiHBRGchO2O{ zs0xwr12LCPGCe*P-^M`t7BY^%R5=DgeL?fj=snHu$Gc1Xe zJZ8Ln5WYmZs%u_S05LFQ{8mU@VJgMRVrtk;SZ_NPJg`n&tdDBdLDH8Y8!q5Z5yDbU zP-FYYxu{U~0iP{paB?#H2s~{1$Y?P9?y$ABwOF7s~wI93WU2u_M^*TRJLwgH zA9Whz*n>`3assLY8)5tbuNRa6>I58P6xuvC=SNnKR1ZEHN+oz8H!AUS5WDEr-6T89 z`Ea9O_#;5FEqP6m2dnK2AKM$)TKd~`z%1m(@?lODH}EUn4Hk$6iW$%vyrCSweXi_y zTv$Gud9d;J%7}%?=%nRZJ=_R3V?aRX7zvBp|K!mAVs7Yuw0T?e887e?(F)eF+hYG% zc(ZSg^as9fKQ0|-Yv~Jza5mt_e3ju!mKKvg{PT@cskaF*zqs^g5jDmK_9asqDv*mL z)Wc}PR3U7;M~;7hAAAK?eo02bwRhgC#_V>Ub!6@TSxs*3nyBKXC#Sb&yVJzKAQ=e& zKZYxB5Yjn3W3l}#96nA07tz~aNsFknsaxm_gv*k@xKy4fabo?Q<9}ZtvWteL5EjpW zI9?@6Dq8>~u96Z-vGV~EO*vd9KB-ECA#Y9%rkt%_w6By+=TQ{)=2vZS48s6m2~d*T z>pc2kpa5cK8t|J;e>>7&w(7H@DJOwgylz9f^FPKP6;0X&siXBD+Y}*y>Bu6CS2(TZ z6yfr~l>jc(_yw5rHfk8O!e?}ICww`q$@NRTJiyh} zmEh!*i0?t>=|<~)`h4QyropVjtkT!g2~fFEejC&i5Jr3LJSQ8O2)LtbDlae3tGPTC zJj0|;gr!r*W~~d=X-br)m>5`oO0PP9^&$qMkWtQ8caxKn?oKwl4_Bp9Qeoizz9XZc z=)X7EdT|@Z@YTskXNV~>8^RW9J$9*`v<{-UJ*rJ3@9vDJr;0l^?ts_-D>i^en7W5f zlZ{Y^3zvsIC%cD&BLZTEU}iBy`q1}gJF#oXTxG!g zBPS?}cP5{fVjzGPa&G3g#YzKtvv#-f-B!;VBgi3#@=Uf@R{^G{u#6R+Ty4}3Ky@)~ z0_ArEt6m1Gy==JjW2QhP>Rt))%=f@gJg##F)f7r>w^|;KryB0(dB>G`!W!>#wkv38 zvib=8i8>Kn__g#&G!GdCIi02B`SB=AP$T_UW58;$-Y0eQSQoyMt2+!_K_zIHa!+J5 zT1=J$mL);Q3YhdB5-A`a!8!MmP-P!E_yo1{XEp&Z>J$@^q3J zHpf(D(AAk4JK3W0#pN2zge%)3LW&Vdfl-o^BWz_=a&yQEP|?x(CZ*dLPb;OQCZ?q1 z)0{)*8GLHDT`P5aggLw3KVr;_Cj6Nt;8Und1F_K)(#6I$642luPex5m4G|a|49U;W zKZ-YH5%Fj9M_pZdLGg*owIry4c#F9;05$VxB5ovG^$D-PxWN}6gP6b+>$P^uM%_=q z4*5XQ3?LLaZXjo1s$$X}$G2;9u3f2@~HAIPz+Y09wLFm*O6gJ_}fUh zVRgJ?$r~^|oIZC&9&BR*3;bd_lNJC1x71~GefyZqZ?PgiGQe8_+25=%01l>9WXSdH zA}yy?tXuu0E7tstZZl`QRv}w&m%?ZEdI!)M2y-b%G|XBGVlC7|P~|T=#EOHuPL3`) zY$2hPNb=t<3%N=T50*#>A!JayPe{kfPSVCkpDiwo`a0YlSI2^yta$LCDN~^9YAoHN zq8+a$EHu2b2){)*_NXKALC2I0?$AutnI3WZ8HC+%y8@-Wyey4_b$FAB^2TUM_*-5~ zt7IqH?6k`Dm%knRo+yqeFGhG$QfHa#jm~=IKm6M=8*V~7%ZB4z*02P}i9rE(G#&xU zZ%m}#Ge?sn%4l=U9pP2JeNr#2aK=;AN<}P`uveyo(V$XyqMTg^c^l_6NJHjQJ%9zF zR^m_M3}$R%iHn&DV91oxjaSF$Bzv2;94H=GU*eN*c3Jcw!+}g_hUH{43fPa2ZJ6Rc zbv|l>*W@yC%N$~M`$&kSlz&<6PK3)!tPM`BjEN{Ulqj;PhAa53h+>RJ7=)HtH5y7Y zz`kG>bUx%@vV<28I4u2YPcq=dm4#Jwe&P?fH)Z*nz3bpFSGW_A^g?&q+nalXObS@q z*x=&huV@CSXe&z}9K@1yceiAD6Vc1jAI=mVRYfuAHS_TB&?J_+9nU{b7zl~q8Tdnf z{-IH&Ujo+CR);k+I|{(Y#!lXcph<-7^uqMZgeN2v6@?NItZ6~arwE@)$H;>Ka6Um< zJV1}RvKgXY;&ptX%w5J+;1`H4<+G!<=C)9}x;ht0vz}dnpo&m$Km1t2?=v?&A38lv zGvP)@OAkTPFg&vyUpwugrtJINRK3y#0pv{JbI?1M6wxOfbCd)ej2#?Vn7h$~X5}gs zf!VM3hkRaU0x}To_szoM;`M>?C2h}t@7wG>!LH!!2+=qnVPAdkby>fJ6pS_+g)UeES5&2aIbUvzdpw))HUp& zk@#v*9Gr|8K_vePWU3)z^N$AY{A}A@679vS^;p+46Ms6KY$*~A>T$x^=rqjPpKnmc zuF8w&DdHraR|GF5P4Bq+H5Ir-d)bzqB*VZaAdr*{{Ye2-Z?83PcRIHqh4fbv%S1@w zme#U-@6A--P+K-H-e4 z^DxAY8?j`Ap(-KW>mAH=Tcc!7EL2F3?=jzkZBM@GVYt-K6BVT$>OfN#Yju$Q0hu?${E?ozkcoQeC5HzZ?XYZW@m5Q9`_z|VwC!cTiNg~u24{} zJ%3?D1-$PZi`hLBw{IKj>)$un4Z8eM;2Yrg_Vif4rH(285|~(;N*YwtU38K5i&leY z41=j7WPrX3>^Dbma65=imaIkQ;>OpGg)uUY{@MQfpbC z8BC#o$;aTCoSvlUFO)7)pB4Zi{p(R=^7coi;K%ibd|-&vOVsYzD5Z85L<}9G=RZ-O zd)T>(=UR5Q67v3X>Rauj_5rS7D`T0$dc? zU~*y7l4e(<8)Ewj=;GZ(~WA2&0UzJp}qA8+5y5wqrk@@+N zc~vL79%-xBXHN}Zq6C|f*X}vaIwMw611NE1t^v1W=k;vt zl(yoYuqoQwY#06st!3pC_(#i&{~`g)33EZr%P`sAeJUiZ4~Ou`!Z8rx*r_np^mL`($$%?KXcKb zsyEn3(S{8_Cu*vj)5V&q%8VSZ6aAA|Q+`5=_hriGv1S|Yix!&;6TVauLp-ohDcR5c zo1fui^lx_%ip_LA7cZ)&c@h>9t2b)BZOvh4PEgN#9Lqw}d5WxA{(*3Lf8@%n)>ki{ zjmfa2&OP&|+e)KsFbNwwyZ-R*5%&s9>hdys*GQIt-~7~~=Bq6+tqjupF^aAf*<)T; zS4Ye)qeMyesRH3Qx7}kY_lHDKU{c4kGelr~xb;gYjohaZi0)ba3~n@asNZdD7P_ic z2G~Ii2})i(la_PpsK35_>%S!z+fFXm;us4!jHh-OZ<-JBqy$Kk&38I(Q8J-`{Qhi+ z_skvg;O5=v{j+nD3ebJfYgV$rTAg(3_>9~@0t9Y+s5OEL@7?s&dWfB`M5 z`}mK}QfGo+Pw(%kCa%Ghvnf}Kzg{<2#P4c`bck&H8W~h=1?kn9nxQ&gu`fa)$UY4W zg&uysz~Q$u+#*FcI;|N>7##xOKI+K4w&A)Gp^z^)#dfgi{Z30D9VdH6B_8I!_u+8V zOIBzsxlVr`&QZw?AtDtR}%2-~3`vVduQX>6~r5HsSm-Lz43S1xs4UnaTw zccWcIGlg<2-MSd342_?LC`eJ=Bk@p}6_UT3fsHPLQHg0WTK|vi4sO`ARg%W(Oga~) ztUh>?y=~RXOF}sq2bMvPQ$?EVCM$pNL3RD2!?_dY4Dq zx`fyJRl;58Ox2xsEu$=(R;IgmZY9r$;a4tY_{vS4u5&2>_H)(v{_j~?je|aaockM1 zAaVb?|8AJIh_y{GTL|+?58Apb5qr#rC=Wx&wlRIah%RLC9!$K-ENDB){RBhb#o~WB z=0PA?ADkOx#XR&k4a1@c3Q&~&lGQzh|K}0$E|5Z>kH$-{z4%Dz&V$gqW>aJD1eX|- zeBE7HWI6i&vO2$aAzYZ!Zr+fiRwalSH=V;a4YXXMDi;5n-!!PHPIx%p6c6uaH`eL0 z)^hnIHRxmC$^&BMO0Si=Yb4=lLQ>|`t)SjX=W`Od6bcGTf0;%}3F?(83$XEp((kn) z@s`s7D)2q-yk#X7+d2`D@33m+;^chav)@j4XzlP;tGL!r%3>THec!QB5f#nb*c}<@ z1f%CDa4m3pDJcqJV{^(_mH0IM#%=ccMeVRW)m-CM>&wj~yF`Z(7_YnPqm8I|%bj-f zbEby-&#K%QA1dMYdSdFLHp>nGJBFS9Ih766Y-15!qkPp~9A)XStYx4Qm+@Sh`O&Sn z(vI`@{0+M!GM}4SkXRaAC`9BKIYe=k3i@RPULH|jP0@o(D>u_~WkeKS8jAV6Z7dX< zz;=-e2J{uWUrJ-7J8`{i4&CV}CJR&X;|@p2R9haM3b|~0r#e6q&h1}@;F1el@Em^s z_Ippwu9>08WgmB@kbrBp`JO)N(MWZ-c~n}CH>HRneg+~l6nynpSki(;O8QL^jhK&? zO#&7uSz9L5EeI&b`?%^vpBg``tez_WG8=kbn?o4YTh- z4@?;)r6&OlfF^m~Y_;9wA|f5^*MZ2;83zkKH_qF?KI=6$GpOb8 zZV|}H2+- zzT0$?ThfKf-ieQDdpYPZl6;IvI|Te(^3ErsNY3BZ%1>-(w(l7faO)kyh5O04(uM!J z*ojf>8c~L3TSgyP!KI=YZ2F{33KPZlJ*Ze3TT2oQ7 zc7HgER9~Ng)v6(3+}){sUoaIS7YqcH+a6_vPfoS?9BZd@*jdYh+Q1w0gl%&%0>wo* zC_jlUmObYD6zZ(}l%dQ+N6Ndtd8QV-h7SNr#h4F+g$xWrEt4uG|FqG|oTg zO!Rrwa78nv=B6Y{vlA1Lr3}r|-xk^v^K30wu&b@&l*wW(Ds&6^wGou?Vd1_z{(e0M z`&u&XcHUz!dO%Iyp7dqI+e| zlk}2xv`C+Iz5})RFz!9U=P+jWmtPFQnED<(k@@0QXYA2AW}m(J>U<9pJ{Y<1kKqC* zqe_51q>lPfo+VO)UyMY2Yon(6|1M#Y?E9+fMtU3lcc+o5i&4 zt0o5oLLO5`y<^`yy8DBgZO>S;&`!@@$1W^bnf=jqC-T_*9gNF0dL)fEhW=)fn{QOK z^fq(SRn^t#gxM9^hlzRyexh3ogz4>20@}2jxE4 zaIWj`^Q+5Q;F7`+7 z)k;*Gq5dPhU}iF#T3SNsrye3%7@kO-k_$K?ne(rW#F-#CmfVZ(YQaqA)BWl8VqMsu zP$v@`KIbB7mGY%e31_d|+walHeb)cNngq~J2N+1k{ddlo4-Ym)4N_~1yyPl+;A>_|41I`k>U4%YBY#hOTb*`oALrU>vw+{x+mu=6;@=_>gO8wLi)<{gT0c8wj1-_?h<`{?DIZMDRubgOlCB)GB`>_Pase zzQ8?Y{9n+RqA2t5FahEv=_@_)hdtqc7>>yQuKT_T{@)9v|Eo0G{|8W}_6>DQ>|ZhK zzte6A$i2z!0qk!kW+6uZT1OKp7Vmwx)&Ge7|3T0G&n9z9*$%caq9Fe(g&yPXe@M3f z;|~9?dig)<_`fRrzdQPWbN~Ol@c#gHIgDu>-+_M$@$c{NSIFY`xjj;olCqMi$ct`N zzs6pEN72a0$lk%VB)pdxj5&|V>1pe=)`py%oakuzk;uov2zSvmr$1Jc{S=<#;_tdk zTgn?$093!jes66xjVw1j3h8PM7_r$Cu}Vu4@GH&9u`)KE{u}~U%6+nzrbv8NsFa4W z({mI*k$>uU zc+XuKf5>uYwdW!j4SBZ(KWMW8vsUQGaC1kwEiyGZ1&x21jDA>f!m7ERUai@{%eI}J zXd3f>R5e+YC0(rT4a59kpsK2)KdLi?zWl=#ywkF{KY_H)EE;xqIuPaBbItj0RR0Q4 zKBxE_*Mc7lo{w|`_I57a#;OIk9tO*$msQ3qHA2kYmL_aoP!J%)FFSMN*3F;WtU3qN zh+iA6#cRjXZcTUs`i?9F>J}*jkhQOUTSLFDdif>>$i8AH%LK2q04&q!$@?^ zA4e{Jr%1Oa3Ej70&F9|6(vsj|@z5XXYz=&Sv1x#MX0y_Gn*Uc{^m}u$YN_bvCYDO! zhew>lx8%h(gOY;XC%|x(V{zLr*)KKmN_E1SyojPN$i~yJgH-(;suU>s%_^GGLT;#a zmp7QGqO3xkUnsyu;N)tfSzPe#@r!QHr}&Ga{CrAE3J4)->4={{*(Gj8`(EbC7EYHN z{gf_(3$1{}@36lT6HAJQb|Yd`FW)P4DFnEa!JYm>#K>%cQhh4*F=%sCh% z1p0@IO%yr}(&ol_XBHK9@GeCZH7}KxRif`j!LnvIHDCB&YL~q%oPsl<3$Zj9N=Lnz z1#fa{Jj{6or#5~h2X9#y(6}J(g+*`)qz=gRZ_5u6o9$h-*HI$8C+nuk2i8A>>le}+ z1=#M;VfFy{_X`0Nva~isX*Qm(B)g=Jwsh8qvJ6Ai7of)<`^oRUsLRMYcDH{%@Znv7gwEv=V*luXlrZ`w6l}-Tgl= z{DADysZvnf8-Z#{nqcC}6^gy~t@bF)ya!#+&6QIbgX?!3@})Zmgz%qVqdcIqZ{CF*ae;&=GUC-D zfP7lWB$GW+I+mSXb zKC=Ul#`Y?osI=xY>u7K7-hptajLx&YS%={Pd@leH3RigBS*i;2)6lV4@CrK{PN#lI z4B&M?Rbp(mndtNwFF_-CwA!kEa|bQ2+~92Sn5@B|s|mcyK42;3jrf6zDbF%?R~zsh zY(sY{CW`B~WptmHJxn5`YHU9pQvnsyWqZ$gAS{r)oLq+>M%f2=Sx95Yb5#K`@7_A$}nnZ)43a_$}MY85lHd97ks=NzAL9)ZjEZKj= zZ&pRnxFA!>X{u0Efs=UH4W7l#4n~nGtA+i%T)&LHZkhL>gyo#yz_CzFOUpXPcW*vm z-1-vomxgLSc6 zS(Cuu0A>a@#vJG)IQcKtn-mtV;Vb(XueRg{NNfzIH{-!NKc*%kal5*n#0zZ^Lo zROOw@yGRw_gmgln0jdrs?X7@hNV{x+r4B2>!^&0LYE=|2`=J3mZ^4@d6GK_2&xGJN_#b+2%VZXjmd$ z@J>c|5r#9d(SdJkLHB;0XP>yuS3{E-ci&`^tt_oS;2r38M*ZdFAS6{wq5ih6J0$RVG=Q_yA9J%w zc;7$IO79C!tpKoJa#nHX+h7K-01~zn7Xd@qpH0^@WAgz+CYt2tqDRz>0eIh^lw@x0>>PA-V*|RdsNObr;|$k>i>j*AeQ6KHlBFj*PX^FzLXYZS zdjqtcq^()aO7cz~GUXzhmIml*@XpWo#mX!?@x9oB%z7XCW?l!P))-}^RQ8SzPp>Tn zHQ|74ho*VHGbAHgEC3V!O0<@JVEoA%P5~bXTp141GH$CfOvo0^&-fGwi%^;@ae9wp zKl1^|mNN|K$mpWkt{u^Bvl3`iaculLh>T8{N^?;N!y&$=wCrUA&f> z_?-^`c%08?hCZVh*&`g-V3YqEu_#68uX+t3=cQoOSUpBtt8*^Glr(FI zhJPAX>pGF>Gf8fvxOllmV7_4r3-+0~7lZ@GYE-!6+?N&0S8Hzn#_y}yT=HFU1C}4s zCaGIdA^OGBMO@T@?sS{XtIy-+N3i28?wr$~Zz1~T80U^?3+&zOTUh8P!1@R%r8Y^DSuc)!v0*C$v+KCdg&rK??;3!V1&=8akeVPIM0f4(2tA!Y_^7N30J>MlTgRqs0?et} zxm3+de^oP`Q(?ZPLXZ*G5yM+;ENig}d6Swx-Yvo@YlCt@quFyNTnnFWwC>J(e81n0-s-g5;@qlM3m|TMNM-8g zcWg(Y4h}_M5yMrEX!jKD!Kr6*bSJLH7*r4>_Dim1HRP4oIyhjTnZhg==d}18Z#vSm zz99+=)*byOm)d@66$@irb7lm%bKR+lh;BM_axxroB001X@>&{ z`I~K13Rq;TyqR@AMI5{9B93M5eB@e^82=ijLlhuiZ9K*_VSkkRXcPVIuBhqQ7AF{do3Zjxquxnf6VP3^n zQOBWd(`G~IXueyjwnR1!6?9RsU1r_V?V+Y*u_otGaJ#-u`;s-kK~2 zhh3CShk(~Y8b|=Q$aIKT)89sQM3M&&09vXzG8mfURLY@^r%%R{edR*hte}3aHEY~p z06g`0rlkl#_KdeJCp~~Ac<44~fI;wt-+E*NX?gEvZ}Pqc|MXQKQE@;aj|!Ny;g|9& z$j{>~QJC{kPA)K|rUoWw(iajGB@Ms3oHADIapON--tgdm9AIx7IA+{ks>^NL61mOV z&}v}jbhCT>$y-GX6{t_!_14-70BN86mSbk20&7Ek#D`%qm%As~4gZBoU+6tgpSHHH zRBX=mI{-t`14^emvN$fEweLTH$uf(WD~6=4cy`ibnk2G{)6t-DSqv!CH|qQ`y@51V zeKW-VY&3IeVNfftq_^?yaKh5>rLfO0opE~F#J(cZclwxIn#A3{TjK`)C-M|_SE$_S z4)-&Sdj5NlUy>PDX;rJV@qDB$InAsc{0=)_S4_;ntr4rtBlG3B4x*5J?OwN}XzG0O z(PGje;aKU=BujLD*#(~wX53%myHle_`J|Q~4@_~5c1`Urf>%F>RZ0>j_mIO6-M&B6PyP8U?St8VU5;U*_oPH$W2yl0$49 zHdtDDfai!lft_cTM9pKG*oyP&(hoOU_AEfcw!mY>2qfO(Cw19s=W5b}6X;G^25Gru zh>_`DOfG&~91{6f+PHoAc=GOQw`$Ap903>IjNiY1?>bolk1#&J`vr*c`3@fskBIL= z#!iD>wNqAGxG-A?z03Y=*#+^~%jj98@TiDL@1#o(9v~Nn^m|r~|8{>>vNn<@BEwjz z?XNn|?>N%t{Q5mtxzQnx;n((rSJ$>R8Z1$g0VerIS6S?s)Prl`!d1U|=$jJ?7z^ZHUR zq0><+nI_TyO&KyHpD?N9Od?Nx=g((ZADx7(Z7y3I?+D5TXLRAR*)%F40nfMEqWh6j z8R{&R7=Y#jH9gWy#8100LWI1W4YN{TA za>;^V4^))-Vh$JFV%0>4rP;|zAW>Rvw;(+Y4=>Ti#P1pf?hUh0y6>0b)YNjN#=?pt zKed{90!==GjigJW|6svj7n!E+$M0J{kqrwf-;^z9GV&CF`b}4MPSP{N);rA6jsSwC zNF>n7(TkAuQCXLNxISx0yLX&2&6{?LW(o?;t2fPJwd>&~7_zWRf#MYE^*eJ3J*H4U zR2U99K%s&9{d-i0{`P%1LgRJ(S^~YG>ji}2MK**iiT7O2GBl~M!!#ZP6j8Z*2Nsdht zstJeas9Y59cjD!JcDMO=1BMFB2{L-f@uM6dEMqKwyU)rok@%LRaLD9-j@wqZa2+kGh8o2#}{-xbNG6w4@rR;r3VIS#;j37upLIu)1FYHbQ6zh6Gx4Em%eVl+5do~ig zU>?4WZ9>uV>ZdQ-1(ljniu=*PSHa4_?LVizFZ$1$fw!uWg2)& zT^!jCAIn^V34R3BEVijsKM6y6d#_j=uBa+>xZc9AnHPJ1-0``Jdd47(6i#ZWELBS< zq~(o~^+Zop_7v59Y0m$Z#js!0wKs9po~6MYLv*;5QO<$(RiVjlPEqyou^=-AbD^oG zVIn)HY%RsWGXkH-gsibWx~kC|v4YVRWf5tyCkE~50RhZQr-R4jzUt`)2Y!F=W-3pD z0p$YJX;SpPaL@O=g(h&t<6+mpl4p4&3ASA;UEIUw^~b}k;76<&_(*Dkgy=oO!^M0- z=%tCC_I*xr#nb>>9Um_0=SNyRARA>e5sF?FprJ7+1eg{U;1nT3Oco-cvN1vsg|cSi z0L7B9G8MUaFJ-GizoM#66!|wv_ht_W&mj_sz+3>Jf`Jjj`?a4+X#=C9S+#m3}|?Mqs-3 zI@uSILq`J~SrPVpx#3M?8woMIvah(fdX?+VsP?)~6OaXHyduw~1^&>RhTyZhogdkC zj@aeiFX?7>vA`09+{v5DnN@gw9-E4DUJ zSg5hFxTB=1+&NNuK^%8Nx2z@#V&lwzV)@r}p@zKV3@Y^lUGS;-JH>bnREHcjD9Ja@>*Xmll( zxzK7@0FNV5HZC5SaTC`LHhr_9pC{Sv=klgBqSEYc*|MA2njYll-{`uozH5&)dgCdo zp3X=3;fJu3SBE8pd*6cj4ZfD=zPnj0pL*QN>wzG||X%~>!3SJ!3$@{mG!2PJvA9yc-Nb$hMx)6vS# zB#I;lDl-fKsIY#7jGv~!ObHT;M~zyyP1=zVn?-TdWm@uz3PJIsa5vkUbG0mdQ>uBG zTKV`6s?2dzectVR`TQj(_M*4rc& zvwHT#=pZ=W?4}W?@QhOCN%-fq0HmNnK->=-`y(GH=aXtgmK_~^f9WRoAqAN!eX32S zE=v|K`K+tWpsSG(kuzcVlR?HCTjmd)UnuI)(MbJ`F{_Iw zz7Uj`&H)g*9cbn2*134hr)$o2#Bu0JU1i48(C|1Gc{&mkQu%gp>r1I+C# zeQfnOhj~wqq~KkpbOk}d*)QdPd{0R0R;FA8B&b(v+P~f|Waq>^Z3s`Mp|$AdR8+hh zq~%6;(40np_1PS5KPv=ZMhDH+oPR_$F=%g zzJuSEwl+?w*+$kOAHLjOL418B5`ETj!gXZ#2RZq0CS7QLMxnFE{+Pf(hqfAp*ac}T zHLC|xJ=zRAhIJj9&bd-=Z)a%vFWvv{`*XFW5nxp<(j~vX`=^C1|25xfQ)sfoiTSKfLm$Ok9truz z+?It8^Xij(wD8)MqQ-&l8G})3UFW^O`53RM1a<}OOjo`=;UXQ|dB?>1(@T}78`oel z@vn3eO$}4DAJ@H{@-^G&e}SfFTX^dnWY^z@iZTWow+}I|t2&$WMH?$P+`;?{y5g6A zcFIMziIN%&{vX!fJF2N~Ya0!q(yJgKP30#DND&021r!9O2!iwyI#NRo%>)z>l%~?V zNbew>0HJq?NH3v7=*18MB*2a5yytuGAKxEm+;PVUW9;lC?6u}zbIoTyvuswUlr$&X zo`kiY^~2vUxb>6;3eLXi0)%lu*I5!kY;8Km_9bNNz4^>Lx@@6>Q>6a)0=S1uh{%34 z+&7t-SK<2uIlQTqs0GK`{Q){zRh@L|@1@D@3aSAruW#Yzcq9E>&HX=pD=N#(Ufvd7 zSJ;F|Q0!SeIkRx=Na1gm3-$1*TJmSVZn@BDsUro{P+vN3H|U?X+Tjv*F6{~n&ZcAwp4J)kZSNZH zsui@c`SRAD;9RNG^vRT=oIfOn2Jq8{&L#3SdT@k~o9yE?*VIk8Y=9nHWk$Z6`Fh_q zwkknQ((L7e`lI*l8`0*7ffkMGYlyLNp&s9{o6;9HO(#_wz zDJ&>33KIAKX6Uig7{lqVoplBQ~F$jylDyH2!?$O)X z==nuc7p?KDn8)>H{tR8v*|u^vX4Uc8;=qgpF$MIeu{9lF4zernv+c`Qy{Cc{AgaJ5 z*Zb|Mh6_@NNJuTdW5b;EVr0?Zl`46lsHCj8&HB%WcJzk%>>p4pub%Mb^O_Vh)9^pX z8S>hs?LR#$AKz)W*-KsP=Fkqy`(oC+F89ubgqSz;Fz_4cN)>Kwe$Ss}Cg|@Q&+wwFPc>uLf=Q3Ou53g{)YTILX6aB^UvI;hIllU$ zV8T$IFY5=xPsk)DW|NCkbvWigwIGIU{`#K7s3>#ea>hc+3uQ z40=jW7ME_>{7OZS-G2j(3n7H<{A_*_{57tLu%Jf`h(}Tpc(c^jmy9gPmQPAbJ>C6lJe_o=6{9;d|J&_T-PT}i9wrb&){!x%AdnW72vfb z!Kc;LU-vB?F4h&Wf|!GIYFCxb?SJvxU;ja|zPs?LzcHf8%G%qN9vcJ1pB}|or*5RC z&~k~NhV!V#{d3lDa9Dx(9WWDwd;R&-WS;9m<~Z1uEmeR!pF zy3EM?(b(7o27H~6kuIqKT~1Ld(N@?HOc?O`W_VgbPHoX@RP*+wZC3@zp2O|M3&j^r zAjhDtqZrkX+Fn%4zN)6Z3{=(kw%vW_u~fuTx8}g$_5RK}0&$U;m31Hylv!Jg zO`7?bCWQ*hYL`d(FTU%=pSyiq8dJEP*>bFyb0-i&vrz<(8$5Mn&eM9r7W4?jDi6v$rNQ{C?TyJ`Ds$oYU3znyjO8u9-0dfiVM zCWx8ZjZe${n%?@=l3T_OoJlfa&2ZbhkK3^gvj)o6S3$Gm|>z~m0tmN&M2cLC+?zG7qxB}&mQI;su&)A#JJDJl)X)KP|J_c(S>s=`} zW)U3w8!Sh3`nG$Y(*!p64ANKYpMF2M?NaZ;P^M4EspIwih7IMlr-Dx(zqdr&Wbv}? z<0C~pXOsIJaet;&9aR-7*nQ}^#3Y;o;$Hh{@lxpe-{Ucvpl6WdA=^+#q4hVY5=e;g z{C~alnQ3s0#G>$A(l3Lyw@O&>Pd$fjMG~F)5&HYLB3bbA#|4i*;{Se^X;MW^4b^&4 zKQUls_=2L>quvem+o^W?EISGdzJLxkZi}7xKhU;WKGpN(Ay%0`{i38e>VJ72T`C_k zYG3k`r_03WdMW8GE-v#D1K~z93w=4nvw#U|&qn!s_kP$9r_yx3X#rLgF5L3GZPO6@ zKjWj|!~Eyy-^2JjYRmuq_`g2Mw$JlH)gsFo4ieFz)Ew*Een zhqGr6*Ol?5?goCzX7=g9z`c^yPychFHv`EArxmGgpnA@$Kir?NByCpBt}>T-TPHs9 z8bne3l~ys&zUe*d{dT%0>Db$U&oEpox*hRiW4Wkwp&&o7ETkOO5avMn@BgU#vb|#a z-v3PKzm@^T|LvRqD+Aq>*Oshz5;6vpDjvH`1Mu0pn4SJOe}C1x2!BxRW>=TNw9Wg< zt8g+D_s)llVB(Ef1~NV1>@|l&P?nExN1p+{PpGOo)?&9O?2M%dz6);kUYxxcDLCqt zfbbP{C=pzbh*2flt$X_SK_22ahJ_5vRaKpwri#t0?W2cErbIyfUGAW7kILGth6g36 zXq0eQ?%xM!o<7G)5vT2`{hB4tU!-COQ^AU8FmDg^z(Ynvdtc2r`wKnGar~Y8Ix;le zX%k!Tc5yLSb(0vqKgAhU#?MuM81T3@okQsu4iKO;P%SHhC@ zkl{4KBuqJs=4tl{c+spSlNs;`0}WC<{Y~% z<#Y_V+jX`A#i4w!E__5Rn!RN&hLq_gBa6)8$2=E}P8}w&&6Ss~DW6ei1OjQyO~yrC zbz&>5Vf)CaD2n#|85g=lyC*UHp-*-pQ%2h>-5OMlgiHKR{8?P}j>AKFCy`5#)|-s{ zeyHkkP^HOwi~AWOCS&R5taJp$Nb*OR(Zxr(s({_u>UX18;@jI;BGu*gHc9pNdyc{d ztFDI7x3!!2Gl}quE!ls1UU(UYDTCz>^4}@iG&b7|d>i6t)!Mr)?1X`xNCYqMW>crG zb)D1vRcWi+`)QgRjG{4TnBB&rF$cGW20B3gaIz0!DvsBJ6Vr|6c%1sDI%uT4D1?t^ z6320M-Xuj^^Ti8-Ys%9LzW`67)5r~DLTO79a&(^(ukzZ=n*m|Ml5xC!SJ8QH#S6$f-jX;<6GWQPEB|Jo~sr!lr#) zs6P8Ctr+LxY0n4k7&Gx;-NheM@l&9K86`@9G%Q^LyH|l^&)KfwIns6M-*6+Go|sFnQwb08ZDh0x7ae5ukDGmV4xFdTCEe!xF2HNKWuUA8 z!XK`ig;-is+S@X@84DMY4pT^r0s1pwh**zl9}=; zW=G6#^H-uQpdrv6Jxh#jq8^7d1+*jz@P352Uoh0p?GYQnodz{V?H#zPOGs)4{LEgS zZYL|5L?Qeb0Dhn3&JRb_B)wJb%o3X2KAettj66=Uv-XrX@%dsa!??AjoARYWVd{Alt@j&z}{v zS5m4Ry*StWyOyIGJLR;apo?b5-f{_PPfZ5%`sa(;A##&8fTjHut`cc3L_GN@ZQPC8 z=2}Qlne;7e?9pk=e)!OE!{Yq$z~@zefLBjGHbb&HC@O#G0G+Vou>Pu1JXhj2+uUImr}=%jHh?n?V4OFFnKbk3kaM8QeT0ir}i zif~`hvs2c~ljZ)AzrQV}&Iwb=NAV_Q)cNae9}W8YX=Q|Rhp72P%3hAlrBzH@Vxx|w zcnxFRyj^kmw0NY2#B}llv}k-2t&rTc{5a=Hu7*PG?I}vwONGj9Oasosjq7zv06f_g zz9B3-XK&jQb(PuV*teR)bRNyQ*qRvb-;qJMC5kYE!AZoYnytvvr>cM)ZUn^WO1nd)?@3P zBVw~SdU`I5y8r^)w$#02?^8zNj!!e}E&q9fx5O1)alOi5@wGn5y^kPM8i}c2I2Cbn z***dV%^Z^ME_fCF%E>SlKaajyb+vFjRuQxtSb^|CP0s{A`|=}Dj=vtVXVNVrzkWA# zyxd%f9~Z-);o~xv?+v|rE~Gf!;Ii#?PR$|3CFFkfLv25Ey2iV{5w$s`c=7h=M|SN< znQ;sDHsA_v=o}+~%7@jQgv2he5!$fhwBerSSH)3eBS5da75ce_E@(sGt|=#BQX zX)oYu0g7BsZcLaW{{RSE!_IUV*Pe31+ap5zMC=}-EfA`QQ{I5gr_n=g+&T5?k%U-# zAUm&{CJ7)Ik=&%9HFj@QGHs~yuU$N+9RvXA0d0V_jLVsE|H&Uk(4BS^Z<@iblrQJi zq?$7;lBZOz-VtkwfLXuMdOj zJ!^m9f~L!x;E~0ko*z8iW=&|tA`1MoFj33B*t8f40q{f9=C@{%)r>?d}KQ}?&?tD!1$k`BGXn?fWxP4yT@lD@2 zQsyK==4YK3&ovSc7T^_{r69hQ#TMViANL2%FT4F#Kh~;-oZjA-PN}|9g04sSd~$G*}@i2%wPQxs%~1SxEA4bK6J_- zAC4n16Z{r!#h;(5w@s+Loqsa*%!Ti!7?orv;H}gBm~>1~-0bs)pwXKj^}k7hf5ka}C>bGvAKB5nMDWW_R*aMwugE zrzJhzjJtSh>JdOw63RU7S?wk03J$q zCM&bTWCB{vf1ZU)_yUU4{FX%a=8sL^AZEC1ysjQz_1M)h01ty)GQ}Vz5`Z<=?Eu%z zv}yFEl2o6{#BbJ~qFbEe|%DllJ`C#2XfS8zMRN z0G--ttw?7Xm$zQnSUD=tA7K8neCcx%ToH?nHY;uUy{4zX8%E8k4%V4Xpxz$zA+4?+ z^Xz$368??8PafR98@uW^_(nWN(*@HtEq=hACH?@0{U{@GBIT0!OT5b2Gtwx!eqnW3 z3UFNv0EjXnu@qVoDyjB>rGD ztU_XY#o_V&@q?2$zptc*Qvw<+-c@%=z2useD(qUUQ~T>=Eng08>HVy(_B}vJZ_ZeO zT+il>6MOrIO@rtIAV!drVPa92{j02%KWgV6eQG{>u?IcnV5~~6J+20qWS>IbPtY6T z#?o_toe4CWcn+-M&bTAX$?p+d0Zt21|22unU$J=cGz8Y3- ze$-GT5j?4)KPFa=(_CPxA0Whi@$gnIi&S7v?xA&#mi17FmU4TFuqxnc?d?z-#OT*;sY0v1W;3(Zg;Xy&=I+|;j~2d| z$;EQY20JDzfxMX+Y3EI!%(}J|P0pJWg?8cXIs78GQTGlum%Wb_tlsJ%k2a{7n)tQjw5id19!1(_ z;0^dFA9pJMdDG<4P{wXrcad>ET!s%25fNjp>FY@a+NiET|5;cV9sA3RCr+-DN~WZT(Mv;HTB3N(D~y0{?GxbC)a7LiOYcfuyV75xdCBf5KmO-x!C z>)m7%GaFtfIh~tn{9ewbcfRobPeuJYHa%+6sm&c;NKIY7*vAMPKFTM}EMm->hW7O} zf@#WqFAkAY|FM%$fX)MEo(hvQ%j$fh^b1~PZN2lDjWQ-4^SDeNuj&g8#;=nA?0P?8 z0@|i3XwqsR$fIgQk&FwU-al2!toDSRynXr)t#e~#v{z?uJB`VLu@yW9E~hQ+657&X zg_8pG960FDJ#8{0@KuxN>dO+01YsH2vYmeEH#(wW0O(9G2h;9c4%8t&EEW*ks1RKQ z))CP0F?EuN`G#Ms8>)#L`1^8R3_Y9&ub5va@$1ZsGJWTbed~_9M|R#M+W=c;q6ZKr zl*2_?-0lfx>q%UyY)iA>09?BDjIem!avfP(HrZ}Y5t7d6Uk9{#@BG%PR^`zbJhoG( zB~PmIAWX{~>|I{$W+N4Wp8QRAz4oK@bQ!>-+O31j`U7ML`+FCxovvNlilVyW+5Q zjD1wW60=kpBO5%+)pG^MxmVcSPPL*1zApR5kp|O31rA?pTLgY>+6lnsuH7K*c_bhp z>oqmyXv{Z;ih^=X@-+07kg$DR_VPSnJ|yg2@Msw|NDj+w#`5knj%m27^}!J#0FAM<7AIJ? zZhJVznGPgL0tmwY>6VWuejnyf1pA@a*4klY(W))WW3jzaX-g&%UVmy*cq%Nm$~S?4 zO}#{DtLx6^`y8yX<_Sbzr868JWh4=#VyXZb-^A~$$$%fSVWyeLR3;oz&NG2FsU*Rd zDUFyb>A<5lNMr)-Y;217F-R|{#uM<%`#jaR3|z*Rd|by;gY(_QT>gHKBS&Z_sMi@+ z`#Dtg@f}68e-x04kN>TaB;sJva`H=gSpF8uf%~1?yMb9P=K`4P8jBMYb&FROTZ$BO zv$z(#280I9)mU%OzlDyIWz72ia@Zh7EhlE_GVG_D{DTt?<8M{V2~y!oUbR zCUKSp07f5aeU?a|ysK{aOXDM`)qDLb#yh_CCAxgLb}`}vuUVbFs_HV^qm(xV;tk>1(xLpqjil!bIXMSS9lr&uBH?*N(I&Ew?!lo&Qtl5M`d8`3)jORFL_n%z_ZQnJyg}vpNhvG`*#)A<$c2q(vZA0 ziRFxI+{R?p!-h9k^(?qgDtCM#o=7x}FUo?R1Ixx`nfR0qAcPQ5;CGC$zlid{s0@163KcKtg)!RzBC zdOH?7-<_r1y2t;ZdDjmR;4FotyK!G{2SB}R-vY$e*gZWxT_J(h`A3q@lckO;fBusT zK!IGXH7d>2xN+88I~Hmg_WdT}ayH+`Ic7CT@gi_e%Jp3iAPeP>sTpRhGusUl+MI^gl z{L-;xz4j?p()mrvE$H5?Pspg>&cWPYx^}WyzuKauiDh5C<-4u`ksakpKo}>|4m*9< zN_{L7cS+vsMLb~qBfEl2{^)50RXuCJcQ832|4T)n!;0eB%pB>!Ho2pjX756f7g?6L zt2bJ8sEkKX8!fpz!)ya3f@B(;kif%1B8#b%H@DZD@tQe{V|Eern zzS!t(Uragv=;-CDDx@tSm53;wUXSyCx8y6XZP)vMnCE=~rLTkQG&wqg37Jz2N`hm_ zE0rJpC@SlTH}MNYFoee=)=b2(WWq`EU;gE7gC!{_B(f zA_o8ctAB@k{vQALC;vZO@qY*WCGd9GA!vOsCSAUIW?+u%P2a(si&RXtuDWre9or=O zK&`LzTH#Fl^wSu!ka;p4z)IeIrMDg$6nVdElmz@7-=F)6b)k>q9ie+KW-{*GS)rE) zci7OAk0;8CeC4`ZBOGY-;qt}i>j-}v#xgYK7lw!OiHPF^xT8SDPu(&k_sJ*~kRmD5 z+udNn<@E#OMs?xQbzK-6LH^$8R1&(ic7B))V89^|H)u}RwrEa0lg`s7d4InNV{=2u zX%7PLcDXgB@EkGlfXyK9A>y?hxY|hAnvV@U_2#<-2SZ9!p(LXPEPVAzILacR{)0sD zdVmpMobwweXw56t+@nADIxgnrB^=1Lat%aSZ^vNdk;Y~5x&}qI#ypIOk4EdEKj|jl zBRRhae&+e=rZif~Y-Jyu)thMzzXRo_UPx|Or8;-0o=^^{H-}fAh`R@IgN3|Y5Dp$ds-9uf=yY;N?X~M)0%DWu} zY>EeKVO*~n4o)tb;^@zyt*5jv9#y9d+ouiR01(lJc*@WPcC#Su5JLw#OyA&!qui>D z{%C&0-z$FU`SmkJsg{97kuGH{mL%AU&7kO?Pw3RR)dAH*F3PNnZuWsQgwtkk17xeYO;Q%kb6DH_tXaVTr9Z^Y#1m zgBNETt0qG5`i0paFa~xcb7q#s$fE;8v(W`ozf>W0o?5gOZlR>yT)TUSrOU?jL|#B1 zf^@je9BtAVq4&-B$C^8Io+9K+eVEb)Rjfl2yv{{CAanimi1bshu*$8XT<6e{!v#9? z&4<~#CCpUUubb%TSo%&64l}j-cAHit%HeX;?yLJ|5Bl6s8q^m|NI>UODXMf$LdaQI zb|Pa=KMkoevoYS&pW6?SSj@@2pJ}o^tGg)7d{+7G|A^&I$DH`T#BvMJwHK472pJ35 z)%L|d?AwReELHjVi)xeaHu}^fx#_Fcw68xA6Xn&cH2ab3tW}#UNBO$A-wm3(z zU0`U>en{@ZtKijwY#5N73NI>C`N7U7Lh6WV>vNP@z z8yqxw6cQf;x(BBx8D{#xg>Z*!cTGcY10jngtK_8JF`xz%`6CGEcVkBjDk+`8Zlpn# zU{J!+30%vB5MadlSmW}%2<`RwaUM9mZlGMzE-+zsji1z43r`a#oww>L(wZ* zR*zXcy2)URyDLu|>U$LH&S|$iC39lC=v&WkCoe5u>;V~NHLgwVkksVfJa%@r}CXFG2Jv3eQcF z$v2Mm|L_)f@t}sc85pa^yP3)%ys&?B`E2vjU+&;?;tJeIsUWfp*6gmaKy>b93lkD^ z>+5RTa8pNtLrWD)=kn{~r+HQQr)5axu$orH)N;0uVk}E^lBR*1J!Z8jN2zNL%772c z_^I~KyBDNQ``4hNGkI9CENbLZfwjrq#pN3S^1Povs!qHg3f2x-*G=!%5nG;Wg{q3g zQ3q|_$wRTu29zN61UjKnr=4FY53anW6(hFvFXSGxP1MVrc%5-%dty0XER~Hz6jg|G ziElqxpnN}PpaSJyF3ZCDQ<=68Ai;2LR1N8Z?mH;=k?W_qli3^9Ky9Pxi#B42U=}Lv zi)WgKuJr8dy)UchhQRAH_gcxC&3#E-$^oqV+E8xMMS8{{RKhg$E>gMhMeo6$3C7DF zQv!???FA1@&y+S7o8)2wb1gD?izb|^XI$#%w660dnJD~26t0Rq51j-&3;b9e+auq5 z^mK65u=vf0DRq||MDBVpg(R>4Nb<01Su@Js*ODJzg=Zi>=0>qsJ7nh+H5o=zjw0s8 z!z^QX{_!E`64#_095j1m=nog2JqoPDf0MoZR?cdVyKA-!j3EA$22Q{1io2@=7tL4| zm8aS^JMMp4G=yVpl)a3gh%TKz3NHo5GRGk9DDv!=M7Sfwyo7c=o`?-Fx1Xn&=iD=P ztZ#I^3e)z@>TIRWL(#t?N-Lo_#0~e{_E5ta7qWednz=*CiNHN{3KF()=RzpKwL3cZ z$G%UZj~0(2Yw%1HXv&YM0v0gt`jg8qGf)JV;_3QnXa9cEye}m_Q@d(a^nnL~q@14Z z%PP(lHz)(6%yIKrhN;;8)8j9c?0?;@8foFF{c zW-h1(mkKT&4$1rXmECt+#d>*|;h;ZI)AnBd%~RKRpH>SJ1VTVJ8Sm;Zy>~Xb`yxyR z#-Ni$UJ0b1M7N*I8)d24Lb=f-a`NL874cYqB^|p=u_B~$=XW6en>SGI!xA7piv#Yt z3@q#eF?HsfiGr0;R=Coa9n9Z!3oGJW9K%q5H5J1GQ8s|`7*4a)n0%Pq#y>4a>Ny%t z`)mk5;mmZP{Nf#EPKiJ(Un{TFASn#dB~^OS7(jSxY?Y+MU@(1uxurvd);5ps2(oBM zdr4GT`@{#Oz4YU@;xe3&tz8&{;dD1Ka9W&;3Ky%#Gx^JzSxKiGOk)@4KGopc&8%46 z!_4ceuYGMSgY!J@72S(ddJ4&!bqNjD)7>am51!X63)xcN@h{rT^m?gnJ%v>6hrK|q_!P>sT9PJeg1oTK<@ODuEfKOtEk)ouiR*=_xGF5iVbm$#NU{bcqkB1%_%@8kr4*)$7yHZS+INW8wgUnEbR$D|hNqzL{CiV-~HXOmjU6HQ!1rIaGx*i`$Ct)U>th zeyFmJsGQ>>R!2l1ga}qLg3Uv*{?9;J z)5Jv%zk%pZu`g*O}7_XML!84}bf0rJR{;ajyP z_mhP7zp_9&A5gHjc^Hw<<@c7@~ zx#FW#HU*%!?9HG$MOc#v_t!m#662BwW%XR zEqLrH9W57~6S^omr&)iOe!8JjhDz>9WWjYb`CB^^J((9+|H3XvmiqQyB~Uw|7)fT7 z#c#HQ>63Huu05JN7|7T@mdz^&0k7R#6F-gk2zh?>z=h+}G zJ2UZ9p}zgNKMC<2ydj_fILL>nrFVyy=$}Hl8_ld7*vWg1qi^WfI_cYlnWs%Sp=H$H zM@KNoREI57!R^o(v06M#?(dHGm7Ne411h?=l_{6_SuyH~- z9K3INCq4#cZ7C{$ZPGu17!)TrPGs3YgcPAQt)23Y@J`-XM} z2VxhiQP@u(5*+vAigMn+eqY@rrpr=f_V0zvP>S>dkE1C=XkerXjX?Ulp?zzmQd9s= z2wYM$cGji67UmIT8J4 zkYSXdUY1%YN;*wzVjoV=TWv{>A17!p&v&UZsj}0_+4<^3rm$l9IUUFW3$HNL1JLKw zJ#(7P>36$ENP?f;)hpV^7HB4XC))nMg5WMi>QPFZD(hwyG^o>au-FMbaG%~YYkfWP z(=)+@FG>G14U8OIZTgTdEt>Sf8^K2PEwiUlZwgW2eL?y&Q}T^!@U9z_<6KpHLoVsn1;5nFNfS%OUD(M^t zC8IwIO?qLdNu3v!6I<8dR*HP0JQN)I8p^GBfVT6L5=7v}OODmXNhL3OWPL2JdV%RhyL3Z6EsU z`ZXX~B@`p0KV8t6KL;!EB~{VzcI^`d_PyrN!mu)0F!esC5`~W1k)_V|cb#MP*BTVr z&xM1B_7YwQ)Xs5*GK7Z&I1Aj@nGP66B{bYBh;Rqxd}@o3Po9~JPd#7)eChwh7nS)V z`C*;2%=OSO6sjVE!L@(r43;;{@&E=4*|xR-%XKa(xt1^f{B`*^_FycfNV0^VKzjSb z#WAdZ$Gb4|ZNnQuueE=E{R$yYs*EnP-YY$06`;gt$k0d=q3H=f2p_*ro<~Pc91fk@ za+Gp}X1};#t={>$^g);}lv|pe;1oUUR;^Feh`A%Ymkl*dC&dtIN>x>slMbv;dpvMobUkUl3`h)w743>&sv+okC##qmHtUYmd;_TPIoS9~k2a|r4(Uwb4 zfBAmaZ^63OX&J>NziEf zT(B%BBph`88FUKG=;IyQ7ZF7aWDD|&-@`z=^}EmI^#jAquZSX^hTc&jTD@{`sX@^Y zvy0S%t+<>s_KhW!n+8{6(12Vg13ZYo6St^GE)vHGu$=h%ee`8@_dwlHA_qBN?JE5@ z6!~i(m$&*OOX0ul4#Q{n;|?qFKR6ZNY|&h{?ZFyvtv%EbNJtv2N-TaD5y9)dsi%aKNi>zxAUCE+B-Zq*Zze zIQ@3$S1_%nrAu?a``-Mg3pqvTPS;Hdi#>zbySJfcyz~;8tnh>FQ_1rtr|)#(<|0WN z{#mh*s|BW}c-=G>Km-#tPd19x>fczO-YAQF2lFQSTs|0r_-1=Jrc+v}5d!ZJd6Cpk z#Q53(0Q2a1;RE%T-ah7g=VeSQiO7p>3|X1IB5ZLz8vJl1@VwKIiG=`CQ$*ECT&R|_ z|8SCax*Dn`W2$j?S{(#E$b4DFC7+xU=P>5$w-J_@1aZ+uB)W!@Hz5e>=m5cqteuV0 zzMZPWyjv4P>}iu%xUM`odz~Hbw=zrJPhGLhog?7IYEt=tUaSBs; zrO;k+%7@7hEDaqZWotsaRLS>q>+yF`vrXI;f6;z~(5`w7opgL^vWh}C))hysC>>cY z2c64&x$Ug>YP~7_eIt3D>rhPKaz=)h99Ig@oM%aZWmbmauQMdWil9$^okkNfZ+H4JyJDLXL&jd48D^`eoSb(ul;ITrx3nf_p z7n^%TZw%y?MKlJ5))1Z@%nKqsDMg8T5=6F`y4Bvm-L#%yed%=aH}8pxC6m;=zI`2_ zS3gJ{3IFl~Ten?*pc6?#Mc>u^;8oFF-ETdrRD9a@vSkco;cbA@NZ-1p)O!LjMbn;K zYcElBBE^T*BqGVO zNpz~y?tiexy?nddn)@$uhjJI_SAz#!a{*8hd$_~Y59$c?V69ZdQb+}a2a#$hZz$$e z+jA!8dE;^F+vm$15`c}BFxO8e<~dr6|6su!1tUg33321R>{o|Ml%L$>?ihO!R{O_p zA#hveybfNj_ZLCmwgZ7|`-Y53I}bEBCh%~E=?3OFA>=-IJ%TpVMxMS8oTso} z=%?PgW-R*P#;J}-dj)cJqOxg@%P?hQM$Df`*aeEz0qIXISX;W7KYJ1Ea5E)9FINS% zy9;W?nnG=JXLjsi^7I~#&lU8jOrYEqu74@I#MYHAgQCA~f2?5&+~UvCT36mqo*&cZ z+2@;()&$9wJIiKwJN5&ibH5K*em#7uQaZBlTps6AepmsdkFUn(WrjCQUvxm1D(vP- zp&7H~S$dluR!{+LJ&&b-w83yZ*@ktm0`sdWio5zrIg!RrwH9`F#sU)HAe1(@|3oZiarFE7|7+DpH}i2y+s zeQzN8&gFIe;lCXsP9@)#6r;%7A&Pf;dBoxL9eco21&wQKr_ed*y{$N<{yiYL@6miq zT2h)9??szoUxtMprKc z9KcHq8r7c)r?9$VOk8s>Vxz0kqKiKFk*W`pd<9m)z@~u5LVjW9HTV~ -WIb!7o+ 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 Date: Tue, 9 Oct 2018 18:09:36 -0600 Subject: [PATCH 21/29] Update readme --- examples/inbound-parse-docker/README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md index 7adeb2351..1bb5a921d 100644 --- a/examples/inbound-parse-docker/README.md +++ b/examples/inbound-parse-docker/README.md @@ -1,23 +1,32 @@ # Introduction -This is an example project for using a Docker container as a webhook receiver. +This is an example project for using a Docker container as a [SendGrid](https://sendgrid.com) [inbound parse email](https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/) webhooks. # Prerequisites * [Docker CE 18](https://www.docker.com/get-started) +* (Optional for development) A local Kubernetes installation * SendGrid Account with inbound parse enabled to send form data (default) -* Ngrok or Internet accessible URL + * [Setup Inbound Parse Webhook](https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/) +* [Ngrok](https://ngrok.com/) or Internet accessible URL -# Usage +# Usage Locally (aka for Development) * `cd ` * Build the container: `docker-compose build` * Run the container: `docker-compose up` -* Build & Run the container: `docker-compose build && docker-compose up` +* (Optional to save your self both commands above) Build & Run the container: `docker-compose build && docker-compose up` +* Run ngrok: `ngrok http 3000` +* Create an entry in the [Settings > Inbound Parse](https://app.sendgrid.com/settings/parse) with the ngrok URL. Use the `https` ngrok entry. +* Send an email to your inbound parse email address. + +NOTE: ngrok has a "replay" feature so you don't have to keep sending emails to yourself. You can access that when ngrok is running at [http://127.0.0.1:4040/inspect/http](http://127.0.0.1:4040/inspect/http) + +# Modifying the application At the moment, the `app.js` only prints data to the console. You can extend this project by adding more business logic to the `/parse_webhook` route. -# A note on processing events +## A note on processing events This project uses the [express-formidable](https://github.com/utatti/express-formidable) middleware to process the form data sent by SendGrid's Inbound Parse Webhook. @@ -35,7 +44,7 @@ app.use(formidable({ Additionally, the [docker-compose](docker-compose.yml) has a volume mapping commented out if you'd prefer to store them in persistent storage outside of the container. -# Deploy to Kubernetes +# Deployment to Kubernetes * In order for Kubernetes to use the container described in this project, the container must be built and stored in a container registry. You can choose to use a private registry in your cloud provider or a public registry (e.g., Docker Hub). You can also run a development environment of Kubernetes via [Docker for Mac or Windows](https://www.docker.com/get-started) * In this project, the [Kubernetes (k8s) manifest](k8s/inbound-parse.yml) uses the `imagePullPolicy: IfNotPresent` to pull from a local registry on the dev machine running Kubernetes as part of Docker. If you were deploying to Google Cloud, for example, you should disable that option. From 08e5b864d8fd80a4fb337134f76271e066ca030a Mon Sep 17 00:00:00 2001 From: Elmer Thomas Date: Tue, 9 Oct 2018 19:51:31 -0700 Subject: [PATCH 22/29] Just a few more installation steps --- examples/inbound-parse-docker/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/inbound-parse-docker/README.md b/examples/inbound-parse-docker/README.md index 1bb5a921d..218951b03 100644 --- a/examples/inbound-parse-docker/README.md +++ b/examples/inbound-parse-docker/README.md @@ -12,7 +12,11 @@ This is an example project for using a Docker container as a [SendGrid](https:// # Usage Locally (aka for Development) +* `mkdir ` * `cd ` +* `git clone https://github.com/sendgrid/sendgrid-nodejs.git` +* `cp -R sendgrid-nodejs/examples/inbound-parse-docker/* .` +* `rm -rf sendgrid-nodejs/` * Build the container: `docker-compose build` * Run the container: `docker-compose up` * (Optional to save your self both commands above) Build & Run the container: `docker-compose build && docker-compose up` From b7ffffff036ae2df3efaa1ce2a3b844dbb2f5a15 Mon Sep 17 00:00:00 2001 From: Arshad Kazmi Date: Wed, 10 Oct 2018 20:29:51 +0530 Subject: [PATCH 23/29] Changed links to relative links, removed 'md' from filename with relative links --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9326f6758..a7811fedc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ npm install First, get your free SendGrid account [here](https://sendgrid.com/free?source=sendgrid-nodejs). -You will need to setup the following environment to use the SendGrid examples in the [README.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/README.md), [USAGE.md](https://github.com/sendgrid/sendgrid-nodejs/blob/master/USAGE.md) and [USE_CASES](https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/README.md) files: +You will need to setup the following environment to use the SendGrid examples in the [README](README.md), [USAGE](USAGE.md) and [USE_CASES](use-cases/README.md) files: ```bash echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env From 18b9cf39f054fb22c0c8decbe3c76b9f2795adbb Mon Sep 17 00:00:00 2001 From: Daksh Date: Thu, 11 Oct 2018 00:28:23 +0530 Subject: [PATCH 24/29] fix typo --- TROUBLESHOOTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index effbf904e..b7b951f0a 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -86,7 +86,7 @@ becomes In the first case SENDGRID_API_KEY is in reference to the name of the environment variable, while the second case references the actual SendGrid API Key. -If you're using Kubernetes Secrets and passing the API Keys to the Environment using it, You may find that there is a `\n` charachter in the environment variable. You can use the trim function to remove it like this: +If you're using Kubernetes Secrets and passing the API Keys to the Environment using it, You may find that there is a `\n` character in the environment variable. You can use the trim function to remove it like this: ``` process.env.SENDGRID_API_KEY.trim(); From c01735ba34e92a56ad33e069db4bfc71f92a35d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Antonio=20Chio?= Date: Sat, 13 Oct 2018 01:21:02 -0400 Subject: [PATCH 25/29] Update OSI code of conduct link --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b2439f6ad..98ce2e089 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -37,5 +37,5 @@ Finally, just a reminder, changes to the SendGrid repositories will only be acce SendGrid thanks the following, on which it draws for content and inspiration: * [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/) -* [Open Source Initiative General Code of Conduct](https://opensource.org/codeofconduct) +* [Open Source Initiative General Code of Conduct](https://opensource.org/codeofconduct/licensing) * [Apache Code of Conduct](https://www.apache.org/foundation/policies/conduct.html) From 10238856ad0e057e42a7b620a89f049d8c40751a Mon Sep 17 00:00:00 2001 From: Siddhant Sharma Date: Sun, 14 Oct 2018 00:30:28 +0530 Subject: [PATCH 26/29] Updated with revised changes! @reedsa Updated the file with your requested changes! --- TROUBLESHOOTING.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 8a56721d2..1f2a5fc04 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -133,24 +133,24 @@ console.log(body); You can write blog posts using e-mail with the help of SENDGRID API, like so: ```javascript -sgMail.setApiKey(process.env.SENDGRID_API_KEY); - let msg = { - to: '@blogger.com', - from: '@gmail.com', - subject: title, - html: html, - }; - sgMail.send(msg); +sgMail.setApiKey(process.env.SendGrid_API_KEY); +let msg = { + to: '@blogger.com', + from: '@gmail.com', + subject: title, + html: html, +}; +sgMail.send(msg); ``` You can also wrap the text in the HTML to make a multi-line blog post: ```javascript -
- int a = 10; - int b = 10; - int d = 10; - +
+ + int a = 10; + int b = 10; + int d = 10; +
-``` From 13ca322435a76ebecad6851c3d617da91e16be3d Mon Sep 17 00:00:00 2001 From: Ishaan Malhi Date: Sun, 14 Oct 2018 13:02:44 +0530 Subject: [PATCH 27/29] docs: Change title --- use-cases/kitchen-sink.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/use-cases/kitchen-sink.md b/use-cases/kitchen-sink.md index bec00e71c..3108f1187 100644 --- a/use-cases/kitchen-sink.md +++ b/use-cases/kitchen-sink.md @@ -49,6 +49,6 @@ sgMail .catch(error => console.error(error.toString())); ``` -### Error Handling: +### Caveats: As per [issue #288](https://github.com/sendgrid/sendgrid-nodejs/issues/288), please note that the `customArgs` feild *must* have a string value. From 799fc50e6ad550b1d3d443c6809784500b8ca13a Mon Sep 17 00:00:00 2001 From: Siddhant Sharma Date: Mon, 15 Oct 2018 23:50:20 +0530 Subject: [PATCH 28/29] Update TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 1f2a5fc04..f2d8f36be 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -131,7 +131,7 @@ console.log(body); ## Wrapping Text -You can write blog posts using e-mail with the help of SENDGRID API, like so: +You can write blog posts using e-mail with the help of SendGrid API, like so: ```javascript sgMail.setApiKey(process.env.SendGrid_API_KEY); let msg = { From 957f806b6436a109e6f3382ba3da222b5ad9826a Mon Sep 17 00:00:00 2001 From: Siddhant Sharma Date: Mon, 15 Oct 2018 23:51:55 +0530 Subject: [PATCH 29/29] Update TROUBLESHOOTING.md --- TROUBLESHOOTING.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index f2d8f36be..a8c6a1e2b 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -151,21 +151,3 @@ You can also wrap the text in the HTML to make a multi-line blog post: int d = 10;
- - - - - - - - - - - - - - - - - -