diff --git a/source/backend/api/Repositories/Ches/ChesRepository.cs b/source/backend/api/Repositories/Ches/ChesRepository.cs index d350979e69..441618da1a 100644 --- a/source/backend/api/Repositories/Ches/ChesRepository.cs +++ b/source/backend/api/Repositories/Ches/ChesRepository.cs @@ -2,7 +2,6 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; -using System.Security.Authentication; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -12,6 +11,7 @@ using Pims.Api.Models.CodeTypes; using Pims.Api.Models.Config; using Pims.Api.Models.Requests.Http; +using Pims.Core.Api.Exceptions; using Polly.Registry; namespace Pims.Api.Repositories.Ches @@ -98,33 +98,49 @@ public async Task> SendEmailAsync(EmailRequest r _logger.LogError("CHES email send failed: {Status} {Reason} {Body}", response.StatusCode, response.ReasonPhrase, errorBody); result.Message = $"CHES email send failed: {response.StatusCode} {response.ReasonPhrase}. Response body: {errorBody}"; } + + return result; } catch (HttpRequestException ex) { result.Status = ExternalResponseStatus.Error; result.Message = $"HTTP error sending CHES email: {ex.Message}"; _logger.LogError(ex, "HTTP error sending CHES email."); + + return result; } catch (TaskCanceledException ex) { result.Status = ExternalResponseStatus.Error; result.Message = $"Timeout sending CHES email: {ex.Message}"; _logger.LogError(ex, "Timeout sending CHES email."); + + return result; } catch (JsonException ex) { result.Status = ExternalResponseStatus.Error; result.Message = $"Serialization error: {ex.Message}"; _logger.LogError(ex, "Serialization error sending CHES email."); + + return result; } catch (AuthenticationException ex) { - result.Status = ExternalResponseStatus.Error; + result.Status = ExternalResponseStatus.NotExecuted; result.Message = $"Authentication error: {ex.Message}"; _logger.LogError(ex, "Authentication error sending CHES email."); + + return result; } + catch (Exception ex) + { + result.Status = ExternalResponseStatus.Error; + result.Message = $"Unexpected error sending CHES email: {ex.Message}"; + _logger.LogError(ex, "Unexpected error sending CHES email."); - return result; + return result; + } } public async Task TryGetHealthAsync() diff --git a/source/backend/api/Services/NotificationUserService.cs b/source/backend/api/Services/NotificationUserService.cs index ac58982115..ca55e4ebd3 100644 --- a/source/backend/api/Services/NotificationUserService.cs +++ b/source/backend/api/Services/NotificationUserService.cs @@ -57,8 +57,6 @@ public async Task PushNotificationUser(long notificationUserId) } userNotification.NotificationRetryCnt = userNotification.NotificationRetryCnt.HasValue ? ++userNotification.NotificationRetryCnt : 1; - var updatedNotification = _notificationUserOutputRepository.Update(userNotification); - _notificationUserOutputRepository.CommitTransaction(); if (userNotification.NotificationOutputTypeCode == NotificationOutputTypes.EMAIL.ToString()) { @@ -77,24 +75,24 @@ public async Task PushNotificationUser(long notificationUserId) { case ExternalResponseStatus.Success: { - updatedNotification.NotificationSentDt = DateTime.UtcNow; + userNotification.NotificationSentDt = DateTime.UtcNow; } break; case ExternalResponseStatus.Error: case ExternalResponseStatus.NotExecuted: { - updatedNotification.NotificationErrorDt = DateOnly.FromDateTime(DateTime.UtcNow); - updatedNotification.NotificationErrorReason = chesResponse.Message; + userNotification.NotificationErrorDt = DateOnly.FromDateTime(DateTime.UtcNow); + userNotification.NotificationErrorReason = chesResponse.Message; } break; } } else { - updatedNotification.NotificationSentDt = DateTime.UtcNow; + userNotification.NotificationSentDt = DateTime.UtcNow; } - _notificationUserOutputRepository.Update(updatedNotification); + _ = await _notificationUserOutputRepository.Update(userNotification); _notificationUserOutputRepository.CommitTransaction(); return; diff --git a/source/backend/dal/Repositories/Interfaces/INotificationUserOutputRepository.cs b/source/backend/dal/Repositories/Interfaces/INotificationUserOutputRepository.cs index 024838cead..a601db0d14 100644 --- a/source/backend/dal/Repositories/Interfaces/INotificationUserOutputRepository.cs +++ b/source/backend/dal/Repositories/Interfaces/INotificationUserOutputRepository.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Pims.Api.Models.Models.Concepts.Notification; using Pims.Dal.Entities; @@ -10,6 +11,6 @@ public interface INotificationUserOutputRepository : IRepository public PimsNotificationUserOutput GetById(long notificationUserOutputId); - public PimsNotificationUserOutput Update(PimsNotificationUserOutput userNotification); + public Task Update(PimsNotificationUserOutput userNotification); } } diff --git a/source/backend/dal/Repositories/NotificationUserOutputRepository.cs b/source/backend/dal/Repositories/NotificationUserOutputRepository.cs index f67de4c77d..f59e132c2b 100644 --- a/source/backend/dal/Repositories/NotificationUserOutputRepository.cs +++ b/source/backend/dal/Repositories/NotificationUserOutputRepository.cs @@ -1,8 +1,9 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; +using System.Threading.Tasks; using LinqKit; +using Medallion.Threading; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Pims.Api.Models.Models.Concepts.Notification; @@ -14,12 +15,16 @@ namespace Pims.Dal.Repositories { public class NotificationUserOutputRepository : BaseRepository, INotificationUserOutputRepository { + private readonly IDistributedLockProvider _synchronizationProvider; + public NotificationUserOutputRepository( PimsContext dbContext, ClaimsPrincipal user, - ILogger logger) + IDistributedLockProvider synchronizationProvider, + ILogger logger) : base(dbContext, user, logger) { + _synchronizationProvider = synchronizationProvider; } public PimsNotificationUserOutput GetById(long notificationUserOutputId) @@ -75,25 +80,21 @@ public PimsNotificationUserOutput GetById(long notificationUserOutputId) .FirstOrDefault(x => x.NotificationUserOutputId == notificationUserOutputId) ?? throw new KeyNotFoundException(); } - public PimsNotificationUserOutput Update(PimsNotificationUserOutput userNotification) + public async Task Update(PimsNotificationUserOutput userNotification) { - try + var @lock = _synchronizationProvider.CreateLock("NotificationUserOutputLock"); + await using (await @lock.AcquireAsync()) { - using var scope = Logger.QueryScope(); userNotification.ThrowIfNull(nameof(userNotification)); var existingUserNotification = Context.PimsNotificationUserOutputs .FirstOrDefault(x => x.NotificationUserOutputId == userNotification.NotificationUserOutputId); Context.Entry(existingUserNotification).CurrentValues.SetValues(userNotification); + Context.Update(existingUserNotification); return existingUserNotification; } - catch(Exception ex) - { - Logger.LogError(ex.Message); - return null; - } } public IEnumerable GetAllByFilter(NotificationUserSearchFilterModel filter) @@ -122,7 +123,7 @@ public IEnumerable GetAllByFilter(NotificationUserSe if(filter.NotificationTriggerDate is not null) { - predicateBuilder.And(x => x.NotificationUser.Notification.NotificationTriggerDate == filter.NotificationTriggerDate); + predicateBuilder.And(x => x.NotificationUser.Notification.NotificationTriggerDate <= filter.NotificationTriggerDate); } var query = Context.PimsNotificationUserOutputs