< Summary

Information
Class: OzonOrdersWeb.Areas.Studio2.Controllers.ManualTriggerRequest
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/TransactionJobManagementController.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 3
Coverable lines: 3
Total lines: 320
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_OzonClientId()100%210%
get_DaysBack()100%210%
get_JobType()100%210%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Controllers/TransactionJobManagementController.cs

#LineLine coverage
 1using Hangfire;
 2using Hangfire.Common;
 3using Hangfire.States;
 4using Microsoft.AspNetCore.Authorization;
 5using Microsoft.AspNetCore.Mvc;
 6using Microsoft.EntityFrameworkCore;
 7using OzonDomains.Models;
 8using OzonRepositories.Context;
 9using Services.BackgroundJobsServices;
 10using Services.HangfireService;
 11
 12namespace OzonOrdersWeb.Areas.Studio2.Controllers;
 13
 14[Authorize(Roles = "Admin")]
 15[Area("Studio2")]
 16[Route("api/transaction-jobs")]
 17[ApiController]
 18public class TransactionJobManagementController : ControllerBase
 19{
 20    private readonly IBackgroundJobScheduleService _scheduleService;
 21    private readonly IBackgroundJobLoggingService _loggingService;
 22    private readonly IAutomatedTransactionProcessingService _processingService;
 23    private readonly IBackgroundJobClient _backgroundJobClient;
 24    private readonly ApplicationDbContext _context;
 25
 26    public TransactionJobManagementController(
 27        IBackgroundJobScheduleService scheduleService,
 28        IBackgroundJobLoggingService loggingService,
 29        IAutomatedTransactionProcessingService processingService,
 30        IBackgroundJobClient backgroundJobClient,
 31        ApplicationDbContext context)
 32    {
 33        _scheduleService = scheduleService;
 34        _loggingService = loggingService;
 35        _processingService = processingService;
 36        _backgroundJobClient = backgroundJobClient;
 37        _context = context;
 38    }
 39
 40    // GET: api/transaction-jobs/schedules
 41    [HttpGet("schedules")]
 42    public async Task<IActionResult> GetSchedules([FromQuery] bool activeOnly = false)
 43    {
 44        var schedules = activeOnly
 45            ? await _scheduleService.GetActiveSchedulesAsync()
 46            : await _scheduleService.GetAllSchedulesAsync();
 47
 48        return Ok(schedules);
 49    }
 50
 51    // GET: api/background-jobs/schedules/{id}
 52    [HttpGet("schedules/{id}")]
 53    public async Task<IActionResult> GetSchedule(int id)
 54    {
 55        var schedule = await _scheduleService.GetScheduleByIdAsync(id);
 56        if (schedule == null)
 57            return NotFound(new { error = "Schedule not found" });
 58
 59        return Ok(schedule);
 60    }
 61
 62    // POST: api/background-jobs/schedules
 63    [HttpPost("schedules")]
 64    public async Task<IActionResult> CreateSchedule([FromBody] BackgroundJobSchedule schedule)
 65    {
 66        if (!ModelState.IsValid)
 67            return BadRequest(ModelState);
 68
 69        try
 70        {
 71            var id = await _scheduleService.CreateScheduleAsync(schedule);
 72            var created = await _scheduleService.GetScheduleByIdAsync(id);
 73            return CreatedAtAction(nameof(GetSchedule), new { id }, created);
 74        }
 75        catch (Exception ex)
 76        {
 77            return BadRequest(new { error = ex.Message });
 78        }
 79    }
 80
 81    // PUT: api/background-jobs/schedules/{id}
 82    [HttpPut("schedules/{id}")]
 83    public async Task<IActionResult> UpdateSchedule(int id, [FromBody] BackgroundJobSchedule schedule)
 84    {
 85        if (id != schedule.Id)
 86            return BadRequest(new { error = "ID mismatch" });
 87
 88        if (!ModelState.IsValid)
 89            return BadRequest(ModelState);
 90
 91        try
 92        {
 93            await _scheduleService.UpdateScheduleAsync(schedule);
 94            return NoContent();
 95        }
 96        catch (InvalidOperationException ex)
 97        {
 98            return NotFound(new { error = ex.Message });
 99        }
 100        catch (Exception ex)
 101        {
 102            return BadRequest(new { error = ex.Message });
 103        }
 104    }
 105
 106    // DELETE: api/background-jobs/schedules/{id}
 107    [HttpDelete("schedules/{id}")]
 108    public async Task<IActionResult> DeleteSchedule(int id)
 109    {
 110        try
 111        {
 112            await _scheduleService.DeleteScheduleAsync(id);
 113            return NoContent();
 114        }
 115        catch (InvalidOperationException ex)
 116        {
 117            return NotFound(new { error = ex.Message });
 118        }
 119        catch (Exception ex)
 120        {
 121            return BadRequest(new { error = ex.Message });
 122        }
 123    }
 124
 125    // POST: api/background-jobs/schedules/{id}/activate
 126    [HttpPost("schedules/{id}/activate")]
 127    public async Task<IActionResult> ActivateSchedule(int id)
 128    {
 129        try
 130        {
 131            await _scheduleService.ActivateScheduleAsync(id);
 132            return Ok(new { message = "Schedule activated successfully" });
 133        }
 134        catch (InvalidOperationException ex)
 135        {
 136            return NotFound(new { error = ex.Message });
 137        }
 138        catch (Exception ex)
 139        {
 140            return BadRequest(new { error = ex.Message });
 141        }
 142    }
 143
 144    // POST: api/background-jobs/schedules/{id}/deactivate
 145    [HttpPost("schedules/{id}/deactivate")]
 146    public async Task<IActionResult> DeactivateSchedule(int id)
 147    {
 148        try
 149        {
 150            await _scheduleService.DeactivateScheduleAsync(id);
 151            return Ok(new { message = "Schedule deactivated successfully" });
 152        }
 153        catch (InvalidOperationException ex)
 154        {
 155            return NotFound(new { error = ex.Message });
 156        }
 157        catch (Exception ex)
 158        {
 159            return BadRequest(new { error = ex.Message });
 160        }
 161    }
 162
 163    // POST: api/background-jobs/trigger/{scheduleId}
 164    [HttpPost("trigger/{scheduleId}")]
 165    public async Task<IActionResult> TriggerSchedule(int scheduleId, [FromQuery] string? jobId = null)
 166    {
 167        var schedule = await _scheduleService.GetScheduleByIdAsync(scheduleId);
 168        if (schedule == null)
 169            return NotFound(new { error = "Schedule not found" });
 170
 171        if (!schedule.OzonClientId.HasValue)
 172            return BadRequest(new { error = "Schedule has no Ozon Client configured" });
 173
 174        try
 175        {
 176            var signalRJobId = string.IsNullOrWhiteSpace(jobId)
 177                ? $"manual-job-{scheduleId}-{Guid.NewGuid():N}"[..24]
 178                : jobId.Trim();
 179
 180            var daysBack = ReportDeliveryDateFilter.TransactionJobDaysBack;
 181            var hangfireJob = schedule.JobType switch
 182            {
 183                "CustomerReturn" => Job.FromExpression<IAutomatedTransactionProcessingService>(service =>
 184                    service.ProcessCustomerReturnManualAsync(
 185                        schedule.OzonClientId.Value,
 186                        daysBack,
 187                        signalRJobId,
 188                        CancellationToken.None)),
 189                _ => Job.FromExpression<IAutomatedTransactionProcessingService>(service =>
 190                    service.ProcessShippedToCommissionerManualAsync(
 191                        schedule.OzonClientId.Value,
 192                        daysBack,
 193                        signalRJobId,
 194                        CancellationToken.None))
 195            };
 196            var hangfireJobId = _backgroundJobClient.Create(hangfireJob, new EnqueuedState(HangfireQueueNames.Main));
 197
 198            return Ok(new { message = "Job triggered successfully", jobId = signalRJobId, hangfireJobId });
 199        }
 200        catch (Exception ex)
 201        {
 202            return BadRequest(new { error = ex.InnerException?.Message ?? ex.Message });
 203        }
 204    }
 205
 206    // POST: api/background-jobs/trigger-manual
 207    [HttpPost("trigger-manual")]
 208    public async Task<IActionResult> TriggerManual([FromBody] ManualTriggerRequest request)
 209    {
 210        await Task.CompletedTask;
 211        if (!ModelState.IsValid)
 212            return BadRequest(ModelState);
 213
 214        try
 215        {
 216            // Generate unique job ID for SignalR tracking
 217            var jobId = $"manual-job-{Guid.NewGuid().ToString().Substring(0, 8)}";
 218
 219            var daysBack = ReportDeliveryDateFilter.TransactionJobDaysBack;
 220            var hangfireJob = string.Equals(request.JobType, "CustomerReturn", StringComparison.OrdinalIgnoreCase)
 221                ? Job.FromExpression<IAutomatedTransactionProcessingService>(service =>
 222                    service.ProcessCustomerReturnManualAsync(
 223                        request.OzonClientId,
 224                        daysBack,
 225                        jobId,
 226                        CancellationToken.None))
 227                : Job.FromExpression<IAutomatedTransactionProcessingService>(service =>
 228                    service.ProcessShippedToCommissionerManualAsync(
 229                        request.OzonClientId,
 230                        daysBack,
 231                        jobId,
 232                        CancellationToken.None));
 233            _backgroundJobClient.Create(hangfireJob, new EnqueuedState(HangfireQueueNames.Main));
 234
 235            return Ok(new { message = "Job triggered successfully", jobId });
 236        }
 237        catch (Exception ex)
 238        {
 239            return BadRequest(new { error = ex.Message });
 240        }
 241    }
 242
 243    // GET: api/background-jobs/executions
 244    [HttpGet("executions")]
 245    public async Task<IActionResult> GetExecutions(
 246        [FromQuery] string? status = null,
 247        [FromQuery] string? triggerType = null,
 248        [FromQuery] int page = 1,
 249        [FromQuery] int pageSize = 20)
 250    {
 251        var query = _context.BackgroundJobExecutions
 252            .Include(e => e.Schedule)
 253            .AsQueryable();
 254
 255        if (!string.IsNullOrWhiteSpace(status))
 256            query = query.Where(e => e.Status == status);
 257
 258        if (!string.IsNullOrWhiteSpace(triggerType))
 259            query = query.Where(e => e.TriggerType == triggerType);
 260
 261        var total = await query.CountAsync();
 262        var executions = await query
 263            .OrderByDescending(e => e.StartedAt)
 264            .Skip((page - 1) * pageSize)
 265            .Take(pageSize)
 266            .ToListAsync();
 267
 268        return Ok(new
 269        {
 270            total,
 271            page,
 272            pageSize,
 273            items = executions
 274        });
 275    }
 276
 277    // GET: api/background-jobs/executions/{id}
 278    [HttpGet("executions/{id}")]
 279    public async Task<IActionResult> GetExecutionDetails(long id)
 280    {
 281        var execution = await _loggingService.GetExecutionAsync(id);
 282        if (execution == null)
 283            return NotFound(new { error = "Execution not found" });
 284
 285        var steps = await _loggingService.GetExecutionStepsAsync(id);
 286
 287        var reportLoads = await _context.BackgroundReportLoads
 288            .Include(r => r.OzonClient)
 289            .Where(r => r.ExecutionId == id)
 290            .ToListAsync();
 291
 292        var orderMatches = await _context.BackgroundOrderMatches
 293            .Include(m => m.Order)
 294            .ThenInclude(o => o!.AppStatus)
 295            .Where(m => m.ExecutionId == id)
 296            .ToListAsync();
 297
 298        var transactionResults = await _context.BackgroundTransactionResults
 299            .Include(r => r.Order)
 300            .Include(r => r.Transaction)
 301            .Where(r => r.ExecutionId == id)
 302            .ToListAsync();
 303
 304        return Ok(new
 305        {
 306            execution,
 307            steps,
 308            reportLoads,
 309            orderMatches,
 310            transactionResults
 311        });
 312    }
 313}
 314
 315public class ManualTriggerRequest
 316{
 0317    public int OzonClientId { get; set; }
 0318    public int DaysBack { get; set; } = 1;
 0319    public string JobType { get; set; } = "ShippedToCommissioner";
 320}