1
0
mirror of https://github.com/bitwarden/server synced 2026-02-27 18:03:17 +00:00

Refactor IntegrationHandlerResult to provide more detail around failures (#6736)

* Refactor IntegrationHandlerResult to provide more detail around failures

* ServiceUnavailable now retryable, more explicit http status handling, more consistency with different handlers, additional xmldocs

* Address PR feedback
This commit is contained in:
Brant DeBow
2025-12-17 11:43:53 -05:00
committed by GitHub
parent bbe682dae9
commit 886ba9ae6d
15 changed files with 663 additions and 113 deletions

View File

@@ -0,0 +1,37 @@
namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations;
/// <summary>
/// Categories of event integration failures used for classification and retry logic.
/// </summary>
public enum IntegrationFailureCategory
{
/// <summary>
/// Service is temporarily unavailable (503, upstream outage, maintenance).
/// </summary>
ServiceUnavailable,
/// <summary>
/// Authentication failed (401, 403, invalid_auth, token issues).
/// </summary>
AuthenticationFailed,
/// <summary>
/// Configuration error (invalid config, channel_not_found, etc.).
/// </summary>
ConfigurationError,
/// <summary>
/// Rate limited (429, rate_limited).
/// </summary>
RateLimited,
/// <summary>
/// Transient error (timeouts, 500, network errors).
/// </summary>
TransientError,
/// <summary>
/// Permanent failure unrelated to authentication/config (e.g., unrecoverable payload/format issue).
/// </summary>
PermanentFailure
}

View File

@@ -1,16 +1,84 @@
namespace Bit.Core.AdminConsole.Models.Data.EventIntegrations;
/// <summary>
/// Represents the result of an integration handler operation, including success status,
/// failure categorization, and retry metadata. Use the <see cref="Succeed"/> factory method
/// for successful operations or <see cref="Fail"/> for failures with automatic retry-ability
/// determination based on the failure category.
/// </summary>
public class IntegrationHandlerResult
{
public IntegrationHandlerResult(bool success, IIntegrationMessage message)
/// <summary>
/// True if the integration send succeeded, false otherwise.
/// </summary>
public bool Success { get; }
/// <summary>
/// The integration message that was processed.
/// </summary>
public IIntegrationMessage Message { get; }
/// <summary>
/// Optional UTC date/time indicating when a failed operation should be retried.
/// Will be used by the retry queue to delay re-sending the message.
/// Usually set based on the Retry-After header from rate-limited responses.
/// </summary>
public DateTime? DelayUntilDate { get; private init; }
/// <summary>
/// Category of the failure. Null for successful results.
/// </summary>
public IntegrationFailureCategory? Category { get; private init; }
/// <summary>
/// Detailed failure reason or error message. Empty for successful results.
/// </summary>
public string? FailureReason { get; private init; }
/// <summary>
/// Indicates whether the operation is retryable.
/// Computed from the failure category.
/// </summary>
public bool Retryable => Category switch
{
IntegrationFailureCategory.RateLimited => true,
IntegrationFailureCategory.TransientError => true,
IntegrationFailureCategory.ServiceUnavailable => true,
IntegrationFailureCategory.AuthenticationFailed => false,
IntegrationFailureCategory.ConfigurationError => false,
IntegrationFailureCategory.PermanentFailure => false,
null => false,
_ => false
};
/// <summary>
/// Creates a successful result.
/// </summary>
public static IntegrationHandlerResult Succeed(IIntegrationMessage message)
{
return new IntegrationHandlerResult(success: true, message: message);
}
/// <summary>
/// Creates a failed result with a failure category and reason.
/// </summary>
public static IntegrationHandlerResult Fail(
IIntegrationMessage message,
IntegrationFailureCategory category,
string failureReason,
DateTime? delayUntil = null)
{
return new IntegrationHandlerResult(success: false, message: message)
{
Category = category,
FailureReason = failureReason,
DelayUntilDate = delayUntil
};
}
private IntegrationHandlerResult(bool success, IIntegrationMessage message)
{
Success = success;
Message = message;
}
public bool Success { get; set; } = false;
public bool Retryable { get; set; } = false;
public IIntegrationMessage Message { get; set; }
public DateTime? DelayUntilDate { get; set; }
public string FailureReason { get; set; } = string.Empty;
}