Skip to content
Draft
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
78 changes: 3 additions & 75 deletions BinDays.Api/Controllers/CollectorsController.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
namespace BinDays.Api.Controllers
namespace BinDays.Api.Controllers
{
using BinDays.Api.Collectors.Exceptions;
using BinDays.Api.Collectors.Models;
using BinDays.Api.Collectors.Services;
using BinDays.Api.Collectors.Utilities;
using BinDays.Api.Incidents;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
Expand Down Expand Up @@ -32,11 +31,6 @@ public class CollectorsController : ControllerBase
/// </summary>
private readonly ILogger<CollectorsController> _logger;

/// <summary>
/// Store for recording failed incidents.
/// </summary>
private readonly IIncidentStore _incidentStore;

/// <summary>
/// Distributed cache for storing responses.
/// </summary>
Expand All @@ -53,13 +47,11 @@ public class CollectorsController : ControllerBase
/// <param name="collectorService">Service for retrieving collector information.</param>
/// <param name="logger">Logger for the controller.</param>
/// <param name="cache">Distributed cache for storing responses.</param>
/// <param name="incidentStore">Store for recording failed incidents.</param>
public CollectorsController(CollectorService collectorService, ILogger<CollectorsController> logger, IDistributedCache cache, IIncidentStore incidentStore)
public CollectorsController(CollectorService collectorService, ILogger<CollectorsController> logger, IDistributedCache cache)
{
_collectorService = collectorService;
_logger = logger;
_cache = cache;
_incidentStore = incidentStore;
}

/// <summary>
Expand All @@ -73,7 +65,7 @@ private static string FormatPostcodeForCacheKey(string postcode)
}

/// <summary>
/// Attempts to retrieve and deserialize an object from the cache.
/// Attempts to retrieve and deserialize an object from the cache.
/// Handles deserialization errors by evicting the bad cache entry.
/// </summary>
/// <typeparam name="T">The type to deserialize into.</typeparam>
Expand Down Expand Up @@ -176,7 +168,6 @@ public IActionResult GetCollector(string postcode, [FromBody] ClientSideResponse
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred while retrieving collector for postcode: {Postcode}.", postcode);
RecordIncident(null, IncidentOperation.GetCollector, ex);
return StatusCode(StatusCodes.Status500InternalServerError, "An unexpected error occurred while fetching the collector for the specified postcode. Please try again later.");
}
}
Expand Down Expand Up @@ -231,7 +222,6 @@ public IActionResult GetAddresses(string govUkId, string postcode, [FromBody] Cl
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred while retrieving addresses for gov.uk ID: {GovUkId}, postcode: {Postcode}.", govUkId, postcode);
RecordIncident(govUkId, IncidentOperation.GetAddresses, ex);
return StatusCode(StatusCodes.Status500InternalServerError, "An unexpected error occurred while fetching addresses. Please try again later.");
}
}
Expand Down Expand Up @@ -297,70 +287,8 @@ public IActionResult GetBinDays(string govUkId, string postcode, string uid, [Fr
catch (Exception ex)
{
_logger.LogError(ex, "An unexpected error occurred while retrieving bin days for gov.uk ID: {GovUkId}, postcode: {Postcode}, UID: {Uid}.", govUkId, postcode, uid);
RecordIncident(govUkId, IncidentOperation.GetBinDays, ex);
return StatusCode(StatusCodes.Status500InternalServerError, "An unexpected error occurred while fetching bin days. Please try again later.");
}
}

/// <summary>
/// Records an unexpected incident for diagnostics.
/// </summary>
/// <param name="govUkId">The collector identifier, if known.</param>
/// <param name="operation">The operation that was being performed.</param>
/// <param name="exception">The triggering exception.</param>
private void RecordIncident(string? govUkId, IncidentOperation operation, Exception exception)
{
var normalisedGovUkId = string.IsNullOrWhiteSpace(govUkId) ? string.Empty : govUkId.Trim().ToLowerInvariant();

var incident = new IncidentRecord
{
IncidentId = Guid.NewGuid(),
GovUkId = normalisedGovUkId,
OccurredUtc = DateTime.UtcNow,
Category = Classify(exception),
Operation = operation,
MessageHash = ComputeHash(exception),
ExceptionType = exception.GetType().FullName ?? exception.GetType().Name,
};

try
{
_incidentStore.RecordIncident(incident);
}
catch (Exception storeException)
{
_logger.LogError(storeException, "Failed to record incident for collector {GovUkId}.", normalisedGovUkId);
}
}

/// <summary>
/// Classifies an exception into an incident category.
/// </summary>
/// <param name="exception">The exception to classify.</param>
/// <returns>The incident category.</returns>
private static IncidentCategory Classify(Exception exception)
{
return exception switch
{
HttpRequestException or TimeoutException or TaskCanceledException => IncidentCategory.CollectorFailure,
System.Text.Json.JsonException or FormatException or InvalidOperationException => IncidentCategory.IntegrationChanged,
_ => IncidentCategory.SystemFailure,
};
}

/// <summary>
/// Computes a deterministic hash for an exception.
/// </summary>
/// <param name="exception">The exception to hash.</param>
/// <returns>A hexadecimal hash string.</returns>
private static string ComputeHash(Exception exception)
{
var payload = Encoding.UTF8.GetBytes(
$"{exception.GetType().FullName}|{exception.TargetSite?.ToString() ?? "UnknownTarget"}|{exception.Message}"
);
var hash = SHA256.HashData(payload);

return Convert.ToHexString(hash);
}
}
}
48 changes: 0 additions & 48 deletions BinDays.Api/Controllers/HealthIncidentsController.cs

This file was deleted.

22 changes: 0 additions & 22 deletions BinDays.Api/Incidents/IIncidentStore.cs

This file was deleted.

35 changes: 0 additions & 35 deletions BinDays.Api/Incidents/InMemoryIncidentStore.cs

This file was deleted.

26 changes: 0 additions & 26 deletions BinDays.Api/Incidents/IncidentCategory.cs

This file was deleted.

26 changes: 0 additions & 26 deletions BinDays.Api/Incidents/IncidentOperation.cs

This file was deleted.

48 changes: 0 additions & 48 deletions BinDays.Api/Incidents/IncidentRecord.cs

This file was deleted.

Loading
Loading