From dc5318ecb5201b12c938265825938cde103e82d8 Mon Sep 17 00:00:00 2001 From: Henning Normann Date: Tue, 24 Oct 2023 12:35:31 +0200 Subject: [PATCH] Added dependency injection for postgres --- .../Altinn.Notifications.Persistence.csproj | 1 + .../Repository/EmailNotificationRepository.cs | 17 +++++- .../Repository/OrderRepository.cs | 25 ++++++++- .../Repository/TelemetryTracker.cs | 56 +++++++++++++++++++ .../Altinn.Notifications.csproj | 1 + 5 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 src/Altinn.Notifications.Persistence/Repository/TelemetryTracker.cs diff --git a/src/Altinn.Notifications.Persistence/Altinn.Notifications.Persistence.csproj b/src/Altinn.Notifications.Persistence/Altinn.Notifications.Persistence.csproj index a44c604d..4f59aa73 100644 --- a/src/Altinn.Notifications.Persistence/Altinn.Notifications.Persistence.csproj +++ b/src/Altinn.Notifications.Persistence/Altinn.Notifications.Persistence.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Altinn.Notifications.Persistence/Repository/EmailNotificationRepository.cs b/src/Altinn.Notifications.Persistence/Repository/EmailNotificationRepository.cs index 56354a2c..608eed29 100644 --- a/src/Altinn.Notifications.Persistence/Repository/EmailNotificationRepository.cs +++ b/src/Altinn.Notifications.Persistence/Repository/EmailNotificationRepository.cs @@ -4,9 +4,8 @@ using Altinn.Notifications.Core.Models.Recipients; using Altinn.Notifications.Core.Repository.Interfaces; using Altinn.Notifications.Persistence.Extensions; - +using Microsoft.ApplicationInsights; using Npgsql; - using NpgsqlTypes; namespace Altinn.Notifications.Persistence.Repository; @@ -17,6 +16,8 @@ namespace Altinn.Notifications.Persistence.Repository; public class EmailNotificationRepository : IEmailNotificationRepository { private readonly NpgsqlDataSource _dataSource; + private readonly TelemetryClient _telemetryClient; + private const string _insertEmailNotificationSql = "call notifications.insertemailnotification($1, $2, $3, $4, $5, $6, $7)"; // (__orderid, _alternateid, _recipientid, _toaddress, _result, _resulttime, _expirytime) private const string _getEmailNotificationsSql = "select * from notifications.getemails_statusnew_updatestatus()"; private const string _updateEmailStatus = "call notifications.updateemailstatus($1, $2, $3)"; // (_alternateid, _result, _operationid) @@ -26,15 +27,18 @@ public class EmailNotificationRepository : IEmailNotificationRepository /// Initializes a new instance of the class. /// /// The npgsql data source. - public EmailNotificationRepository(NpgsqlDataSource dataSource) + /// Telemetry client + public EmailNotificationRepository(NpgsqlDataSource dataSource, TelemetryClient telemetryClient = null) { _dataSource = dataSource; + _telemetryClient = telemetryClient; } /// public async Task AddNotification(EmailNotification notification, DateTime expiry) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertEmailNotificationSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notification.OrderId); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notification.Id); @@ -45,6 +49,7 @@ public async Task AddNotification(EmailNotification notification, DateTime expir pgcom.Parameters.AddWithValue(NpgsqlDbType.TimestampTz, expiry); await pgcom.ExecuteNonQueryAsync(); + tracker.Track(); } /// @@ -52,6 +57,7 @@ public async Task> GetNewNotifications() { List searchResult = new(); await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getEmailNotificationsSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) { @@ -71,6 +77,7 @@ public async Task> GetNewNotifications() } } + tracker.Track(); return searchResult; } @@ -78,10 +85,12 @@ public async Task> GetNewNotifications() public async Task UpdateSendStatus(Guid notificationId, EmailNotificationResultType status, string? operationId = null) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_updateEmailStatus); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notificationId); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, status.ToString()); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, operationId ?? (object)DBNull.Value); await pgcom.ExecuteNonQueryAsync(); + tracker.Track(); } /// @@ -90,6 +99,7 @@ public async Task> GetRecipients(Guid notificationId) List searchResult = new(); await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getEmailRecipients); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notificationId); await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) { @@ -103,6 +113,7 @@ public async Task> GetRecipients(Guid notificationId) } } + tracker.Track(); return searchResult; } } diff --git a/src/Altinn.Notifications.Persistence/Repository/OrderRepository.cs b/src/Altinn.Notifications.Persistence/Repository/OrderRepository.cs index f510d261..6748a981 100644 --- a/src/Altinn.Notifications.Persistence/Repository/OrderRepository.cs +++ b/src/Altinn.Notifications.Persistence/Repository/OrderRepository.cs @@ -5,6 +5,7 @@ using Altinn.Notifications.Core.Models.Orders; using Altinn.Notifications.Core.Repository.Interfaces; using Altinn.Notifications.Persistence.Extensions; +using Microsoft.ApplicationInsights; using Npgsql; using NpgsqlTypes; @@ -16,6 +17,7 @@ namespace Altinn.Notifications.Persistence.Repository; public class OrderRepository : IOrderRepository { private readonly NpgsqlDataSource _dataSource; + private readonly TelemetryClient _telemetryClient; private const string _getOrderByIdSql = "select notificationorder from notifications.orders where alternateid=$1 and creatorname=$2"; private const string _getOrdersBySendersReferenceSql = "select notificationorder from notifications.orders where sendersreference=$1 and creatorname=$2"; @@ -29,15 +31,18 @@ public class OrderRepository : IOrderRepository /// Initializes a new instance of the class. /// /// The npgsql data source. - public OrderRepository(NpgsqlDataSource dataSource) + /// Telemetry client + public OrderRepository(NpgsqlDataSource dataSource, TelemetryClient telemetryClient = null) { _dataSource = dataSource; + _telemetryClient = telemetryClient; } /// public async Task GetOrderById(Guid id, string creator) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getOrderByIdSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, id); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); @@ -51,6 +56,7 @@ public OrderRepository(NpgsqlDataSource dataSource) } } + tracker.Track(); return order; } @@ -60,6 +66,7 @@ public async Task> GetOrdersBySendersReference(string se List searchResult = new(); await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getOrdersBySendersReferenceSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, sendersReference); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); @@ -72,6 +79,7 @@ public async Task> GetOrdersBySendersReference(string se } } + tracker.Track(); return searchResult; } @@ -105,9 +113,11 @@ public async Task Create(NotificationOrder order) public async Task SetProcessingStatus(Guid orderId, OrderProcessingStatus status) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_setProcessCompleted); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, status.ToString()); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, orderId); await pgcom.ExecuteNonQueryAsync(); + tracker.Track(); } /// @@ -116,7 +126,7 @@ public async Task> GetPastDueOrdersAndSetProcessingState List searchResult = new(); await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getOrdersPastSendTimeUpdateStatus); - + using TelemetryTracker tracker = new(_telemetryClient, pgcom); await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync()) { while (await reader.ReadAsync()) @@ -126,6 +136,7 @@ public async Task> GetPastDueOrdersAndSetProcessingState } } + tracker.Track(); return searchResult; } @@ -133,6 +144,7 @@ public async Task> GetPastDueOrdersAndSetProcessingState public async Task GetOrderWithStatusById(Guid id, string creator) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getOrderIncludeStatus); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, id); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, creator); @@ -163,6 +175,7 @@ public async Task> GetPastDueOrdersAndSetProcessingState } } + tracker.Track(); return order; } @@ -189,6 +202,7 @@ public async Task> GetPastDueOrdersAndSetProcessingState private async Task InsertOrder(NotificationOrder order) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertOrderSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, order.Id); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, order.Creator.ShortName); @@ -199,12 +213,16 @@ private async Task InsertOrder(NotificationOrder order) await using NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync(); await reader.ReadAsync(); - return (long)reader.GetValue(0); + + long orderId = (long)reader.GetValue(0); + tracker.Track(); + return orderId; } private async Task InsertEmailText(long dbOrderId, string fromAddress, string subject, string body, string contentType) { await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertEmailTextSql); + using TelemetryTracker tracker = new(_telemetryClient, pgcom); pgcom.Parameters.AddWithValue(NpgsqlDbType.Bigint, dbOrderId); pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, fromAddress); @@ -213,5 +231,6 @@ private async Task InsertEmailText(long dbOrderId, string fromAddress, string su pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, contentType); await pgcom.ExecuteNonQueryAsync(); + tracker.Track(); } } diff --git a/src/Altinn.Notifications.Persistence/Repository/TelemetryTracker.cs b/src/Altinn.Notifications.Persistence/Repository/TelemetryTracker.cs new file mode 100644 index 00000000..93004128 --- /dev/null +++ b/src/Altinn.Notifications.Persistence/Repository/TelemetryTracker.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using Microsoft.ApplicationInsights; +using Npgsql; + +namespace Altinn.Notifications.Persistence.Repository +{ + /// + /// Helper to track application insights dependencies for PostgreSQL invocations + /// + public class TelemetryTracker : IDisposable + { + private readonly DateTime _startTime = DateTime.Now; + private readonly Stopwatch _timer = Stopwatch.StartNew(); + private readonly TelemetryClient _telemetryClient; + private readonly NpgsqlCommand _cmd; + private bool _tracked = false; + + /// + /// Initializes a new instance of the class. + /// + /// Telemetry client from DI + /// The npgsql cmd + public TelemetryTracker(TelemetryClient telemetryClient, NpgsqlCommand cmd) + { + _telemetryClient = telemetryClient; + _cmd = cmd; + } + + /// + public void Dispose() + { + if (!_tracked) + { + Track(false); + _tracked = true; + } + + GC.SuppressFinalize(this); + } + + /// + /// Track the PostgreSQL invocation + /// Outcome of invocation + /// + public void Track(bool success = true) + { + _timer.Stop(); + if (_telemetryClient != null) + { + _telemetryClient.TrackDependency("Postgres", _cmd.CommandText, _cmd.CommandText, _startTime, _timer.Elapsed, success); + } + + _tracked = true; + } + } +} diff --git a/src/Altinn.Notifications/Altinn.Notifications.csproj b/src/Altinn.Notifications/Altinn.Notifications.csproj index c10163e2..1ca0631b 100644 --- a/src/Altinn.Notifications/Altinn.Notifications.csproj +++ b/src/Altinn.Notifications/Altinn.Notifications.csproj @@ -6,6 +6,7 @@ enable True {A791EC3D-DC08-416D-9522-C4BE4540084F} + abbc1054-3be2-45c5-b3e0-36044cb70e42