diff --git a/src/Altinn.Notifications.Core/Models/Notification/SmsNotification.cs b/src/Altinn.Notifications.Core/Models/Notification/SmsNotification.cs new file mode 100644 index 00000000..54077d4d --- /dev/null +++ b/src/Altinn.Notifications.Core/Models/Notification/SmsNotification.cs @@ -0,0 +1,34 @@ +using Altinn.Notifications.Core.Enums; + +namespace Altinn.Notifications.Core.Models.Notification; + +/// +/// Class describing an sns notification and extends the +/// +public class SmsNotification : INotification +{ + /// + public Guid Id { get; internal set; } + + /// + public Guid OrderId { get; internal set; } + + /// + public DateTime RequestedSendTime { get; internal set; } + + /// + public NotificationChannel NotificationChannel { get; } = NotificationChannel.Sms; + + /// + /// Get the id of the recipient of the sms notification + /// + public string? RecipientId { get; internal set; } + + /// + /// Get or sets the mobilenumber of the sms notification + /// + public string RecipientNumber { get; internal set; } = string.Empty; + + /// + public NotificationResult SendResult { get; internal set; } = new(SmsNotificationResultType.New, DateTime.UtcNow); +} diff --git a/src/Altinn.Notifications.Core/Persistence/ISmsNotificationRepository.cs b/src/Altinn.Notifications.Core/Persistence/ISmsNotificationRepository.cs index 9172ef4a..0aca03e4 100644 --- a/src/Altinn.Notifications.Core/Persistence/ISmsNotificationRepository.cs +++ b/src/Altinn.Notifications.Core/Persistence/ISmsNotificationRepository.cs @@ -1,4 +1,5 @@ using Altinn.Notifications.Core.Models; +using Altinn.Notifications.Core.Models.Notification; namespace Altinn.Notifications.Core.Persistence; @@ -7,6 +8,11 @@ namespace Altinn.Notifications.Core.Persistence; /// public interface ISmsNotificationRepository { + /// + /// Adds a new sms notification to the database + /// + public Task AddNotification(SmsNotification notification, DateTime expiry); + /// /// Retrieves all sms notifications with status 'New' /// diff --git a/src/Altinn.Notifications.Persistence/Migration/v0.17/01-setup-procedures.sql b/src/Altinn.Notifications.Persistence/Migration/v0.17/01-setup-procedures.sql new file mode 100644 index 00000000..b2937cce --- /dev/null +++ b/src/Altinn.Notifications.Persistence/Migration/v0.17/01-setup-procedures.sql @@ -0,0 +1,19 @@ +CREATE OR REPLACE PROCEDURE notifications.insertsmsnotification(_orderid uuid, + _alternateid uuid, + _recipientid TEXT, + _mobilenumber TEXT, + _result text, + _resulttime timestamptz, + _expirytime timestamptz + ) +LANGUAGE 'plpgsql' +AS $BODY$ +DECLARE +__orderid BIGINT := (SELECT _id from notifications.orders + where alternateid = _orderid); +BEGIN + +INSERT INTO notifications.smsnotifications(_orderid, alternateid, recipientid, mobilenumber, result, resulttime, expirytime) + VALUES (__orderid, _alternateid, _recipientid, _mobilenumber, _result::smsnotificationresulttype, _resulttime, _expirytime); +END; +$BODY$ \ No newline at end of file diff --git a/src/Altinn.Notifications.Persistence/Repository/SmsNotificationRepository.cs b/src/Altinn.Notifications.Persistence/Repository/SmsNotificationRepository.cs index 332c9139..f3c88e4d 100644 --- a/src/Altinn.Notifications.Persistence/Repository/SmsNotificationRepository.cs +++ b/src/Altinn.Notifications.Persistence/Repository/SmsNotificationRepository.cs @@ -1,60 +1,79 @@ using Altinn.Notifications.Core.Enums; using Altinn.Notifications.Core.Models; +using Altinn.Notifications.Core.Models.Notification; using Altinn.Notifications.Core.Persistence; using Altinn.Notifications.Persistence.Extensions; using Microsoft.ApplicationInsights; - using Npgsql; +using NpgsqlTypes; + +namespace Altinn.Notifications.Persistence.Repository; -namespace Altinn.Notifications.Persistence.Repository +/// +/// Implementation of sms repository logic +/// +public class SmsNotificationRepository : ISmsNotificationRepository { + private readonly NpgsqlDataSource _dataSource; + private readonly TelemetryClient? _telemetryClient; + + private const string _insertSmsNotificationSql = "call notifications.insertsmsnotification($1, $2, $3, $4, $5, $6, $7)"; // (__orderid, _alternateid, _recipientid, _mobilenumber, _result, _resulttime, _expirytime) + private const string _getSmsNotificationsSql = "select * from notifications.getsms_statusnew_updatestatus()"; + /// - /// Implementation of sms notification repository logic + /// Initializes a new instance of the class. /// - public class SmsNotificationRepository : ISmsNotificationRepository + /// The npgsql data source. + /// Telemetry client + public SmsNotificationRepository(NpgsqlDataSource dataSource, TelemetryClient? telemetryClient = null) { - private readonly NpgsqlDataSource _dataSource; - private readonly TelemetryClient? _telemetryClient; + _dataSource = dataSource; + _telemetryClient = telemetryClient; + } - private const string _getSmsNotificationsSql = "select * from notifications.getsms_statusnew_updatestatus()"; + /// + public async Task AddNotification(SmsNotification notification, DateTime expiry) + { + await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertSmsNotificationSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); - /// - /// Initializes a new instance of the class. - /// - /// The npgsql data source. - /// Telemetry client - public SmsNotificationRepository(NpgsqlDataSource dataSource, TelemetryClient? telemetryClient = null) - { - _dataSource = dataSource; - _telemetryClient = telemetryClient; - } + pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notification.OrderId); + pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notification.Id); + pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, notification.RecipientId ?? (object)DBNull.Value); + pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, notification.RecipientNumber); + pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, notification.SendResult.Result.ToString()); + pgcom.Parameters.AddWithValue(NpgsqlDbType.TimestampTz, notification.SendResult.ResultTime); + pgcom.Parameters.AddWithValue(NpgsqlDbType.TimestampTz, expiry); - /// - public async Task> GetNewNotifications() - { - List searchResult = new(); - await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationsSql); - using TelemetryTracker tracker = new(_telemetryClient, pgcom); + await pgcom.ExecuteNonQueryAsync(); + tracker.Track(); + } + + /// + public async Task> GetNewNotifications() + { + List searchResult = new(); + await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationsSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); - await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) + await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) + { + while (await reader.ReadAsync()) { - while (await reader.ReadAsync()) - { - EmailContentType emailContentType = (EmailContentType)Enum.Parse(typeof(EmailContentType), reader.GetValue("contenttype")); - - var sms = new Sms( - reader.GetValue("alternateid"), - reader.GetValue("sendernumber"), - reader.GetValue("mobilenumber"), - reader.GetValue("body")); - - searchResult.Add(sms); - } - } + EmailContentType emailContentType = (EmailContentType)Enum.Parse(typeof(EmailContentType), reader.GetValue("contenttype")); + + var sms = new Sms( + reader.GetValue("alternateid"), + reader.GetValue("sendernumber"), + reader.GetValue("mobilenumber"), + reader.GetValue("body")); - tracker.Track(); - return searchResult; + searchResult.Add(sms); + } } + + tracker.Track(); + return searchResult; } } diff --git a/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsRepositoryTests.cs b/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsRepositoryTests.cs new file mode 100644 index 00000000..56c13297 --- /dev/null +++ b/test/Altinn.Notifications.IntegrationTests/Notifications.Persistence/SmsRepositoryTests.cs @@ -0,0 +1,64 @@ +using Altinn.Notifications.Core.Enums; +using Altinn.Notifications.Core.Models.Notification; +using Altinn.Notifications.Core.Models.Orders; +using Altinn.Notifications.Core.Persistence; +using Altinn.Notifications.IntegrationTests.Utils; +using Altinn.Notifications.Persistence.Repository; +using Xunit; + +namespace Altinn.Notifications.IntegrationTests.Notifications.Persistence; + +public class SmsRepositoryTests : IAsyncLifetime +{ + private List orderIdsToDelete; + + public SmsRepositoryTests() + { + orderIdsToDelete = []; + } + + public async Task InitializeAsync() + { + await Task.CompletedTask; + } + + public async Task DisposeAsync() + { + string deleteSql = $@"DELETE from notifications.orders o where o.alternateid in ('{string.Join("','", orderIdsToDelete)}')"; + await PostgreUtil.RunSql(deleteSql); + } + + [Fact] + public async Task Create_SmsNotification() + { + // Arrange + Guid orderId = await PostgreUtil.PopulateDBWithOrderAndReturnId(); + orderIdsToDelete.Add(orderId); + + // Arrange + SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil + .GetServices(new List() { typeof(ISmsNotificationRepository) }) + .First(i => i.GetType() == typeof(SmsNotificationRepository)); + + Guid notificationId = Guid.NewGuid(); + SmsNotification smsNotification = new() + { + Id = notificationId, + OrderId = orderId, + RequestedSendTime = DateTime.UtcNow, + RecipientId = "12345678", + RecipientNumber = "999999999", + }; + + await repo.AddNotification(smsNotification, DateTime.UtcNow); + + // Assert + string sql = $@"SELECT count(1) + FROM notifications.smsnotifications o + WHERE o.alternateid = '{notificationId}'"; + + int actualCount = await PostgreUtil.RunSqlReturnOutput(sql); + + Assert.Equal(1, actualCount); + } +}