#nullable enable using System.Text.RegularExpressions; using MBN_STOCK_WEBVIEW.Infrastructure; using MMoneyCoderSharp.Data; namespace MBN_STOCK_WEBVIEW.DbWriteSmoke; internal static partial class OwnedManualSmokeResidueRecovery { internal const string RecoveryOption = "--reconcile-known-owned-manual-residue"; private static readonly IReadOnlyDictionary ScenarioCodes = new Dictionary { [ManualFinancialScreenKind.RevenueComposition] = 'P', [ManualFinancialScreenKind.GrowthMetrics] = 'G', [ManualFinancialScreenKind.Sales] = 'S', [ManualFinancialScreenKind.OperatingProfit] = 'O' }; internal static bool IsRequested(IReadOnlyList arguments) => arguments.Contains(RecoveryOption, StringComparer.Ordinal); internal static async Task RunAsync( IReadOnlyList arguments, TextWriter output, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(arguments); ArgumentNullException.ThrowIfNull(output); if (!TryParse(arguments, out var configurationPath)) { await output.WriteLineAsync( "Usage: MBN_STOCK_WEBVIEW.DbWriteSmoke " + RecoveryOption + " " + SmokeCommandLine.WriteAcknowledgement + " [" + SmokeCommandLine.ConfigurationOption + " ]") .ConfigureAwait(false); return DevelopmentDatabaseWriteSmokeApp.NotAcknowledgedExitCode; } try { var options = new DatabaseOptionsLoader().Load(configurationPath); options.Resilience.MaximumRetryCount = 0; options.Resilience.InitialRetryDelayMilliseconds = 0; var runtime = DatabaseRuntime.Create(options); var service = new LegacyManualFinancialScreenService( runtime.Executor, new OracleManualFinancialMutationExecutor( runtime.ConnectionFactory, options.Resilience)); var prefix = SmokeRunIdentity.Prefix + DateTimeOffset.UtcNow.ToString("yyMMdd", System.Globalization.CultureInfo.InvariantCulture); var total = 0; await output.WriteLineAsync( "START: reconcile only the closed, current-UTC-date manual smoke namespace; details redacted.") .ConfigureAwait(false); foreach (var (screen, scenarioCode) in ScenarioCodes) { var result = await service.SearchAsync( screen, prefix, LegacyManualFinancialScreenService.MaximumResults, cancellationToken) .ConfigureAwait(false); var owned = result.Items.Where(item => IsOwned(item.Record.Identity.StockName, prefix, scenarioCode)) .ToArray(); if (result.IsTruncated || owned.Length > 1) { await output.WriteLineAsync( "BLOCKED: the owned residue set was not singular and complete; writesAttempted=false.") .ConfigureAwait(false); return DevelopmentDatabaseWriteSmokeApp.FailureExitCode; } if (owned.Length == 0) { continue; } await service.DeleteAsync(owned[0], cancellationToken).ConfigureAwait(false); total++; try { _ = await service.GetAsync( owned[0].Record.Identity, cancellationToken) .ConfigureAwait(false); } catch (ManualFinancialNotFoundException) { continue; } await output.WriteLineAsync( "FAIL: an owned residue remained after one delete; no retry was issued.") .ConfigureAwait(false); return DevelopmentDatabaseWriteSmokeApp.FailureExitCode; } await output.WriteLineAsync( $"PASS: reconciled={total}; every owned residue is absent; no retry was issued.") .ConfigureAwait(false); return DevelopmentDatabaseWriteSmokeApp.SuccessExitCode; } catch (ManualFinancialMutationException exception) { await output.WriteLineAsync( "FAIL: failureClass=" + RedactedDatabaseFailureClassifier.Classify(exception) + "; outcomeUnknown=" + exception.OutcomeUnknown.ToString().ToLowerInvariant() + "; no retry was issued.") .ConfigureAwait(false); return exception.OutcomeUnknown ? DevelopmentDatabaseWriteSmokeApp.OutcomeUnknownExitCode : DevelopmentDatabaseWriteSmokeApp.FailureExitCode; } catch (Exception exception) { await output.WriteLineAsync( "FAIL: failureClass=" + RedactedDatabaseFailureClassifier.Classify(exception) + "; no retry was issued; details redacted.") .ConfigureAwait(false); return DevelopmentDatabaseWriteSmokeApp.FailureExitCode; } } private static bool IsOwned(string value, string prefix, char scenarioCode) => value.Length == 20 && value.StartsWith(prefix, StringComparison.Ordinal) && value[^1] == scenarioCode && OwnedNamePattern().IsMatch(value); private static bool TryParse( IReadOnlyList arguments, out string? configurationPath) { configurationPath = null; var recovery = false; var acknowledged = false; for (var index = 0; index < arguments.Count; index++) { switch (arguments[index]) { case RecoveryOption when !recovery: recovery = true; break; case SmokeCommandLine.WriteAcknowledgement when !acknowledged: acknowledged = true; break; case SmokeCommandLine.ConfigurationOption when configurationPath is null && index + 1 < arguments.Count: configurationPath = arguments[++index]; if (string.IsNullOrWhiteSpace(configurationPath)) { return false; } break; default: return false; } } return recovery && acknowledged; } [GeneratedRegex("^MBW[0-9]{6}[0-9A-F]{10}[PGSO]$", RegexOptions.CultureInvariant)] private static partial Regex OwnedNamePattern(); }