Skip to content

Commit

Permalink
Service logic for creating new sms notification #GCPActive (#402)
Browse files Browse the repository at this point in the history
* SmsNotificationServie create notification

* Update service to handle empty adresses

* set lower idle connection for integrationtests

* added unit test and migration script

* order processing retry implemented

* fixed code smell

* Seperated tets for order processing

* added test for repository method

* Seperate services for order processing per channel

* added unit tests

* unifying use of switch and if

---------

Co-authored-by: Stephanie Buadu <47737608+acn-sbuad@users.noreply.github.com>
Co-authored-by: acn-sbuad <stephanie.buadu@avanade.com>
  • Loading branch information
3 people authored Feb 2, 2024
1 parent c2f5125 commit f26e059
Show file tree
Hide file tree
Showing 22 changed files with 852 additions and 148 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,10 @@ public enum SmsNotificationResultType
/// <summary>
/// Failed, invalid mobilenumber
/// </summary>
Failed_InvalidRecipient
Failed_InvalidRecipient,

/// <summary>
/// Recipient mobile number was not identified
/// </summary>
Failed_RecipientNotIdentified
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@ public static void AddCoreServices(this IServiceCollection services, IConfigurat
.AddSingleton<IGuidService, GuidService>()
.AddSingleton<IDateTimeService, DateTimeService>()
.AddSingleton<IOrderProcessingService, OrderProcessingService>()
.AddSingleton<IEmailOrderProcessingService, EmailOrderProcessingService>()
.AddSingleton<ISmsOrderProcessingService, SmsOrderProcessingService>()
.AddSingleton<IGetOrderService, GetOrderService>()
.AddSingleton<IOrderRequestService, OrderRequestService>()
.AddSingleton<INotificationSummaryService, NotificationSummaryService>()
.AddSingleton<IEmailNotificationService, EmailNotificationService>()
.AddSingleton<ISmsNotificationService, SmsNotificationService>()
.AddSingleton<IAltinnServiceUpdateService, AltinnServiceUpdateService>()
.AddSingleton<INotificationsEmailServiceUpdateService, NotificationsEmailServiceUpdateService>()
.Configure<KafkaSettings>(config.GetSection("KafkaSettings"))
Expand Down
17 changes: 17 additions & 0 deletions src/Altinn.Notifications.Core/Models/Recipients/SmsRecipient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Altinn.Notifications.Core.Models.Recipients;

/// <summary>
/// Class representing an sms recipient
/// </summary>
public class SmsRecipient
{
/// <summary>
/// Gets or sets the recipient id
/// </summary>
public string? RecipientId { get; set; } = null;

/// <summary>
/// Gets or sets the mobile number
/// </summary>
public string MobileNumber { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public interface IEmailNotificationRepository
public Task UpdateSendStatus(Guid notificationId, EmailNotificationResultType status, string? operationId = null);

/// <summary>
/// Retrieves all email recipients for an order
/// Retrieves all processed email recipients for an order
/// </summary>
/// <returns>A list of emails</returns>
public Task<List<EmailRecipient>> GetRecipients(Guid notificationId);
/// <returns>A list of email recipients</returns>
public Task<List<EmailRecipient>> GetRecipients(Guid orderId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Notification;
using Altinn.Notifications.Core.Models.Recipients;

namespace Altinn.Notifications.Core.Persistence;

Expand All @@ -18,4 +19,10 @@ public interface ISmsNotificationRepository
/// </summary>
/// <returns>A list of sms</returns>
public Task<List<Sms>> GetNewNotifications();

/// <summary>
/// Retrieves all processed sms recipients for an order
/// </summary>
/// <returns>A list of sms recipients</returns>
public Task<List<SmsRecipient>> GetRecipients(Guid orderId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Address;
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Core.Models.Recipients;
using Altinn.Notifications.Core.Persistence;
using Altinn.Notifications.Core.Services.Interfaces;

namespace Altinn.Notifications.Core.Services;

/// <summary>
/// Implementation of the <see cref="IEmailOrderProcessingService"/>
/// </summary>
public class EmailOrderProcessingService : IEmailOrderProcessingService
{
private readonly IEmailNotificationRepository _emailNotificationRepository;
private readonly IEmailNotificationService _emailService;

/// <summary>
/// Initializes a new instance of the <see cref="OrderProcessingService"/> class.
/// </summary>
public EmailOrderProcessingService(
IEmailNotificationRepository emailNotificationRepository,
IEmailNotificationService emailService)
{
_emailNotificationRepository = emailNotificationRepository;
_emailService = emailService;
}

/// <inheritdoc/>
public async Task ProcessOrder(NotificationOrder order)
{
foreach (Recipient recipient in order.Recipients)
{
await _emailService.CreateNotification(order.Id, order.RequestedSendTime, recipient);
}
}

/// <inheritdoc/>
public async Task ProcessOrderRetry(NotificationOrder order)
{
List<EmailRecipient> emailRecipients = await _emailNotificationRepository.GetRecipients(order.Id);
foreach (Recipient recipient in order.Recipients)
{
EmailAddressPoint? addressPoint = recipient.AddressInfo.Find(a => a.AddressType == AddressType.Email) as EmailAddressPoint;

if (!emailRecipients.Exists(er =>
er.RecipientId == (string.IsNullOrEmpty(recipient.RecipientId) ? null : recipient.RecipientId)
&& er.ToAddress.Equals(addressPoint?.EmailAddress)))
{
await _emailService.CreateNotification(order.Id, order.RequestedSendTime, recipient);
}
}
}
}
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 orders
/// </summary>
public interface IEmailOrderProcessingService
{
/// <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
@@ -0,0 +1,14 @@
using Altinn.Notifications.Core.Models;

namespace Altinn.Notifications.Core.Services.Interfaces;

/// <summary>
/// Interface for sms notification service
/// </summary>
public interface ISmsNotificationService
{
/// <summary>
/// Creates a new sms notification based on the provided orderId and recipient
/// </summary>
public Task CreateNotification(Guid orderId, DateTime requestedSendTime, Recipient recipient);
}
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 sms orders
/// </summary>
public interface ISmsOrderProcessingService
{
/// <summary>
/// Processes a notification order
/// </summary>
public Task ProcessOrder(NotificationOrder order);

/// <summary>
/// Retry processing of an order
/// </summary>
public Task ProcessOrderRetry(NotificationOrder order);
}
58 changes: 24 additions & 34 deletions src/Altinn.Notifications.Core/Services/OrderProcessingService.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System.Diagnostics;

using Altinn.Notifications.Core.Configuration;
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Integrations;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Address;
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Core.Models.Recipients;
using Altinn.Notifications.Core.Persistence;
using Altinn.Notifications.Core.Services.Interfaces;

using Microsoft.Extensions.Options;

namespace Altinn.Notifications.Core.Services;
Expand All @@ -18,24 +17,24 @@ namespace Altinn.Notifications.Core.Services;
public class OrderProcessingService : IOrderProcessingService
{
private readonly IOrderRepository _orderRepository;
private readonly IEmailNotificationRepository _emailNotificationRepository;
private readonly IEmailNotificationService _emailService;
private readonly IEmailOrderProcessingService _emailProcessingService;
private readonly ISmsOrderProcessingService _smsProcessingService;
private readonly IKafkaProducer _producer;
private readonly string _pastDueOrdersTopic;

/// <summary>
/// Initializes a new instance of the <see cref="OrderProcessingService"/> class.
/// </summary>
public OrderProcessingService(
IOrderRepository orderRepository,
IEmailNotificationRepository emailNotificationRepository,
IEmailNotificationService emailService,
IKafkaProducer producer,
IOrderRepository orderRepository,
IEmailOrderProcessingService emailProcessingService,
ISmsOrderProcessingService smsProcessingService,
IKafkaProducer producer,
IOptions<KafkaSettings> kafkaSettings)
{
_orderRepository = orderRepository;
_emailNotificationRepository = emailNotificationRepository;
_emailService = emailService;
_emailProcessingService = emailProcessingService;
_smsProcessingService = smsProcessingService;
_producer = producer;
_pastDueOrdersTopic = kafkaSettings.Value.PastDueOrdersTopicName;
}
Expand Down Expand Up @@ -68,14 +67,14 @@ public async Task ProcessOrder(NotificationOrder order)
{
NotificationChannel ch = order.NotificationChannel;

foreach (Recipient recipient in order.Recipients)
switch (ch)
{
switch (ch)
{
case NotificationChannel.Email:
await _emailService.CreateNotification(order.Id, order.RequestedSendTime, recipient);
break;
}
case NotificationChannel.Email:
await _emailProcessingService.ProcessOrder(order);
break;
case NotificationChannel.Sms:
await _smsProcessingService.ProcessOrder(order);
break;
}

await _orderRepository.SetProcessingStatus(order.Id, OrderProcessingStatus.Completed);
Expand All @@ -86,23 +85,14 @@ public async Task ProcessOrderRetry(NotificationOrder order)
{
NotificationChannel ch = order.NotificationChannel;

List<EmailRecipient> emailRecipients = await _emailNotificationRepository.GetRecipients(order.Id);

foreach (Recipient recipient in order.Recipients)
switch (ch)
{
switch (ch)
{
case NotificationChannel.Email:
EmailAddressPoint? addressPoint = recipient.AddressInfo.Find(a => a.AddressType == AddressType.Email) as EmailAddressPoint;

if (!emailRecipients.Exists(er => er.RecipientId == (string.IsNullOrEmpty(recipient.RecipientId) ? null : recipient.RecipientId)
&& er.ToAddress.Equals(addressPoint?.EmailAddress)))
{
await _emailService.CreateNotification(order.Id, order.RequestedSendTime, recipient);
}

break;
}
case NotificationChannel.Email:
await _emailProcessingService.ProcessOrderRetry(order);
break;
case NotificationChannel.Sms:
await _smsProcessingService.ProcessOrderRetry(order);
break;
}

await _orderRepository.SetProcessingStatus(order.Id, OrderProcessingStatus.Completed);
Expand Down
61 changes: 61 additions & 0 deletions src/Altinn.Notifications.Core/Services/SmsNotificationService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Address;
using Altinn.Notifications.Core.Models.Notification;
using Altinn.Notifications.Core.Persistence;
using Altinn.Notifications.Core.Services.Interfaces;

namespace Altinn.Notifications.Core.Services;

/// <summary>
/// Implementation of <see cref="ISmsNotificationService"/>
/// </summary>
public class SmsNotificationService : ISmsNotificationService
{
private readonly IGuidService _guid;
private readonly IDateTimeService _dateTime;
private readonly ISmsNotificationRepository _repository;

/// <summary>
/// Initializes a new instance of the <see cref="SmsNotificationService"/> class.
/// </summary>
public SmsNotificationService(
IGuidService guid,
IDateTimeService dateTime,
ISmsNotificationRepository repository)
{
_guid = guid;
_dateTime = dateTime;
_repository = repository;
}

/// <inheritdoc/>
public async Task CreateNotification(Guid orderId, DateTime requestedSendTime, Recipient recipient)
{
SmsAddressPoint? addressPoint = recipient.AddressInfo.Find(a => a.AddressType == AddressType.Sms) as SmsAddressPoint;

if (!string.IsNullOrEmpty(addressPoint?.MobileNumber))
{
await CreateNotificationForRecipient(orderId, requestedSendTime, recipient.RecipientId, addressPoint!.MobileNumber, SmsNotificationResultType.New);
}
else
{
await CreateNotificationForRecipient(orderId, requestedSendTime, recipient.RecipientId, string.Empty, SmsNotificationResultType.Failed_RecipientNotIdentified);
}
}

private async Task CreateNotificationForRecipient(Guid orderId, DateTime requestedSendTime, string recipientId, string recipientNumber, SmsNotificationResultType type)
{
var smsNotification = new SmsNotification()
{
Id = _guid.NewGuid(),
OrderId = orderId,
RequestedSendTime = requestedSendTime,
RecipientNumber = recipientNumber,
RecipientId = string.IsNullOrEmpty(recipientId) ? null : recipientId,
SendResult = new(type, _dateTime.UtcNow())
};

await _repository.AddNotification(smsNotification, requestedSendTime.AddHours(1));
}
}
Loading

0 comments on commit f26e059

Please sign in to comment.