Improved Webhook Controller Mailtrap event processing

This commit is contained in:
Mia Rose Winter 2024-06-04 14:18:22 +02:00
parent b8bc00b68d
commit 42014a831c
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
2 changed files with 19 additions and 16 deletions

View file

@ -15,16 +15,15 @@ public class WebhookController(ILogger<WebhookController> logger, ApplicationDbC
[HttpPost("mailtrap/{apiKey}")] [HttpPost("mailtrap/{apiKey}")]
[Authorize("EmailApi", AuthenticationSchemes = "ApiKeyInRoute")] [Authorize("EmailApi", AuthenticationSchemes = "ApiKeyInRoute")]
public async Task<IActionResult> Mailtrap(Webhook webhook, string apiKey) { public async Task<IActionResult> Mailtrap(Webhook webhook, string apiKey) {
logger.LogDebug("Start processing webhook events");
foreach (var webhookEvent in webhook.Events) { foreach (var webhookEvent in webhook.Events) {
metrics.WebhookEventReceived("Mailtrap", webhookEvent.Type.ToString()); metrics.WebhookEventReceived("Mailtrap", webhookEvent.Type.ToString());
var subscriber = await context.Set<EmailSubscriber>().FirstOrDefaultAsync(s => s.Email == webhookEvent.Email); var subscriber = await context.Set<EmailSubscriber>().FirstOrDefaultAsync(s => s.Email == webhookEvent.Email);
logger.LogDebug("Received Webhook event {EventType} for {email}", logger.LogDebug("Received {WebhookEvent} event for {email}", webhookEvent.Type, webhookEvent.Email);
webhookEvent.Type, webhookEvent.Email);
if (subscriber is null) { if (subscriber is null) {
logger.LogWarning( logger.LogWarning(
"Received webhook event from mailtrap of type {EventType}, " + "Received {WebhookEvent} from Mailtrap " +
"but failed to find subscriber with E-Mail {email}.", "but failed to find subscriber with E-Mail {email}.",
webhookEvent.Type, webhookEvent.Email); webhookEvent.Type, webhookEvent.Email);
metrics.WebhookEventError("Mailtrap", webhookEvent.Type.ToString(), "unknown email"); metrics.WebhookEventError("Mailtrap", webhookEvent.Type.ToString(), "unknown email");
@ -39,14 +38,13 @@ public class WebhookController(ILogger<WebhookController> logger, ApplicationDbC
case WebhookEventType.Open: case WebhookEventType.Open:
subscriber.LastMailOpened = webhookEvent.EventDateTime; subscriber.LastMailOpened = webhookEvent.EventDateTime;
break; break;
case WebhookEventType.Bounce: case WebhookEventType.SoftBounce:
// Store this message in case it develops into a suspension subscriber.UnsubscribeReason = webhookEvent.Response ?? webhookEvent.Type.Humanize(LetterCasing.Title);
subscriber.UnsubscribeReason = webhookEvent.Response;
break; break;
case WebhookEventType.Suspension: case WebhookEventType.Suspension:
logger.LogWarning("Received Suspension event, you may have send from an unverifyied domain or exceeded your hourly rate."); logger.LogWarning(
return Ok(); "Received Suspension event, you may have send from an unverified domain or exceeded your hourly rate.");
break; continue;
case WebhookEventType.Unsubscribe: case WebhookEventType.Unsubscribe:
subscriber.Unsubscribed = true; subscriber.Unsubscribed = true;
subscriber.UnsubscribeReason = "User Unsubscribed"; subscriber.UnsubscribeReason = "User Unsubscribed";
@ -55,22 +53,23 @@ public class WebhookController(ILogger<WebhookController> logger, ApplicationDbC
subscriber.Unsubscribed = true; subscriber.Unsubscribed = true;
subscriber.UnsubscribeReason = "User reported as Spam"; subscriber.UnsubscribeReason = "User reported as Spam";
break; break;
case WebhookEventType.Bounce:
case WebhookEventType.Reject: case WebhookEventType.Reject:
subscriber.Unsubscribed = true; subscriber.Unsubscribed = true;
subscriber.UnsubscribeReason = webhookEvent.Reason?.Humanize().Titleize() ?? "Rejected"; subscriber.UnsubscribeReason = webhookEvent.Reason ?? webhookEvent.Type.Humanize(LetterCasing.Title);
break; break;
case WebhookEventType.SoftBounce:
case WebhookEventType.Click: case WebhookEventType.Click:
default: default:
logger.LogInformation("Received unsupported event {EventType} for {email}. Skipping.", webhookEvent.Type, webhookEvent.Email); logger.LogInformation("Received unsupported event {EventType} for {email}. Skipping.", webhookEvent.Type, webhookEvent.Email);
metrics.WebhookEventError("Mailtrap", webhookEvent.Type.ToString(), "unknown type"); metrics.WebhookEventError("Mailtrap", webhookEvent.Type.ToString(), "unknown type");
return Ok(); continue;
} }
await context.SaveChangesAsync();
logger.LogDebug("Webhook event {EventType} for {email} processed successfully.", logger.LogDebug("Webhook event {EventType} for {email} processed successfully.",
webhookEvent.Type, webhookEvent.Email); webhookEvent.Type, webhookEvent.Email);
} }
await context.SaveChangesAsync();
logger.LogDebug("All webhook events processed and saved");
return Ok(); return Ok();

View file

@ -1,14 +1,18 @@
using System.Text.Json.Serialization; using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Wave.Data.Api.Mailtrap; namespace Wave.Data.Api.Mailtrap;
[JsonConverter(typeof(JsonStringEnumConverter<WebhookEventType>))]
public enum WebhookEventType { public enum WebhookEventType {
Delivery, Delivery,
[EnumMember(Value = "soft bounce")]
SoftBounce, SoftBounce,
Bounce, Bounce,
Suspension, Suspension,
Unsubscribe, Unsubscribe,
Open, Open,
[EnumMember(Value = "spam complaint")]
SpamComplaint, SpamComplaint,
Click, Click,
Reject Reject
@ -34,7 +38,7 @@ public record WebhookEvent(
[property:JsonPropertyName("response_code")] [property:JsonPropertyName("response_code")]
int? ResponseCode) { int? ResponseCode) {
public WebhookEventType Type => Enum.Parse<WebhookEventType>(EventTypeString.Replace("_", ""), true); public WebhookEventType Type => Enum.Parse<WebhookEventType>(EventTypeString.Replace("_", "").Replace(" ", ""), true);
public DateTimeOffset EventDateTime => DateTimeOffset.FromUnixTimeSeconds(Timestamp); public DateTimeOffset EventDateTime => DateTimeOffset.FromUnixTimeSeconds(Timestamp);
} }