Skip to content

Commit

Permalink
email notifications endpoint implemented #GCPActive (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
acn-sbuad authored Nov 14, 2023
1 parent 8ac7ecb commit a776357
Show file tree
Hide file tree
Showing 31 changed files with 1,193 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Altinn.Notifications.Core.Extensions;

Expand Down Expand Up @@ -32,9 +31,10 @@ public static void AddCoreServices(this IServiceCollection services, IConfigurat
.AddSingleton<IGuidService, GuidService>()
.AddSingleton<IDateTimeService, DateTimeService>()
.AddSingleton<IOrderProcessingService, OrderProcessingService>()
.AddSingleton<IGetOrderService, GetOrderService>()
.AddSingleton<IEmailNotificationOrderService, EmailNotificationOrderService>()
.AddSingleton<INotificationSummaryService, NotificationSummaryService>()
.AddSingleton<IEmailNotificationService, EmailNotificationService>()
.AddSingleton<IGetOrderService, GetOrderService>()
.Configure<KafkaSettings>(config.GetSection("KafkaSettings"))
.Configure<NotificationOrderConfig>(config.GetSection("NotificationOrderConfig"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Altinn.Notifications.Core.Models.Notification
{
/// <summary>
/// An implementation of <see cref="INotificationSummary{TClass}"/> for email notifications"/>
/// </summary>
public class EmailNotificationSummary : INotificationSummary<EmailNotificationWithResult>
{
/// <inheritdoc/>
public Guid OrderId { get; set; }

/// <inheritdoc/>
public string? SendersReference { get; set; }

/// <inheritdoc/>
public int Generated { get; internal set; }

/// <inheritdoc/>
public int Succeeded { get; internal set; }

/// <inheritdoc/>
public List<EmailNotificationWithResult> Notifications { get; set; } = new List<EmailNotificationWithResult>();

/// <summary>
/// Initializes a new instance of the <see cref="EmailNotificationSummary"/> class.
/// </summary>
public EmailNotificationSummary(Guid orderId)
{
OrderId = orderId;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Models.Recipients;

namespace Altinn.Notifications.Core.Models.Notification
{
/// <summary>
/// An implementation of <see cref="INotificationWithResult{TClass, T}"/> for email notifications"/>
/// Using the <see cref="EmailRecipient"/> as recipient type and the <see cref="EmailNotificationResultType"/> as result type
/// </summary>
public class EmailNotificationWithResult : INotificationWithResult<EmailRecipient, EmailNotificationResultType>
{
/// <inheritdoc/>
public Guid Id { get; }

/// <inheritdoc/>
public bool Succeeded { get; internal set; }

/// <inheritdoc/>
public EmailRecipient Recipient { get; }

/// <inheritdoc/>
public NotificationResult<EmailNotificationResultType> ResultStatus { get; }

/// <summary>
/// Initializes a new instance of the <see cref="EmailNotificationWithResult"/> class.
/// </summary>
public EmailNotificationWithResult(Guid id, EmailRecipient recipient, NotificationResult<EmailNotificationResultType> result)
{
Id = id;
Recipient = recipient;
ResultStatus = result;
}

/// <summary>
/// Initializes a new instance of the <see cref="EmailNotificationWithResult"/> class.
/// </summary>
internal EmailNotificationWithResult(Guid id, bool succeeded, EmailRecipient recipient, NotificationResult<EmailNotificationResultType> result)
{
Id = id;
Succeeded = succeeded;
Recipient = recipient;
ResultStatus = result;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Altinn.Notifications.Core.Models.Notification;

/// <summary>
/// An interface representing a summary of the notifications related to an order
/// </summary>
public interface INotificationSummary<TClass>
where TClass : class
{
/// <summary>
/// Gets the notification order id
/// </summary>
public Guid OrderId { get; }

/// <summary>
/// Gets the senders reference of the notification order
/// </summary>
public string? SendersReference { get; }

/// <summary>
/// Gets the number of generated notifications
/// </summary>
public int Generated { get; }

/// <summary>
/// Gets the number of succeeeded notifications
/// </summary>
public int Succeeded { get; }

/// <summary>
/// Gets the list of notifications with send result
/// </summary>
public List<TClass> Notifications { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Altinn.Notifications.Core.Models.Notification;

/// <summary>
/// Interface representing a notification object with send result data
/// </summary>
/// <typeparam name="TClass">The class representing the recipient</typeparam>
/// <typeparam name="TEnum">The enum used to describe the send result</typeparam>
public interface INotificationWithResult<TClass, TEnum>
where TClass : class
where TEnum : struct, Enum
{
/// <summary>
/// Gets the notification id
/// </summary>
public Guid Id { get; }

/// <summary>
/// Gets a boolean indicating if the sending was successful
/// </summary>
public bool Succeeded { get; }

/// <summary>
/// Sets the recipient with contact points
/// </summary>
public TClass Recipient { get; }

/// <summary>
/// Gets the send result
/// </summary>
public NotificationResult<TEnum> ResultStatus { get; }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Altinn.Notifications.Core.Services.Interfaces;

namespace Altinn.Notifications.Core.Models.Notification;
namespace Altinn.Notifications.Core.Models.Notification;

/// <summary>
/// A class represednting a notification result
Expand All @@ -17,6 +15,14 @@ public NotificationResult(TEnum result, DateTime resultTime)
Result = result;
}

/// <summary>
/// Sets the result description
/// </summary>
public void SetResultDescription(string? description)
{
ResultDescription = description;
}

/// <summary>
/// Gets the date and time for when the last result was set.
/// </summary>
Expand All @@ -26,4 +32,9 @@ public NotificationResult(TEnum result, DateTime resultTime)
/// Gets the send result of the notification
/// </summary>
public TEnum Result { get; }

/// <summary>
/// Gets the description of the send result
/// </summary>
public string? ResultDescription { get; private set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Altinn.Notifications.Core.Models.Notification;

namespace Altinn.Notifications.Core.Repository.Interfaces;

/// <summary>
/// Interface describing all repository operations related to notification summaries
/// </summary>
public interface INotificationSummaryRepository
{
/// <summary>
/// Retrieves all email notifications for the provided order id in an email notification summary
/// </summary>
/// <returns>A partial email notification summary object</returns>
public Task<EmailNotificationSummary?> GetEmailSummary(Guid orderId, string creator);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Notification;

namespace Altinn.Notifications.Core.Services.Interfaces
{
/// <summary>
/// Interface describing the notification summary service
/// </summary>
public interface INotificationSummaryService
{
/// <summary>
/// Gets a summary of all the generated email notifications for the provided order id
/// </summary>
/// <param name="orderId">The order id to find notifications for</param>
/// <param name="creator">The creator of the order</param>
public Task<(EmailNotificationSummary? Summary, ServiceError? Error)> GetEmailSummary(Guid orderId, string creator);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Altinn.Notifications.Core.Enums;
using Altinn.Notifications.Core.Models;
using Altinn.Notifications.Core.Models.Notification;
using Altinn.Notifications.Core.Repository.Interfaces;
using Altinn.Notifications.Core.Services.Interfaces;

namespace Altinn.Notifications.Core.Services
{
/// <summary>
/// Implementation of <see cref="INotificationSummaryService"/>
/// </summary>
public class NotificationSummaryService : INotificationSummaryService
{
private readonly INotificationSummaryRepository _summaryRepository;
private readonly static Dictionary<EmailNotificationResultType, string> _emailResultDescriptions = new()
{
{ EmailNotificationResultType.New, "The email has been created, but has not been picked up for processing yet." },
{ EmailNotificationResultType.Sending, "The email is being processed and will be attempted sent shortly." },
{ EmailNotificationResultType.Succeeded, "The email has been accepted by the third party email service and will be sent shortly." },
{ EmailNotificationResultType.Delivered, "The email was delivered to the recipient. No errors reported, making it likely it was received by the recipient." },
{ EmailNotificationResultType.Failed, "The email was not sent due to an unspecified failure." },
{ EmailNotificationResultType.Failed_RecipientNotIdentified, "The email was not sent because the recipient's email address was not found." },
{ EmailNotificationResultType.Failed_InvalidEmailFormat, "The email was not sent because the recipient’s email address is in an invalid format." }
};

private readonly static List<EmailNotificationResultType> _successResults = new()
{
EmailNotificationResultType.Succeeded,
EmailNotificationResultType.Delivered
};

/// <summary>
/// Initializes a new instance of the <see cref="NotificationSummaryService"/> class.
/// </summary>
public NotificationSummaryService(INotificationSummaryRepository summaryRepository)
{
_summaryRepository = summaryRepository;
}

/// <inheritdoc/>
public async Task<(EmailNotificationSummary? Summary, ServiceError? Error)> GetEmailSummary(Guid orderId, string creator)
{
EmailNotificationSummary? summary = await _summaryRepository.GetEmailSummary(orderId, creator);

if (summary == null)
{
return (null, new ServiceError(404));
}

if (summary.Notifications.Any())
{
ProcessNotificationResults(summary);
}

return (summary, null);
}

/// <summary>
/// Processes the notification results setting counts and descriptions
/// </summary>
internal static void ProcessNotificationResults(EmailNotificationSummary summary)
{
summary.Generated = summary.Notifications.Count;

foreach (EmailNotificationWithResult notification in summary.Notifications)
{
NotificationResult<EmailNotificationResultType> resultStatus = notification.ResultStatus;
if (IsSuccessResult(resultStatus.Result))
{
notification.Succeeded = true;
++summary.Succeeded;
}

resultStatus.SetResultDescription(GetResultDescription(resultStatus.Result));
}
}

/// <summary>
/// Checks if the <see cref="EmailNotificationResultType"/> is a success result
/// </summary>
internal static bool IsSuccessResult(EmailNotificationResultType result)
{
return _successResults.Contains(result);
}

/// <summary>
/// Gets the English description of the <see cref="EmailNotificationResultType"/>"
/// </summary>
internal static string GetResultDescription(EmailNotificationResultType result)
{
return _emailResultDescriptions[result];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public static IServiceCollection AddPostgresRepositories(this IServiceCollection
return services
.AddSingleton<IOrderRepository, OrderRepository>()
.AddSingleton<IEmailNotificationRepository, EmailNotificationRepository>()
.AddSingleton<INotificationSummaryRepository, NotificationSummaryRepository>()
.AddNpgsqlDataSource(connectionString, builder => builder.EnableParameterLogging(settings.LogParameters));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
CREATE OR REPLACE FUNCTION notifications.getemailsummary(
_alternateorderid uuid,
_creatorname text)
RETURNS TABLE(
sendersreference text,
alternateid uuid,
recipientid text,
toaddress text,
result emailnotificationresulttype,
resulttime timestamptz)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000

AS $BODY$

BEGIN
RETURN QUERY
SELECT o.sendersreference, n.alternateid, n.recipientid, n.toaddress, n.result, n.resulttime
FROM notifications.emailnotifications n
LEFT JOIN notifications.orders o ON n._orderid = o._id
WHERE o.alternateid = _alternateorderid
and o.creatorname = _creatorname;
IF NOT FOUND THEN
RETURN QUERY
SELECT o.sendersreference, NULL::uuid, NULL::text, NULL::text, NULL::emailnotificationresulttype, NULL::timestamptz
FROM notifications.orders o
WHERE o.alternateid = _alternateorderid
and o.creatorname = _creatorname;
END IF;
END;
$BODY$;
Loading

0 comments on commit a776357

Please sign in to comment.