using JdeScoping.Api.Contracts.ManualSync;
using JdeScoping.DataAccess.Services;
using JdeScoping.DataSync.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace JdeScoping.Api.Controllers;
///
/// API endpoints for manual data sync request management.
///
[Route("api/manual-sync")]
[ApiController]
[Authorize]
public class ManualSyncController : ApiControllerBase
{
private readonly IManualSyncRequestService _manualSyncRequestService;
private readonly IPipelineRegistry _pipelineRegistry;
///
/// Initializes a new instance of the class.
///
/// The service for managing manual sync requests.
/// The pipeline registry for querying pipeline information.
public ManualSyncController(
IManualSyncRequestService manualSyncRequestService,
IPipelineRegistry pipelineRegistry)
{
_manualSyncRequestService = manualSyncRequestService;
_pipelineRegistry = pipelineRegistry;
}
///
/// Gets all manual sync requests, optionally filtered to pending only.
///
/// If true, returns only pending requests. Default is false.
/// Cancellation token.
/// A list of manual sync request view models.
[HttpGet]
[ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task>> GetRequests(
[FromQuery] bool pendingOnly = false,
CancellationToken ct = default)
{
var requests = await _manualSyncRequestService.GetRequestsAsync(pendingOnly, ct);
var viewModels = requests.Select(r => new ManualSyncRequestViewModel
{
Id = r.Id,
PipelineName = r.PipelineName,
SyncType = r.SyncType,
RequestDT = r.RequestDT,
RequestedBy = r.RequestedBy,
CompletedDT = r.CompletedDT,
CancelDT = r.CancelDT,
CancelledBy = r.CancelledBy,
Status = r.Status,
RowVersionBase64 = Convert.ToBase64String(r.RowVersion)
}).ToList();
return Ok(viewModels);
}
///
/// Gets all available pipelines with their supported sync types.
///
/// A list of pipeline information view models.
[HttpGet("pipelines")]
[ProducesResponseType(typeof(List), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public ActionResult> GetPipelines()
{
var pipelines = _pipelineRegistry.GetEnabledPipelines()
.Select(p => new PipelineInfoViewModel
{
Name = p.Name,
SupportedSyncTypes = GetSupportedSyncTypes(p)
})
.ToList();
return Ok(pipelines);
}
///
/// Creates a new manual sync request.
///
/// The create request containing pipeline name and sync type.
/// Cancellation token.
/// The created manual sync request view model.
[HttpPost]
[ProducesResponseType(typeof(ManualSyncRequestViewModel), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
public async Task> CreateRequest(
[FromBody] CreateManualSyncRequestDto dto,
CancellationToken ct = default)
{
var username = User.Identity?.Name;
if (string.IsNullOrEmpty(username))
{
return Unauthorized();
}
// Validate pipeline and sync type combination using the registry
if (!_pipelineRegistry.IsValidPipelineAndSyncType(dto.PipelineName, dto.SyncType))
{
return BadRequest($"Invalid pipeline/sync type combination. Pipeline '{dto.PipelineName}' does not support sync type '{dto.SyncType}'.");
}
var request = await _manualSyncRequestService.CreateRequestAsync(
dto.PipelineName,
dto.SyncType,
username,
ct);
var viewModel = new ManualSyncRequestViewModel
{
Id = request.Id,
PipelineName = request.PipelineName,
SyncType = request.SyncType,
RequestDT = request.RequestDT,
RequestedBy = request.RequestedBy,
CompletedDT = request.CompletedDT,
CancelDT = request.CancelDT,
CancelledBy = request.CancelledBy,
Status = request.Status,
RowVersionBase64 = Convert.ToBase64String(request.RowVersion)
};
return CreatedAtAction(nameof(GetRequests), viewModel);
}
///
/// Cancels a pending manual sync request.
///
/// The ID of the request to cancel.
/// The cancel request containing the row version for concurrency.
/// Cancellation token.
/// A success message if cancelled, or a conflict error if already processed.
[HttpPost("{id:int}/cancel")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task CancelRequest(
int id,
[FromBody] CancelManualSyncRequestDto dto,
CancellationToken ct = default)
{
var username = User.Identity?.Name;
if (string.IsNullOrEmpty(username))
{
return Unauthorized();
}
byte[] rowVersion;
try
{
rowVersion = Convert.FromBase64String(dto.RowVersionBase64);
}
catch (FormatException)
{
return BadRequest("Invalid RowVersionBase64 format.");
}
var cancelled = await _manualSyncRequestService.CancelRequestAsync(
id,
username,
rowVersion,
ct);
if (!cancelled)
{
return Conflict(new { message = "The request has already been completed or cancelled, or the data has been modified by another user." });
}
return Ok(new { message = "Request cancelled successfully." });
}
private static List GetSupportedSyncTypes(DataSync.Configuration.EtlPipelineConfig pipeline)
{
var types = new List();
if (pipeline.SupportsMassSync) types.Add("mass");
if (pipeline.SupportsDailySync) types.Add("daily");
if (pipeline.SupportsHourlySync) types.Add("hourly");
return types;
}
}