Files
agrynco 1631786ce2 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
2026-10-24 12:31:35 +03:00

50 lines
2.5 KiB
C#

using System.Security.Cryptography;
using System.Text.Json;
using HrynCo.NotificationService.DAL.Abstract.Push;
using HrynCo.NotificationService.DAL.Abstract.PushChannels;
namespace HrynCo.NotificationService.Worker.Services.Push;
public sealed class PushChannelTestService(IPushChannelRepository channels, IFirebasePushClient firebase)
{
public async Task<PushDeliveryOutcome> TestAsync(PushChannelTestSettings settings, bool send, CancellationToken ct)
{
if (send && !ValidToken(settings.RecipientToken))
return new PushDeliveryOutcome("Enter a valid recipient FCM token.");
var project = settings.FirebaseProjectId?.Trim() ?? "";
var json = await ResolveCredentialsAsync(settings, project, ct);
if (string.IsNullOrWhiteSpace(json))
return new PushDeliveryOutcome("Provide credentials for this project. Saved credentials can only be used with the unchanged project.");
return await ExecuteAsync(json, project, settings.RecipientToken, send, ct);
}
private async Task<string?> ResolveCredentialsAsync(PushChannelTestSettings settings, string project, CancellationToken ct)
{
if (!string.IsNullOrWhiteSpace(settings.CredentialsJson)) return settings.CredentialsJson;
if (settings.ChannelId is not Guid id) return null;
var saved = await channels.GetByIdAsync(id, ct);
return saved?.FirebaseProjectId == project ? await channels.GetCredentialsAsync(id, ct) : null;
}
private async Task<PushDeliveryOutcome> ExecuteAsync(string json, string project, string? token, bool send, CancellationToken ct)
{
try
{
var credentials = FirebaseServiceAccountCredentials.Parse(json, project);
return send ? await firebase.SendTestAsync(token!.Trim(), credentials, ct)
: await firebase.CheckAuthorizationAsync(credentials, ct);
}
catch (Exception error) when (error is JsonException or FormatException or ArgumentException or CryptographicException)
{
return new PushDeliveryOutcome("Provide a valid Firebase service account JSON for this project.");
}
catch (PushRetryException)
{
return new PushDeliveryOutcome("Firebase is temporarily unavailable or throttled. Try again later; acceptance was not confirmed.");
}
}
private static bool ValidToken(string? token) => !string.IsNullOrWhiteSpace(token)
&& token.Trim().Length <= 4096 && !token.Trim().Any(char.IsWhiteSpace);
}