using Dapper;
using JdeScoping.DataAccess.Interfaces;
using JdeScoping.Domain.Models;
using Microsoft.Extensions.Logging;
namespace JdeScoping.DataAccess.Services;
///
/// Service implementation for managing manual data sync requests.
///
public sealed class ManualSyncRequestService : IManualSyncRequestService
{
private readonly IDbConnectionFactory _connectionFactory;
private readonly ILogger _logger;
///
/// Initializes a new instance of the class.
///
/// The database connection factory.
/// The logger instance.
public ManualSyncRequestService(
IDbConnectionFactory connectionFactory,
ILogger logger)
{
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
///
public async Task> GetRequestsAsync(
bool pendingOnly = false,
CancellationToken ct = default)
{
_logger.LogDebug("Getting manual sync requests (pendingOnly: {PendingOnly})", pendingOnly);
const string sqlAll = """
SELECT ID, PipelineName, SyncType, RequestDT, RequestedBy,
CompletedDT, CancelDT, CancelledBy, RowVersion
FROM dbo.ManualSyncRequest
ORDER BY RequestDT DESC
""";
const string sqlPending = """
SELECT ID, PipelineName, SyncType, RequestDT, RequestedBy,
CompletedDT, CancelDT, CancelledBy, RowVersion
FROM dbo.ManualSyncRequest
WHERE CompletedDT IS NULL AND CancelDT IS NULL
ORDER BY RequestDT ASC
""";
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
var results = await connection.QueryAsync(
pendingOnly ? sqlPending : sqlAll);
var list = results.ToList();
_logger.LogDebug("Retrieved {Count} manual sync requests", list.Count);
return list;
}
///
public async Task GetNextPendingRequestAsync(CancellationToken ct = default)
{
_logger.LogDebug("Getting next pending manual sync request");
const string sql = """
SELECT TOP 1 ID, PipelineName, SyncType, RequestDT, RequestedBy,
CompletedDT, CancelDT, CancelledBy, RowVersion
FROM dbo.ManualSyncRequest
WHERE CompletedDT IS NULL AND CancelDT IS NULL
ORDER BY RequestDT ASC
""";
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
var result = await connection.QueryFirstOrDefaultAsync(sql);
if (result != null)
{
_logger.LogDebug("Found pending request ID {Id} for pipeline {Pipeline}",
result.Id, result.PipelineName);
}
else
{
_logger.LogDebug("No pending manual sync requests found");
}
return result;
}
///
public async Task CreateRequestAsync(
string pipelineName,
string syncType,
string requestedBy,
CancellationToken ct = default)
{
_logger.LogInformation(
"Creating manual sync request for pipeline {Pipeline}, type {SyncType}, by {User}",
pipelineName, syncType, requestedBy);
const string sql = """
INSERT INTO dbo.ManualSyncRequest (PipelineName, SyncType, RequestedBy)
OUTPUT INSERTED.ID, INSERTED.PipelineName, INSERTED.SyncType,
INSERTED.RequestDT, INSERTED.RequestedBy,
INSERTED.CompletedDT, INSERTED.CancelDT, INSERTED.CancelledBy,
INSERTED.RowVersion
VALUES (@PipelineName, @SyncType, @RequestedBy)
""";
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
var result = await connection.QuerySingleAsync(sql, new
{
PipelineName = pipelineName,
SyncType = syncType,
RequestedBy = requestedBy
});
_logger.LogInformation("Created manual sync request with ID {Id}", result.Id);
return result;
}
///
public async Task CancelRequestAsync(
int id,
string cancelledBy,
byte[] rowVersion,
CancellationToken ct = default)
{
_logger.LogInformation(
"Cancelling manual sync request ID {Id} by {User}",
id, cancelledBy);
const string sql = """
UPDATE dbo.ManualSyncRequest
SET CancelDT = @CancelDT, CancelledBy = @CancelledBy
WHERE ID = @Id AND RowVersion = @RowVersion
AND CompletedDT IS NULL AND CancelDT IS NULL
""";
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
var affectedRows = await connection.ExecuteAsync(sql, new
{
Id = id,
CancelDT = DateTime.UtcNow,
CancelledBy = cancelledBy,
RowVersion = rowVersion
});
var success = affectedRows > 0;
if (success)
{
_logger.LogInformation("Successfully cancelled manual sync request ID {Id}", id);
}
else
{
_logger.LogWarning(
"Failed to cancel manual sync request ID {Id} - already completed/cancelled or version mismatch",
id);
}
return success;
}
///
public async Task CompleteRequestAsync(
int id,
byte[] rowVersion,
CancellationToken ct = default)
{
_logger.LogInformation("Completing manual sync request ID {Id}", id);
const string sql = """
UPDATE dbo.ManualSyncRequest
SET CompletedDT = @CompletedDT
WHERE ID = @Id AND RowVersion = @RowVersion
AND CompletedDT IS NULL AND CancelDT IS NULL
""";
await using var connection = await _connectionFactory.CreateLotFinderConnectionAsync(ct);
var affectedRows = await connection.ExecuteAsync(sql, new
{
Id = id,
CompletedDT = DateTime.UtcNow,
RowVersion = rowVersion
});
var success = affectedRows > 0;
if (success)
{
_logger.LogInformation("Successfully completed manual sync request ID {Id}", id);
}
else
{
_logger.LogWarning(
"Failed to complete manual sync request ID {Id} - already completed/cancelled or version mismatch",
id);
}
return success;
}
}