Skip to content

Commit

Permalink
new notification features (#101)
Browse files Browse the repository at this point in the history
* new notification features

* current progress

* app building after merge

* appsettings from main

* currently running ok. Add missing functionality for lookup

* notification to org and person with resource working

* Update EmailNotificationOrdersController.cs
  • Loading branch information
acn-sbuad authored Jun 11, 2024
1 parent c6db4f9 commit 1e28413
Show file tree
Hide file tree
Showing 98 changed files with 1,682 additions and 697 deletions.
4 changes: 2 additions & 2 deletions src/LocalTest.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
Expand All @@ -17,10 +17,10 @@
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
<PackageReference Include="FluentValidation" Version="11.8.0" />
<PackageReference Include="JWTCookieAuthentication" Version="2.4.2" />
<PackageReference Include="libphonenumber-csharp" Version="8.13.34" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="libphonenumber-csharp" Version="8.13.32" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
using Swashbuckle.AspNetCore.Annotations;
using Swashbuckle.AspNetCore.Filters;
#endif

namespace Altinn.Notifications.Controllers;

/// <summary>
Expand Down Expand Up @@ -84,15 +83,10 @@ public async Task<ActionResult<OrderIdExt>> Post(EmailNotificationOrderRequestEx
}
#endif


var orderRequest = emailNotificationOrderRequest.MapToOrderRequest(creator);
Result<NotificationOrder, ServiceError> result = await _orderRequestService.RegisterNotificationOrder(orderRequest);
NotificationOrderRequestResponse result = await _orderRequestService.RegisterNotificationOrder(orderRequest);

return result.Match(
order =>
{
string selfLink = order.GetSelfLink();
return Accepted(selfLink, new OrderIdExt(order.Id));
},
error => StatusCode(error.ErrorCode, error.ErrorMessage));
return Accepted(result.OrderId!.GetSelfLinkFromOrderId(), result.MapToExternal());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,9 @@ public async Task<ActionResult<OrderIdExt>> Post(SmsNotificationOrderRequestExt
}
#endif

NotificationOrderRequest orderRequest = smsNotificationOrderRequest.MapToOrderRequest(creator);
Result<NotificationOrder, ServiceError> result = await _orderRequestService.RegisterNotificationOrder(orderRequest);
var orderRequest = smsNotificationOrderRequest.MapToOrderRequest(creator);
NotificationOrderRequestResponse result = await _orderRequestService.RegisterNotificationOrder(orderRequest);

return result.Match(
registeredOrder =>
{
string selfLink = registeredOrder!.GetSelfLink();
return Accepted(selfLink, new OrderIdExt(registeredOrder!.Id));
},
error => StatusCode(error.ErrorCode, error.ErrorMessage));
return Accepted(result.OrderId.GetSelfLinkFromOrderId(), result.MapToExternal());
}
}
15 changes: 15 additions & 0 deletions src/Notifications/API/Extensions/HttpContextExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Altinn.Notifications.Extensions;

/// <summary>
/// Extensions for HTTP Context
/// </summary>
public static class HttpContextExtensions
{
/// <summary>
/// Get the org string from the context items or null if it is not defined
/// </summary>
public static string? GetOrg(this HttpContext context)
{
return context.Items["Org"] as string;
}
}
27 changes: 20 additions & 7 deletions src/Notifications/API/Extensions/ResourceLinkExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Models;

namespace Altinn.Notifications.Extensions;
Expand Down Expand Up @@ -39,7 +38,6 @@ public static void SetResourceLinks(this NotificationOrderExt order)
{
Self = self,
Status = self + "/status",
Notifications = self + "/notifications"
};
}

Expand All @@ -56,26 +54,41 @@ public static void NotificationSummaryResourceLinks(this NotificationOrderWithSt

string baseUri = $"{_baseUri}/notifications/api/v1/orders/{order!.Id}/notifications/";

if (order.NotificationsStatusSummary?.Email != null)
NotificationsStatusSummaryExt? summary = order.NotificationsStatusSummary;

if (summary?.Email != null)
{
order.NotificationsStatusSummary.Email.Links = new()
summary.Email.Links = new()
{
Self = baseUri + "email"
};
}

if (summary?.Sms != null)
{
summary.Sms.Links = new()
{
Self = baseUri + "sms"
};
}
}

