From 93337019e7eb743ed6473ed1777f9b42e49cc73f Mon Sep 17 00:00:00 2001 From: Sheuly Debnath Date: Sun, 28 Jun 2026 12:15:59 +0200 Subject: [PATCH] Add endpoint to retrieve snapshot by ID --- .../GenerateReliabilitySnapshotRequestDTO.cs | 8 --- .../GenerateReliabilitySnapshotRequestDTO.cs | 8 +++ .../GenerateReliabilitySnapshotResponse.cs | 7 +++ .../GetReliabilitySnapshotResponse.cs | 10 +++ .../Controllers/ReliabilityController.cs | 63 ++++++++++++++++--- .../GetReliabilitySnapshotHandler.cs | 38 +++++++++++ .../GetReliabilitySnapshotResult.cs | 9 +++ .../IReliabilitySnapshotRepository.cs | 4 ++ .../InMemoryReliabilitySnapshotRepository.cs | 21 ++++++- .../ReliabilitySnapshotRepository.cs | 10 +++ 10 files changed, 160 insertions(+), 18 deletions(-) delete mode 100644 src/TransitPulse.API/Contracts/GenerateReliabilitySnapshotRequestDTO.cs create mode 100644 src/TransitPulse.API/Contracts/Requests/GenerateReliabilitySnapshotRequestDTO.cs create mode 100644 src/TransitPulse.API/Contracts/Responses/GenerateReliabilitySnapshotResponse.cs create mode 100644 src/TransitPulse.API/Contracts/Responses/GetReliabilitySnapshotResponse.cs create mode 100644 src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotHandler.cs create mode 100644 src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotResult.cs diff --git a/src/TransitPulse.API/Contracts/GenerateReliabilitySnapshotRequestDTO.cs b/src/TransitPulse.API/Contracts/GenerateReliabilitySnapshotRequestDTO.cs deleted file mode 100644 index 4c03ede..0000000 --- a/src/TransitPulse.API/Contracts/GenerateReliabilitySnapshotRequestDTO.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace TransitPulse.API.Contracts; - -public record GenerateReliabilitySnapshotRequestDTO( - Guid RouteId, - DateTime PeriodStart, - DateTime PeriodEnd - -); diff --git a/src/TransitPulse.API/Contracts/Requests/GenerateReliabilitySnapshotRequestDTO.cs b/src/TransitPulse.API/Contracts/Requests/GenerateReliabilitySnapshotRequestDTO.cs new file mode 100644 index 0000000..90642e1 --- /dev/null +++ b/src/TransitPulse.API/Contracts/Requests/GenerateReliabilitySnapshotRequestDTO.cs @@ -0,0 +1,8 @@ +namespace TransitPulse.API.Contracts.Requests; + +public record GenerateReliabilitySnapshotRequest( + Guid RouteId, + DateTime PeriodStart, + DateTime PeriodEnd + +); diff --git a/src/TransitPulse.API/Contracts/Responses/GenerateReliabilitySnapshotResponse.cs b/src/TransitPulse.API/Contracts/Responses/GenerateReliabilitySnapshotResponse.cs new file mode 100644 index 0000000..9f73620 --- /dev/null +++ b/src/TransitPulse.API/Contracts/Responses/GenerateReliabilitySnapshotResponse.cs @@ -0,0 +1,7 @@ +namespace TransitPulse.API.Contracts.Responses; + +public record GenerateReliabilitySnapshotResponse( + Guid SnapshotId, + double Score, + double AverageDelay, + double CancellationRate); \ No newline at end of file diff --git a/src/TransitPulse.API/Contracts/Responses/GetReliabilitySnapshotResponse.cs b/src/TransitPulse.API/Contracts/Responses/GetReliabilitySnapshotResponse.cs new file mode 100644 index 0000000..587798f --- /dev/null +++ b/src/TransitPulse.API/Contracts/Responses/GetReliabilitySnapshotResponse.cs @@ -0,0 +1,10 @@ +namespace TransitPulse.API.Contracts.Responses; + +public record GetReliabilitySnapshotResponse( + Guid SnapshotId, + double Score, + double AverageDelay, + double CancellationRate, + double OnTimePercentage, + DateTime CalculatedAt +); diff --git a/src/TransitPulse.API/Controllers/ReliabilityController.cs b/src/TransitPulse.API/Controllers/ReliabilityController.cs index 37f4c5d..5afc0ee 100644 --- a/src/TransitPulse.API/Controllers/ReliabilityController.cs +++ b/src/TransitPulse.API/Controllers/ReliabilityController.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Mvc; -using TransitPulse.API.Contracts; +using TransitPulse.API.Contracts.Requests; +using TransitPulse.API.Contracts.Responses; using TransitPulse.Application.Features.Reliability.GenerateReliabilitySnapshot; +using TransitPulse.Application.Features.Reliability.GetReliabilitySnapshot; using TransitPulse.Application.Features.Reliability.GetReliabilitySnapshots; namespace TransitPulse.API.Controllers; @@ -11,14 +13,19 @@ public class ReliabilityController : ControllerBase { private readonly GenerateReliabilitySnapshotHandler _generateHandler; private readonly GetReliabilitySnapshotsHandler _getSnapshotsHandler; - public ReliabilityController(GenerateReliabilitySnapshotHandler handler, GetReliabilitySnapshotsHandler getSnapshotsHandler) + + private readonly GetReliabilitySnapshotHandler _getSnapshotHandler; + public ReliabilityController(GenerateReliabilitySnapshotHandler handler, + GetReliabilitySnapshotsHandler getSnapshotsHandler, + GetReliabilitySnapshotHandler getSnapshotHandler) { _generateHandler = handler; _getSnapshotsHandler = getSnapshotsHandler; + _getSnapshotHandler = getSnapshotHandler; } - [HttpPost("snapshots/generate")] - public async Task GenerateSnapshot(GenerateReliabilitySnapshotRequestDTO request, + [HttpPost("snapshots")] + public async Task GenerateSnapshot(GenerateReliabilitySnapshotRequest request, CancellationToken cancellationToken) { var command = new GenerateReliabilitySnapshotCommand( @@ -28,17 +35,59 @@ public async Task GenerateSnapshot(GenerateReliabilitySnapshotReq var result = await _generateHandler.HandleAsync(command, cancellationToken); - return Ok(result); + var response = new GenerateReliabilitySnapshotResponse( + + result.SnapshotId, + result.Score, + result.AverageDelay, + result.CancellationRate); + return CreatedAtAction( + nameof(GetSnapshots), + new { routeId = request.RouteId }, + response); + //return Ok(result); } [HttpGet("routes/{routeId}/snapshots")] public async Task GetSnapshots(Guid routeId, CancellationToken cancellationToken) { - var result = await _getSnapshotsHandler.HandleAsync(routeId, cancellationToken); + var snapshots = await _getSnapshotsHandler.HandleAsync(routeId, cancellationToken); + + var response = snapshots.Select(snapshot => + new GetReliabilitySnapshotResponse( + snapshot.SnapshotId, + snapshot.Score, + snapshot.AverageDelay, + snapshot.CancellationRate, + snapshot.OnTimePercentage, + snapshot.CalculatedAt)) + .ToList(); + + return Ok(response); + } + + [HttpGet("snapshots/{snapshotId}")] + + public async Task Getsnapshot(Guid snapshotId, + CancellationToken cancellationToken) + { + var snapshot = await _getSnapshotHandler.HandleAsync(snapshotId, cancellationToken); + + var response = new GetReliabilitySnapshotResponse( + snapshot.SnapshotId, + snapshot.Score, + snapshot.AverageDelay, + snapshot.CancellationRate, + snapshot.OnTimePercentage, + snapshot.CalculatedAt); + + return Ok(response); + + + - return Ok(result); } [HttpGet("health")] diff --git a/src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotHandler.cs b/src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotHandler.cs new file mode 100644 index 0000000..5e1a792 --- /dev/null +++ b/src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotHandler.cs @@ -0,0 +1,38 @@ +using TransitPulse.Application.Exceptions; +using TransitPulse.Application.Interfaces; + +namespace TransitPulse.Application.Features.Reliability.GetReliabilitySnapshot; + +public class GetReliabilitySnapshotHandler +{ + private readonly IReliabilitySnapshotRepository _snapshotRepository; + + public GetReliabilitySnapshotHandler( + IReliabilitySnapshotRepository snapshotRepository) + { + _snapshotRepository = snapshotRepository; + } + + public async Task HandleAsync( + Guid snapshotId, + CancellationToken cancellationToken) + { + var snapshot = await _snapshotRepository.GetByIdAsync( + snapshotId, + cancellationToken); + + if (snapshot is null) + { + throw new NotFoundException( + $"Snapshot {snapshotId} was not found."); + } + + return new GetReliabilitySnapshotResult( + snapshot.Id, + snapshot.Score, + snapshot.AverageDelay, + snapshot.CancellationRate, + snapshot.OnTimePercentage, + snapshot.CalculatedAt); + } +} \ No newline at end of file diff --git a/src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotResult.cs b/src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotResult.cs new file mode 100644 index 0000000..9dfa6ff --- /dev/null +++ b/src/TransitPulse.Application/Features/Reliability/GetReliabilitySnapshot/GetReliabilitySnapshotResult.cs @@ -0,0 +1,9 @@ +namespace TransitPulse.Application.Features.Reliability.GetReliabilitySnapshot; + +public record GetReliabilitySnapshotResult( + Guid SnapshotId, + double Score, + double AverageDelay, + double CancellationRate, + double OnTimePercentage, + DateTime CalculatedAt); \ No newline at end of file diff --git a/src/TransitPulse.Application/Interfaces/IReliabilitySnapshotRepository.cs b/src/TransitPulse.Application/Interfaces/IReliabilitySnapshotRepository.cs index fe73df6..f3e933f 100644 --- a/src/TransitPulse.Application/Interfaces/IReliabilitySnapshotRepository.cs +++ b/src/TransitPulse.Application/Interfaces/IReliabilitySnapshotRepository.cs @@ -7,4 +7,8 @@ public interface IReliabilitySnapshotRepository Task AddAsync(ReliabilitySnapshot snapshot, CancellationToken cancellationToken); Task> GetByRouteAsync(Guid routeId, CancellationToken cancellationToken); + Task GetByIdAsync( + Guid snapshotId, + CancellationToken cancellationToken); + } diff --git a/src/TransitPulse.Infrastructure/Repositories/InMemoryReliabilitySnapshotRepository.cs b/src/TransitPulse.Infrastructure/Repositories/InMemoryReliabilitySnapshotRepository.cs index d6a384c..6e5bae8 100644 --- a/src/TransitPulse.Infrastructure/Repositories/InMemoryReliabilitySnapshotRepository.cs +++ b/src/TransitPulse.Infrastructure/Repositories/InMemoryReliabilitySnapshotRepository.cs @@ -15,10 +15,25 @@ public Task AddAsync( return Task.CompletedTask; } - - public Task> GetByRouteAsync(Guid routeId, + public Task> GetByRouteAsync( + Guid routeId, CancellationToken cancellationToken) { - throw new NotImplementedException(); + IReadOnlyList snapshots = _snapshots + .Where(snapshot => snapshot.RouteId == routeId) + .OrderByDescending(snapshot => snapshot.CalculatedAt) + .ToList(); + + return Task.FromResult(snapshots); + } + + public Task GetByIdAsync( + Guid snapshotId, + CancellationToken cancellationToken) + { + var snapshot = _snapshots.FirstOrDefault( + snapshot => snapshot.Id == snapshotId); + + return Task.FromResult(snapshot); } } \ No newline at end of file diff --git a/src/TransitPulse.Infrastructure/Repositories/ReliabilitySnapshotRepository.cs b/src/TransitPulse.Infrastructure/Repositories/ReliabilitySnapshotRepository.cs index bc6aa9f..446a3a5 100644 --- a/src/TransitPulse.Infrastructure/Repositories/ReliabilitySnapshotRepository.cs +++ b/src/TransitPulse.Infrastructure/Repositories/ReliabilitySnapshotRepository.cs @@ -27,4 +27,14 @@ public async Task> GetByRouteAsync(Guid route .OrderByDescending(snapshot => snapshot.CalculatedAt) .ToListAsync(cancellationToken); } + + public async Task GetByIdAsync( + Guid snapshotId, + CancellationToken cancellationToken) + { + return await _context.ReliabilitySnapshots + .FirstOrDefaultAsync( + snapshot => snapshot.Id == snapshotId, + cancellationToken); + } } \ No newline at end of file