diff --git a/.travis.yml b/.travis.yml index ac74209f5..8f8874dd6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,9 +25,15 @@ script: - dotnet restore - dotnet build ./src/SendGrid -c Release - dotnet test ./tests/SendGrid.Tests/SendGrid.Tests.csproj -c Release -f netcoreapp1.0 - - ls /home/travis/build/sendgrid/sendgrid-csharp/src/SendGrid/bin/Release/net452/ - - ls /home/travis/build/sendgrid/sendgrid-csharp/src/SendGrid/bin/Release/netstandard1.3/ + - ls ./src/SendGrid/bin/Release/net452/ + - ls ./src/SendGrid/bin/Release/netstandard1.3/ - dotnet pack ./src/SendGrid -c Release + - curl -s https://codecov.io/bash > .codecov + - chmod +x .codecov + - ./.codecov + +after_success: + - bash <(curl -s https://codecov.io/bash) notifications: hipchat: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 22453042a..e3941a4a3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ Hello! Thank you for choosing to help contribute to one of the SendGrid open sou - [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 [GitHub Projects](https://github.com/sendgrid/sendgrid-csharp/projects) to help define current roadmaps, please feel free to grab an issue from our [GitHub Issues](https://github.com/sendgrid/sendgrid-csharp/issues). 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. @@ -75,7 +76,7 @@ git clone https://github.com/sendgrid/sendgrid-csharp.git - Open `sendgrid-csharp/SendGrid.sln` -## Environment Variables +### Environment Variables First, get your free SendGrid account [here](https://sendgrid.com/free?source=sendgrid-csharp). @@ -90,23 +91,23 @@ Next, update your Environment with your [SENDGRID_APIKEY](https://app.sendgrid.c ## Understanding the Code Base -**/examples** +**[/examples](https://github.com/sendgrid/sendgrid-csharp/blob/master/examples)** Examples that demonstrate usage. -**/ExampleCoreProject/Example.cs** +**[/ExampleCoreProject/Example.cs](https://github.com/sendgrid/sendgrid-csharp/blob/master/ExampleCoreProject/Example.cs)** A working .NET Core example project for testing. -**/ExampleNet45Project/Example.cs** +**[/ExampleNet45Project/Example.cs](https://github.com/sendgrid/sendgrid-csharp/blob/master/ExampleNet45Project/Example.cs)** A working .NET 4.5.2 example project for testing. -**src/SendGrid/SendGridClient.cs** +**[/src/SendGrid/SendGridClient.cs](https://github.com/sendgrid/sendgrid-csharp/blob/master/src/SendGrid/SendGridClient.cs)** The main interface to the SendGrid API is the class `SendGridClient`. -**/tests/SendGrid.Tests/Integration.cs** +**[/tests/SendGrid.Tests/Integration.cs](https://github.com/sendgrid/sendgrid-csharp/blob/master/tests/SendGrid.Tests/Integration.cs)** Integration tests @@ -187,3 +188,8 @@ Generally, we follow the style guidelines as suggested by the official language. 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. \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index f3cd0ef82..f7f4643ad 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012-2017 SendGrid, Inc. +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 diff --git a/README.md b/README.md index b95d20707..9084b5e6a 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,7 @@ Quick links: - [Bug Reports](https://github.com/sendgrid/sendgrid-csharp/tree/master/CONTRIBUTING.md#submit-a-bug-report) - [Sign the CLA to Create a Pull Request](https://github.com/sendgrid/sendgrid-csharp/tree/master/CONTRIBUTING.md#cla) - [Improvements to the Codebase](https://github.com/sendgrid/sendgrid-csharp/tree/master/CONTRIBUTING.md#improvements-to-the-codebase) +- [Review Pull Requests](https://github.com/sendgrid/sendgrid-csharp/tree/master/CONTRIBUTING.md#code-reviews) # Troubleshooting diff --git a/USAGE.md b/USAGE.md index c0d35293f..84b5bf008 100644 --- a/USAGE.md +++ b/USAGE.md @@ -3135,7 +3135,7 @@ Console.ReadLine(); **This endpoint returns a list of all scopes that this user has access to.** -API Keys can be used to authenticate the use of [SendGrids v3 Web API](https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html), or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). API Keys may be assigned certain permissions, or scopes, that limit which API endpoints they are able to access. For a more detailed explanation of how you can use API Key permissios, please visit our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/api_keys.html#-API-Key-Permissions) or [Classroom](https://sendgrid.com/docs/Classroom/Basics/API/api_key_permissions.html). +API Keys can be used to authenticate the use of [SendGrids v3 Web API](https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html), or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). API Keys may be assigned certain permissions, or scopes, that limit which API endpoints they are able to access. For a more detailed explanation of how you can use API Key permissions, please visit our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/api_keys.html#-API-Key-Permissions) or [Classroom](https://sendgrid.com/docs/Classroom/Basics/API/api_key_permissions.html). ### GET /scopes diff --git a/USE_CASES.md b/USE_CASES.md index 4752d2d51..cf910100e 100644 --- a/USE_CASES.md +++ b/USE_CASES.md @@ -12,6 +12,7 @@ This documentation provides examples for specific use cases. Please [open an iss * [Transient Fault Handling](#transient-faults) * [How to Setup a Domain Whitelabel](#domain-whitelabel) * [How to View Email Statistics](#email-stats) +* [How to transform HTML to plain text](#html-to-plain-text) # Attachments @@ -845,3 +846,33 @@ 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-csharp/blob/master/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 transform HTML to plain text + +Although the HTML tags could be removed using regular expressions, the best solution is parsing the HTML code with a specific library, such as [HTMLAgilityPack](http://html-agility-pack.net/). + +The following code shows how to parse an input string with HTML code and remove all tags: + +```csharp +using HtmlAgilityPack; + +namespace Example { + + internal class Example + { + /// + /// Convert the HTML content to plain text + /// + /// The html content which is going to be converted + /// A string + public static string HtmlToPlainText(string html) + { + HtmlDocument document = new HtmlDocument(); + document.LoadHtml(html); + return document.DocumentNode == null ? string.Empty : document.DocumentNode.InnerText; + } + } +} + +``` \ No newline at end of file diff --git a/nuspec/Sendgrid.9.9.0.nuspec b/nuspec/Sendgrid.9.9.0.nuspec index 8dd2f9359..02ef4ad27 100644 --- a/nuspec/Sendgrid.9.9.0.nuspec +++ b/nuspec/Sendgrid.9.9.0.nuspec @@ -19,16 +19,26 @@ SendGrid, Inc. 2017 SendGrid Email Mail Microsoft Azure Transactional .NET Core - + + + + - + + + + + + + + diff --git a/src/SendGrid/Helpers/Mail/MailHelper.cs b/src/SendGrid/Helpers/Mail/MailHelper.cs index 0e525cce6..91a0ed909 100644 --- a/src/SendGrid/Helpers/Mail/MailHelper.cs +++ b/src/SendGrid/Helpers/Mail/MailHelper.cs @@ -1,302 +1,301 @@ -// -// Copyright (c) SendGrid. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -namespace SendGrid.Helpers.Mail +// +// Copyright (c) SendGrid. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace SendGrid.Helpers.Mail { - using System; - using System.Collections.Generic; - using System.Text.RegularExpressions; - - /// - /// Simplified email sending for common use cases - /// - public class MailHelper - { - private const string NameGroup = "name"; - private const string EmailGroup = "email"; - private static readonly Regex Rfc2822Regex = new Regex( - $@"(?:(?<{NameGroup}>)(?<{EmailGroup}>[^\<]*@.*[^\>])|(?<{NameGroup}>[^\<]*)\<(?<{EmailGroup}>.*@.*)\>)", - RegexOptions.ECMAScript); - - /// - /// Send a single simple email - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// An email object that may contain the recipient’s name, but must always contain the recipient’s email. - /// The subject of your email. This may be overridden by SetGlobalSubject(). - /// The text/plain content of the email body. - /// The text/html content of the email body. - /// A SendGridMessage object. - public static SendGridMessage CreateSingleEmail( - EmailAddress from, - EmailAddress to, - string subject, - string plainTextContent, - string htmlContent) - { - var msg = new SendGridMessage(); - msg.SetFrom(from); - msg.SetSubject(subject); - if (!string.IsNullOrEmpty(plainTextContent)) - { - msg.AddContent(MimeType.Text, plainTextContent); - } - - if (!string.IsNullOrEmpty(htmlContent)) - { - msg.AddContent(MimeType.Html, htmlContent); - } - - msg.AddTo(to); - return msg; - } - - /// - /// Send a single dynamic template email - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// An email object that may contain the recipient’s name, but must always contain the recipient’s email. - /// The ID of the template. - /// The data with which to populate the dynamic template. - /// A SendGridMessage object. - public static SendGridMessage CreateSingleDynamicTemplateEmail( - EmailAddress from, - EmailAddress to, - string templateId, - object dynamicTemplateData) + /// + /// Simplified email sending for common use cases + /// + public class MailHelper + { + private const string NameGroup = "name"; + private const string EmailGroup = "email"; + private static readonly Regex Rfc2822Regex = new Regex( + $@"(?:(?<{NameGroup}>)(?<{EmailGroup}>[^\<]*@.*[^\>])|(?<{NameGroup}>[^\<]*)\<(?<{EmailGroup}>.*@.*)\>)", + RegexOptions.ECMAScript); + + /// + /// Send a single simple email + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// An email object that may contain the recipient’s name, but must always contain the recipient’s email. + /// The subject of your email. This may be overridden by SetGlobalSubject(). + /// The text/plain content of the email body. + /// The text/html content of the email body. + /// A SendGridMessage object. + public static SendGridMessage CreateSingleEmail( + EmailAddress from, + EmailAddress to, + string subject, + string plainTextContent, + string htmlContent) + { + var msg = new SendGridMessage(); + msg.SetFrom(from); + msg.SetSubject(subject); + if (!string.IsNullOrEmpty(plainTextContent)) + { + msg.AddContent(MimeType.Text, plainTextContent); + } + + if (!string.IsNullOrEmpty(htmlContent)) + { + msg.AddContent(MimeType.Html, htmlContent); + } + + msg.AddTo(to); + return msg; + } + + /// + /// Send a single dynamic template email + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// An email object that may contain the recipient’s name, but must always contain the recipient’s email. + /// The ID of the template. + /// The data with which to populate the dynamic template. + /// A SendGridMessage object. + public static SendGridMessage CreateSingleDynamicTemplateEmail( + EmailAddress from, + EmailAddress to, + string templateId, + object dynamicTemplateData) { if (string.IsNullOrWhiteSpace(templateId)) { throw new ArgumentException($"{nameof(templateId)} is required when creating a dynamic template email.", nameof(templateId)); } - var msg = new SendGridMessage(); - msg.SetFrom(from); - msg.AddTo(to); - msg.TemplateId = templateId; - + var msg = new SendGridMessage(); + msg.SetFrom(from); + msg.AddTo(to); + msg.TemplateId = templateId; + if (dynamicTemplateData != null) { msg.SetDynamicTemplateData(dynamicTemplateData); - } - - return msg; - } - - /// - /// Send a single simple email to multiple recipients - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. - /// The subject of your email. This may be overridden by SetGlobalSubject(). - /// The text/plain content of the email body. - /// The text/html content of the email body. - /// A SendGridMessage object. - public static SendGridMessage CreateSingleEmailToMultipleRecipients( - EmailAddress from, - List tos, - string subject, - string plainTextContent, - string htmlContent) - { - var msg = new SendGridMessage(); - msg.SetFrom(from); - msg.SetGlobalSubject(subject); - if (!string.IsNullOrEmpty(plainTextContent)) - { - msg.AddContent(MimeType.Text, plainTextContent); - } - - if (!string.IsNullOrEmpty(htmlContent)) - { - msg.AddContent(MimeType.Html, htmlContent); - } - - for (var i = 0; i < tos.Count; i++) - { - msg.AddTo(tos[i], i); - } - - return msg; - } - - /// - /// Send a single simple email to multiple recipients - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. - /// The ID of the template. - /// The data with which to populate the dynamic template. - /// A SendGridMessage object. - public static SendGridMessage CreateSingleDynamicTemplateEmailToMultipleRecipients( - EmailAddress from, - List tos, - string templateId, - object dynamicTemplateData) - { + } + + return msg; + } + + /// + /// Send a single simple email to multiple recipients + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. + /// The subject of your email. This may be overridden by SetGlobalSubject(). + /// The text/plain content of the email body. + /// The text/html content of the email body. + /// A SendGridMessage object. + public static SendGridMessage CreateSingleEmailToMultipleRecipients( + EmailAddress from, + List tos, + string subject, + string plainTextContent, + string htmlContent) + { + var msg = new SendGridMessage(); + msg.SetFrom(from); + msg.SetGlobalSubject(subject); + if (!string.IsNullOrEmpty(plainTextContent)) + { + msg.AddContent(MimeType.Text, plainTextContent); + } + + if (!string.IsNullOrEmpty(htmlContent)) + { + msg.AddContent(MimeType.Html, htmlContent); + } + + for (var i = 0; i < tos.Count; i++) + { + msg.AddTo(tos[i], i); + } + + return msg; + } + + /// + /// Send a single simple email to multiple recipients + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. + /// The ID of the template. + /// The data with which to populate the dynamic template. + /// A SendGridMessage object. + public static SendGridMessage CreateSingleDynamicTemplateEmailToMultipleRecipients( + EmailAddress from, + List tos, + string templateId, + object dynamicTemplateData) + { if (string.IsNullOrWhiteSpace(templateId)) { throw new ArgumentException($"{nameof(templateId)} is required when creating a dynamic template email.", nameof(templateId)); } - var msg = new SendGridMessage(); + var msg = new SendGridMessage(); msg.SetFrom(from); - msg.TemplateId = templateId; + msg.TemplateId = templateId; var setDynamicTemplateDataValues = dynamicTemplateData != null; - for (var i = 0; i < tos.Count; i++) - { - msg.AddTo(tos[i], i); - + for (var i = 0; i < tos.Count; i++) + { + msg.AddTo(tos[i], i); + if (setDynamicTemplateDataValues) { msg.SetDynamicTemplateData(dynamicTemplateData, i); - } - } - - return msg; - } - - /// - /// Send multiple emails to multiple recipients. - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. - /// The subject of your email. This may be overridden by SetGlobalSubject(). - /// The text/plain content of the email body. - /// The text/html content of the email body. - /// Substitution key/values to customize the content for each email. - /// A SendGridMessage object. - public static SendGridMessage CreateMultipleEmailsToMultipleRecipients( - EmailAddress from, - List tos, - List subjects, - string plainTextContent, - string htmlContent, - List> substitutions) - { - var msg = new SendGridMessage(); - msg.SetFrom(from); - if (!string.IsNullOrEmpty(plainTextContent)) - { - msg.AddContent(MimeType.Text, plainTextContent); - } - - if (!string.IsNullOrEmpty(htmlContent)) - { - msg.AddContent(MimeType.Html, htmlContent); - } - - for (var i = 0; i < tos.Count; i++) - { - msg.AddTo(tos[i], i); - msg.SetSubject(subjects[i], i); - msg.AddSubstitutions(substitutions[i], i); - } - - return msg; - } - - /// - /// Send multiple emails to multiple recipients. - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. - /// The ID of the template. - /// The data with which to populate the dynamic template. - /// A SendGridMessage object. - public static SendGridMessage CreateMultipleDynamicTemplateEmailsToMultipleRecipients( - EmailAddress from, - List tos, - string templateId, - List dynamicTemplateData) - { + } + } + + return msg; + } + + /// + /// Send multiple emails to multiple recipients. + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. + /// The subject of your email. This may be overridden by SetGlobalSubject(). + /// The text/plain content of the email body. + /// The text/html content of the email body. + /// Substitution key/values to customize the content for each email. + /// A SendGridMessage object. + public static SendGridMessage CreateMultipleEmailsToMultipleRecipients( + EmailAddress from, + List tos, + List subjects, + string plainTextContent, + string htmlContent, + List> substitutions) + { + var msg = new SendGridMessage(); + msg.SetFrom(from); + if (!string.IsNullOrEmpty(plainTextContent)) + { + msg.AddContent(MimeType.Text, plainTextContent); + } + + if (!string.IsNullOrEmpty(htmlContent)) + { + msg.AddContent(MimeType.Html, htmlContent); + } + + for (var i = 0; i < tos.Count; i++) + { + msg.AddTo(tos[i], i); + msg.SetSubject(subjects[i], i); + msg.AddSubstitutions(substitutions[i], i); + } + + return msg; + } + + /// + /// Send multiple emails to multiple recipients. + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. + /// The ID of the template. + /// The data with which to populate the dynamic template. + /// A SendGridMessage object. + public static SendGridMessage CreateMultipleDynamicTemplateEmailsToMultipleRecipients( + EmailAddress from, + List tos, + string templateId, + List dynamicTemplateData) + { if (string.IsNullOrWhiteSpace(templateId)) { throw new ArgumentException($"{nameof(templateId)} is required when creating a dynamic template email.", nameof(templateId)); - } - - var msg = new SendGridMessage(); - msg.SetFrom(from); - msg.TemplateId = templateId; - - var setDynamicTemplateDataValues = dynamicTemplateData != null; - - for (var i = 0; i < tos.Count; i++) - { - msg.AddTo(tos[i], i); - + } + + var msg = new SendGridMessage(); + msg.SetFrom(from); + msg.TemplateId = templateId; + + var setDynamicTemplateDataValues = dynamicTemplateData != null; + + for (var i = 0; i < tos.Count; i++) + { + msg.AddTo(tos[i], i); + if (setDynamicTemplateDataValues) { - msg.SetDynamicTemplateData(dynamicTemplateData[i], i); - } - } - - return msg; - } - - /// - /// Uncomplex conversion of a "]]> to EmailAddress - /// - /// "email@email.com" or "]]> string - /// EmailsAddress Object - public static EmailAddress StringToEmailAddress(string rfc2822Email) - { - var match = Rfc2822Regex.Match(rfc2822Email); - if (!match.Success) - { - return new EmailAddress(rfc2822Email); - } - - var email = match.Groups[EmailGroup].Value.Trim(); - var name = match.Groups[NameGroup].Value.Trim(); - return new EmailAddress(email, name); - } - - /// - /// Send a single simple email to multiple recipients with option for displaying all the recipients present in "To" section of email - /// - /// An email object that may contain the recipient’s name, but must always contain the sender’s email. - /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. - /// The subject of your email. This may be overridden by SetGlobalSubject(). - /// The text/plain content of the email body. - /// The text/html content of the email body. - /// Displays all the recipients present in the "To" section of email.The default value is false - /// A SendGridMessage object. - public static SendGridMessage CreateSingleEmailToMultipleRecipients( - EmailAddress from, - List tos, - string subject, - string plainTextContent, - string htmlContent, - bool showAllRecipients = false) - { - var msg = new SendGridMessage(); - if (showAllRecipients) - { - msg.SetFrom(from); - msg.SetGlobalSubject(subject); - if (!string.IsNullOrEmpty(plainTextContent)) - { - msg.AddContent(MimeType.Text, plainTextContent); - } - - if (!string.IsNullOrEmpty(htmlContent)) - { - msg.AddContent(MimeType.Html, htmlContent); - } - - msg.AddTos(tos); - } - else - { - msg = CreateSingleEmailToMultipleRecipients(from, tos, subject, plainTextContent, htmlContent); - } - - return msg; - } - } -} + msg.SetDynamicTemplateData(dynamicTemplateData[i], i); + } + } + + return msg; + } + + /// + /// Uncomplex conversion of a "]]> to EmailAddress + /// + /// "email@email.com" or "]]> string + /// EmailsAddress Object + public static EmailAddress StringToEmailAddress(string rfc2822Email) + { + var match = Rfc2822Regex.Match(rfc2822Email); + if (!match.Success) + { + return new EmailAddress(rfc2822Email); + } + + var email = match.Groups[EmailGroup].Value.Trim(); + var name = match.Groups[NameGroup].Value.Trim(); + return new EmailAddress(email, name); + } + + /// + /// Send a single simple email to multiple recipients with option for displaying all the recipients present in "To" section of email + /// + /// An email object that may contain the recipient’s name, but must always contain the sender’s email. + /// A list of email objects that may contain the recipient’s name, but must always contain the recipient’s email. + /// The subject of your email. This may be overridden by SetGlobalSubject(). + /// The text/plain content of the email body. + /// The text/html content of the email body. + /// Displays all the recipients present in the "To" section of email.The default value is false + /// A SendGridMessage object. + public static SendGridMessage CreateSingleEmailToMultipleRecipients( + EmailAddress from, + List tos, + string subject, + string plainTextContent, + string htmlContent, + bool showAllRecipients = false) + { + var msg = new SendGridMessage(); + if (showAllRecipients) + { + msg.SetFrom(from); + msg.SetGlobalSubject(subject); + if (!string.IsNullOrEmpty(plainTextContent)) + { + msg.AddContent(MimeType.Text, plainTextContent); + } + + if (!string.IsNullOrEmpty(htmlContent)) + { + msg.AddContent(MimeType.Html, htmlContent); + } + + msg.AddTos(tos); + } + else + { + msg = CreateSingleEmailToMultipleRecipients(from, tos, subject, plainTextContent, htmlContent); + } + + return msg; + } + } +} diff --git a/src/SendGrid/Helpers/Mail/Model/ASM.cs b/src/SendGrid/Helpers/Mail/Model/ASM.cs index 9a94b8ebe..636d8fa3a 100644 --- a/src/SendGrid/Helpers/Mail/Model/ASM.cs +++ b/src/SendGrid/Helpers/Mail/Model/ASM.cs @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; +using System.Collections.Generic; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - using System.Collections.Generic; - /// /// An object allowing you to specify how to handle unsubscribes. /// diff --git a/src/SendGrid/Helpers/Mail/Model/Attachment.cs b/src/SendGrid/Helpers/Mail/Model/Attachment.cs index ebc72d2e5..35c0aa9ff 100644 --- a/src/SendGrid/Helpers/Mail/Model/Attachment.cs +++ b/src/SendGrid/Helpers/Mail/Model/Attachment.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Gets or sets an array of objects in which you can specify any attachments you want to include. /// diff --git a/src/SendGrid/Helpers/Mail/Model/BCCSettings.cs b/src/SendGrid/Helpers/Mail/Model/BCCSettings.cs index d929726e0..101f0ac1c 100644 --- a/src/SendGrid/Helpers/Mail/Model/BCCSettings.cs +++ b/src/SendGrid/Helpers/Mail/Model/BCCSettings.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Gets or sets the address specified in the mail_settings.bcc object will receive a blind carbon copy (BCC) of the very first personalization defined in the personalizations array. /// diff --git a/src/SendGrid/Helpers/Mail/Model/BypassListManagement.cs b/src/SendGrid/Helpers/Mail/Model/BypassListManagement.cs index 49bd03eaf..f59ee795a 100644 --- a/src/SendGrid/Helpers/Mail/Model/BypassListManagement.cs +++ b/src/SendGrid/Helpers/Mail/Model/BypassListManagement.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Allows you to bypass all unsubscribe groups and suppressions to ensure that the email is delivered to every single recipient. This should only be used in emergencies when it is absolutely necessary that every recipient receives your email. Ex: outage emails, or forgot password emails. /// diff --git a/src/SendGrid/Helpers/Mail/Model/ClickTracking.cs b/src/SendGrid/Helpers/Mail/Model/ClickTracking.cs index e11bda2e6..5966d4b2b 100644 --- a/src/SendGrid/Helpers/Mail/Model/ClickTracking.cs +++ b/src/SendGrid/Helpers/Mail/Model/ClickTracking.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Allows you to track whether a recipient clicked a link in your email. /// diff --git a/src/SendGrid/Helpers/Mail/Model/Content.cs b/src/SendGrid/Helpers/Mail/Model/Content.cs index 767d0be97..dbbabc934 100644 --- a/src/SendGrid/Helpers/Mail/Model/Content.cs +++ b/src/SendGrid/Helpers/Mail/Model/Content.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Specifies the content of your email. You can include multiple mime types of content, but you must specify at least one. To include more than one mime type, simply add another object to the array containing the type and value parameters. If included, text/plain and text/html must be the first indices of the array in this order. If you choose to include the text/plain or text/html mime types, they must be the first indices of the content array in the order text/plain, text/html.*Content is NOT mandatory if you using a transactional template and have defined the template_id in the Request /// diff --git a/src/SendGrid/Helpers/Mail/Model/EmailAddress.cs b/src/SendGrid/Helpers/Mail/Model/EmailAddress.cs index 2d79bf62f..36c7620ae 100644 --- a/src/SendGrid/Helpers/Mail/Model/EmailAddress.cs +++ b/src/SendGrid/Helpers/Mail/Model/EmailAddress.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// An email object containing the email address and name of the sender or recipient. /// diff --git a/src/SendGrid/Helpers/Mail/Model/FooterSettings.cs b/src/SendGrid/Helpers/Mail/Model/FooterSettings.cs index c80b7e8dd..d9160abc2 100644 --- a/src/SendGrid/Helpers/Mail/Model/FooterSettings.cs +++ b/src/SendGrid/Helpers/Mail/Model/FooterSettings.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// The default footer that you would like appended to the bottom of every email. /// diff --git a/src/SendGrid/Helpers/Mail/Model/Ganalytics.cs b/src/SendGrid/Helpers/Mail/Model/Ganalytics.cs index 6196b209e..c9c8be368 100644 --- a/src/SendGrid/Helpers/Mail/Model/Ganalytics.cs +++ b/src/SendGrid/Helpers/Mail/Model/Ganalytics.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Allows you to enable tracking provided by Google Analytics. /// diff --git a/src/SendGrid/Helpers/Mail/Model/HtmlContent.cs b/src/SendGrid/Helpers/Mail/Model/HtmlContent.cs index 7c31c0c7b..cdb6e2609 100644 --- a/src/SendGrid/Helpers/Mail/Model/HtmlContent.cs +++ b/src/SendGrid/Helpers/Mail/Model/HtmlContent.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail.Model { - using Newtonsoft.Json; - /// /// Helper class for plain html mime types /// diff --git a/src/SendGrid/Helpers/Mail/Model/MailSettings.cs b/src/SendGrid/Helpers/Mail/Model/MailSettings.cs index 25318dd9d..e83f0a304 100644 --- a/src/SendGrid/Helpers/Mail/Model/MailSettings.cs +++ b/src/SendGrid/Helpers/Mail/Model/MailSettings.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// A collection of different mail settings that you can use to specify how you would like this email to be handled. /// diff --git a/src/SendGrid/Helpers/Mail/Model/OpenTracking.cs b/src/SendGrid/Helpers/Mail/Model/OpenTracking.cs index 63f650e3c..9f5df67aa 100644 --- a/src/SendGrid/Helpers/Mail/Model/OpenTracking.cs +++ b/src/SendGrid/Helpers/Mail/Model/OpenTracking.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Allows you to track whether the email was opened or not, but including a single pixel image in the body of the content. When the pixel is loaded, we can log that the email was opened. /// diff --git a/src/SendGrid/Helpers/Mail/Model/Personalization.cs b/src/SendGrid/Helpers/Mail/Model/Personalization.cs index 5a03205d7..8b9ac0bcb 100644 --- a/src/SendGrid/Helpers/Mail/Model/Personalization.cs +++ b/src/SendGrid/Helpers/Mail/Model/Personalization.cs @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; +using System.Collections.Generic; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - using System.Collections.Generic; - /// /// An array of messages and their metadata. Each object within personalizations can be thought of as an envelope - it defines who should receive an individual message and how that message should be handled. For more information, please see our documentation on Personalizations. Parameters in personalizations will override the parameters of the same name from the message level. /// https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/personalizations.html diff --git a/src/SendGrid/Helpers/Mail/Model/SandboxMode.cs b/src/SendGrid/Helpers/Mail/Model/SandboxMode.cs index d9cd00812..d0623ca33 100644 --- a/src/SendGrid/Helpers/Mail/Model/SandboxMode.cs +++ b/src/SendGrid/Helpers/Mail/Model/SandboxMode.cs @@ -1,12 +1,12 @@ -// +// // Copyright (c) SendGrid. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// This allows you to send a test email to ensure that your request body is valid and formatted correctly. For more information, please see our Classroom. /// https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/sandbox_mode.html diff --git a/src/SendGrid/Helpers/Mail/Model/SpamCheck.cs b/src/SendGrid/Helpers/Mail/Model/SpamCheck.cs index 344642371..b0d966803 100644 --- a/src/SendGrid/Helpers/Mail/Model/SpamCheck.cs +++ b/src/SendGrid/Helpers/Mail/Model/SpamCheck.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// This allows you to test the content of your email for spam. /// diff --git a/src/SendGrid/Helpers/Mail/Model/SubscriptionTracking.cs b/src/SendGrid/Helpers/Mail/Model/SubscriptionTracking.cs index dd8c28fa7..b1e7a7c12 100644 --- a/src/SendGrid/Helpers/Mail/Model/SubscriptionTracking.cs +++ b/src/SendGrid/Helpers/Mail/Model/SubscriptionTracking.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Allows you to insert a subscription management link at the bottom of the text and html bodies of your email. If you would like to specify the location of the link within your email, you may use the substitution_tag. /// diff --git a/src/SendGrid/Helpers/Mail/Model/TrackingSettings.cs b/src/SendGrid/Helpers/Mail/Model/TrackingSettings.cs index 09bb688de..048f8243d 100644 --- a/src/SendGrid/Helpers/Mail/Model/TrackingSettings.cs +++ b/src/SendGrid/Helpers/Mail/Model/TrackingSettings.cs @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; + namespace SendGrid.Helpers.Mail { - using Newtonsoft.Json; - /// /// Settings to determine how you would like to track the metrics of how your recipients interact with your email. /// diff --git a/src/SendGrid/Helpers/Mail/SendGridMessage.cs b/src/SendGrid/Helpers/Mail/SendGridMessage.cs index 1f0b53141..5f0ffa4f4 100644 --- a/src/SendGrid/Helpers/Mail/SendGridMessage.cs +++ b/src/SendGrid/Helpers/Mail/SendGridMessage.cs @@ -3,17 +3,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; +using SendGrid.Helpers.Mail.Model; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + namespace SendGrid.Helpers.Mail { - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Model; - using Newtonsoft.Json; - /// /// Class SendGridMessage builds an object that sends an email through SendGrid. /// @@ -144,9 +144,11 @@ public class SendGridMessage public void AddTo(string email, string name = null) { if (string.IsNullOrWhiteSpace(email)) + { throw new ArgumentNullException("email"); + } - AddTo(new EmailAddress(email, name)); + this.AddTo(new EmailAddress(email, name)); } /// @@ -263,9 +265,11 @@ public void AddTos(List emails, int personalizationIndex = 0, Pers public void AddCc(string email, string name = null) { if (string.IsNullOrWhiteSpace(email)) + { throw new ArgumentNullException("email"); + } - AddCc(new EmailAddress(email, name)); + this.AddCc(new EmailAddress(email, name)); } /// @@ -382,9 +386,11 @@ public void AddCcs(List emails, int personalizationIndex = 0, Pers public void AddBcc(string email, string name = null) { if (string.IsNullOrWhiteSpace(email)) + { throw new ArgumentNullException("email"); + } - AddBcc(new EmailAddress(email, name)); + this.AddBcc(new EmailAddress(email, name)); } /// @@ -961,7 +967,7 @@ public void SetFrom(string email, string name = null) throw new ArgumentNullException("email"); } - SetFrom(new EmailAddress(email, name)); + this.SetFrom(new EmailAddress(email, name)); } /// @@ -1471,7 +1477,7 @@ public void SetClickTracking(bool enable, bool enableText) /// /// Gets or sets a value indicating whether this setting is enabled. /// Allows you to specify a substitution tag that you can insert in the body of your email at a location that you desire. This tag will be replaced by the open tracking pixel. - public void SetOpenTracking(bool enable, string substitutionTag) + public void SetOpenTracking(bool enable, string substitutionTag = null) { if (this.TrackingSettings == null) { diff --git a/src/SendGrid/ISendGridClient.cs b/src/SendGrid/ISendGridClient.cs index 0fff04b1c..1eb2ab545 100644 --- a/src/SendGrid/ISendGridClient.cs +++ b/src/SendGrid/ISendGridClient.cs @@ -3,15 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using SendGrid.Helpers.Mail; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; + namespace SendGrid { - using Helpers.Mail; - using System.Collections.Generic; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Threading; - using System.Threading.Tasks; - /// /// A HTTP client wrapper for interacting with SendGrid's API /// diff --git a/src/SendGrid/Reliability/ReliabilitySettings.cs b/src/SendGrid/Reliability/ReliabilitySettings.cs index d3316f936..1ae97afeb 100644 --- a/src/SendGrid/Reliability/ReliabilitySettings.cs +++ b/src/SendGrid/Reliability/ReliabilitySettings.cs @@ -1,7 +1,12 @@ +// +// Copyright (c) SendGrid. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; + namespace SendGrid.Helpers.Reliability { - using System; - /// /// Defines the reliability settings to use on HTTP requests /// diff --git a/src/SendGrid/Reliability/RetryDelegatingHandler.cs b/src/SendGrid/Reliability/RetryDelegatingHandler.cs index 4ce496300..4781bd405 100644 --- a/src/SendGrid/Reliability/RetryDelegatingHandler.cs +++ b/src/SendGrid/Reliability/RetryDelegatingHandler.cs @@ -1,12 +1,17 @@ -namespace SendGrid.Helpers.Reliability +// +// Copyright (c) SendGrid. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace SendGrid.Helpers.Reliability { - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - /// /// A delegating handler that provides retry functionality while executing a request /// @@ -43,6 +48,7 @@ public RetryDelegatingHandler(HttpMessageHandler innerHandler, ReliabilitySettin this.settings = settings; } + /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (this.settings.MaximumNumberOfRetries == 0) diff --git a/src/SendGrid/Response.cs b/src/SendGrid/Response.cs index 0debc3e83..27cf16dda 100644 --- a/src/SendGrid/Response.cs +++ b/src/SendGrid/Response.cs @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; + namespace SendGrid { - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Threading.Tasks; - using Newtonsoft.Json; - /// /// The response received from an API call to SendGrid /// @@ -63,7 +63,7 @@ public HttpStatusCode StatusCode } /// - /// Gets or sets the response headers returned from SendGrid. + /// Gets or sets the response body returned from SendGrid. /// public HttpContent Body { @@ -79,7 +79,7 @@ public HttpContent Body } /// - /// Gets or sets the response body returned from SendGrid. + /// Gets or sets the response headers returned from SendGrid. /// public HttpResponseHeaders Headers { diff --git a/src/SendGrid/SendGrid.csproj b/src/SendGrid/SendGrid.csproj index fd8cab6c1..77fc25908 100644 --- a/src/SendGrid/SendGrid.csproj +++ b/src/SendGrid/SendGrid.csproj @@ -2,7 +2,7 @@ 9.9.0 - netstandard1.3;net452 + netstandard1.3;netstandard2.0;net452 anycpu true SendGrid @@ -19,6 +19,10 @@ full + + + + All @@ -30,6 +34,11 @@ + + + + + diff --git a/src/SendGrid/SendGrid.ruleset b/src/SendGrid/SendGrid.ruleset index dacf2f6fb..f37108671 100644 --- a/src/SendGrid/SendGrid.ruleset +++ b/src/SendGrid/SendGrid.ruleset @@ -1,6 +1,5 @@  - diff --git a/src/SendGrid/SendGridClient.cs b/src/SendGrid/SendGridClient.cs index a4564748b..31ea70752 100644 --- a/src/SendGrid/SendGridClient.cs +++ b/src/SendGrid/SendGridClient.cs @@ -1,23 +1,24 @@ -// +// // Copyright (c) SendGrid. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Newtonsoft.Json; +using SendGrid.Helpers.Mail; +using SendGrid.Helpers.Reliability; +using System; +using System.IO; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + namespace SendGrid { - using Helpers.Mail; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Net; - using System.Net.Http; - using System.Net.Http.Headers; - using System.Reflection; - using System.Text; - using System.Threading; - using System.Threading.Tasks; - using SendGrid.Helpers.Reliability; - /// /// A HTTP client wrapper for interacting with SendGrid's API /// @@ -25,21 +26,6 @@ public class SendGridClient : ISendGridClient { private readonly SendGridClientOptions options = new SendGridClientOptions(); - /// - /// Gets or sets the path to the API resource. - /// - public string UrlPath { get; set; } - - /// - /// Gets or sets the API version. - /// - public string Version { get; set; } - - /// - /// Gets or sets the request media type. - /// - public string MediaType { get; set; } - /// /// The HttpClient instance to use for all calls from this SendGridClient instance. /// @@ -67,16 +53,16 @@ public SendGridClient(IWebProxy webProxy, string apiKey, string host = null, Dic UseDefaultCredentials = false, }; - var retryHandler = new RetryDelegatingHandler(httpClientHandler, options.ReliabilitySettings); + var retryHandler = new RetryDelegatingHandler(httpClientHandler, this.options.ReliabilitySettings); - client = new HttpClient(retryHandler); + this.client = new HttpClient(retryHandler); } else { - client = CreateHttpClientWithRetryHandler(); + this.client = this.CreateHttpClientWithRetryHandler(); } - InitiateClient(apiKey, host, requestHeaders, version, urlPath); + this.InitiateClient(apiKey, host, requestHeaders, version, urlPath); } /// @@ -89,25 +75,6 @@ public SendGridClient(SendGridClientOptions options) { } - /// - /// Initializes a new instance of the class. - /// - /// An optional http client which may me injected in order to facilitate testing. - /// A instance that defines the configuration settings to use with the client - /// Interface to the SendGrid REST API - internal SendGridClient(HttpClient httpClient, SendGridClientOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - this.options = options; - client = (httpClient == null) ? CreateHttpClientWithRetryHandler() : httpClient; - - InitiateClient(options.ApiKey, options.Host, options.RequestHeaders, options.Version, options.UrlPath); - } - /// /// Initializes a new instance of the class. /// @@ -138,63 +105,22 @@ public SendGridClient(string apiKey, string host = null, Dictionary - /// Common method to initiate internal fields regardless of which constructor was used. + /// Initializes a new instance of the class. /// - /// Your SendGrid API key. - /// Base url (e.g. https://api.sendgrid.com) - /// A dictionary of request headers - /// API version, override AddVersion to customize - /// Path to endpoint (e.g. /path/to/endpoint) - private void InitiateClient(string apiKey, string host, Dictionary requestHeaders, string version, string urlPath) + /// An optional http client which may me injected in order to facilitate testing. + /// A instance that defines the configuration settings to use with the client + /// Interface to the SendGrid REST API + internal SendGridClient(HttpClient httpClient, SendGridClientOptions options) { - UrlPath = urlPath; - Version = version; - - var baseAddress = host ?? "https://api.sendgrid.com"; - var clientVersion = GetType().GetTypeInfo().Assembly.GetName().Version.ToString(); - - // standard headers - client.BaseAddress = new Uri(baseAddress); - Dictionary headers = new Dictionary - { - { "Authorization", "Bearer " + apiKey }, - { "Content-Type", "application/json" }, - { "User-Agent", "sendgrid/" + clientVersion + " csharp" }, - { "Accept", "application/json" } - }; - - // set header overrides - if (requestHeaders != null) + if (options == null) { - foreach (var header in requestHeaders) - { - headers[header.Key] = header.Value; - } + throw new ArgumentNullException(nameof(options)); } - // add headers to httpClient - foreach (var header in headers) - { - if (header.Key == "Authorization") - { - var split = header.Value.Split(); - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(split[0], split[1]); - } - else if (header.Key == "Content-Type") - { - client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); - MediaType = header.Value; - } - else - { - client.DefaultRequestHeaders.Add(header.Key, header.Value); - } - } - } + this.options = options; + this.client = (httpClient == null) ? this.CreateHttpClientWithRetryHandler() : httpClient; - private HttpClient CreateHttpClientWithRetryHandler() - { - return new HttpClient(new RetryDelegatingHandler(options.ReliabilitySettings)); + this.InitiateClient(options.ApiKey, options.Host, options.RequestHeaders, options.Version, options.UrlPath); } /// @@ -228,6 +154,21 @@ public enum Method PUT } + /// + /// Gets or sets the path to the API resource. + /// + public string UrlPath { get; set; } + + /// + /// Gets or sets the API version. + /// + public string Version { get; set; } + + /// + /// Gets or sets the request media type. + /// + public string MediaType { get; set; } + /// /// Add the authorization header, override to customize /// @@ -245,9 +186,9 @@ public virtual AuthenticationHeaderValue AddAuthorization(KeyValuePairThe parameters for the API call /// Cancel the asynchronous call /// Response object - public async Task MakeRequest(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task MakeRequest(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { - HttpResponseMessage response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); + HttpResponseMessage response = await this.client.SendAsync(request, cancellationToken).ConfigureAwait(false); return new Response(response.StatusCode, response.Content, response.Headers); } @@ -271,7 +212,7 @@ public async Task RequestAsync( string urlPath = null, CancellationToken cancellationToken = default(CancellationToken)) { - var endpoint = client.BaseAddress + BuildUrl(urlPath, queryParams); + var endpoint = this.client.BaseAddress + this.BuildUrl(urlPath, queryParams); // Build the request body StringContent content = null; @@ -287,7 +228,7 @@ public async Task RequestAsync( RequestUri = new Uri(endpoint), Content = content }; - return await MakeRequest(request, cancellationToken).ConfigureAwait(false); + return await this.MakeRequest(request, cancellationToken).ConfigureAwait(false); } /// @@ -298,7 +239,7 @@ public async Task RequestAsync( /// A Response object. public async Task SendEmailAsync(SendGridMessage msg, CancellationToken cancellationToken = default(CancellationToken)) { - return await RequestAsync( + return await this.RequestAsync( Method.POST, msg.Serialize(), urlPath: "mail/send", @@ -318,11 +259,11 @@ private string BuildUrl(string urlPath, string queryParams = null) string url = null; // create urlPAth - from parameter if overridden on call or from ctor parameter - var urlpath = urlPath ?? UrlPath; + var urlpath = urlPath ?? this.UrlPath; - if (Version != null) + if (this.Version != null) { - url = Version + "/" + urlpath; + url = this.Version + "/" + urlpath; } else { @@ -331,16 +272,19 @@ private string BuildUrl(string urlPath, string queryParams = null) if (queryParams != null) { - var ds_query_params = JsonConvert.DeserializeObject>(queryParams); + var ds_query_params = ParseJson(queryParams); string query = "?"; foreach (var pair in ds_query_params) { - if (query != "?") + foreach(var element in pair.Value) { - query = query + "&"; - } + if (query != "?") + { + query = query + "&"; + } - query = query + pair.Key + "=" + pair.Value.ToString(); + query = query + pair.Key + "=" + element; + } } url = url + query; @@ -348,5 +292,110 @@ private string BuildUrl(string urlPath, string queryParams = null) return url; } + + private HttpClient CreateHttpClientWithRetryHandler() + { + return new HttpClient(new RetryDelegatingHandler(this.options.ReliabilitySettings)); + } + + /// + /// Common method to initiate internal fields regardless of which constructor was used. + /// + /// Your SendGrid API key. + /// Base url (e.g. https://api.sendgrid.com) + /// A dictionary of request headers + /// API version, override AddVersion to customize + /// Path to endpoint (e.g. /path/to/endpoint) + private void InitiateClient(string apiKey, string host, Dictionary requestHeaders, string version, string urlPath) + { + this.UrlPath = urlPath; + this.Version = version; + + var baseAddress = host ?? "https://api.sendgrid.com"; + var clientVersion = this.GetType().GetTypeInfo().Assembly.GetName().Version.ToString(); + + // standard headers + this.client.BaseAddress = new Uri(baseAddress); + Dictionary headers = new Dictionary + { + { "Authorization", "Bearer " + apiKey }, + { "Content-Type", "application/json" }, + { "User-Agent", "sendgrid/" + clientVersion + " csharp" }, + { "Accept", "application/json" } + }; + + // set header overrides + if (requestHeaders != null) + { + foreach (var header in requestHeaders) + { + headers[header.Key] = header.Value; + } + } + + // add headers to httpClient + foreach (var header in headers) + { + if (header.Key == "Authorization") + { + var split = header.Value.Split(); + this.client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(split[0], split[1]); + } + else if (header.Key == "Content-Type") + { + this.client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(header.Value)); + this.MediaType = header.Value; + } + else + { + this.client.DefaultRequestHeaders.Add(header.Key, header.Value); + } + } + } + + /// + /// Parses a JSON string without removing duplicate keys. + /// + /// + /// This function flattens all Objects/Array. + /// This means that for example {'id': 1, 'id': 2, 'id': 3} and + /// {'id': [1, 2, 3]} result in the same output. + /// + /// The JSON string to parse. + /// A dictionary of all values. + private Dictionary> ParseJson(string json) + { + var dict = new Dictionary>(); + + using(var sr = new StringReader(json)) + using(var reader = new JsonTextReader(sr)) + { + var propertyName = ""; + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + { + propertyName = reader.Value.ToString(); + if(!dict.ContainsKey(propertyName)) + dict.Add(propertyName, new List()); + break; + } + case JsonToken.Boolean: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Bytes: + case JsonToken.String: + case JsonToken.Date: + { + dict[propertyName].Add(reader.Value); + break; + } + } + } + } + return dict; + } } } diff --git a/src/SendGrid/SendGridClientOptions.cs b/src/SendGrid/SendGridClientOptions.cs index 1d830c700..c7ab5c305 100644 --- a/src/SendGrid/SendGridClientOptions.cs +++ b/src/SendGrid/SendGridClientOptions.cs @@ -1,9 +1,14 @@ -namespace SendGrid -{ - using System; - using System.Collections.Generic; - using SendGrid.Helpers.Reliability; +// +// Copyright (c) SendGrid. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using SendGrid.Helpers.Reliability; +using System; +using System.Collections.Generic; +namespace SendGrid +{ /// /// Defines the options to use with the SendGrid client /// @@ -16,9 +21,9 @@ public class SendGridClientOptions /// public SendGridClientOptions() { - RequestHeaders = new Dictionary(); - Host = "https://api.sendgrid.com"; - Version = "v3"; + this.RequestHeaders = new Dictionary(); + this.Host = "https://api.sendgrid.com"; + this.Version = "v3"; } /// diff --git a/src/SendGrid/stylecop.json b/src/SendGrid/stylecop.json index 5ae51ee0b..5faf5fb55 100644 --- a/src/SendGrid/stylecop.json +++ b/src/SendGrid/stylecop.json @@ -8,7 +8,7 @@ }, "documentationRules": { "companyName": "SendGrid", - "copyrightText": "Copyright (c) {companyName}. All rights reserved. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", + "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information.", "variables": { "licenseName": "MIT", "licenseFile": "LICENSE" diff --git a/tests/SendGrid.Tests/Integration.cs b/tests/SendGrid.Tests/Integration.cs index 746859e57..903bccf57 100644 --- a/tests/SendGrid.Tests/Integration.cs +++ b/tests/SendGrid.Tests/Integration.cs @@ -5992,14 +5992,11 @@ public async Task TestWhenHttpCallTimesOutThenExceptionIsThrown() * the original exception and throws another, custom exception. So I'll only * assert that ANY exception is thrown. * **************************************************************************************** */ - var exceptionTask = Record.ExceptionAsync(async () => + var thrownException = await Record.ExceptionAsync(async () => { var response = await sg.SendEmailAsync(msg); }); - Assert.NotNull(exceptionTask); - - var thrownException = exceptionTask.Result; Assert.NotNull(thrownException); // If we are certain that we don't want custom exceptions to be thrown, @@ -6090,8 +6087,6 @@ public async Task TestRetryBehaviourThrowsTimeoutException() Host = "http://localhost:4010" }; - var id = "test_url_param"; - var retryHandler = new RetryDelegatingHandler(new HttpClientHandler(), options.ReliabilitySettings); HttpClient clientToInject = new HttpClient(retryHandler) { Timeout = TimeSpan.FromMilliseconds(1) }; @@ -6117,8 +6112,6 @@ public async Task TestRetryBehaviourSucceedsOnSecondAttempt() ReliabilitySettings = new ReliabilitySettings(1, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(1)) }; - var id = "test_url_param"; - var httpMessageHandler = new RetryTestBehaviourDelegatingHandler(); httpMessageHandler.AddBehaviour(httpMessageHandler.TaskCancelled); httpMessageHandler.AddBehaviour(httpMessageHandler.OK); diff --git a/tests/SendGrid.Tests/LicenseTests.cs b/tests/SendGrid.Tests/LicenseTests.cs new file mode 100644 index 000000000..4955da33b --- /dev/null +++ b/tests/SendGrid.Tests/LicenseTests.cs @@ -0,0 +1,19 @@ +namespace SendGrid.Tests +{ + using System; + using System.IO; + using System.Linq; + using System.Reflection; + using Xunit; + + public class LicenseTests + { + [Fact] + public void ShouldHaveCurrentYearInLicense() + { + var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory()); + var line = File.ReadLines(Path.Combine(directoryInfo.Parent.Parent.Parent.Parent.Parent.FullName, "LICENSE.txt")).Skip(2).Take(1).First(); + Assert.Contains(DateTime.Now.Year.ToString(), line); + } + } +} \ No newline at end of file