Skip to content
Merged
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
64 changes: 32 additions & 32 deletions cscglobal-caplugin/CSCGlobalCAPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
/// Parameterless constructor retained for compatibility with older gateway hosts that don't
/// perform DI. When constructed this way the plugin runs without DNS auto-publishing.
/// </summary>
public CSCGlobalCAPlugin()

Check warning on line 47 in cscglobal-caplugin/CSCGlobalCAPlugin.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Non-nullable property 'CscGlobalClient' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

Check warning on line 47 in cscglobal-caplugin/CSCGlobalCAPlugin.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Non-nullable field '_certificateDataReader' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
Logger = LogHandler.GetClassLogger<CSCGlobalCAPlugin>();
_requestManager = new RequestManager();
Expand All @@ -56,7 +56,7 @@
/// factory. When non-null, CNAME DCV records returned by CSC are auto-published via the
/// framework's registered DNS providers (resolved per-domain).
/// </summary>
public CSCGlobalCAPlugin(IDomainValidatorFactory validatorFactory)

Check warning on line 59 in cscglobal-caplugin/CSCGlobalCAPlugin.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Non-nullable field '_certificateDataReader' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
{
Logger = LogHandler.GetClassLogger<CSCGlobalCAPlugin>();
_requestManager = new RequestManager();
Expand Down Expand Up @@ -678,8 +678,8 @@
flow.Fail("ParseResponse", "API returned null");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "Enrollment failed: CSC API returned a null response."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nEnrollment failed: CSC API returned a null response."
};
}
flow.Step("ParseResponse", $"error={enrollmentResponse.RegistrationError != null}");
Expand All @@ -690,8 +690,8 @@
flow.Fail("RejectExpiredRenew", "PriorCertSN present on New enrollment");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "You cannot renew an expired cert please perform an new enrollment."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nYou cannot renew an expired cert please perform an new enrollment."
};
}

Expand Down Expand Up @@ -726,8 +726,8 @@
flow.Fail("ValidatePriorSN", "PriorCertSN is empty");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "RenewOrReissue failed: PriorCertSN is required but was not provided."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nRenewOrReissue failed: PriorCertSN is required but was not provided."
};
}

Expand All @@ -742,8 +742,8 @@
flow.Fail("ValidateOrderId", $"no order found for SN={priorSn}");
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"RenewOrReissue failed: could not find order ID for serial number '{priorSn}'."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nRenewOrReissue failed: could not find order ID for serial number '{priorSn}'."
};
}

Expand All @@ -752,8 +752,8 @@
flow.Fail("ValidateOrderId", $"order_id too short ({order_id.Length} chars)");
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"RenewOrReissue failed: order ID '{order_id}' is too short to extract a UUID."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nRenewOrReissue failed: order ID '{order_id}' is too short to extract a UUID."
};
}
flow.Step("ValidateOrderId", $"orderId={order_id}");
Expand Down Expand Up @@ -801,8 +801,8 @@
flow.Fail("FallbackExpiryCheck", fallbackEx.Message);
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"RenewOrReissue failed: unable to determine renewal status for order '{order_id}'. {fallbackEx.Message}"
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nRenewOrReissue failed: unable to determine renewal status for order '{order_id}'. {fallbackEx.Message}"
};
}
}
Expand All @@ -825,8 +825,8 @@
flow.Fail("ValidateRenewalUUID", "could not resolve PriorCertSN");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "Renewal failed: could not resolve prior certificate serial number to a request ID."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nRenewal failed: could not resolve prior certificate serial number to a request ID."
};
}
flow.Step("ValidateRenewalUUID", $"uuid={uUId}");
Expand All @@ -850,8 +850,8 @@
flow.Fail("ParseRenewalResponse", "API returned null");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "Renewal failed: CSC API returned a null response."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nRenewal failed: CSC API returned a null response."
};
}

Expand All @@ -871,9 +871,9 @@
flow.Fail("MissingEnrollmentParams", "Applicant Last Name not present — one-click renew unavailable");
return new EnrollmentResult
{
Status = 30,
Status = (int)EndEntityStatus.FAILED,
StatusMessage =
"One click Renew Is Not Available for this Certificate Type. Use the configure button instead."
$"{flow.GetSummary()}\n\nOne click Renew Is Not Available for this Certificate Type. Use the configure button instead."
};
}

Expand All @@ -892,8 +892,8 @@
flow.Fail("ValidateReissueRequestId", "could not resolve PriorCertSN");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "Reissue failed: could not resolve prior certificate serial number to a request ID."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nReissue failed: could not resolve prior certificate serial number to a request ID."
};
}

Expand All @@ -902,8 +902,8 @@
flow.Fail("ValidateReissueRequestId", $"requestid too short ({requestid.Length} chars)");
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"Reissue failed: request ID '{requestid}' is too short to extract a UUID."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nReissue failed: request ID '{requestid}' is too short to extract a UUID."
};
}

Expand All @@ -929,8 +929,8 @@
flow.Fail("ParseReissueResponse", "API returned null");
return new EnrollmentResult
{
Status = 30,
StatusMessage = "Reissue failed: CSC API returned a null response."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nReissue failed: CSC API returned a null response."
};
}

