Skip to content

Commit 80d82a3

Browse files
authored
Repository logic for retrieving sms notifications #GCPActive (#401)
* functionality implemented * all tests running green * added integration test for get new notifications * explicitly closing reader connection. * Connection Idle Lifetime=60 for integration tests * fixed code smells * fixed pr comments
1 parent fb1f0ff commit 80d82a3

File tree

10 files changed

+223
-12
lines changed

10 files changed

+223
-12
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System.Text.Json;
2+
3+
namespace Altinn.Notifications.Core.Models;
4+
5+
/// <summary>
6+
/// Class representing an sms
7+
/// </summary>
8+
public class Sms
9+
{
10+
/// <summary>
11+
/// Gets or sets the id of the sms.
12+
/// </summary>
13+
public Guid NotificationId { get; set; }
14+
15+
/// <summary>
16+
/// Gets or sets the sender of the sms message
17+
/// </summary>
18+
/// <remarks>
19+
/// Can be a literal string or a phone number
20+
/// </remarks>
21+
public string Sender { get; set; }
22+
23+
/// <summary>
24+
/// Gets or sets the recipient of the sms message
25+
/// </summary>
26+
public string Recipient { get; set; }
27+
28+
/// <summary>
29+
/// Gets or sets the contents of the sms message
30+
/// </summary>
31+
public string Message { get; set; }
32+
33+
/// <summary>
34+
/// Initializes a new instance of the <see cref="Sms"/> class.
35+
/// </summary>
36+
public Sms(Guid notificationId, string sender, string recipient, string message)
37+
{
38+
NotificationId = notificationId;
39+
Recipient = recipient;
40+
Sender = sender;
41+
Message = message;
42+
}
43+
44+
/// <summary>
45+
/// Json serializes the <see cref="Sms"/>
46+
/// </summary>
47+
public string Serialize()
48+
{
49+
return JsonSerializer.Serialize(this, JsonSerializerOptionsProvider.Options);
50+
}
51+
}

src/Altinn.Notifications.Core/Persistence/ISmsNotificationRepository.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Altinn.Notifications.Core.Models.Notification;
1+
using Altinn.Notifications.Core.Models;
2+
using Altinn.Notifications.Core.Models.Notification;
23

34
namespace Altinn.Notifications.Core.Persistence;
45

@@ -11,4 +12,10 @@ public interface ISmsNotificationRepository
1112
/// Adds a new sms notification to the database
1213
/// </summary>
1314
public Task AddNotification(SmsNotification notification, DateTime expiry);
15+
16+
/// <summary>
17+
/// Retrieves all sms notifications with status 'New'
18+
/// </summary>
19+
/// <returns>A list of sms</returns>
20+
public Task<List<Sms>> GetNewNotifications();
1421
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CREATE OR REPLACE FUNCTION notifications.getsms_statusnew_updatestatus()
2+
RETURNS TABLE(alternateid uuid, sendernumber text, mobilenumber text, body text)
3+
LANGUAGE 'plpgsql'
4+
COST 100
5+
VOLATILE PARALLEL UNSAFE
6+
ROWS 1000
7+
AS $BODY$
8+
BEGIN
9+
10+
RETURN query
11+
WITH updated AS (
12+
UPDATE notifications.smsnotifications
13+
SET result = 'Sending', resulttime = now()
14+
WHERE result = 'New'
15+
RETURNING notifications.smsnotifications.alternateid, _orderid, notifications.smsnotifications.mobilenumber)
16+
SELECT u.alternateid, st.sendernumber, u.mobilenumber, st.body
17+
FROM updated u, notifications.smstexts st
18+
WHERE u._orderid = st._orderid;
19+
END;
20+
$BODY$;
21+
22+
ALTER FUNCTION notifications.getsms_statusnew_updatestatus()
23+
OWNER TO platform_notifications_admin;

src/Altinn.Notifications.Persistence/Repository/EmailNotificationRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace Altinn.Notifications.Persistence.Repository;
1212

