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:
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user