diff --git a/Dockerfile b/Dockerfile
index a9ee12341..005486060 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,18 +1,10 @@
-FROM mono:latest
+FROM mcr.microsoft.com/dotnet/sdk:3.1
-ENV FrameworkPathOverride /usr/lib/mono/4.5/
-
-RUN apt-get update \
- && apt-get install -y curl make apt-transport-https
-
-RUN curl -sSL https://packages.microsoft.com/config/ubuntu/19.10/packages-microsoft-prod.deb -o packages-microsoft-prod.deb \
- && dpkg --install packages-microsoft-prod.deb
+COPY prism/prism/nginx/cert.crt /usr/local/share/ca-certificates/cert.crt
RUN apt-get update \
- && apt-get install -y dotnet-sdk-2.1
-
-COPY prism/prism/nginx/cert.crt /usr/local/share/ca-certificates/cert.crt
-RUN update-ca-certificates
+ && apt-get install -y make apt-transport-https \
+ && update-ca-certificates
COPY . .
diff --git a/ExampleCoreProject/ExampleCoreProject.csproj b/ExampleCoreProject/ExampleCoreProject.csproj
index 0d5601d03..42acb21cb 100644
--- a/ExampleCoreProject/ExampleCoreProject.csproj
+++ b/ExampleCoreProject/ExampleCoreProject.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp3.1
Exe
false
@@ -16,9 +16,7 @@
-
-
-
+
diff --git a/ExampleNet45Project/ExampleNet45.csproj b/ExampleNet45Project/ExampleNet45.csproj
index e2bdaec5e..3db276096 100644
--- a/ExampleNet45Project/ExampleNet45.csproj
+++ b/ExampleNet45Project/ExampleNet45.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/examples/eventwebhook/consumer/Dockerfile b/examples/eventwebhook/consumer/Dockerfile
index bebfb684b..2ffcecb17 100644
--- a/examples/eventwebhook/consumer/Dockerfile
+++ b/examples/eventwebhook/consumer/Dockerfile
@@ -1,21 +1,21 @@
-FROM microsoft/dotnet:2.1-sdk AS build
-WORKDIR /App
+FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build
+WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.sln .
-COPY Src/EventWebhook/*.csproj ./Src/EventWebhook/
-COPY Tests/EventWebhook.Tests/*.csproj ./Tests/EventWebhook.Tests/
+COPY src/EventWebhook/*.csproj ./src/EventWebhook/
+COPY tests/EventWebhook.Tests/*.csproj ./tests/EventWebhook.Tests/
RUN dotnet restore
# copy everything else and build app
-COPY Src/EventWebhook/. ./Src/EventWebhook/
-WORKDIR /App/Src/EventWebhook
-RUN dotnet publish -c Release -o Out
+COPY src/EventWebhook/. ./src/EventWebhook/
+WORKDIR /app/src/EventWebhook
+RUN dotnet publish -c Release -o out
-FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
-WORKDIR /App
-COPY --from=build /App/Src/EventWebhook/Out ./
+FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS runtime
+WORKDIR /app
+COPY --from=build /app/src/EventWebhook/out ./
-RUN echo "ASPNETCORE_URLS=http://0.0.0.0:\$PORT\nDOTNET_RUNNING_IN_CONTAINER=true" > /App/SetupHerokuEnv.sh && chmod +x /App/SetupHerokuEnv.sh
+RUN echo "ASPNETCORE_URLS=http://0.0.0.0:\$PORT\nDOTNET_RUNNING_IN_CONTAINER=true" > /app/SetupHerokuEnv.sh && chmod +x /app/SetupHerokuEnv.sh
-CMD /bin/bash -c "source /App/SetupHerokuEnv.sh && dotnet EventWebhook.dll"
\ No newline at end of file
+CMD /bin/bash -c "source /app/SetupHerokuEnv.sh && dotnet EventWebhook.dll"
\ No newline at end of file
diff --git a/examples/eventwebhook/consumer/README.md b/examples/eventwebhook/consumer/README.md
index 9ec23f614..e089e8d52 100644
--- a/examples/eventwebhook/consumer/README.md
+++ b/examples/eventwebhook/consumer/README.md
@@ -42,7 +42,7 @@ cd sendgrid-csharp/examples/eventwebhook/consumer
dotnet restore
-dotnet run --project .\Src\EventWebhook\EventWebhook.csproj
+dotnet run --project .\src\EventWebhook\EventWebhook.csproj
```
Above will start server listening on a random port like below
diff --git a/examples/eventwebhook/consumer/SendGridEventWebhookConsumer.sln b/examples/eventwebhook/consumer/SendGridEventWebhookConsumer.sln
index edc1dedce..00eb188e6 100644
--- a/examples/eventwebhook/consumer/SendGridEventWebhookConsumer.sln
+++ b/examples/eventwebhook/consumer/SendGridEventWebhookConsumer.sln
@@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{B08185CF-3F2E-4638-877B-587F5F60CA74}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B08185CF-3F2E-4638-877B-587F5F60CA74}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventWebhook", "Src\EventWebhook\EventWebhook.csproj", "{3897C29A-AE26-4FE5-8421-71896EC935C5}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventWebhook", "src\EventWebhook\EventWebhook.csproj", "{3897C29A-AE26-4FE5-8421-71896EC935C5}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{81CAC535-9854-47AD-9D3E-385AC2668C35}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{81CAC535-9854-47AD-9D3E-385AC2668C35}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventWebhook.Tests", "Tests\EventWebhook.Tests\EventWebhook.Tests.csproj", "{5C6AA0EB-57B7-4E76-804A-70F7A7DF4FC0}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventWebhook.Tests", "tests\EventWebhook.Tests\EventWebhook.Tests.csproj", "{5C6AA0EB-57B7-4E76-804A-70F7A7DF4FC0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Controllers/EventWebhookController.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Controllers/EventWebhookController.cs
deleted file mode 100644
index ea544a0f2..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Controllers/EventWebhookController.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using EventWebhook.Models;
-using EventWebhook.Parser;
-using Microsoft.AspNetCore.Mvc;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace EventWebhook.Controllers
-{
- [Route("/")]
- public class EventWebhookController : Controller
- {
- ///
- /// GET : Index page
- ///
- [Route("")]
- public IActionResult Index()
- {
- return View();
- }
-
- ///
- /// POST : Event webhook handler
- ///
- ///
- [Route("/events")]
- [HttpPost]
- public async Task Events()
- {
- IEnumerable events = await EventParser.ParseAsync(Request.Body);
-
- return Ok();
- }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Converters/CategoryConverter.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Converters/CategoryConverter.cs
deleted file mode 100644
index 966dc2af6..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Converters/CategoryConverter.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using EventWebhook.Models;
-using Newtonsoft.Json;
-using System;
-using System.Linq;
-
-namespace EventWebhook.Converters
-{
- public class CategoryConverter : JsonConverter
- {
- private readonly Type[] _types;
-
- public CategoryConverter()
- {
- _types = new Type[] { typeof(string), typeof(string[]) };
- }
-
- public override bool CanConvert(Type objectType)
- {
- return _types.Any(t => t == objectType);
- }
-
- public override bool CanWrite => true;
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.TokenType == JsonToken.StartArray)
- {
- return new Category(serializer.Deserialize(reader), JsonToken.StartArray);
- }
- else
- {
- return new Category(new[] { serializer.Deserialize(reader) }, reader.TokenType);
- }
- }
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- if (value is Category category)
- {
- if (category.IsArray)
- {
- serializer.Serialize(writer, category);
- } else
- {
- serializer.Serialize(writer, category.Value[0]);
- }
- }
- }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Converters/UriConverter.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Converters/UriConverter.cs
deleted file mode 100644
index 06c55f1e8..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Converters/UriConverter.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Newtonsoft.Json;
-using System;
-
-namespace EventWebhook.Converters
-{
- public class UriConverter : JsonConverter
- {
- public override bool CanConvert(Type objectType) => objectType == typeof(string);
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- if (reader.TokenType == JsonToken.Null)
- {
- return null;
- }
-
- if (reader.TokenType == JsonToken.String)
- {
- return new Uri((string)reader.Value);
- }
-
- throw new InvalidOperationException("Invalid Url");
- }
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- if (null == value)
- {
- writer.WriteNull();
- return;
- }
-
- if (value is Uri)
- {
- writer.WriteValue(((Uri)value).OriginalString);
- return;
- }
- }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/EventWebhook.csproj b/examples/eventwebhook/consumer/Src/EventWebhook/EventWebhook.csproj
deleted file mode 100644
index 1318472b7..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/EventWebhook.csproj
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- netcoreapp2.1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/BounceEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/BounceEvent.cs
deleted file mode 100644
index 484c8911b..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/BounceEvent.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
-
-namespace EventWebhook.Models
-{
- public class BounceEvent : DroppedEvent
- {
- [JsonConverter(typeof(StringEnumConverter))]
- public BounceEventType BounceType { get; set; }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/Category.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/Category.cs
deleted file mode 100644
index b6762e74d..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/Category.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using Newtonsoft.Json;
-
-namespace EventWebhook.Models
-{
- public class Category
- {
- public string[] Value { get; }
- private JsonToken _jsonToken;
-
- public Category(string[] value, JsonToken jsonToken)
- {
- Value = value;
- _jsonToken = jsonToken;
- }
-
- [JsonIgnore]
- public bool IsArray => _jsonToken == JsonToken.StartArray;
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/ClickEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/ClickEvent.cs
deleted file mode 100644
index 94a8be67c..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/ClickEvent.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using EventWebhook.Converters;
-using Newtonsoft.Json;
-using System;
-
-namespace EventWebhook.Models
-{
- public class ClickEvent : OpenEvent
- {
- [JsonConverter(typeof(UriConverter))]
- public Uri Url { get; set; }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/DeferredEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/DeferredEvent.cs
deleted file mode 100644
index 2704527a7..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/DeferredEvent.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace EventWebhook.Models
-{
- public class DeferredEvent : DeliveredEvent
- {
- public int Attempt { get; set; }
-
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/GroupResubscribeEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/GroupResubscribeEvent.cs
deleted file mode 100644
index 4034c5384..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/GroupResubscribeEvent.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace EventWebhook.Models
-{
- public class GroupResubscribeEvent : GroupUnsubscribeEvent { }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/GroupUnsubscribeEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/GroupUnsubscribeEvent.cs
deleted file mode 100644
index 4763182f2..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/GroupUnsubscribeEvent.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Newtonsoft.Json;
-
-namespace EventWebhook.Models
-{
- public class GroupUnsubscribeEvent : ClickEvent
- {
- [JsonProperty("asm_group_id")]
- public int AsmGroupId { get; set; }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/SpamReportEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/SpamReportEvent.cs
deleted file mode 100644
index 0f1b6c738..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/SpamReportEvent.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace EventWebhook.Models
-{
- public class SpamReportEvent : Event { }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Models/UnsubscribeEvent.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Models/UnsubscribeEvent.cs
deleted file mode 100644
index 350dbf09e..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Models/UnsubscribeEvent.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace EventWebhook.Models
-{
- public class UnsubscribeEvent : Event { }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Parser/EventConverter.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Parser/EventConverter.cs
deleted file mode 100644
index 7a5602525..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Parser/EventConverter.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using EventWebhook.Models;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System;
-using System.Collections.Generic;
-
-namespace EventWebhook.Parser
-{
- public class EventConverter : JsonConverter
- {
- private static readonly Dictionary> eventConverters =
- new Dictionary>()
- {
- { EventType.Bounce, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Click, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Deferred, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Delivered, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Dropped, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.GroupResubscribe, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.GroupUnsubscribe, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Open, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Processed, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.SpamReport, (json) => JsonConvert.DeserializeObject(json) },
- { EventType.Unsubscribe, (json) => JsonConvert.DeserializeObject(json) },
- };
-
- private static Event DeserializeEvent(EventType type, string json)
- {
- if (!eventConverters.ContainsKey(type))
- {
- throw new ArgumentOutOfRangeException($"Unknown event type: {type.ToString()}");
- }
-
- return eventConverters.GetValueOrDefault(type)(json);
- }
-
- public override bool CanConvert(Type objectType) => typeof(Event) == objectType;
-
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
- {
- var jsonObject = JObject.Load(reader);
-
- jsonObject.TryGetValue("event", StringComparison.OrdinalIgnoreCase, out JToken eventTypeJsonProperty);
-
- var eventType = (EventType)eventTypeJsonProperty.ToObject(typeof(EventType));
-
- var webhookEvent = DeserializeEvent(eventType, jsonObject.ToString());
-
- return webhookEvent;
- }
-
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- throw new NotImplementedException();
- }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Parser/EventParser.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Parser/EventParser.cs
deleted file mode 100644
index 31b09b5e7..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Parser/EventParser.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using EventWebhook.Models;
-using Newtonsoft.Json;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace EventWebhook.Parser
-{
- public class EventParser
- {
- public static async Task> ParseAsync(string json)
- {
- using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
- {
- return await ParseAsync(stream);
- }
- }
-
- public static async Task> ParseAsync(Stream stream)
- {
- var reader = new StreamReader(stream);
-
- var json = await reader.ReadToEndAsync();
-
- return JsonConvert.DeserializeObject>(json, new EventConverter());
- }
-
- public static IEnumerable Parse(string json)
- {
- using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
- {
- return Parse(stream);
- }
- }
-
- public static IEnumerable Parse(Stream stream)
- {
- var reader = new StreamReader(stream);
-
- var json = reader.ReadToEnd();
-
- return JsonConvert.DeserializeObject>(json, new EventConverter());
- }
- }
-}
diff --git a/examples/eventwebhook/consumer/Src/EventWebhook/Program.cs b/examples/eventwebhook/consumer/Src/EventWebhook/Program.cs
deleted file mode 100644
index 3e5ac6ad8..000000000
--- a/examples/eventwebhook/consumer/Src/EventWebhook/Program.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore;
-using Microsoft.AspNetCore.Hosting;
-using Microsoft.Extensions.Configuration;
-using Microsoft.Extensions.Logging;
-
-namespace EventWebhook
-{
- 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/examples/eventwebhook/consumer/src/EventWebhook/Controllers/EventWebhookController.cs b/examples/eventwebhook/consumer/src/EventWebhook/Controllers/EventWebhookController.cs
new file mode 100644
index 000000000..956057105
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Controllers/EventWebhookController.cs
@@ -0,0 +1,23 @@
+using System.Threading.Tasks;
+using EventWebhook.Parser;
+using Microsoft.AspNetCore.Mvc;
+
+namespace EventWebhook.Controllers
+{
+ [Route("/events")]
+ [ApiController]
+ public class EventWebhookController : ControllerBase
+ {
+ ///
+ /// POST : Event webhook handler
+ ///
+ ///
+ [HttpPost]
+ public async Task Events()
+ {
+ var events = await EventParser.ParseAsync(Request.Body);
+
+ return Ok(events);
+ }
+ }
+}
diff --git a/examples/eventwebhook/consumer/src/EventWebhook/Controllers/HomeController.cs b/examples/eventwebhook/consumer/src/EventWebhook/Controllers/HomeController.cs
new file mode 100644
index 000000000..a6b3c9873
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Controllers/HomeController.cs
@@ -0,0 +1,14 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Inbound.Controllers
+{
+ [Route("/")]
+ public class HomeController : Controller
+ {
+ // GET
+ public IActionResult Index()
+ {
+ return View();
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/eventwebhook/consumer/src/EventWebhook/Converters/CategoryConverter.cs b/examples/eventwebhook/consumer/src/EventWebhook/Converters/CategoryConverter.cs
new file mode 100644
index 000000000..b66320faa
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Converters/CategoryConverter.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using EventWebhook.Models;
+
+namespace EventWebhook.Converters
+{
+ public class CategoryConverter : JsonConverter
+ {
+ public override Category Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return reader.TokenType == JsonTokenType.StartArray
+ ? new Category(JsonSerializer.Deserialize(ref reader), true)
+ : new Category(new []{reader.GetString()},false);
+ }
+
+ public override void Write(Utf8JsonWriter writer, Category value, JsonSerializerOptions options)
+ {
+ if (value.IsArray)
+ {
+ writer.WriteStartArray();
+ foreach (var item in value.Value)
+ {
+ writer.WriteStringValue(item);
+ }
+ writer.WriteEndArray();
+ }
+ else
+ {
+ writer.WriteStringValue(value.Value.FirstOrDefault());
+ }
+ }
+ }
+}
diff --git a/examples/eventwebhook/consumer/src/EventWebhook/Converters/EventConverter.cs b/examples/eventwebhook/consumer/src/EventWebhook/Converters/EventConverter.cs
new file mode 100644
index 000000000..24823475e
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Converters/EventConverter.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Reflection;
+using System.Runtime.Serialization;
+using EventWebhook.Models;
+
+namespace EventWebhook.Converters
+{
+ public class EventConverter : JsonConverter>
+ {
+ private static readonly IDictionary EnumNameFor = new Dictionary();
+
+ static EventConverter()
+ {
+ var enumType = typeof(EventType);
+ foreach (var name in enumType.GetEnumNames())
+ {
+ var field = enumType.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
+ var enumMemberAttribute = field?.GetCustomAttribute(true);
+ if (enumMemberAttribute == null) continue;
+ EnumNameFor[enumMemberAttribute.Value] = name;
+ }
+ }
+ public override IEnumerable Read(ref Utf8JsonReader reader, Type typeToConvert,
+ JsonSerializerOptions options)
+ {
+ if (reader.TokenType != JsonTokenType.StartArray)
+ {
+ throw new JsonException($"Unrecognized token: {reader.TokenType}");
+ }
+
+ var elementType = typeToConvert.IsArray
+ ? typeToConvert.GetElementType()
+ : typeToConvert.GenericTypeArguments.FirstOrDefault();
+ if (elementType == null)
+ {
+ throw new JsonException($"Impossible to read JSON array to fill type: {typeToConvert.Name}");
+ }
+
+ var list = typeToConvert.IsArray || typeToConvert.IsAbstract
+ ? (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType))
+ : (IList) Activator.CreateInstance(typeToConvert);
+ while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+ {
+ list.Add(ReadObject(ref reader, elementType, options));
+ }
+
+ if (!typeToConvert.IsArray)
+ {
+ return (IEnumerable) list;
+ }
+
+ var array = Array.CreateInstance(elementType, list.Count);
+ list.CopyTo(array, 0);
+ return (Event[]) array;
+ }
+
+ private static Event ReadObject(ref Utf8JsonReader reader, Type elementType, JsonSerializerOptions options)
+ {
+ using var doc = JsonDocument.ParseValue(ref reader);
+ if (!doc.RootElement.TryGetProperty("event", out var eventProperty))
+ {
+ throw new JsonException("event property not found");
+ }
+
+ var eventTypeString = eventProperty.GetString();
+ if (EnumNameFor.TryGetValue(eventTypeString, out var enumName))
+ {
+ eventTypeString = enumName;
+ }
+
+ if (!Enum.TryParse(eventTypeString, true, out var eventType))
+ {
+ throw new JsonException($"event type not found: [{eventTypeString}]");
+ }
+
+ var typeName = string.Join(".", typeof(Event).Namespace, eventType + "Event");
+ var type = Type.GetType(typeName);
+ if (type == null)
+ {
+ throw new JsonException($"event type not found: [{typeName}]");
+ }
+
+ using var utf8Json = new MemoryStream();
+ using (var utf8JsonWriter = new Utf8JsonWriter(utf8Json))
+ {
+ doc.RootElement.WriteTo(utf8JsonWriter);
+ }
+
+ utf8Json.Seek(0, SeekOrigin.Begin);
+ return (Event) JsonSerializer.Deserialize(utf8Json.ToArray(), type, options);
+ }
+
+ public override void Write(Utf8JsonWriter writer, IEnumerable value, JsonSerializerOptions options)
+ {
+ JsonSerializer.Serialize(writer, value, value.GetType(), options);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/eventwebhook/consumer/src/EventWebhook/Converters/UnixDateTimeConverter.cs b/examples/eventwebhook/consumer/src/EventWebhook/Converters/UnixDateTimeConverter.cs
new file mode 100644
index 000000000..670721adc
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Converters/UnixDateTimeConverter.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace EventWebhook.Converters
+{
+ public class UnixDateTimeConverter : JsonConverter
+ {
+ private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ if (reader.TokenType == JsonTokenType.String && long.TryParse(reader.GetString(),out var seconds))
+ {
+ return DateTimeOffset.FromUnixTimeSeconds(seconds).UtcDateTime;
+ }
+ return DateTimeOffset.FromUnixTimeSeconds(reader.GetInt64()).UtcDateTime;
+ }
+
+ public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
+ {
+ var seconds = (long)(value.ToUniversalTime() - UnixEpoch).TotalSeconds;
+ writer.WriteNumberValue(seconds);
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/eventwebhook/consumer/src/EventWebhook/Converters/UriConverter.cs b/examples/eventwebhook/consumer/src/EventWebhook/Converters/UriConverter.cs
new file mode 100644
index 000000000..fc67960f1
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Converters/UriConverter.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace EventWebhook.Converters
+{
+ public class UriConverter : JsonConverter
+ {
+ public override Uri Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new Uri(reader.GetString());
+ }
+
+ public override void Write(Utf8JsonWriter writer, Uri value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.OriginalString);
+ }
+ }
+}
diff --git a/examples/eventwebhook/consumer/src/EventWebhook/Converters/ValueTypeStringConverter.cs b/examples/eventwebhook/consumer/src/EventWebhook/Converters/ValueTypeStringConverter.cs
new file mode 100644
index 000000000..7f9fa2b30
--- /dev/null
+++ b/examples/eventwebhook/consumer/src/EventWebhook/Converters/ValueTypeStringConverter.cs
@@ -0,0 +1,102 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace EventWebhook.Converters
+{
+ public class ValueTypeStringConverter : JsonConverter
-
+
diff --git a/tests/SendGrid.Extensions.DependencyInjection.Tests/SendGrid.Extensions.DependencyInjection.Tests.csproj b/tests/SendGrid.Extensions.DependencyInjection.Tests/SendGrid.Extensions.DependencyInjection.Tests.csproj
index 96da4a46c..329434ee5 100644
--- a/tests/SendGrid.Extensions.DependencyInjection.Tests/SendGrid.Extensions.DependencyInjection.Tests.csproj
+++ b/tests/SendGrid.Extensions.DependencyInjection.Tests/SendGrid.Extensions.DependencyInjection.Tests.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp3.1
true
false
@@ -11,10 +11,13 @@
-
-
-
-
+
+
+
+
+
+
+
diff --git a/tests/SendGrid.Tests/SendGrid.Tests.csproj b/tests/SendGrid.Tests/SendGrid.Tests.csproj
index f61ad8027..72dead4f0 100644
--- a/tests/SendGrid.Tests/SendGrid.Tests.csproj
+++ b/tests/SendGrid.Tests/SendGrid.Tests.csproj
@@ -1,7 +1,7 @@
- netcoreapp2.1
+ netcoreapp3.1
true
false
@@ -11,12 +11,12 @@
-
-
-
-
+
+
+
+
-
+