-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
API endpoint for sms notification summaries #GCPActive (#418)
- Loading branch information
Showing
9 changed files
with
679 additions
and
2 deletions.
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
src/Altinn.Notifications/Controllers/SmsNotificationsController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
using Altinn.Notifications.Configuration; | ||
using Altinn.Notifications.Core.Models.Notification; | ||
using Altinn.Notifications.Core.Services.Interfaces; | ||
using Altinn.Notifications.Core.Shared; | ||
using Altinn.Notifications.Extensions; | ||
using Altinn.Notifications.Mappers; | ||
|
||
using Microsoft.AspNetCore.Authorization; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
using Swashbuckle.AspNetCore.Annotations; | ||
|
||
namespace Altinn.Notifications.Controllers | ||
{ | ||
/// <summary> | ||
/// Controller for all operations related to sms notifications | ||
/// </summary> | ||
[Authorize(Policy = AuthorizationConstants.POLICY_CREATE_SCOPE_OR_PLATFORM_ACCESS)] | ||
[Route("notifications/api/v1/orders/{id}/notifications/sms")] | ||
[ApiController] | ||
[SwaggerResponse(401, "Caller is unauthorized")] | ||
[SwaggerResponse(403, "Caller is not authorized to access the requested resource")] | ||
public class SmsNotificationsController : ControllerBase | ||
{ | ||
private readonly ISmsNotificationSummaryService _summaryService; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="SmsNotificationsController"/> class. | ||
/// </summary> | ||
/// <param name="summaryService">The notifications summary service</param> | ||
public SmsNotificationsController(ISmsNotificationSummaryService summaryService) | ||
{ | ||
_summaryService = summaryService; | ||
} | ||
|
||
/// <summary> | ||
/// Endpoint for retrieving a summary of all sms notifications related to an order | ||
/// </summary> | ||
/// <param name="id">The order id</param> | ||
/// <returns>Sumarized order details and a list containing all sms notifications and their send status</returns> | ||
[HttpGet] | ||
[Produces("application/json")] | ||
[SwaggerResponse(200, "The notification order was accepted", typeof(SmsNotificationSummaryExt))] | ||
[SwaggerResponse(404, "No notification order mathching the id was found")] | ||
public async Task<ActionResult<SmsNotificationSummaryExt>> Get([FromRoute] Guid id) | ||
{ | ||
string? expectedCreator = HttpContext.GetOrg(); | ||
|
||
if (expectedCreator == null) | ||
{ | ||
return Forbid(); | ||
} | ||
|
||
Result<SmsNotificationSummary, ServiceError> result = await _summaryService.GetSummary(id, expectedCreator); | ||
|
||
return result.Match( | ||
summary => Ok(summary.MapToSmsNotificationSummaryExt()), | ||
error => StatusCode(error.ErrorCode, error.ErrorMessage)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
src/Altinn.Notifications/Mappers/SmsNotificationSummaryMapper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using Altinn.Notifications.Core.Models.Notification; | ||
|
||
namespace Altinn.Notifications.Mappers | ||
{ | ||
/// <summary> | ||
/// Mapper for <see cref="SmsNotificationSummaryExt"/> | ||
/// </summary> | ||
public static class SmsNotificationSummaryMapper | ||
{ | ||
/// <summary> | ||
/// Maps a <see cref="SmsNotificationSummary"/> to a <see cref="SmsNotificationSummaryExt"/> | ||
/// </summary> | ||
public static SmsNotificationSummaryExt MapToSmsNotificationSummaryExt(this SmsNotificationSummary summary) | ||
{ | ||
return new SmsNotificationSummaryExt() | ||
{ | ||
OrderId = summary.OrderId, | ||
SendersReference = summary.SendersReference, | ||
Generated = summary.Generated, | ||
Succeeded = summary.Succeeded, | ||
Notifications = summary.Notifications.MapToSmsNotificationWithResultExt(), | ||
}; | ||
} | ||
|
||
/// <summary> | ||
/// Maps a list of <see cref="SmsNotificationWithResult"/> to a list of <see cref="SmsNotificationWithResultExt"/> | ||
/// </summary> | ||
public static List<SmsNotificationWithResultExt> MapToSmsNotificationWithResultExt(this List<SmsNotificationWithResult> notifications) | ||
{ | ||
List<SmsNotificationWithResultExt> result = notifications.Select(n => n.MapToSmsNotificationWithResultExt()).ToList(); | ||
|
||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Maps a <see cref="SmsNotificationWithResult"/> to a <see cref="SmsNotificationWithResultExt"/> | ||
/// </summary> | ||
public static SmsNotificationWithResultExt MapToSmsNotificationWithResultExt(this SmsNotificationWithResult notification) | ||
{ | ||
return new SmsNotificationWithResultExt() | ||
{ | ||
Id = notification.Id, | ||
Succeeded = notification.Succeeded, | ||
Recipient = new() | ||
{ | ||
MobileNumber = notification.Recipient.MobileNumber | ||
}, | ||
SendStatus = new() | ||
{ | ||
Status = notification.ResultStatus.Result.ToString(), | ||
StatusDescription = notification.ResultStatus.ResultDescription, | ||
LastUpdate = notification.ResultStatus.ResultTime | ||
} | ||
}; | ||
} | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/Altinn.Notifications/Models/SmsNotificationSummaryExt.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Altinn.Notifications.Core.Models.Notification | ||
{ | ||
/// <summary> | ||
/// A class representing an sms notification summary | ||
/// </summary> | ||
/// <remarks> | ||
/// External representaion to be used in the API. | ||
/// </remarks> | ||
public class SmsNotificationSummaryExt | ||
{ | ||
/// <summary> | ||
/// The order id | ||
/// </summary> | ||
[JsonPropertyName("orderId")] | ||
public Guid OrderId { get; set; } | ||
|
||
/// <summary> | ||
/// The senders reference | ||
/// </summary> | ||
[JsonPropertyName("sendersReference")] | ||
public string? SendersReference { get; set; } | ||
|
||
/// <summary> | ||
/// The number of generated sms notifications | ||
/// </summary> | ||
[JsonPropertyName("generated")] | ||
public int Generated { get; set; } | ||
|
||
/// <summary> | ||
/// The number of sms notifications that were sent successfully | ||
/// </summary> | ||
[JsonPropertyName("succeeded")] | ||
public int Succeeded { get; set; } | ||
|
||
/// <summary> | ||
/// A list of notifications with send result | ||
/// </summary> | ||
[JsonPropertyName("notifications")] | ||
public List<SmsNotificationWithResultExt> Notifications { get; set; } = []; | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/Altinn.Notifications/Models/SmsNotificationWithResultExt.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System.Text.Json.Serialization; | ||
|
||
using Altinn.Notifications.Models; | ||
|
||
namespace Altinn.Notifications.Core.Models.Notification | ||
{ | ||
/// <summary> | ||
/// A class representing an sms notification with result | ||
/// </summary> | ||
/// <remarks> | ||
/// External representaion to be used in the API. | ||
/// </remarks> | ||
public class SmsNotificationWithResultExt | ||
{ | ||
/// <summary> | ||
/// The notification id | ||
/// </summary> | ||
[JsonPropertyName("id")] | ||
public Guid Id { get; set; } | ||
|
||
/// <summary> | ||
/// Boolean indicating if the sending of the notification was successful | ||
/// </summary> | ||
[JsonPropertyName("succeeded")] | ||
public bool Succeeded { get; set; } | ||
|
||
/// <summary> | ||
/// The recipient of the notification | ||
/// </summary> | ||
[JsonPropertyName("recipient")] | ||
public RecipientExt Recipient { get; set; } = new(); | ||
|
||
/// <summary> | ||
/// The result status of the notification | ||
/// </summary> | ||
[JsonPropertyName("sendStatus")] | ||
public StatusExt SendStatus { get; set; } = new(); | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
...ltinn.Notifications.IntegrationTests/Notifications/SmsNotificationsController/GetTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
using System.Net; | ||
using System.Net.Http.Headers; | ||
using System.Text.Json; | ||
|
||
using Altinn.Common.AccessToken.Services; | ||
using Altinn.Notifications.Core.Models.Notification; | ||
using Altinn.Notifications.Core.Models.Orders; | ||
using Altinn.Notifications.IntegrationTests.Utils; | ||
using Altinn.Notifications.Tests.Notifications.Mocks.Authentication; | ||
using Altinn.Notifications.Tests.Notifications.Utils; | ||
|
||
using AltinnCore.Authentication.JwtCookie; | ||
|
||
using Microsoft.AspNetCore.TestHost; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Options; | ||
using Microsoft.IdentityModel.Logging; | ||
|
||
using Xunit; | ||
|
||
namespace Altinn.Notifications.IntegrationTests.Notifications.SmsNotificationsController | ||
{ | ||
public class GetTests : IClassFixture<IntegrationTestWebApplicationFactory<Controllers.SmsNotificationsController>>, IAsyncLifetime | ||
{ | ||
private readonly string _basePath; | ||
private readonly IntegrationTestWebApplicationFactory<Controllers.SmsNotificationsController> _factory; | ||
private readonly List<Guid> _orderIdsToDelete; | ||
|
||
public GetTests(IntegrationTestWebApplicationFactory<Controllers.SmsNotificationsController> factory) | ||
{ | ||
_basePath = $"/notifications/api/v1/orders"; | ||
_factory = factory; | ||
_orderIdsToDelete = new List<Guid>(); | ||
} | ||
|
||
public async Task InitializeAsync() | ||
{ | ||
await Task.CompletedTask; | ||
} | ||
|
||
async Task IAsyncLifetime.DisposeAsync() | ||
{ | ||
if (_orderIdsToDelete.Count != 0) | ||
{ | ||
string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", _orderIdsToDelete)}')"; | ||
await PostgreUtil.RunSql(deleteSql); | ||
} | ||
} | ||
|
||
[Fact] | ||
public async Task Get_NonExistingOrder_NotFound() | ||
{ | ||
// Arrange | ||
string uri = $"{_basePath}/{Guid.NewGuid()}/notifications/sms"; | ||
|
||
HttpClient client = GetTestClient(); | ||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); | ||
|
||
HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); | ||
|
||
// Act | ||
HttpResponseMessage response = await client.SendAsync(httpRequestMessage); | ||
|
||
// Assert | ||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); | ||
} | ||
|
||
[Fact] | ||
public async Task Get_OrderIdForAnotherCreator_NotFound() | ||
{ | ||
// Arrange | ||
NotificationOrder order = await PostgreUtil.PopulateDBWithSmsOrder(); | ||
_orderIdsToDelete.Add(order.Id); | ||
|
||
string uri = $"{_basePath}/{order.Id}/notifications/sms"; | ||
|
||
HttpClient client = GetTestClient(); | ||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("nav", scope: "altinn:serviceowner/notifications.create")); | ||
|
||
HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); | ||
|
||
// Act | ||
HttpResponseMessage response = await client.SendAsync(httpRequestMessage); | ||
|
||
// Assert | ||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); | ||
} | ||
|
||
[Fact] | ||
public async Task Get_ValidOrderId_Ok() | ||
{ | ||
// Arrange | ||
(NotificationOrder order, SmsNotification notification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification(); | ||
string uri = $"{_basePath}/{order.Id}/notifications/sms"; | ||
_orderIdsToDelete.Add(order.Id); | ||
|
||
HttpClient client = GetTestClient(); | ||
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", PrincipalUtil.GetOrgToken("ttd", scope: "altinn:serviceowner/notifications.create")); | ||
|
||
HttpRequestMessage httpRequestMessage = new(HttpMethod.Get, uri); | ||
|
||
// Act | ||
HttpResponseMessage response = await client.SendAsync(httpRequestMessage); | ||
string responseString = await response.Content.ReadAsStringAsync(); | ||
|
||
// Assert | ||
Assert.Equal(HttpStatusCode.OK, response.StatusCode); | ||
SmsNotificationSummaryExt? summary = JsonSerializer.Deserialize<SmsNotificationSummaryExt>(responseString); | ||
Assert.True(summary?.Notifications.Count > 0); | ||
Assert.Equal(order.Id, summary?.OrderId); | ||
Assert.Equal(notification.Id, summary?.Notifications[0].Id); | ||
Assert.Equal(1, summary?.Generated); | ||
Assert.Equal(0, summary?.Succeeded); | ||
} | ||
|
||
private HttpClient GetTestClient() | ||
{ | ||
HttpClient client = _factory.WithWebHostBuilder(builder => | ||
{ | ||
IdentityModelEventSource.ShowPII = true; | ||
|
||
builder.ConfigureTestServices(services => | ||
{ | ||
// Set up mock authentication and authorization | ||
services.AddSingleton<IPostConfigureOptions<JwtCookieOptions>, JwtCookiePostConfigureOptionsStub>(); | ||
services.AddSingleton<IPublicSigningKeyProvider, PublicSigningKeyProviderMock>(); | ||
}); | ||
}).CreateClient(); | ||
|
||
return client; | ||
} | ||
} | ||
} |
Oops, something went wrong.