/// <summary>
/// Gets the self link for the provided notification order
/// </summary>
/// <exception cref="InvalidOperationException">Exception if class has not been initialized in Program.cs</exception>
public static string GetSelfLink(this NotificationOrder order)
public static string GetSelfLinkFromOrderId(this Guid? orderId)
{
if (_baseUri == null)
{
throw new InvalidOperationException("ResourceLinkExtensions has not been initialized with the base URI.");
}

return _baseUri + "/notifications/api/v1/orders/" + order!.Id;
if (orderId == null)
{
return string.Empty;
}

return _baseUri + "/notifications/api/v1/orders/" + orderId.ToString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Models;

namespace Altinn.Notifications.Mappers;

/// <summary>
/// Mapper class
/// </summary>
public static class NotificationOrderRequestResponseMapper
{
/// <summary>
/// Maps a <see cref="NotificationOrderRequestResponse"/> to a <see cref="NotificationOrderRequestResponseExt"/>
/// </summary>
public static NotificationOrderRequestResponseExt MapToExternal(this NotificationOrderRequestResponse requestResponse)
{
NotificationOrderRequestResponseExt ext = new()
{
OrderId = requestResponse.OrderId
};

if (requestResponse.RecipientLookup != null)
{
ext.RecipientLookup = new RecipientLookupResultExt
{
Status = Enum.Parse<RecipientLookupStatusExt>(requestResponse.RecipientLookup.Status.ToString(), true),
IsReserved = requestResponse.RecipientLookup?.IsReserved,
MissingContact = requestResponse.RecipientLookup?.MissingContact,
};
}

return ext;
}
}
66 changes: 48 additions & 18 deletions src/Notifications/API/Mappers/OrderMapper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#nullable enable
using Altinn.Notifications.Core.Enums;
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.Models.NotificationTemplate;
using Altinn.Notifications.Core.Models.Orders;
using Altinn.Notifications.Extensions;
Expand All @@ -20,20 +18,36 @@ public static class OrderMapper
/// </summary>
public static NotificationOrderRequest MapToOrderRequest(this EmailNotificationOrderRequestExt extRequest, string creator)
{
var emailTemplate = new EmailTemplate(null, extRequest.Subject, extRequest.Body, (EmailContentType)extRequest.ContentType);
var emailTemplate = new EmailTemplate(
null,
extRequest.Subject,
extRequest.Body,
(EmailContentType?)extRequest.ContentType ?? EmailContentType.Plain);

List<Recipient> recipients =
extRequest.Recipients
.Select(r =>
{
List<IAddressPoint> addresses = new();
var recipients = new List<Recipient>();
if (!string.IsNullOrEmpty(r.EmailAddress))
{
addresses.Add(new EmailAddressPoint(r.EmailAddress));
}
recipients.AddRange(
extRequest.Recipients.Select(r => new Recipient(new List<IAddressPoint>() { new EmailAddressPoint(r.EmailAddress!) })));
return new Recipient(addresses, r.OrganizationNumber, r.NationalIdentityNumber);
})
.ToList();

return new NotificationOrderRequest(
extRequest.SendersReference,
creator,
new(){ emailTemplate },
new List<INotificationTemplate>() { emailTemplate },
extRequest.RequestedSendTime.ToUniversalTime(),
NotificationChannel.Email,
recipients);
recipients,
extRequest.IgnoreReservation,
extRequest.ResourceId);
}

/// <summary>
Expand All @@ -43,18 +57,30 @@ public static NotificationOrderRequest MapToOrderRequest(this SmsNotificationOrd
{
INotificationTemplate smsTemplate = new SmsTemplate(extRequest.SenderNumber, extRequest.Body);

List<Recipient> recipients = new();
List<Recipient> recipients =
extRequest.Recipients
.Select(r =>
{
List<IAddressPoint> addresses = new();
recipients.AddRange(
extRequest.Recipients.Select(r => new Recipient(new List<IAddressPoint>() { new SmsAddressPoint(r.MobileNumber!) })));
if (!string.IsNullOrEmpty(r.MobileNumber))
{
addresses.Add(new SmsAddressPoint(r.MobileNumber));
}
return new Recipient(addresses, r.OrganizationNumber, r.NationalIdentityNumber);
})
.ToList();

return new NotificationOrderRequest(
extRequest.SendersReference,
creator,
new(){ smsTemplate },
new List<INotificationTemplate>() { smsTemplate },
extRequest.RequestedSendTime.ToUniversalTime(),
NotificationChannel.Sms,
recipients);
recipients,
extRequest.IgnoreReservation,
extRequest.ResourceId);
}