1313
/// <summary>
14-
/// Implementation of order repository logic
14+
/// Implementation of email notification repository logic
1515
/// </summary>
1616
public class EmailNotificationRepository : IEmailNotificationRepository
1717
{

src/Altinn.Notifications.Persistence/Repository/SmsNotificationRepository.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
using Altinn.Notifications.Core.Models.Notification;
1+
using Altinn.Notifications.Core.Models;
2+
using Altinn.Notifications.Core.Models.Notification;
23
using Altinn.Notifications.Core.Persistence;
4+
using Altinn.Notifications.Persistence.Extensions;
5+
36
using Microsoft.ApplicationInsights;
7+
48
using Npgsql;
9+
510
using NpgsqlTypes;
611

712
namespace Altinn.Notifications.Persistence.Repository;
@@ -15,6 +20,7 @@ public class SmsNotificationRepository : ISmsNotificationRepository
1520
private readonly TelemetryClient? _telemetryClient;
1621

1722
private const string _insertSmsNotificationSql = "call notifications.insertsmsnotification($1, $2, $3, $4, $5, $6, $7)"; // (__orderid, _alternateid, _recipientid, _mobilenumber, _result, _resulttime, _expirytime)
23+
private const string _getSmsNotificationsSql = "select * from notifications.getsms_statusnew_updatestatus()";
1824

1925
/// <summary>
2026
/// Initializes a new instance of the <see cref="SmsNotificationRepository"/> class.
@@ -44,4 +50,29 @@ public async Task AddNotification(SmsNotification notification, DateTime expiry)
4450
await pgcom.ExecuteNonQueryAsync();
4551
tracker.Track();
4652
}
53+
54+
/// <inheritdoc/>
55+
public async Task<List<Sms>> GetNewNotifications()
56+
{
57+
List<Sms> searchResult = new();
58+
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationsSql);
59+
using TelemetryTracker tracker = new(_telemetryClient, pgcom);
60+
61+
await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync())
62+
{
63+
while (await reader.ReadAsync())
64+
{
65+
var sms = new Sms(
66+
reader.GetValue<Guid>("alternateid"),
67+
reader.GetValue<string>("sendernumber"),
68+
reader.GetValue<string>("mobilenumber"),
69+
reader.GetValue<string>("body"));
70+
71+
searchResult.Add(sms);
72+
}
73+
}
74+
75+
tracker.Track();
76+
return searchResult;
77+
}
4778
}

test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/OrderRepositoryTests.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Altinn.Notifications.IntegrationTests.Notifications.Persistence
1010
{
1111
public class OrderRepositoryTests : IAsyncLifetime
1212
{
13-
private List<Guid> orderIdsToDelete;
13+
private readonly List<Guid> orderIdsToDelete;
1414

1515
public OrderRepositoryTests()
1616
{
@@ -44,7 +44,8 @@ public async Task Create_OrderWithSmsTemplate_SmsTextsPersisted()
4444
Templates = new List<INotificationTemplate>()
4545
{
4646
new SmsTemplate("Altinn", "This is the body")
47-
}
47+
},
48+
RequestedSendTime = DateTime.UtcNow
4849
};
4950

5051
orderIdsToDelete.Add(order.Id);

test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsRepositoryTests.cs renamed to test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsNotificationRepositoryTests.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
using Altinn.Notifications.Core.Enums;
1+
using Altinn.Notifications.Core.Models;
22
using Altinn.Notifications.Core.Models.Notification;
33
using Altinn.Notifications.Core.Models.Orders;
44
using Altinn.Notifications.Core.Persistence;
55
using Altinn.Notifications.IntegrationTests.Utils;
66
using Altinn.Notifications.Persistence.Repository;
7+
78
using Xunit;
89

910
namespace Altinn.Notifications.IntegrationTests.Notifications.Persistence;
1011

11-
public class SmsRepositoryTests : IAsyncLifetime
12+
public class SmsNotificationRepositoryTests : IAsyncLifetime
1213
{
13-
private List<Guid> orderIdsToDelete;
14+
private readonly List<Guid> orderIdsToDelete;
1415

15-
public SmsRepositoryTests()
16+
public SmsNotificationRepositoryTests()
1617
{
1718
orderIdsToDelete = [];
1819
}
@@ -29,7 +30,7 @@ public async Task DisposeAsync()
2930
}
3031

3132
[Fact]
32-
public async Task Create_SmsNotification()
33+
public async Task AddNotification()
3334
{
3435
// Arrange
3536
Guid orderId = await PostgreUtil.PopulateDBWithOrderAndReturnId();
@@ -60,5 +61,23 @@ FROM notifications.smsnotifications o
6061
int actualCount = await PostgreUtil.RunSqlReturnOutput<int>(sql);
6162

6263
Assert.Equal(1, actualCount);
63-
}
64+
}
65+
66+
[Fact]
67+
public async Task GetNewNotifications()
68+
{
69+
// Arrange
70+
(NotificationOrder order, SmsNotification smsNotification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification();
71+
orderIdsToDelete.Add(order.Id);
72+
73+
SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil
74+
.GetServices(new List<Type>() { typeof(ISmsNotificationRepository) })
75+
.First(i => i.GetType() == typeof(SmsNotificationRepository));
76+
77+
// Act
78+
List<Sms> smsToBeSent = await repo.GetNewNotifications();
79+
80+
// Assert
81+
Assert.NotEmpty(smsToBeSent.Where(s => s.NotificationId == smsNotification.Id));
82+
}
6483
}

test/Altinn.Notifications.IntegrationTests/Utils/PostgreUtil.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@ public static async Task<NotificationOrder> PopulateDBWithOrderAndEmailNotificat
7171
return o;
7272
}
7373

