1631786ce2
Add push contracts, provider delivery and channel diagnostics. Link email channels to services and document migration and validation. Ref: IT-1074
69 lines
3.0 KiB
C#
69 lines
3.0 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text.Json;
|
|
using HrynCo.NotificationService.DAL.Abstract.PushChannels;
|
|
using HrynCo.NotificationService.Services.Core;
|
|
using HrynCo.NotificationService.Services.Logging;
|
|
using static HrynCo.NotificationService.Services.Core.ServiceResultHelper;
|
|
|
|
namespace HrynCo.NotificationService.Services.PushChannels;
|
|
|
|
internal sealed class SavePushChannelHandler(
|
|
IContextualSerilogLogger<SavePushChannelCommand> logger, IPushChannelRepository channels)
|
|
: RequestHandler<SavePushChannelCommand, ServiceResult<Guid>>(logger)
|
|
{
|
|
protected override async Task<ServiceResult<Guid>> DoOnHandle(SavePushChannelCommand request, CancellationToken cancellationToken)
|
|
{
|
|
var existing = await channels.GetByIdAsync(request.Id, cancellationToken);
|
|
if (request.Id != Guid.Empty && existing is null)
|
|
return Failure<Guid>("Push channel not found.", ServiceErrorCode.NotFound);
|
|
var projectId = request.FirebaseProjectId.Trim();
|
|
var credentials = await ResolveCredentialsAsync(request, existing, projectId, cancellationToken);
|
|
if (!ValidCredentials(credentials, projectId))
|
|
return Failure<Guid>("Provide a valid Firebase service account JSON for this project. Changing project requires new credentials.", ServiceErrorCode.InvalidRequest);
|
|
var channel = new PushChannel
|
|
{
|
|
Id = existing?.Id ?? Guid.NewGuid(), ServiceId = request.ServiceId,
|
|
FirebaseProjectId = projectId, IsActive = request.IsActive
|
|
};
|
|
return await SaveAsync(channel, credentials!, existing is null, cancellationToken);
|
|
}
|
|
|
|
private async Task<string?> ResolveCredentialsAsync(SavePushChannelCommand request, PushChannel? existing,
|
|
string projectId, CancellationToken ct)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(request.CredentialsJson))
|
|
return request.CredentialsJson;
|
|
if (existing is null || existing.FirebaseProjectId != projectId)
|
|
return null;
|
|
return await channels.GetCredentialsAsync(existing.Id, ct);
|
|
}
|
|
|
|
private static bool ValidCredentials(string? json, string projectId)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(json)) return false;
|
|
try
|
|
{
|
|
FirebaseServiceAccountCredentials.Parse(json, projectId);
|
|
return true;
|
|
}
|
|
catch (Exception error) when (error is JsonException or FormatException or ArgumentException or CryptographicException)
|
|
{
|
|
// Neither parser exceptions nor submitted JSON are sent to logs or clients.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private async Task<ServiceResult<Guid>> SaveAsync(PushChannel channel, string credentials, bool isNew, CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
await channels.SaveAsync(channel, credentials, isNew, ct);
|
|
return Success(channel.Id);
|
|
}
|
|
catch (PushChannelWriteException error)
|
|
{
|
|
return Failure<Guid>(error.Message, error.IsConflict ? ServiceErrorCode.Conflict : ServiceErrorCode.InvalidRequest);
|
|
}
|
|
}
|
|
}
|