/// <summary>
Expand All @@ -66,6 +92,7 @@ public static NotificationOrderExt MapToNotificationOrderExt(this NotificationOr

orderExt.MapBaseNotificationOrder(order);
orderExt.Recipients = order.Recipients.MapToRecipientExt();
orderExt.IgnoreReservation = order.IgnoreReservation;

foreach (var template in order.Templates)
{
Expand Down Expand Up @@ -174,23 +201,26 @@ internal static List<RecipientExt> MapToRecipientExt(this List<Recipient> recipi
recipientExt.AddRange(
recipients.Select(r => new RecipientExt
{
OrganisationNumber = r.OrganisationNumber,
NationalIdentityNumber = r.NationalIdentityNumber,
EmailAddress = GetEmailFromAddressList(r.AddressInfo),
MobileNumber = GetMobileNumberFromAddressList(r.AddressInfo)
MobileNumber = GetMobileNumberFromAddressList(r.AddressInfo),
NationalIdentityNumber = r.NationalIdentityNumber,
OrganizationNumber = r.OrganizationNumber,
IsReserved = r.IsReserved
}));

return recipientExt;
}

private static IBaseNotificationOrderExt MapBaseNotificationOrder(this IBaseNotificationOrderExt orderExt, IBaseNotificationOrder order)
private static BaseNotificationOrderExt MapBaseNotificationOrder(this BaseNotificationOrderExt orderExt, IBaseNotificationOrder order)
{
orderExt.Id = order.Id.ToString();
orderExt.SendersReference = order.SendersReference;
orderExt.Created = order.Created;
orderExt.Creator = order.Creator.ShortName;
orderExt.NotificationChannel = (NotificationChannelExt)order.NotificationChannel;
orderExt.RequestedSendTime = order.RequestedSendTime;
orderExt.IgnoreReservation = order.IgnoreReservation;
orderExt.ResourceId = order.ResourceId;

return orderExt;
}
Expand Down
61 changes: 61 additions & 0 deletions src/Notifications/API/Models/BaseNotificationOrderExt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System.Text.Json.Serialization;

namespace Altinn.Notifications.Models;

/// <summary>
/// A class representing the base properties of a registered notification order.
/// </summary>
/// <remarks>
/// External representaion to be used in the API.
/// </remarks>
public class BaseNotificationOrderExt
{
/// <summary>
/// Gets or sets the id of the notification order
/// </summary>
[JsonPropertyName("id")]
public string Id { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the senders reference of the notification
/// </summary>
[JsonPropertyName("sendersReference")]
public string? SendersReference { get; set; }

/// <summary>
/// Gets or sets the requested send time of the notification
/// </summary>
[JsonPropertyName("requestedSendTime")]
public DateTime RequestedSendTime { get; set; }

/// <summary>
/// Gets or sets the short name of the creator of the notification order
/// </summary>
[JsonPropertyName("creator")]
public string Creator { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the date and time of when the notification order was created
/// </summary>
[JsonPropertyName("created")]
public DateTime Created { get; set; }

/// <summary>
/// Gets or sets the preferred notification channel of the notification order
/// </summary>
[JsonPropertyName("notificationChannel")]
[JsonConverter(typeof(JsonStringEnumConverter))]
public NotificationChannelExt NotificationChannel { get; set; }

/// <summary>
/// Gets or sets whether notifications generated by this order should ignore KRR reservations
/// </summary>
[JsonPropertyName("ignoreReservation")]
public bool? IgnoreReservation { get; set; }

/// <summary>
/// Gets or sets the id of the resource that the notification is related to
/// </summary>
[JsonPropertyName("resourceId")]
public string? ResourceId { get; set; }
}
3 changes: 1 addition & 2 deletions src/Notifications/API/Models/EmailContentTypeExt.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
namespace Altinn.Notifications.Models;
namespace Altinn.Notifications.Models;

/// <summary>
/// Enum describing available content types for an email.
Expand Down
Loading

0 comments on commit 1e28413

Please sign in to comment.