feat(push): add service-owned firebase channels and delivery
Add push contracts, provider delivery and channel diagnostics. Link email channels to services and document migration and validation. Ref: IT-1074
This commit is contained in:
@@ -20,5 +20,18 @@ builder.Services.Configure<RabbitMqSettings>(
|
||||
|
||||
builder.Services.AddHostedService<SendEmailConsumer>();
|
||||
|
||||
var pushSettings = builder.Configuration.GetSection("FcmPush")
|
||||
.Get<HrynCo.NotificationService.Worker.Services.Push.FcmPushSettings>() ?? new();
|
||||
builder.Services.AddSingleton(pushSettings);
|
||||
if (pushSettings.Enabled)
|
||||
{
|
||||
builder.Services.AddScoped<HrynCo.NotificationService.Worker.Services.Push.IFirebasePushClient,
|
||||
HrynCo.NotificationService.Worker.Services.Push.FirebasePushClient>();
|
||||
builder.Services.AddScoped<HrynCo.NotificationService.Worker.Services.Push.IPushSender,
|
||||
HrynCo.NotificationService.Worker.Services.Push.FcmPushSender>();
|
||||
builder.Services.AddScoped<HrynCo.NotificationService.Worker.Services.Push.PushDeliveryService>();
|
||||
builder.Services.AddHostedService<SendPushConsumer>();
|
||||
}
|
||||
|
||||
var host = builder.Build();
|
||||
host.Run();
|
||||
|
||||
@@ -8,7 +8,6 @@ using Microsoft.Extensions.Options;
|
||||
public sealed class SendEmailConsumer : RabbitMqConsumerBase<SendEmailMessage, SendEmailMessageData>
|
||||
{
|
||||
internal const string IncomingQueue = "notification.send-email";
|
||||
internal const string SupportedMessageType = "Notification.SendEmail.v1";
|
||||
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly ILogger<SendEmailConsumer> _logger;
|
||||
@@ -33,7 +32,7 @@ public sealed class SendEmailConsumer : RabbitMqConsumerBase<SendEmailMessage, S
|
||||
validationError = SendEmailDeliveryValidator.GetValidationError(
|
||||
message,
|
||||
context,
|
||||
SupportedMessageType);
|
||||
SupportedMessageType.SendEmailV1);
|
||||
return validationError is null;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ internal static class SendEmailDeliveryValidator
|
||||
public static string? GetValidationError(
|
||||
SendEmailMessage message,
|
||||
RabbitMqMessageContext context,
|
||||
string supportedMessageType)
|
||||
SupportedMessageType supportedMessageType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(context.MessageId))
|
||||
return "AMQP MessageId is required.";
|
||||
|
||||
if (!string.Equals(context.MessageType, supportedMessageType, StringComparison.Ordinal))
|
||||
if (SupportedMessageTypeParser.Parse(context.MessageType) != supportedMessageType)
|
||||
return $"Unsupported AMQP message type '{context.MessageType ?? "<missing>"}'.";
|
||||
|
||||
try
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace HrynCo.NotificationService.Worker;
|
||||
|
||||
using HrynCo.NotificationService.Contracts.Messages;
|
||||
using HrynCo.NotificationService.Worker.Services.Push;
|
||||
using Hrynco.RabbitMq;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
public sealed class SendPushConsumer(
|
||||
IOptionsMonitor<RabbitMqSettings> options, IServiceScopeFactory scopes,
|
||||
ILogger<SendPushConsumer> logger)
|
||||
: RabbitMqConsumerBase<SendPushMessage, SendPushMessageData>(options, logger)
|
||||
{
|
||||
protected override string QueueName => "notification.send-push";
|
||||
|
||||
protected override bool TryValidateMessage(SendPushMessage message, RabbitMqMessageContext context, out string? validationError)
|
||||
{
|
||||
bool valid = SendPushDeliveryValidator.IsValid(message, context);
|
||||
validationError = valid ? null : "Invalid push envelope.";
|
||||
return valid;
|
||||
}
|
||||
|
||||
protected override async Task HandleMessageAsync(SendPushMessage message, RabbitMqMessageContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using var scope = scopes.CreateAsyncScope();
|
||||
await scope.ServiceProvider.GetRequiredService<PushDeliveryService>()
|
||||
.ProcessAsync(Guid.Parse(context.MessageId!), message, cancellationToken);
|
||||
}
|
||||
|
||||
protected override async Task HandleMessageRetriesExhaustedAsync(SendPushMessage message,
|
||||
RabbitMqMessageContext context, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
await using var scope = scopes.CreateAsyncScope();
|
||||
await scope.ServiceProvider.GetRequiredService<PushDeliveryService>()
|
||||
.CompleteFailureAsync(Guid.Parse(context.MessageId!), message, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace HrynCo.NotificationService.Worker;
|
||||
|
||||
using HrynCo.NotificationService.Contracts.Messages;
|
||||
using HrynCo.NotificationService.Worker.Services.Push;
|
||||
using Hrynco.RabbitMq;
|
||||
|
||||
internal static class SendPushDeliveryValidator
|
||||
{
|
||||
public static bool IsValid(SendPushMessage message, RabbitMqMessageContext context) =>
|
||||
SupportedMessageTypeParser.Parse(context.MessageType) == SupportedMessageType.SendPushV1 &&
|
||||
Guid.TryParse(context.MessageId, out _) && context.MessageId == message.CorrelationContext?.CorrelationId &&
|
||||
PushMessageValidator.IsValid(message);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace HrynCo.NotificationService.Worker;
|
||||
|
||||
using HrynCo.NotificationService.Contracts.Messages;
|
||||
|
||||
internal static class SupportedMessageTypeParser
|
||||
{
|
||||
public static SupportedMessageType? Parse(string? value) => value switch
|
||||
{
|
||||
"Notification.SendEmail.v1" => SupportedMessageType.SendEmailV1,
|
||||
"Notification.SendPush.v1" => SupportedMessageType.SendPushV1,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user