From 309f3fbb19fd6212acbe3a00ed9f0e6f57bd7c9c Mon Sep 17 00:00:00 2001 From: aevitas Date: Thu, 4 Jul 2019 14:56:56 +0200 Subject: [PATCH 1/7] Add support for ASP.NET Core DI --- src/SendGrid/SendGrid.csproj | 139 ++++++++++++++++++--------------- src/SendGrid/SendGridClient.cs | 10 +++ 2 files changed, 84 insertions(+), 65 deletions(-) diff --git a/src/SendGrid/SendGrid.csproj b/src/SendGrid/SendGrid.csproj index 38d1ae4e3..67b64f62b 100644 --- a/src/SendGrid/SendGrid.csproj +++ b/src/SendGrid/SendGrid.csproj @@ -1,65 +1,74 @@ - - - - 9.11.0 - netstandard1.3;netstandard2.0;net452 - anycpu - true - Library - ../../tools/sendgrid-csharp.snk - true - true - true - full - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb;.xml - - - - SendGrid - Elmer Thomas;Twilio DX Team - Twilio SendGrid - Twilio SendGrid - MIT - https://sendgrid.com/ - https://github.com/sendgrid/sendgrid-csharp.git - git - https://sendgrid.com/wp-content/themes/sgdotcom/pages/resource/brand//2016/SendGrid-Logomark.png - C# client library and examples for using Twilio SendGrid API's to send mail and access Web API v3 endpoints with .NET Standard 1.3 and .NET Core support. - Please see: https://github.com/sendgrid/sendgrid-csharp/releases - Twilio;SendGrid;Email;Mail;Microsoft;Azure;Transactional;.NET Core - Twilio SendGrid, Inc. 2019 - - - - - <_Parameter1>Twilio SendGrid - - - - - - - - - - All - - - - - - - - - - - - - - - - - - - - + + + + 9.11.0 + netstandard1.3;netstandard2.0;net452 + anycpu + true + Library + ../../tools/sendgrid-csharp.snk + true + true + true + full + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb;.xml + + + + SendGrid + Elmer Thomas;Twilio DX Team + Twilio SendGrid + Twilio SendGrid + MIT + https://sendgrid.com/ + https://github.com/sendgrid/sendgrid-csharp.git + git + https://sendgrid.com/wp-content/themes/sgdotcom/pages/resource/brand//2016/SendGrid-Logomark.png + C# client library and examples for using Twilio SendGrid API's to send mail and access Web API v3 endpoints with .NET Standard 1.3 and .NET Core support. + Please see: https://github.com/sendgrid/sendgrid-csharp/releases + Twilio;SendGrid;Email;Mail;Microsoft;Azure;Transactional;.NET Core + Twilio SendGrid, Inc. 2019 + + + + + <_Parameter1>Twilio SendGrid + + + + + + + + + + All + + + + + + + 1.1.2 + + + + + + + 2.2.0 + + + + + + + + + + + 1.1.2 + + + + diff --git a/src/SendGrid/SendGridClient.cs b/src/SendGrid/SendGridClient.cs index f8cc6e8be..7998c0ae3 100644 --- a/src/SendGrid/SendGridClient.cs +++ b/src/SendGrid/SendGridClient.cs @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using Microsoft.Extensions.Options; using Newtonsoft.Json; using SendGrid.Helpers.Mail; using SendGrid.Helpers.Reliability; @@ -73,6 +74,15 @@ public SendGridClient(SendGridClientOptions options) { } + /// + /// Initializes a new instance of the class. + /// + /// An instance specifying the configuration to be used with the client. + public SendGridClient(IOptions options) + : this(options.Value) + { + } + /// /// Initializes a new instance of the class. /// From 834eb14d08e598f435f40e8fbbe7b12e7db0e0c2 Mon Sep 17 00:00:00 2001 From: aevitas Date: Thu, 4 Jul 2019 16:00:37 +0200 Subject: [PATCH 2/7] Add ASP.NET Core example project and fix up SendGridClient for DI --- .../Controllers/EmailController.cs | 48 ++++++++ .../ExampleAspNetCoreProject.csproj | 18 +++ .../Models/SendEmailModel.cs | 21 ++++ ExampleAspNetCoreProject/Program.cs | 17 +++ .../Properties/launchSettings.json | 28 +++++ ExampleAspNetCoreProject/Startup.cs | 44 +++++++ .../appsettings.Development.json | 9 ++ ExampleAspNetCoreProject/appsettings.json | 14 +++ SendGrid.sln | 114 ++++++++++-------- src/SendGrid/SendGridClient.cs | 83 ++++++++++--- 10 files changed, 326 insertions(+), 70 deletions(-) create mode 100644 ExampleAspNetCoreProject/Controllers/EmailController.cs create mode 100644 ExampleAspNetCoreProject/ExampleAspNetCoreProject.csproj create mode 100644 ExampleAspNetCoreProject/Models/SendEmailModel.cs create mode 100644 ExampleAspNetCoreProject/Program.cs create mode 100644 ExampleAspNetCoreProject/Properties/launchSettings.json create mode 100644 ExampleAspNetCoreProject/Startup.cs create mode 100644 ExampleAspNetCoreProject/appsettings.Development.json create mode 100644 ExampleAspNetCoreProject/appsettings.json diff --git a/ExampleAspNetCoreProject/Controllers/EmailController.cs b/ExampleAspNetCoreProject/Controllers/EmailController.cs new file mode 100644 index 000000000..4c7013bca --- /dev/null +++ b/ExampleAspNetCoreProject/Controllers/EmailController.cs @@ -0,0 +1,48 @@ +using System; +using System.Threading.Tasks; +using ExampleAspNetCoreProject.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using SendGrid; +using SendGrid.Helpers.Mail; + +namespace ExampleAspNetCoreProject.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class EmailController : ControllerBase + { + private readonly ILogger logger; + private readonly ISendGridClient client; + + public EmailController(ILogger logger, ISendGridClient client) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.client = client ?? throw new ArgumentNullException(nameof(client)); + } + + [HttpPost("send")] + public async Task SendEmail([FromBody] SendEmailModel model) + { + if (!ModelState.IsValid) + return BadRequest(ModelState); + + var message = new SendGridMessage + { + HtmlContent = model.Content, + Subject = model.Subject, + From = new EmailAddress(model.From) + }; + + message.AddTo(model.Recipient); + + logger.LogInformation($"Sending email to {model.Recipient} with subject {model.Subject}."); + + var response = await client.SendEmailAsync(message); + + logger.LogInformation($"SendGrid responded with status code: {response.StatusCode}"); + + return StatusCode((int) response.StatusCode); + } + } +} diff --git a/ExampleAspNetCoreProject/ExampleAspNetCoreProject.csproj b/ExampleAspNetCoreProject/ExampleAspNetCoreProject.csproj new file mode 100644 index 000000000..193dc3950 --- /dev/null +++ b/ExampleAspNetCoreProject/ExampleAspNetCoreProject.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp2.2 + InProcess + adf5331b-3d39-4f3d-94f2-e5808d7e3a7a + + + + + + + + + + + + diff --git a/ExampleAspNetCoreProject/Models/SendEmailModel.cs b/ExampleAspNetCoreProject/Models/SendEmailModel.cs new file mode 100644 index 000000000..701c8f796 --- /dev/null +++ b/ExampleAspNetCoreProject/Models/SendEmailModel.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace ExampleAspNetCoreProject.Models +{ + public class SendEmailModel + { + [Required] + [EmailAddress] + public string Recipient { get; set; } + + [Required] + [EmailAddress] + public string From { get; set; } + + [Required] + public string Subject { get; set; } + + [Required] + public string Content { get; set; } + } +} diff --git a/ExampleAspNetCoreProject/Program.cs b/ExampleAspNetCoreProject/Program.cs new file mode 100644 index 000000000..780e8bc23 --- /dev/null +++ b/ExampleAspNetCoreProject/Program.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace ExampleAspNetCoreProject +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/ExampleAspNetCoreProject/Properties/launchSettings.json b/ExampleAspNetCoreProject/Properties/launchSettings.json new file mode 100644 index 000000000..304794289 --- /dev/null +++ b/ExampleAspNetCoreProject/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52446", + "sslPort": 44352 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "ExampleAspNetCoreProject": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/ExampleAspNetCoreProject/Startup.cs b/ExampleAspNetCoreProject/Startup.cs new file mode 100644 index 000000000..bedbf3644 --- /dev/null +++ b/ExampleAspNetCoreProject/Startup.cs @@ -0,0 +1,44 @@ +using System.Net.Http; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using SendGrid; + +namespace ExampleAspNetCoreProject +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + // Configure the SendGrid client based on the values specified in the application settings. + // We then register the Client and its options with the DI container, passing a singleton HttpClient instance + // to be used for making requests to the API. This allows us to inject the client into our controllers. + services.Configure(Configuration.GetSection("SendGrid")); + + services.AddSingleton(new HttpClient()); + services.AddSingleton(); + + services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + app.UseMvc(); + } + } +} diff --git a/ExampleAspNetCoreProject/appsettings.Development.json b/ExampleAspNetCoreProject/appsettings.Development.json new file mode 100644 index 000000000..a2880cbf1 --- /dev/null +++ b/ExampleAspNetCoreProject/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/ExampleAspNetCoreProject/appsettings.json b/ExampleAspNetCoreProject/appsettings.json new file mode 100644 index 000000000..2f211bc5f --- /dev/null +++ b/ExampleAspNetCoreProject/appsettings.json @@ -0,0 +1,14 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "SendGrid": { + "ApiKey": "", + "Host": "", + "Version": "", + "UrlPath": "" + } +} diff --git a/SendGrid.sln b/SendGrid.sln index 135014b58..1ee7ffb10 100644 --- a/SendGrid.sln +++ b/SendGrid.sln @@ -1,52 +1,62 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.12 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{71C09624-020B-410E-A8FE-1FD216A1FE31}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D06BDAE9-BE83-448F-8AD4-3044BB187C11}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Example Projects", "Example Projects", "{D3201F71-A289-4136-AC7E-E5204ACB9183}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SendGrid", "src\SendGrid\SendGrid.csproj", "{377C20E4-2297-488F-933B-FB635C56D8FC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SendGrid.Tests", "tests\SendGrid.Tests\SendGrid.Tests.csproj", "{D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleCoreProject", "ExampleCoreProject\ExampleCoreProject.csproj", "{4BD07A97-8AD2-4134-848E-6A74EB992050}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleNet45", "ExampleNet45Project\ExampleNet45.csproj", "{3B3F2699-F720-4498-8044-262EFE110A22}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {377C20E4-2297-488F-933B-FB635C56D8FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {377C20E4-2297-488F-933B-FB635C56D8FC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {377C20E4-2297-488F-933B-FB635C56D8FC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {377C20E4-2297-488F-933B-FB635C56D8FC}.Release|Any CPU.Build.0 = Release|Any CPU - {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Release|Any CPU.Build.0 = Release|Any CPU - {4BD07A97-8AD2-4134-848E-6A74EB992050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4BD07A97-8AD2-4134-848E-6A74EB992050}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4BD07A97-8AD2-4134-848E-6A74EB992050}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4BD07A97-8AD2-4134-848E-6A74EB992050}.Release|Any CPU.Build.0 = Release|Any CPU - {3B3F2699-F720-4498-8044-262EFE110A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3B3F2699-F720-4498-8044-262EFE110A22}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3B3F2699-F720-4498-8044-262EFE110A22}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3B3F2699-F720-4498-8044-262EFE110A22}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {377C20E4-2297-488F-933B-FB635C56D8FC} = {71C09624-020B-410E-A8FE-1FD216A1FE31} - {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3} = {D06BDAE9-BE83-448F-8AD4-3044BB187C11} - {4BD07A97-8AD2-4134-848E-6A74EB992050} = {D3201F71-A289-4136-AC7E-E5204ACB9183} - {3B3F2699-F720-4498-8044-262EFE110A22} = {D3201F71-A289-4136-AC7E-E5204ACB9183} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29025.244 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{71C09624-020B-410E-A8FE-1FD216A1FE31}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D06BDAE9-BE83-448F-8AD4-3044BB187C11}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Example Projects", "Example Projects", "{D3201F71-A289-4136-AC7E-E5204ACB9183}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SendGrid", "src\SendGrid\SendGrid.csproj", "{377C20E4-2297-488F-933B-FB635C56D8FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SendGrid.Tests", "tests\SendGrid.Tests\SendGrid.Tests.csproj", "{D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExampleCoreProject", "ExampleCoreProject\ExampleCoreProject.csproj", "{4BD07A97-8AD2-4134-848E-6A74EB992050}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleNet45", "ExampleNet45Project\ExampleNet45.csproj", "{3B3F2699-F720-4498-8044-262EFE110A22}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleAspNetCoreProject", "ExampleAspNetCoreProject\ExampleAspNetCoreProject.csproj", "{7C3D92E0-86DE-46A6-BE16-1E798DF728C8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {377C20E4-2297-488F-933B-FB635C56D8FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {377C20E4-2297-488F-933B-FB635C56D8FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {377C20E4-2297-488F-933B-FB635C56D8FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {377C20E4-2297-488F-933B-FB635C56D8FC}.Release|Any CPU.Build.0 = Release|Any CPU + {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3}.Release|Any CPU.Build.0 = Release|Any CPU + {4BD07A97-8AD2-4134-848E-6A74EB992050}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BD07A97-8AD2-4134-848E-6A74EB992050}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BD07A97-8AD2-4134-848E-6A74EB992050}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BD07A97-8AD2-4134-848E-6A74EB992050}.Release|Any CPU.Build.0 = Release|Any CPU + {3B3F2699-F720-4498-8044-262EFE110A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B3F2699-F720-4498-8044-262EFE110A22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B3F2699-F720-4498-8044-262EFE110A22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B3F2699-F720-4498-8044-262EFE110A22}.Release|Any CPU.Build.0 = Release|Any CPU + {7C3D92E0-86DE-46A6-BE16-1E798DF728C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7C3D92E0-86DE-46A6-BE16-1E798DF728C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7C3D92E0-86DE-46A6-BE16-1E798DF728C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7C3D92E0-86DE-46A6-BE16-1E798DF728C8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {377C20E4-2297-488F-933B-FB635C56D8FC} = {71C09624-020B-410E-A8FE-1FD216A1FE31} + {D89ADAEA-2BE8-49AC-B5BC-6EABBB2AE4E3} = {D06BDAE9-BE83-448F-8AD4-3044BB187C11} + {4BD07A97-8AD2-4134-848E-6A74EB992050} = {D3201F71-A289-4136-AC7E-E5204ACB9183} + {3B3F2699-F720-4498-8044-262EFE110A22} = {D3201F71-A289-4136-AC7E-E5204ACB9183} + {7C3D92E0-86DE-46A6-BE16-1E798DF728C8} = {D3201F71-A289-4136-AC7E-E5204ACB9183} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {12F463FA-F03B-48C9-8F64-5BED32F8D5D0} + EndGlobalSection +EndGlobal diff --git a/src/SendGrid/SendGridClient.cs b/src/SendGrid/SendGridClient.cs index 7998c0ae3..7471dc676 100644 --- a/src/SendGrid/SendGridClient.cs +++ b/src/SendGrid/SendGridClient.cs @@ -42,12 +42,12 @@ public class SendGridClient : ISendGridClient /// /// The configuration to use with current instance /// - private readonly SendGridClientOptions options; + private SendGridClientOptions options; /// /// The HttpClient instance to use for all calls from this SendGridClient instance. /// - private readonly HttpClient client; + private HttpClient client; /// /// Initializes a new instance of the class. @@ -80,7 +80,7 @@ public SendGridClient(SendGridClientOptions options) /// An instance specifying the configuration to be used with the client. public SendGridClient(IOptions options) : this(options.Value) - { + { } /// @@ -112,6 +112,21 @@ public SendGridClient(string apiKey, string host = null, Dictionary + /// Initializes a new instance of the class. + /// + /// The HTTP Client used to send requests to the SendGrid API. + /// An instance specifying the configuration to be used with the client. + public SendGridClient(HttpClient httpClient, IOptions options) + { + if (options?.Value == null) + { + throw new ArgumentNullException(nameof(options)); + } + + this.InitializeClient(httpClient, options.Value); + } + /// /// Initializes a new instance of the class. /// @@ -120,12 +135,12 @@ public SendGridClient(string apiKey, string host = null, DictionaryInterface to the Twilio SendGrid REST API internal SendGridClient(HttpClient httpClient, SendGridClientOptions options) { - this.options = options ?? throw new ArgumentNullException(nameof(options)); - this.client = httpClient ?? CreateHttpClientWithRetryHandler(); - if (this.options.RequestHeaders != null && this.options.RequestHeaders.TryGetValue(ContentType, out var contentType)) + if (options == null) { - this.MediaType = contentType; + throw new ArgumentNullException(nameof(options)); } + + this.InitializeClient(httpClient, options); } /// @@ -371,15 +386,15 @@ private Dictionary> ParseJson(string json) switch (reader.TokenType) { case JsonToken.PropertyName: - { - propertyName = reader.Value.ToString(); - if (!dict.ContainsKey(propertyName)) { - dict.Add(propertyName, new List()); - } + propertyName = reader.Value.ToString(); + if (!dict.ContainsKey(propertyName)) + { + dict.Add(propertyName, new List()); + } - break; - } + break; + } case JsonToken.Boolean: case JsonToken.Integer: @@ -387,15 +402,47 @@ private Dictionary> ParseJson(string json) case JsonToken.Bytes: case JsonToken.String: case JsonToken.Date: - { - dict[propertyName].Add(reader.Value); - break; - } + { + dict[propertyName].Add(reader.Value); + break; + } } } } return dict; } + + private void InitializeClient(HttpClient httpClient, SendGridClientOptions clientOptions) + { + if (clientOptions == null) + { + throw new ArgumentNullException(nameof(clientOptions)); + } + + // When using ASP.NET's Configuration subsystem, leaving settings empty can cause essential client options to be unspecified. + // Therefore, we ensure the configuration contains the proper defaults here, while still allowing users to override them in the settings. + if (clientOptions.RequestHeaders == null) + { + clientOptions.RequestHeaders = new Dictionary(); + } + + if (string.IsNullOrWhiteSpace(clientOptions.Host)) + { + clientOptions.Host = "https://api.sendgrid.com"; + } + + if (string.IsNullOrWhiteSpace(clientOptions.Version)) + { + clientOptions.Version = "v3"; + } + + this.options = clientOptions; + this.client = httpClient ?? CreateHttpClientWithRetryHandler(); + if (this.options.RequestHeaders != null && this.options.RequestHeaders.TryGetValue(ContentType, out var contentType)) + { + this.MediaType = contentType; + } + } } } From 4b9de2d5dfd741eba5c62c4d8235a11bb2f434c0 Mon Sep 17 00:00:00 2001 From: aevitas Date: Thu, 4 Jul 2019 16:05:08 +0200 Subject: [PATCH 3/7] Add Extensions.Options to .NET 4.5 example project --- ExampleNet45Project/ExampleNet45.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ExampleNet45Project/ExampleNet45.csproj b/ExampleNet45Project/ExampleNet45.csproj index 024746f96..a969a2fc6 100644 --- a/ExampleNet45Project/ExampleNet45.csproj +++ b/ExampleNet45Project/ExampleNet45.csproj @@ -47,6 +47,9 @@ + + 1.1.2 + From 7231c4074edc1da83d2c0067eccb4b70f09353bd Mon Sep 17 00:00:00 2001 From: aevitas Date: Thu, 4 Jul 2019 22:52:52 +0200 Subject: [PATCH 4/7] Remove redundant settings checks and InitializeClient method --- ExampleAspNetCoreProject/appsettings.json | 5 +-- src/SendGrid/SendGridClient.cs | 47 +++------------------- tests/SendGrid.Tests/SendGrid.Tests.csproj | 4 +- 3 files changed, 8 insertions(+), 48 deletions(-) diff --git a/ExampleAspNetCoreProject/appsettings.json b/ExampleAspNetCoreProject/appsettings.json index 2f211bc5f..168e93f94 100644 --- a/ExampleAspNetCoreProject/appsettings.json +++ b/ExampleAspNetCoreProject/appsettings.json @@ -6,9 +6,6 @@ }, "AllowedHosts": "*", "SendGrid": { - "ApiKey": "", - "Host": "", - "Version": "", - "UrlPath": "" + "ApiKey": "" } } diff --git a/src/SendGrid/SendGridClient.cs b/src/SendGrid/SendGridClient.cs index 7471dc676..59fc2c10f 100644 --- a/src/SendGrid/SendGridClient.cs +++ b/src/SendGrid/SendGridClient.cs @@ -118,13 +118,8 @@ public SendGridClient(string apiKey, string host = null, DictionaryThe HTTP Client used to send requests to the SendGrid API. /// An instance specifying the configuration to be used with the client. public SendGridClient(HttpClient httpClient, IOptions options) + : this(httpClient, options?.Value ?? throw new ArgumentNullException(nameof(options))) { - if (options?.Value == null) - { - throw new ArgumentNullException(nameof(options)); - } - - this.InitializeClient(httpClient, options.Value); } /// @@ -135,12 +130,12 @@ public SendGridClient(HttpClient httpClient, IOptions opt /// Interface to the Twilio SendGrid REST API internal SendGridClient(HttpClient httpClient, SendGridClientOptions options) { - if (options == null) + this.options = options; + this.client = httpClient ?? CreateHttpClientWithRetryHandler(); + if (this.options.RequestHeaders != null && this.options.RequestHeaders.TryGetValue(ContentType, out var contentType)) { - throw new ArgumentNullException(nameof(options)); + this.MediaType = contentType; } - - this.InitializeClient(httpClient, options); } /// @@ -412,37 +407,5 @@ private Dictionary> ParseJson(string json) return dict; } - - private void InitializeClient(HttpClient httpClient, SendGridClientOptions clientOptions) - { - if (clientOptions == null) - { - throw new ArgumentNullException(nameof(clientOptions)); - } - - // When using ASP.NET's Configuration subsystem, leaving settings empty can cause essential client options to be unspecified. - // Therefore, we ensure the configuration contains the proper defaults here, while still allowing users to override them in the settings. - if (clientOptions.RequestHeaders == null) - { - clientOptions.RequestHeaders = new Dictionary(); - } - - if (string.IsNullOrWhiteSpace(clientOptions.Host)) - { - clientOptions.Host = "https://api.sendgrid.com"; - } - - if (string.IsNullOrWhiteSpace(clientOptions.Version)) - { - clientOptions.Version = "v3"; - } - - this.options = clientOptions; - this.client = httpClient ?? CreateHttpClientWithRetryHandler(); - if (this.options.RequestHeaders != null && this.options.RequestHeaders.TryGetValue(ContentType, out var contentType)) - { - this.MediaType = contentType; - } - } } } diff --git a/tests/SendGrid.Tests/SendGrid.Tests.csproj b/tests/SendGrid.Tests/SendGrid.Tests.csproj index b76f91437..c94320cf4 100644 --- a/tests/SendGrid.Tests/SendGrid.Tests.csproj +++ b/tests/SendGrid.Tests/SendGrid.Tests.csproj @@ -1,11 +1,11 @@  - netcoreapp1.0 + netcoreapp2.2 SendGrid.Tests SendGrid.Tests true - 1.0.4 + 2.2.0 false false false From c7393ae9273db949921b08c57e9066d402b430bf Mon Sep 17 00:00:00 2001 From: aevitas Date: Thu, 4 Jul 2019 22:56:26 +0200 Subject: [PATCH 5/7] Revert accidental runtime bump for tests --- tests/SendGrid.Tests/SendGrid.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/SendGrid.Tests/SendGrid.Tests.csproj b/tests/SendGrid.Tests/SendGrid.Tests.csproj index c94320cf4..b76f91437 100644 --- a/tests/SendGrid.Tests/SendGrid.Tests.csproj +++ b/tests/SendGrid.Tests/SendGrid.Tests.csproj @@ -1,11 +1,11 @@  - netcoreapp2.2 + netcoreapp1.0 SendGrid.Tests SendGrid.Tests true - 2.2.0 + 1.0.4 false false false From 945ca3d70309648fd9b2aa7bf8aa9273d5243477 Mon Sep 17 00:00:00 2001 From: aevitas Date: Fri, 5 Jul 2019 12:44:50 +0200 Subject: [PATCH 6/7] Mark options and client as readonly again --- src/SendGrid/SendGridClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SendGrid/SendGridClient.cs b/src/SendGrid/SendGridClient.cs index 59fc2c10f..57a88ca17 100644 --- a/src/SendGrid/SendGridClient.cs +++ b/src/SendGrid/SendGridClient.cs @@ -42,12 +42,12 @@ public class SendGridClient : ISendGridClient /// /// The configuration to use with current instance /// - private SendGridClientOptions options; + private readonly SendGridClientOptions options; /// /// The HttpClient instance to use for all calls from this SendGridClient instance. /// - private HttpClient client; + private readonly HttpClient client; /// /// Initializes a new instance of the class. From 20c7392d74dcc967200e63949d0d246a33bd6b32 Mon Sep 17 00:00:00 2001 From: aevitas Date: Fri, 5 Jul 2019 17:24:50 +0200 Subject: [PATCH 7/7] Check for null options only once --- src/SendGrid/SendGridClient.cs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/SendGrid/SendGridClient.cs b/src/SendGrid/SendGridClient.cs index 57a88ca17..88e9ee0d3 100644 --- a/src/SendGrid/SendGridClient.cs +++ b/src/SendGrid/SendGridClient.cs @@ -79,7 +79,7 @@ public SendGridClient(SendGridClientOptions options) /// /// An instance specifying the configuration to be used with the client. public SendGridClient(IOptions options) - : this(options.Value) + : this(options?.Value) { } @@ -118,24 +118,24 @@ public SendGridClient(string apiKey, string host = null, DictionaryThe HTTP Client used to send requests to the SendGrid API. /// An instance specifying the configuration to be used with the client. public SendGridClient(HttpClient httpClient, IOptions options) - : this(httpClient, options?.Value ?? throw new ArgumentNullException(nameof(options))) + : this(httpClient, options?.Value) { - } - + } + /// /// 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 Twilio SendGrid REST API - internal SendGridClient(HttpClient httpClient, SendGridClientOptions options) - { - this.options = options; - this.client = httpClient ?? CreateHttpClientWithRetryHandler(); - if (this.options.RequestHeaders != null && this.options.RequestHeaders.TryGetValue(ContentType, out var contentType)) - { - this.MediaType = contentType; - } + internal SendGridClient(HttpClient httpClient, SendGridClientOptions options) + { + this.options = options ?? throw new ArgumentNullException(nameof(options)); + this.client = httpClient ?? CreateHttpClientWithRetryHandler(); + if (this.options.RequestHeaders != null && this.options.RequestHeaders.TryGetValue(ContentType, out var contentType)) + { + this.MediaType = contentType; + } } ///