Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split a data update query into two separate queries #713

Merged
merged 5 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/Altinn.Notifications.Core/Models/Sms.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class Sms
/// <summary>
/// Gets or sets the unique identifier of the SMS.
/// </summary>
public Guid Id { get; set; }
public Guid NotificationId { get; set; }

/// <summary>
/// Gets or sets the sender.
Expand All @@ -23,7 +23,7 @@ public class Sms
/// <summary>
/// Gets or sets the recipient phone number.
/// </summary>
public string RecipientNumber { get; set; }
public string Recipient { get; set; }

/// <summary>
/// Gets or sets the content of the SMS message.
Expand All @@ -33,16 +33,16 @@ public class Sms
/// <summary>
/// Initializes a new instance of the <see cref="Sms"/> class with the specified parameters.
/// </summary>
/// <param name="id">The unique identifier of the SMS.</param>
/// <param name="notificationId">The unique identifier of the SMS.</param>
/// <param name="sender">The sender of the SMS message.</param>
/// <param name="recipientNumber">The recipient of the SMS message.</param>
/// <param name="recipient">The recipient of the SMS message.</param>
/// <param name="message">The content of the SMS message.</param>
public Sms(Guid id, string sender, string recipientNumber, string message)
public Sms(Guid notificationId, string sender, string recipient, string message)
{
Id = id;
Sender = sender;
Message = message;
RecipientNumber = recipientNumber;
Recipient = recipient;
NotificationId = notificationId;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface ISmsNotificationRepository
Task AddNotification(SmsNotification notification, DateTime expiry, int count);

/// <summary>
/// Retrieves all SMS notifications with a status 'New'.
/// Retrieves all SMS notifications that have the status 'New'.
/// </summary>
/// <returns>A task that represents the asynchronous operation. The task result contains a list of new SMS notifications.</returns>
Task<List<Sms>> GetNewNotifications();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public async Task SendNotifications()
bool success = await _producer.ProduceAsync(_smsQueueTopicName, sms.Serialize());
if (!success)
{
await _repository.UpdateSendStatus(sms.Id, SmsNotificationResultType.New);
await _repository.UpdateSendStatus(sms.NotificationId, SmsNotificationResultType.New);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,23 @@ 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, $8, $9, $10)"; // (__orderid, _alternateid, _recipientorgno, _recipientnin, _mobilenumber, _customizedbody, _result, _smscount, _resulttime, _expirytime)
private const string _getSmsNotificationsSql = "select * from notifications.getsms_statusnew_updatestatus()";
private const string _getSmsRecipients = "select * from notifications.getsmsrecipients_v2($1)"; // (_orderid)
private const string _getNewSmsNotificationsSql = "select * from notifications.getsms_statusnew_updatestatus()";
private const string _getSmsNotificationRecipientsSql = "select * from notifications.getsmsrecipients_v2($1)"; // (_orderid)
private const string _insertNewSmsNotificationSql = "call notifications.insertsmsnotification($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"; // (__orderid, _alternateid, _recipientorgno, _recipientnin, _mobilenumber, _customizedbody, _result, _smscount, _resulttime, _expirytime)

private const string _updateSmsNotificationStatus =
private const string _updateSmsNotificationBasedOnIdentifierSql =
@"UPDATE notifications.smsnotifications
SET result = $1::smsnotificationresulttype,
resulttime = now(),
gatewayreference = $2
gatewayreference = $2
WHERE alternateid = $3"; // (_result, _gatewayreference, _alternateid)

private const string _updateSmsNotificationBasedOnGatewayReferenceSql =
@"UPDATE notifications.smsnotifications
SET result = $1::smsnotificationresulttype,
resulttime = now()
WHERE gatewayreference = $2"; // (_result, _gatewayreference)

/// <summary>
/// Initializes a new instance of the <see cref="SmsNotificationRepository"/> class.
/// </summary>
Expand All @@ -46,7 +52,7 @@ public SmsNotificationRepository(NpgsqlDataSource dataSource, TelemetryClient? t
/// <inheritdoc/>
public async Task AddNotification(SmsNotification notification, DateTime expiry, int count)
{
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertSmsNotificationSql);
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_insertNewSmsNotificationSql);
using TelemetryTracker tracker = new(_telemetryClient, pgcom);

pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, notification.OrderId);
Expand All @@ -69,11 +75,11 @@ public async Task<List<SmsRecipient>> GetRecipients(Guid orderId)
{
List<SmsRecipient> recipients = [];

await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsRecipients);
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationRecipientsSql);
using TelemetryTracker tracker = new(_telemetryClient, pgcom);

pgcom.Parameters.AddWithValue(NpgsqlDbType.Uuid, orderId);

await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
Expand All @@ -95,7 +101,7 @@ public async Task<List<SmsRecipient>> GetRecipients(Guid orderId)
public async Task<List<Sms>> GetNewNotifications()
{
List<Sms> readyToSendSMS = [];
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getSmsNotificationsSql);
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_getNewSmsNotificationsSql);
using TelemetryTracker tracker = new(_telemetryClient, pgcom);