Expand All @@ -950,17 +950,17 @@
flow.Fail("MissingEnrollmentParams", "Applicant Last Name not present — one-click reissue unavailable");
return new EnrollmentResult
{
Status = 30,
Status = (int)EndEntityStatus.FAILED,
StatusMessage =
"One click Renew Is Not Available for this Certificate Type. Use the configure button instead."
$"{flow.GetSummary()}\n\nOne click Reissue Is Not Available for this Certificate Type. Use the configure button instead."
};

default:
flow.Fail("UnhandledType", $"enrollmentType={enrollmentType}");
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"Enroll failed: unhandled enrollment type '{enrollmentType}'."
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nEnroll failed: unhandled enrollment type '{enrollmentType}'."
};
}
}
Expand All @@ -971,8 +971,8 @@
Logger.LogError(inner, "Enroll: AggregateException during {EnrollmentType}: {Message}", enrollmentType, inner?.Message ?? ae.Message);
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"Enrollment failed with error: {inner?.Message ?? ae.Message}"
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nEnrollment failed with error: {inner?.Message ?? ae.Message}"
};
}
catch (Exception ex)
Expand All @@ -981,8 +981,8 @@
Logger.LogError(ex, "Enroll: unhandled exception during {EnrollmentType}: {Message}", enrollmentType, ex.Message);
return new EnrollmentResult
{
Status = 30,
StatusMessage = $"Enrollment failed with error: {ex.Message}"
Status = (int)EndEntityStatus.FAILED,
StatusMessage = $"{flow.GetSummary()}\n\nEnrollment failed with error: {ex.Message}"
};
}
}
Expand Down
33 changes: 33 additions & 0 deletions cscglobal-caplugin/FlowLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
}

/// <summary>Record a completed step.</summary>
public FlowLogger Step(string name, string detail = null)

Check warning on line 58 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 58 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 58 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.
{
var step = new FlowStep { Name = name, Status = FlowStepStatus.Success, Detail = detail };
AddStep(step);
Expand All @@ -65,7 +65,7 @@
}

/// <summary>Record a step that executes an action and times it.</summary>
public FlowLogger Step(string name, Action action, string detail = null)

Check warning on line 68 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 68 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 68 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.
{
var sw = Stopwatch.StartNew();
var step = new FlowStep { Name = name, Detail = detail };
Expand Down Expand Up @@ -95,7 +95,7 @@
}

/// <summary>Record an async step that executes and times it.</summary>
public async Task<FlowLogger> StepAsync(string name, Func<Task> action, string detail = null)

Check warning on line 98 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 98 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 98 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.
{
var sw = Stopwatch.StartNew();
var step = new FlowStep { Name = name, Detail = detail };
Expand Down Expand Up @@ -125,7 +125,7 @@
}

/// <summary>Record a failed step without throwing.</summary>
public FlowLogger Fail(string name, string reason = null)

Check warning on line 128 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 128 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 128 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.
{
var step = new FlowStep { Name = name, Status = FlowStepStatus.Failed, Detail = reason };
AddStep(step);
Expand All @@ -135,7 +135,7 @@
}

/// <summary>Record a skipped step.</summary>
public FlowLogger Skip(string name, string reason = null)

Check warning on line 138 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 138 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.

Check warning on line 138 in cscglobal-caplugin/FlowLogger.cs

View workflow job for this annotation

GitHub Actions / call-starter-workflow / call-dotnet-build-and-release-workflow / dotnet-build-and-release

Cannot convert null literal to non-nullable reference type.
{
var step = new FlowStep { Name = name, Status = FlowStepStatus.Skipped, Detail = reason };
AddStep(step);
Expand Down Expand Up @@ -219,6 +219,39 @@
return sb.ToString();
}

/// <summary>
/// Concise step-by-step summary suitable for surfacing in a user-facing failure message
/// (unlike <see cref="RenderFlow" />'s ASCII-art tree, which is meant for Trace logs only).
/// </summary>
public string GetSummary()
{
var hasFailures = _steps.Any(s => s.Status == FlowStepStatus.Failed) ||
_steps.SelectMany(s => s.Children).Any(c => c.Status == FlowStepStatus.Failed);
var overallStatus = hasFailures ? "FAILED" : "OK";

var sb = new StringBuilder();
sb.AppendLine($"Flow: {_flowName} [{overallStatus}] Total: {_totalTimer.ElapsedMilliseconds}ms");
sb.AppendLine("----------------------------------------");

foreach (var step in _steps)
{
AppendSummaryLine(sb, step, 0);
foreach (var child in step.Children)
AppendSummaryLine(sb, child, 1);
}

return sb.ToString();
}

private static void AppendSummaryLine(StringBuilder sb, FlowStep step, int indentLevel)
{
var indent = new string(' ', indentLevel * 2);
var icon = GetStatusIcon(step.Status);
var elapsed = step.ElapsedMs > 0 ? $" ({step.ElapsedMs}ms)" : "";
var detail = !string.IsNullOrEmpty(step.Detail) ? $" - {step.Detail}" : "";
sb.AppendLine($"{indent}{icon} {step.Name}{elapsed}{detail}");
}

private static string GetStatusIcon(FlowStepStatus status)
{
return status switch
Expand Down
Loading