Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Email and SmsPreferred support in Altinn.Notifications.Core #581

Merged
merged 7 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/Altinn.Notifications.Core/Enums/NotificationChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,15 @@ public enum NotificationChannel
/// <summary>
/// The selected channel for the notification is SMS.
/// </summary>
Sms
Sms,

/// <summary>
/// The selected channel for the notification is email and to use SMS if email is not available.
/// </summary>
EmailPreferred,

/// <summary>
/// The selected channel for the notification is SMS and to use email if SMS is not available.
/// </summary>
SmsPreferred
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public static void AddCoreServices(this IServiceCollection services, IConfigurat
.AddSingleton<IOrderProcessingService, OrderProcessingService>()
.AddSingleton<IEmailOrderProcessingService, EmailOrderProcessingService>()
.AddSingleton<ISmsOrderProcessingService, SmsOrderProcessingService>()
.AddSingleton<IPreferredChannelProcessingService, PreferredChannelProcessingService>()
.AddSingleton<IGetOrderService, GetOrderService>()
.AddSingleton<IOrderRequestService, OrderRequestService>()
.AddSingleton<IEmailNotificationSummaryService, EmailNotificationSummaryService>()
Expand Down
111 changes: 110 additions & 1 deletion src/Altinn.Notifications.Core/Services/ContactPointService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Altinn.Notifications.Core.Helpers;
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Helpers;
using Altinn.Notifications.Core.Integrations;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Address;
Expand Down Expand Up @@ -84,6 +85,114 @@ await AugmentRecipients(
});
}

/// <inheritdoc/>
public async Task AddPreferredContactPoints(NotificationChannel channel, List<Recipient> recipients, string? resourceId)
{
await AugmentRecipients(
recipients,
resourceId,
(recipient, userContactPoints) =>
{
if (channel == NotificationChannel.EmailPreferred)
{
AddPreferredOrFallbackContactPoint(
recipient,
userContactPoints.Email,
userContactPoints.MobileNumber,
email => new EmailAddressPoint(email),
mobile => new SmsAddressPoint(mobile));
}
else if (channel == NotificationChannel.SmsPreferred)
{
AddPreferredOrFallbackContactPoint(
recipient,
userContactPoints.MobileNumber,
userContactPoints.Email,
mobile => new SmsAddressPoint(mobile),
email => new EmailAddressPoint(email));
}

return recipient;
},
(recipient, orgContactPoints) =>
{
if (channel == NotificationChannel.EmailPreferred)
{
AddPreferredOrFallbackContactPointList(
recipient,
orgContactPoints.EmailList,
orgContactPoints.MobileNumberList,
e => new EmailAddressPoint(e),
m => new SmsAddressPoint(m));

foreach (var userContact in orgContactPoints.UserContactPoints)
{
AddPreferredOrFallbackContactPoint(
recipient,
userContact.Email,
userContact.MobileNumber,
email => new EmailAddressPoint(email),
mobile => new SmsAddressPoint(mobile));
}
}
else if (channel == NotificationChannel.SmsPreferred)
{
AddPreferredOrFallbackContactPointList(
recipient,
orgContactPoints.MobileNumberList,
orgContactPoints.EmailList,
m => new SmsAddressPoint(m),
e => new EmailAddressPoint(e));

foreach (var userContact in orgContactPoints.UserContactPoints)
{
AddPreferredOrFallbackContactPoint(
recipient,
userContact.MobileNumber,
userContact.Email,
mobile => new SmsAddressPoint(mobile),
email => new EmailAddressPoint(email));
}
}

return recipient;
});
}

private static void AddPreferredOrFallbackContactPointList<TPreferred, TFallback>(
Recipient recipient,
List<TPreferred> preferredList,
List<TFallback> fallbackList,
Func<TPreferred, IAddressPoint> preferredSelector,
Func<TFallback, IAddressPoint> fallbackSelector)
{
if (preferredList.Count > 0)
{
recipient.AddressInfo.AddRange(preferredList.Select(preferredSelector).ToList());
}
else
{
recipient.AddressInfo.AddRange(fallbackList.Select(fallbackSelector).ToList());
}
}

