up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 00:20:26 +02:00
parent e1f1bef4c1
commit 564df71bfb
2376 changed files with 334389 additions and 328032 deletions

View File

@@ -1,107 +1,107 @@
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StellaOps.Notify.Models;
namespace StellaOps.Notifier.Worker.Channels;
/// <summary>
/// Channel adapter for Slack webhook delivery.
/// </summary>
public sealed class SlackChannelAdapter : INotifyChannelAdapter
{
private readonly HttpClient _httpClient;
private readonly ILogger<SlackChannelAdapter> _logger;
public SlackChannelAdapter(HttpClient httpClient, ILogger<SlackChannelAdapter> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public NotifyChannelType ChannelType => NotifyChannelType.Slack;
public async Task<ChannelDispatchResult> SendAsync(
NotifyChannel channel,
NotifyDeliveryRendered rendered,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(channel);
ArgumentNullException.ThrowIfNull(rendered);
var endpoint = channel.Config?.Endpoint;
if (string.IsNullOrWhiteSpace(endpoint))
{
return ChannelDispatchResult.Fail("Slack webhook URL not configured", shouldRetry: false);
}
if (!Uri.TryCreate(endpoint, UriKind.Absolute, out var uri))
{
return ChannelDispatchResult.Fail($"Invalid Slack webhook URL: {endpoint}", shouldRetry: false);
}
// Build Slack message payload
var slackPayload = new
{
channel = channel.Config?.Target,
text = rendered.Title,
blocks = new object[]
{
new
{
type = "section",
text = new
{
type = "mrkdwn",
text = rendered.Body
}
}
}
};
try
{
using var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = JsonContent.Create(slackPayload, options: new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
var statusCode = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
_logger.LogInformation(
"Slack delivery to channel {Target} succeeded.",
channel.Config?.Target ?? "(default)");
return ChannelDispatchResult.Ok(statusCode);
}
var shouldRetry = statusCode >= 500 || statusCode == 429;
_logger.LogWarning(
"Slack delivery failed with status {StatusCode}. Retry: {ShouldRetry}.",
statusCode,
shouldRetry);
return ChannelDispatchResult.Fail(
$"HTTP {statusCode}",
shouldRetry: shouldRetry,
httpStatusCode: statusCode);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Slack delivery failed with network error.");
return ChannelDispatchResult.Fail(ex.Message, shouldRetry: true);
}
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
{
throw;
}
catch (TaskCanceledException ex)
{
_logger.LogWarning(ex, "Slack delivery timed out.");
return ChannelDispatchResult.Fail("Request timeout", shouldRetry: true);
}
}
}
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using StellaOps.Notify.Models;
namespace StellaOps.Notifier.Worker.Channels;
/// <summary>
/// Channel adapter for Slack webhook delivery.
/// </summary>
public sealed class SlackChannelAdapter : INotifyChannelAdapter
{
private readonly HttpClient _httpClient;
private readonly ILogger<SlackChannelAdapter> _logger;
public SlackChannelAdapter(HttpClient httpClient, ILogger<SlackChannelAdapter> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public NotifyChannelType ChannelType => NotifyChannelType.Slack;
public async Task<ChannelDispatchResult> SendAsync(
NotifyChannel channel,
NotifyDeliveryRendered rendered,
CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(channel);
ArgumentNullException.ThrowIfNull(rendered);
var endpoint = channel.Config?.Endpoint;
if (string.IsNullOrWhiteSpace(endpoint))
{
return ChannelDispatchResult.Fail("Slack webhook URL not configured", shouldRetry: false);
}
if (!Uri.TryCreate(endpoint, UriKind.Absolute, out var uri))
{
return ChannelDispatchResult.Fail($"Invalid Slack webhook URL: {endpoint}", shouldRetry: false);
}
// Build Slack message payload
var slackPayload = new
{
channel = channel.Config?.Target,
text = rendered.Title,
blocks = new object[]
{
new
{
type = "section",
text = new
{
type = "mrkdwn",
text = rendered.Body
}
}
}
};
try
{
using var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = JsonContent.Create(slackPayload, options: new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
using var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
var statusCode = (int)response.StatusCode;
if (response.IsSuccessStatusCode)
{
_logger.LogInformation(
"Slack delivery to channel {Target} succeeded.",
channel.Config?.Target ?? "(default)");
return ChannelDispatchResult.Ok(statusCode);
}
var shouldRetry = statusCode >= 500 || statusCode == 429;
_logger.LogWarning(
"Slack delivery failed with status {StatusCode}. Retry: {ShouldRetry}.",
statusCode,
shouldRetry);
return ChannelDispatchResult.Fail(
$"HTTP {statusCode}",
shouldRetry: shouldRetry,
httpStatusCode: statusCode);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Slack delivery failed with network error.");
return ChannelDispatchResult.Fail(ex.Message, shouldRetry: true);
}
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested)
{
throw;
}
catch (TaskCanceledException ex)
{
_logger.LogWarning(ex, "Slack delivery timed out.");
return ChannelDispatchResult.Fail("Request timeout", shouldRetry: true);
}
}
}