74+
public static async Task<(NotificationOrder Order, SmsNotification SmsNotification)> PopulateDBWithOrderAndSmsNotification(string? sendersReference = null)
75+
{
76+
(NotificationOrder order, SmsNotification smsNotification) = TestdataUtil.GetOrderAndSmsNotification();
77+
var serviceList = ServiceUtil.GetServices(new List<Type>() { typeof(IOrderRepository), typeof(ISmsNotificationRepository) });
78+
79+
OrderRepository orderRepo = (OrderRepository)serviceList.First(i => i.GetType() == typeof(OrderRepository));
80+
SmsNotificationRepository notificationRepo = (SmsNotificationRepository)serviceList.First(i => i.GetType() == typeof(SmsNotificationRepository));
81+
82+
if (sendersReference != null)
83+
{
84+
order.SendersReference = sendersReference;
85+
}
86+
87+
await orderRepo.Create(order);
88+
await notificationRepo.AddNotification(smsNotification, DateTime.UtcNow.AddDays(1));
89+
90+
return (order, smsNotification);
91+
}
92+
7493
public static async Task DeleteOrderFromDb(string sendersRef)
7594
{
7695
string sql = $"delete from notifications.orders where sendersreference = '{sendersRef}'";
@@ -87,7 +106,6 @@ public static async Task<T> RunSqlReturnOutput<T>(string query)
87106
await reader.ReadAsync();
88107

89108
T result = reader.GetValue<T>(0);
90-
91109
return result;
92110
}
93111

test/Altinn.Notifications.IntegrationTests/Utils/TestdataUtil.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,26 @@ namespace Altinn.Notifications.IntegrationTests.Utils;
99

1010
public static class TestdataUtil
1111
{
12+
public static (NotificationOrder Order, SmsNotification Notification) GetOrderAndSmsNotification()
13+
{
14+
NotificationOrder order = NotificationOrder_SmsTemplate_OneRecipient();
15+
order.Id = Guid.NewGuid();
16+
var recipient = order.Recipients[0];
17+
SmsAddressPoint? addressPoint = recipient.AddressInfo.Find(a => a.AddressType == AddressType.Sms) as SmsAddressPoint;
18+
19+
var smsNotification = new SmsNotification()
20+
{
21+
Id = Guid.NewGuid(),
22+
OrderId = order.Id,
23+
RequestedSendTime = order.RequestedSendTime,
24+
RecipientNumber = addressPoint!.MobileNumber,
25+
RecipientId = recipient.RecipientId,
26+
SendResult = new(SmsNotificationResultType.New, DateTime.UtcNow)
27+
};
28+
29+
return (order, smsNotification);
30+
}
31+
1232
public static (NotificationOrder Order, EmailNotification Notification) GetOrderAndEmailNotification()
1333
{
1434
NotificationOrder order = NotificationOrder_EmailTemplate_OneRecipient();
@@ -69,4 +89,43 @@ public static NotificationOrder NotificationOrder_EmailTemplate_OneRecipient()
6989
}
7090
};
7191
}
92+
93+
/// <summary>
94+
/// NOTE! Overwrite id with a new GUID to ensure it is unique in the test scope.
95+
/// </summary>
96+
public static NotificationOrder NotificationOrder_SmsTemplate_OneRecipient()
97+
{
98+
return new NotificationOrder()
99+
{
100+
SendersReference = "local-testing",
101+
Templates = new List<INotificationTemplate>()
102+
{
103+
new SmsTemplate()
104+
{
105+
Type = NotificationTemplateType.Sms,
106+
Body = "email-body",
107+
SenderNumber = "Altinn local test"
108+
}
109+
},
110+
RequestedSendTime = new DateTime(2023, 06, 16, 08, 50, 00, DateTimeKind.Utc),
111+
NotificationChannel = NotificationChannel.Sms,
112+
Creator = new("ttd"),
113+
Created = new DateTime(2023, 06, 16, 08, 45, 00, DateTimeKind.Utc),
114+
Recipients = new List<Recipient>()
115+
{
116+
new Recipient()
117+
{
118+
RecipientId = "recipient1",
119+
AddressInfo = new()
120+
{
121+
new SmsAddressPoint()
122+
{
123+
AddressType = AddressType.Sms,
124+
MobileNumber = "+4799999999"
125+
}
126+
}
127+
}
128+
}
129+
};
130+
}
72131
}

test/Altinn.Notifications.IntegrationTests/appsettings.IntegrationTest.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"PostgreSQLSettings": {
33
"EnableDBConnection": true,
4+
"AdminConnectionString": "Host=localhost;Port=5432;Username=platform_notifications_admin;Password={0};Database=notificationsdb;Connection Idle Lifetime=60",
5+
"ConnectionString": "Host=localhost;Port=5432;Username=platform_notifications;Password={0};Database=notificationsdb;Connection Idle Lifetime=60",
46
"MigrationScriptPath": "../../../../src/Altinn.Notifications.Persistence/Migration",
57
"EnableDebug": false
68
},

0 commit comments

Comments
 (0)