diff --git a/src/ReC.Application/Common/Procedures/SqlExceptionTranslator.cs b/src/ReC.Application/Common/Procedures/SqlExceptionTranslator.cs new file mode 100644 index 0000000..921eaea --- /dev/null +++ b/src/ReC.Application/Common/Procedures/SqlExceptionTranslator.cs @@ -0,0 +1,61 @@ +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Options; +using ReC.Application.Common.Options; + +namespace ReC.Application.Common.Procedures; + +internal interface ISqlExceptionTranslator +{ + bool IsBadRequestDataIssue(SqlException exception); + + string BuildBadRequestMessage(string operation, string? entity, SqlException exception); +} + +internal sealed class SqlExceptionTranslator : ISqlExceptionTranslator +{ + private readonly IOptionsMonitor _optionsMonitor; + + public SqlExceptionTranslator(IOptionsMonitor optionsMonitor) + { + _optionsMonitor = optionsMonitor; + } + + public bool IsBadRequestDataIssue(SqlException exception) + { + var options = _optionsMonitor.CurrentValue; + + if (options.BadRequestErrorNumbers is { Count: > 0 } && + options.BadRequestErrorNumbers.Contains(exception.Number)) + { + return true; + } + + return options.BadRequestMessages?.ContainsKey(exception.Number) ?? false; + } + + public string BuildBadRequestMessage(string operation, string? entity, SqlException exception) + { + var target = string.IsNullOrWhiteSpace(entity) ? "object" : entity; + var options = _optionsMonitor.CurrentValue; + var template = ResolveTemplate(options, exception.Number); + + return template + .Replace("{Operation}", operation) + .Replace("{Target}", target) + .Replace("{ErrorMessage}", exception.Message); + } + + private static string ResolveTemplate(SqlExceptionTranslatorOptions options, int errorNumber) + { + if (options.BadRequestMessages is not null && + options.BadRequestMessages.TryGetValue(errorNumber, out var template) && + !string.IsNullOrWhiteSpace(template)) + { + return template; + } + + return string.IsNullOrWhiteSpace(options.DefaultBadRequestMessage) + ? SqlExceptionTranslatorOptions.DefaultFallbackMessage + : options.DefaultBadRequestMessage; + } +}