< Summary

Information
Class: Intentum.Events.WebhookIntentEventHandler
Assembly: Intentum.Events
File(s): /home/runner/work/Intentum/Intentum/src/Intentum.Events/WebhookIntentEventHandler.cs
Line coverage
90%
Covered lines: 46
Uncovered lines: 5
Coverable lines: 51
Total lines: 85
Line coverage: 90.1%
Branch coverage
60%
Covered branches: 6
Total branches: 10
Branch coverage: 60%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor(...)50%66100%
HandleAsync()100%22100%
SendWithRetryAsync()50%2278.26%
.ctor(...)100%11100%

File(s)

/home/runner/work/Intentum/Intentum/src/Intentum.Events/WebhookIntentEventHandler.cs

#LineLine coverage
 1using System.Net.Http.Json;
 2using System.Text.Json;
 3using JetBrains.Annotations;
 4using Microsoft.Extensions.Options;
 5
 6namespace Intentum.Events;
 7
 8/// <summary>
 9/// Dispatches intent events to configured webhook URLs via HTTP POST with retry.
 10/// </summary>
 11public sealed class WebhookIntentEventHandler : IIntentEventHandler
 12{
 13    private readonly IHttpClientFactory _httpClientFactory;
 14    private readonly IntentumEventsOptions _options;
 115    private static readonly JsonSerializerOptions JsonOptions = new()
 116    {
 117        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
 118        WriteIndented = false
 119    };
 20
 621    public WebhookIntentEventHandler(IHttpClientFactory httpClientFactory, IOptions<IntentumEventsOptions>? options)
 622    {
 623        _httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
 624        _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
 625    }
 26
 27    /// <inheritdoc />
 28    public async Task HandleAsync(
 29        IntentEventPayload payload,
 30        IntentumEventType eventType,
 31        CancellationToken cancellationToken = default)
 532    {
 533        var eventTypeName = eventType.ToString();
 534        var webhooks = _options.Webhooks
 635            .Where(w => w.EventTypes.Contains(eventTypeName))
 536            .ToList();
 37
 2538        foreach (var webhook in webhooks)
 539        {
 540            await SendWithRetryAsync(webhook.Url, payload, eventTypeName, cancellationToken).ConfigureAwait(false);
 541        }
 542    }
 43
 44    private async Task SendWithRetryAsync(
 45        string url,
 46        IntentEventPayload payload,
 47        string eventType,
 48        CancellationToken cancellationToken)
 549    {
 550        var client = _httpClientFactory.CreateClient(IntentumEventsOptions.HttpClientName);
 551        var dto = new WebhookPayloadDto(
 552            payload.BehaviorSpaceId,
 553            payload.Intent.Name,
 554            payload.Intent.Confidence.Level,
 555            payload.Intent.Confidence.Score,
 556            payload.Decision.ToString(),
 557            payload.RecordedAt,
 558            eventType);
 59
 560        var maxAttempts = Math.Max(1, _options.RetryCount + 1);
 1061        for (var attempt = 0; attempt < maxAttempts; attempt++)
 562        {
 63            try
 564            {
 565                var response = await client.PostAsJsonAsync(url, dto, JsonOptions, cancellationToken).ConfigureAwait(fal
 566                response.EnsureSuccessStatusCode();
 567                return;
 68            }
 069            catch (HttpRequestException) when (attempt < maxAttempts - 1)
 070            {
 071                await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), cancellationToken).ConfigureAwait(false);
 072            }
 073        }
 574    }
 75
 76    [UsedImplicitly]
 577    private sealed record WebhookPayloadDto(
 278        string? BehaviorSpaceId,
 279        string IntentName,
 280        string ConfidenceLevel,
 281        double ConfidenceScore,
 282        string Decision,
 283        DateTimeOffset RecordedAt,
 784        string EventType);
 85}