private static void AddPreferredOrFallbackContactPoint<TPreferred, TFallback>(
Recipient recipient,
TPreferred preferredContact,
TFallback fallbackContact,
Func<TPreferred, IAddressPoint> preferredSelector,
Func<TFallback, IAddressPoint> fallbackSelector)
{
if (!string.IsNullOrEmpty(preferredContact?.ToString()))
{
recipient.AddressInfo.Add(preferredSelector(preferredContact));
}
else if (!string.IsNullOrEmpty(fallbackContact?.ToString()))
{
recipient.AddressInfo.Add(fallbackSelector(fallbackContact));
}
}

private async Task<List<Recipient>> AugmentRecipients(
List<Recipient> recipients,
string? resourceId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public async Task ProcessOrder(NotificationOrder order)

await _contactPointService.AddEmailContactPoints(recipientsWithoutEmail, order.ResourceId);

await ProcessOrderWithoutAddressLookup(order, recipients);
}

/// <inheritdoc/>
public async Task ProcessOrderWithoutAddressLookup(NotificationOrder order, List<Recipient> recipients)
{
foreach (Recipient recipient in recipients)
{
await _emailService.CreateNotification(order.Id, order.RequestedSendTime, recipient, order.IgnoreReservation ?? false);
Expand All @@ -52,9 +58,15 @@ public async Task ProcessOrderRetry(NotificationOrder order)

await _contactPointService.AddEmailContactPoints(recipientsWithoutEmail, order.ResourceId);

await ProcessOrderRetryWithoutAddressLookup(order, recipients);
}

/// <inheritdoc/>
public async Task ProcessOrderRetryWithoutAddressLookup(NotificationOrder order, List<Recipient> recipients)
{
List<EmailRecipient> emailRecipients = await _emailNotificationRepository.GetRecipients(order.Id);

foreach (Recipient recipient in order.Recipients)
foreach (Recipient recipient in recipients)
{
EmailAddressPoint? addressPoint = recipient.AddressInfo.Find(a => a.AddressType == AddressType.Email) as EmailAddressPoint;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Models;

namespace Altinn.Notifications.Core.Services.Interfaces
{
Expand All @@ -24,5 +25,15 @@ public interface IContactPointService
/// <returns>The list of recipients augumented with SMS address points where available</returns>
/// <remarks>Implementation alters the recipient reference object directly</remarks>
public Task AddSmsContactPoints(List<Recipient> recipients, string? resourceId);

/// <summary>
/// Looks up and adds the SMS contact points for recipients based on their national identity number or organization number
/// </summary>
/// <param name="channel">The notification channel specifying which channel is preferred</param>
/// <param name="recipients">List of recipients to retrieve contact points for</param>
/// <param name="resourceId">The resource to find contact points in relation to</param>
/// <returns>The list of recipients augumented with SMS address points where available</returns>
/// <remarks>Implementation alters the recipient reference object directly</remarks>
public Task AddPreferredContactPoints(NotificationChannel channel, List<Recipient> recipients, string? resourceId);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Orders;

namespace Altinn.Notifications.Core.Services.Interfaces;

Expand All @@ -13,7 +14,19 @@ public interface IEmailOrderProcessingService
public Task ProcessOrder(NotificationOrder order);

/// <summary>
/// Retry processing of an order
/// Processes a notification order for the provided list of recipients
/// without looking up additional recipient data
/// </summary>
public Task ProcessOrderWithoutAddressLookup(NotificationOrder order, List<Recipient> recipients);

/// <summary>
/// Retry processing of a notification order
/// </summary>
public Task ProcessOrderRetry(NotificationOrder order);

/// <summary>
/// Retryprocessing of a notification order for the provided list of recipients
/// without looking up additional recipient data
/// </summary>
public Task ProcessOrderRetryWithoutAddressLookup(NotificationOrder order, List<Recipient> recipients);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Altinn.Notifications.Core.Models.Orders;

namespace Altinn.Notifications.Core.Services.Interfaces;

/// <summary>
/// Interface for the order processing service speficic to email or sms preferred orders
/// </summary>
public interface IPreferredChannelProcessingService
{
/// <summary>
/// Processes a notification order
/// </summary>
public Task ProcessOrder(NotificationOrder order);

/// <summary>
/// Retry processing of an order
/// </summary>
public Task ProcessOrderRetry(NotificationOrder order);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Orders;

namespace Altinn.Notifications.Core.Services.Interfaces;

Expand All @@ -12,8 +13,20 @@ public interface ISmsOrderProcessingService
/// </summary>
public Task ProcessOrder(NotificationOrder order);

/// <summary>
/// Processes a notification order for the provided list of recipients
/// without looking up additional recipient data
/// </summary>
public Task ProcessOrderWithoutAddressLookup(NotificationOrder order, List<Recipient> recipients);

/// <summary>
/// Retry processing of an order
/// </summary>
public Task ProcessOrderRetry(NotificationOrder order);

/// <summary>
/// Retryprocessing of a notification order for the provided list of recipients
/// without looking up additional recipient data
/// </summary>
public Task ProcessOrderRetryWithoutAddressLookup(NotificationOrder order, List<Recipient> recipients);
}
11 changes: 11 additions & 0 deletions src/Altinn.Notifications.Core/Services/OrderProcessingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
private readonly IOrderRepository _orderRepository;
private readonly IEmailOrderProcessingService _emailProcessingService;
private readonly ISmsOrderProcessingService _smsProcessingService;
private readonly IPreferredChannelProcessingService _preferredChannelProcessingService;
private readonly IConditionClient _conditionClient;
private readonly IKafkaProducer _producer;
private readonly string _pastDueOrdersTopic;
Expand All @@ -29,10 +30,11 @@
/// <summary>
/// Initializes a new instance of the <see cref="OrderProcessingService"/> class.
/// </summary>
public OrderProcessingService(

Check warning on line 33 in src/Altinn.Notifications.Core/Services/OrderProcessingService.cs

View workflow job for this annotation

GitHub Actions / Build, test & analyze

Constructor has 8 parameters, which is greater than the 7 authorized. (https://rules.sonarsource.com/csharp/RSPEC-107)

Check warning on line 33 in src/Altinn.Notifications.Core/Services/OrderProcessingService.cs

View workflow job for this annotation

GitHub Actions / Build, test & analyze

Constructor has 8 parameters, which is greater than the 7 authorized. (https://rules.sonarsource.com/csharp/RSPEC-107)

Check warning on line 33 in src/Altinn.Notifications.Core/Services/OrderProcessingService.cs

View workflow job for this annotation

GitHub Actions / Build, test & analyze

Constructor has 8 parameters, which is greater than the 7 authorized. (https://rules.sonarsource.com/csharp/RSPEC-107)
IOrderRepository orderRepository,
IEmailOrderProcessingService emailProcessingService,
ISmsOrderProcessingService smsProcessingService,
IPreferredChannelProcessingService preferredChannelProcessingService,
IConditionClient conditionClient,
IKafkaProducer producer,
IOptions<KafkaSettings> kafkaSettings,
Expand All @@ -41,6 +43,7 @@
_orderRepository = orderRepository;
_emailProcessingService = emailProcessingService;
_smsProcessingService = smsProcessingService;
_preferredChannelProcessingService = preferredChannelProcessingService;
_conditionClient = conditionClient;
_producer = producer;
_pastDueOrdersTopic = kafkaSettings.Value.PastDueOrdersTopicName;
Expand Down Expand Up @@ -89,6 +92,10 @@
case NotificationChannel.Sms:
await _smsProcessingService.ProcessOrder(order);
break;
case NotificationChannel.EmailPreferred:
case NotificationChannel.SmsPreferred:
await _preferredChannelProcessingService.ProcessOrder(order);
break;
}

await _orderRepository.SetProcessingStatus(order.Id, OrderProcessingStatus.Completed);
Expand All @@ -113,6 +120,10 @@
case NotificationChannel.Sms:
await _smsProcessingService.ProcessOrderRetry(order);
break;
case NotificationChannel.EmailPreferred:
case NotificationChannel.SmsPreferred:
await _preferredChannelProcessingService.ProcessOrderRetry(order);
break;
}

await _orderRepository.SetProcessingStatus(order.Id, OrderProcessingStatus.Completed);
Expand Down
Loading
Loading