Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ public async Task<IActionResult> Post(WebhookEventBase webhookEvent, string id)
{
webhookEvent.WebhookEndpointName = id;

Task result = webhookEvent switch
Task? result = webhookEvent switch
{
MessageParsedEvent eventItem => ProcessEvent(eventItem),
ValidationEvent eventItem => ProcessEvent(eventItem),
_ => throw new InvalidOperationException("Unable to convert event type.")
_ => null
};

if (result == null)
{
_logger.LogError("Unable to convert event type: {EventType} for MessageId: {MessageId}", webhookEvent.GetType().Name, webhookEvent.MessageId);
return BadRequest($"Unknown event type: {webhookEvent.GetType().Name}");
}

await result;
}
catch (Exception ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
using Microsoft.Extensions.Options;
using SocketLabs.EventWebhooks.Extensions.Configuration;
using SocketLabs.EventWebhooks.Extensions.Models.Events;
using SocketLabs.EventWebhooks.Extensions.Models.Inbound;

namespace SocketLabs.EventWebhooks.Extensions.Controllers
{
[Route("api/v1/[controller]")]
[Route("api/v1/[controller]/{id}")]
[ApiController]
public class WebhookEventsController : ControllerBase
{
Expand All @@ -24,44 +25,69 @@ IOptionsMonitor<WebhookOptions> options
_logger = logger;
_options = options.CurrentValue;
}

[HttpPost]
[Route("{id}")]
public async Task<IActionResult> Post(WebhookEventBase webhookEvent, string id)
public async Task<IActionResult> Post(WebhookEventBatch? webhookEvents, string id)
{
if (!_options.TryGetWebhook(id, out var endpoint) || endpoint?.SecretKey != webhookEvent.SecretKey)
{
if (webhookEvents == null) return BadRequest();

if(!_options.TryGetWebhook(id, out var endpoint) || endpoint==null)
return Unauthorized();
}

try
{
webhookEvent.WebhookEndpointName = id;
// Collect all events with mismatched secret keys
var mismatchedEvents = webhookEvents
.Where(e => e.SecretKey != endpoint.SecretKey)
.Select(e => e.MessageId ?? "(no MessageId)")
.ToList();

Task result = webhookEvent switch
if (mismatchedEvents.Any())
{
_logger.LogWarning("SecretKey mismatch for events: {EventIds} on endpoint {Endpoint}", string.Join(", ", mismatchedEvents), id);
return Unauthorized(new
{
ComplaintEvent eventItem => ProcessEvent(eventItem),
DeferredEvent eventItem => ProcessEvent(eventItem),
EngagementEvent eventItem => ProcessEvent(eventItem),
FailedEvent eventItem => ProcessEvent(eventItem),
QueuedEvent eventItem => ProcessEvent(eventItem),
SentEvent eventItem => ProcessEvent(eventItem),
ValidationEvent eventItem => ProcessEvent(eventItem),
_ => throw new InvalidOperationException("Unable to convert event type.")
};

await result;
error = "One or more events have an invalid SecretKey.",
eventIds = mismatchedEvents
});
}
catch (Exception ex)

foreach (var webhookEvent in webhookEvents)
{
_logger.LogError(ex, "Unable to process webhook event.");
try
{
webhookEvent.WebhookEndpointName = id;

Task? result = webhookEvent switch
{
ComplaintEvent eventItem => ProcessEvent(eventItem),
DeferredEvent eventItem => ProcessEvent(eventItem),
EngagementEvent eventItem => ProcessEvent(eventItem),
FailedEvent eventItem => ProcessEvent(eventItem),
QueuedEvent eventItem => ProcessEvent(eventItem),
SentEvent eventItem => ProcessEvent(eventItem),
ValidationEvent eventItem => ProcessEvent(eventItem),
_ => null
};

if (result == null)
{
_logger.LogError("Unable to convert event type: {EventType} for MessageId: {MessageId}",
webhookEvent.GetType().Name, webhookEvent.MessageId);
return BadRequest($"Unknown event type: {webhookEvent.GetType().Name}");
}

return BadRequest();
await result;
}
catch (Exception ex)
{
_logger.LogError(ex, "Unable to process webhook event.");
return BadRequest();
}
}

return Ok();
}


private async Task ProcessEvent(ComplaintEvent webhookEvent)
{
webhookEvent.Type = "Complaint";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ namespace SocketLabs.EventWebhooks.Extensions.Models.Events
//[JsonDerivedType(typeof(QueuedEvent), typeDiscriminator: "Queued")]
//[JsonDerivedType(typeof(DeferredEvent), typeDiscriminator: "Deferred")]

[JsonConverter(typeof(SingleOrArrayConverter))]
public class WebhookEventBatch : List<WebhookEventBase>
{}

[JsonConverter(typeof(WebhookEventConverter))]
public abstract class WebhookEventBase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,38 @@

namespace SocketLabs.EventWebhooks.Extensions.Models.Events
{
internal class SingleOrArrayConverter : JsonConverter<WebhookEventBatch?>
{
public override WebhookEventBatch? Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
switch (reader.TokenType)
{
case JsonTokenType.Null:
return null;
case JsonTokenType.StartArray:
var list = new WebhookEventBatch();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
break;
list.Add(JsonSerializer.Deserialize<WebhookEventBase>(ref reader, options));
}
return list;
default:
return [JsonSerializer.Deserialize<WebhookEventBase>(ref reader, options)];
}
}

public override void Write(
Utf8JsonWriter writer,
WebhookEventBatch? objectToWrite,
JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, objectToWrite, objectToWrite.GetType(), options);
}

internal class WebhookEventConverter : JsonConverter<WebhookEventBase>
{
public override WebhookEventBase? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>

<LangVersion>13.0</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down