| | | 1 | | namespace AsyncResponse; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Raised by the lost-subscriber fallback when a response payload published through |
| | | 5 | | /// <see cref="IAsyncResponsePublisher.SetResponse{T}"/> reports — via |
| | | 6 | | /// <see cref="IAsyncResponsePayload.ShouldResumeOnRecovery"/> returning <c>false</c> — that it |
| | | 7 | | /// should <em>not</em> resume the flow, and no subscriber was listening on the correlation channel |
| | | 8 | | /// (typically after a redeploy/restart). |
| | | 9 | | /// <para> |
| | | 10 | | /// Instances of this exception are passed to the failure callback registered through |
| | | 11 | | /// <see cref="IRecoverableAsyncResponseBuilder"/>, so a failed domain response takes the same |
| | | 12 | | /// failure path as a technical <c>SetException</c>. Handlers can pattern-match on this type to |
| | | 13 | | /// distinguish a domain failure (with its payload) from a technical error. |
| | | 14 | | /// </para> |
| | | 15 | | /// </summary> |
| | | 16 | | public sealed class AsyncResponseDomainFailureException : Exception |
| | | 17 | | { |
| | | 18 | | /// <summary>Runs the AsyncResponseDomainFailureException operation.</summary> |
| | | 19 | | public AsyncResponseDomainFailureException( |
| | | 20 | | string? correlationId, |
| | | 21 | | string? payloadTypeFullName, |
| | | 22 | | string? payloadJson) |
| | 3 | 23 | | : base(BuildMessage(correlationId, payloadTypeFullName)) |
| | | 24 | | { |
| | 3 | 25 | | CorrelationId = correlationId; |
| | 3 | 26 | | PayloadTypeFullName = payloadTypeFullName; |
| | 3 | 27 | | PayloadJson = payloadJson; |
| | 3 | 28 | | } |
| | | 29 | | |
| | | 30 | | /// <summary>The correlation id of the channel the response was published on.</summary> |
| | | 31 | | public string? CorrelationId { get; } |
| | | 32 | | |
| | | 33 | | /// <summary>Full name of the payload type the original waiter was registered for.</summary> |
| | | 34 | | public string? PayloadTypeFullName { get; } |
| | | 35 | | |
| | | 36 | | /// <summary> |
| | | 37 | | /// JSON snapshot of the payload that declined to resume the flow. This is business data and may |
| | | 38 | | /// carry PII — it lives on the property (opt-in to read) and is deliberately <em>not</em> |
| | | 39 | | /// embedded in <see cref="Exception.Message"/>, so it is not swept into generic exception |
| | | 40 | | /// logging by default. |
| | | 41 | | /// </summary> |
| | | 42 | | public string? PayloadJson { get; } |
| | | 43 | | |
| | | 44 | | private static string BuildMessage(string? correlationId, string? payloadTypeFullName) |
| | 3 | 45 | | => $"Async response for correlationId '{correlationId}' (payload type '{payloadTypeFullName}') " + |
| | 3 | 46 | | "declined to resume the flow while no subscriber was listening. " + |
| | 3 | 47 | | "See the PayloadJson property for the response payload."; |
| | | 48 | | } |