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 notifications endpoint implemented #GCPActive #299

Merged
merged 14 commits into from
Nov 14, 2023
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
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
Loading