await using (NpgsqlDataReader reader = await pgcom.ExecuteReaderAsync())
Expand All @@ -117,15 +123,40 @@ public async Task<List<Sms>> GetNewNotifications()
}

/// <inheritdoc/>
/// <exception cref="ArgumentException">Throws if the provided SMS identifier is empty.</exception>
/// <exception cref="ArgumentException">Throws if the provided SMS identifier is invalid.</exception>
public async Task UpdateSendStatus(Guid id, SmsNotificationResultType result, string? gatewayReference = null)
{
if (id == Guid.Empty && string.IsNullOrWhiteSpace(gatewayReference))
{
throw new ArgumentException("The provided SMS identifier is invalid.");
}

if (id != Guid.Empty)
{
await UpdateSendStatusById(id, result, gatewayReference);
}
else if (!string.IsNullOrWhiteSpace(gatewayReference))
{
await UpdateSendStatusByGatewayReference(gatewayReference, result);
}
}

/// <summary>
/// Updates the send status of an SMS notification based on its identifier and sets the gateway reference.
/// </summary>
/// <param name="id">The unique identifier of the SMS notification.</param>
/// <param name="result">The result status of sending the SMS notification.</param>
/// <param name="gatewayReference">The gateway reference (optional). If provided, it will be updated in the database.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <exception cref="ArgumentException">Thrown when the provided SMS identifier is invalid.</exception>
private async Task UpdateSendStatusById(Guid id, SmsNotificationResultType result, string? gatewayReference = null)
{
if (id == Guid.Empty)
{
throw new ArgumentException("The provided SMS identifier is invalid.");
}

await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_updateSmsNotificationStatus);
await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_updateSmsNotificationBasedOnIdentifierSql);
using TelemetryTracker tracker = new(_telemetryClient, pgcom);
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, result.ToString());
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, string.IsNullOrWhiteSpace(gatewayReference) ? DBNull.Value : gatewayReference);
Expand All @@ -134,4 +165,27 @@ public async Task UpdateSendStatus(Guid id, SmsNotificationResultType result, st
await pgcom.ExecuteNonQueryAsync();
tracker.Track();
}

/// <summary>
/// Updates the send status of an SMS notification based on its gateway reference.
/// </summary>
/// <param name="gatewayReference">The gateway reference of the SMS notification.</param>
/// <param name="result">The result status of sending the SMS notification.</param>
/// <returns>A task that represents the asynchronous operation.</returns>
/// <exception cref="ArgumentException">Thrown when the provided gateway reference is invalid.</exception>
private async Task UpdateSendStatusByGatewayReference(string gatewayReference, SmsNotificationResultType result)
{
if (string.IsNullOrWhiteSpace(gatewayReference))
{
throw new ArgumentException("The provided gateway reference is invalid.");
}

await using NpgsqlCommand pgcom = _dataSource.CreateCommand(_updateSmsNotificationBasedOnGatewayReferenceSql);
using TelemetryTracker tracker = new(_telemetryClient, pgcom);
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, result.ToString());
pgcom.Parameters.AddWithValue(NpgsqlDbType.Text, gatewayReference);

await pgcom.ExecuteNonQueryAsync();
tracker.Track();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public async Task GetNewNotifications()
List<Sms> smsToBeSent = await repo.GetNewNotifications();

// Assert
Assert.Contains(smsToBeSent, s => s.Id == smsNotification.Id);
Assert.Contains(smsToBeSent, s => s.NotificationId == smsNotification.Id);
}

[Fact]
Expand Down Expand Up @@ -213,4 +213,38 @@ public async Task UpdateSendStatus_WithEmptyGuid_ThrowsArgumentException()

Assert.Equal("The provided SMS identifier is invalid.", exception.Message);
}

[Fact]
public async Task UpdateSendStatus_WithoutNotificationId_WithGatewayRef()
{
// Arrange
(NotificationOrder order, SmsNotification smsNotification) = await PostgreUtil.PopulateDBWithOrderAndSmsNotification();
_orderIdsToDelete.Add(order.Id);

SmsNotificationRepository repo = (SmsNotificationRepository)ServiceUtil
.GetServices(new List<Type>() { typeof(ISmsNotificationRepository) })
.First(i => i.GetType() == typeof(SmsNotificationRepository));

string gatewayReference = Guid.NewGuid().ToString();

string setGateqwaySql = $@"Update notifications.smsnotifications
SET gatewayreference = '{gatewayReference}'
WHERE alternateid = '{smsNotification.Id}'";

await PostgreUtil.RunSql(setGateqwaySql);

// Act
await repo.UpdateSendStatus(Guid.Empty, SmsNotificationResultType.Accepted, gatewayReference);

// Assert
string sql = $@"SELECT count(1)
FROM notifications.smsnotifications sms
WHERE sms.alternateid = '{smsNotification.Id}'
AND sms.result = '{SmsNotificationResultType.Accepted}'
AND sms.gatewayreference = '{gatewayReference}'";

int actualCount = await PostgreUtil.RunSqlReturnOutput<int>(sql);

Assert.Equal(1, actualCount);
}
}
Loading