| | | 1 | | namespace AsyncResponse; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Transport-neutral ingress: wire your message broker subscriptions (Google Pub/Sub, RabbitMQ, |
| | | 5 | | /// Kafka, an HTTP webhook, …) to these methods to feed inbound messages into AsyncResponse. |
| | | 6 | | /// <para> |
| | | 7 | | /// The ingress deliberately makes only a <em>transport-level</em> decision: a message that |
| | | 8 | | /// parses as JSON is a response payload and is delivered through the channel's raw ingress path |
| | | 9 | | /// untyped and uninterpreted — a payload whose domain state is failed is still a valid response |
| | | 10 | | /// that active waiters consume through their <c>Until</c> predicates. A message that does not |
| | | 11 | | /// parse carries no payload and is reported through <see cref="IAsyncResponsePublisher.SetException"/>. |
| | | 12 | | /// Domain-state classification happens only in the lost-subscriber fallback, because "nobody is |
| | | 13 | | /// listening" is only knowable after publishing. |
| | | 14 | | /// </para> |
| | | 15 | | /// </summary> |
| | | 16 | | public interface IAsyncResponseIngress |
| | | 17 | | { |
| | | 18 | | /// <summary> |
| | | 19 | | /// Handles an inbound response message (raw JSON) for the given correlation id. |
| | | 20 | | /// Transient handling failures are retried in-process, then escalated through |
| | | 21 | | /// <see cref="IAsyncResponsePublisher.SetException"/> so the registered failure callback |
| | | 22 | | /// runs, keeping broker subscription loops alive. It throws only when that escalation |
| | | 23 | | /// itself fails — the response would otherwise be acknowledged while existing nowhere — |
| | | 24 | | /// so the transport's redelivery/dead-letter policy gets to retry the delivery. |
| | | 25 | | /// </summary> |
| | | 26 | | /// <param name="messageJson">The raw message body.</param> |
| | | 27 | | /// <param name="correlationId"> |
| | | 28 | | /// The correlation id the calling adapter extracted from the message (attribute/header/body). |
| | | 29 | | /// It is nullable because extraction from an untrusted broker message can legitimately yield |
| | | 30 | | /// nothing — a <c>null</c> or blank id means the message is unroutable, so it is logged and |
| | | 31 | | /// skipped. There is no ambient fallback: pass the extracted id explicitly. |
| | | 32 | | /// </param> |
| | | 33 | | Task HandleResponseMessageAsync(string messageJson, string? correlationId); |
| | | 34 | | |
| | | 35 | | /// <summary> |
| | | 36 | | /// Handles an inbound worker-job message (a serialized <see cref="WorkerJobEnvelope"/>): |
| | | 37 | | /// restores the correlation context and executes the described service method via the DI |
| | | 38 | | /// container. Execution failures are logged and then propagated — the transport dispatcher |
| | | 39 | | /// owns the retry/dead-letter decision, so swallowing here would acknowledge failed jobs |
| | | 40 | | /// as successes and disable redelivery. |
| | | 41 | | /// </summary> |
| | | 42 | | /// <param name="messageJson">The raw message body.</param> |
| | | 43 | | Task HandleWorkerMessageAsync(string messageJson); |
| | | 44 | | |
| | | 45 | | /// <summary> |
| | | 46 | | /// Whether <paramref name="messageJson"/> exceeds |
| | | 47 | | /// <c>AsyncResponseOptions.MaxInboundMessageChars</c>. Adapters call this before doing any |
| | | 48 | | /// work on the body that the budget is meant to bound — above all, extracting a correlation id |
| | | 49 | | /// from the body, which parses the whole payload into a <c>JsonDocument</c>. |
| | | 50 | | /// <para> |
| | | 51 | | /// Without this the budget arrived too late to mean what it says. A headerless response carries |
| | | 52 | | /// its id in the body, so the adapter parses first and the ingress checks the size second: the |
| | | 53 | | /// allocation the budget exists to prevent had already happened. An over-budget message is |
| | | 54 | | /// still handed to <see cref="HandleResponseMessageAsync"/> (with a <c>null</c> id) so the |
| | | 55 | | /// rejection, its log line and its metric all stay in one place. |
| | | 56 | | /// </para> |
| | | 57 | | /// <para> |
| | | 58 | | /// Phrased as "is it over?" rather than "is it within?" so that <c>false</c> — the value any |
| | | 59 | | /// implementation unaware of this method yields, the default below included — means "carry on". |
| | | 60 | | /// </para> |
| | | 61 | | /// <para> |
| | | 62 | | /// Defaulted rather than required: this interface is the documented seam for hand-written |
| | | 63 | | /// adapters (an HTTP webhook, a broker the library does not ship), and making it abstract would |
| | | 64 | | /// break every external implementation on upgrade for a guard that is an optimization at the |
| | | 65 | | /// call site — the ingress enforces the budget either way. |
| | | 66 | | /// </para> |
| | | 67 | | /// </summary> |
| | 4 | 68 | | bool IsOverInboundBudget(string messageJson) => false; |
| | | 69 | | } |