docs: Document expectations for protocol bindings
Signed-off-by: Jon Skeet <jonskeet@google.com>
This commit is contained in:
parent
7f6fa9380b
commit
83cdc5a1b4
|
@ -0,0 +1,6 @@
|
|||
# SDK documentation
|
||||
|
||||
This directory contains documentation on:
|
||||
|
||||
- Using the SDK as a consumer
|
||||
- Implementing new event formats and protocol bindings
|
|
@ -0,0 +1,177 @@
|
|||
# Implementing a protocol binding
|
||||
|
||||
From the [specification](https://github.com/cloudevents/spec/blob/v1.0.1/spec.md#protocol-binding):
|
||||
|
||||
> A protocol binding describes how events are sent and received over a given protocol.
|
||||
|
||||
In SDK terms, this usually means methods converting between
|
||||
the SDK `CloudEvent` type and protocol-specific request/response
|
||||
types.
|
||||
|
||||
This document describes suggested conventions for implementing a new
|
||||
protocol binding. These are only conventions rather than
|
||||
requirements, but following them will promote consistency, leading
|
||||
to a more predictable user experience.
|
||||
|
||||
In this document, it is assumed that the binding has the concepts
|
||||
of "structured" or "binary" content modes. Readers should
|
||||
make appropriate choices where this is not the case.
|
||||
|
||||
It is encouraged to provide conversions in both directions where
|
||||
this makes sense, but that won't be the case in all situations.
|
||||
|
||||
## Conversions to CloudEvent
|
||||
|
||||
Provide extension methods on the protocol-specific message types,
|
||||
with the following pattern, demonstrated with an imaginary
|
||||
`ProtocolMessage` type (such as `HttpRequestMessage` or
|
||||
`HttpResponseMessage`):
|
||||
|
||||
```
|
||||
public static bool IsCloudEvent(this ProtocolMessage message);
|
||||
|
||||
public static CloudEvent ToCloudEvent(
|
||||
this ProtocolMessage message,
|
||||
CloudEventFormatter formatter,
|
||||
params CloudEventAtribute[] extensionAttributes);
|
||||
|
||||
public static CloudEvent ToCloudEvent(
|
||||
this ProtocolMessage message,
|
||||
CloudEventFormatter formatter,
|
||||
IEnumerable<CloudEventAtribute> extensionAttributes);
|
||||
```
|
||||
|
||||
Where message content can only be read asynchronously, async methods
|
||||
(with a type `Task<CloudEvent>` or `ValueTask<CloudEvent>` and an
|
||||
`Async` method name suffix) should be provided instead.
|
||||
|
||||
The reason for providing two overloads is for caller convenience to
|
||||
cover three scenarios:
|
||||
|
||||
- No extension attribute (call uses the `params` overload)
|
||||
- A single extension attribute, or multiple known individual ones
|
||||
(call uses the `params` overload)
|
||||
- An immutable collection of extension attributes, either provided by
|
||||
the SDK (e.g. `Sampling.AllAttriutes`) or constructed once by the
|
||||
caller.
|
||||
|
||||
### IsCloudEvent
|
||||
|
||||
The `IsCloudEvent` method should provide a reasonable indication of
|
||||
whether or not the message contains a CloudEvent, but should not
|
||||
perform full decoding and validation. Typically it checks metadata
|
||||
for one of:
|
||||
|
||||
- A content type beginning with "application/cloudevents" (for
|
||||
structured content mode events)
|
||||
- The presence of a CloudEvents specification version header (for
|
||||
binary mode events)
|
||||
|
||||
### ToCloudEvent methods
|
||||
|
||||
Typically multiple overloads of `ToCloudEvent` will resolve to a
|
||||
single internal implementation. The implementation should use the
|
||||
following pseudo-code as structure:
|
||||
|
||||
- Validate that `message` and `formatter` are non-null. (The
|
||||
`extensionAttributes` parameter may be null, which is equivalent
|
||||
to an empty collection.)
|
||||
- Determine the content mode of the message
|
||||
- For a structured content mode message, delegate to the
|
||||
`CloudEventFormatter` specified in the `formatter` parameter,
|
||||
using its `DecodeStructuredModeMessage` method. The formatter
|
||||
should take care of validating the resulting event.
|
||||
- For binary content mode message:
|
||||
- Determine the specification version based on metadata. (The
|
||||
transport *may* specify a default specification, but typically
|
||||
this is required metadata which serves to check that the message
|
||||
is intended to represent a CloudEvent.) Use
|
||||
`CloudEventsSpecVersion.FromVersionId` and check for a null return
|
||||
value.
|
||||
- Construct a `CloudEvent` instance with the given specification
|
||||
version and extension attributes.
|
||||
- Look for all CloudEvent attributes within metadata, and populate
|
||||
the attributes within the `CloudEvent` instance.
|
||||
- If the message contains content, call the
|
||||
`formatter.DecodeBinaryModeEventData` method to populate the
|
||||
`CloudEvent.Data` property appropriately.
|
||||
- Return the result of `CloudEvent.ValidateForConversion` which
|
||||
will validate the event, and either return the original reference if
|
||||
the event is valid, or throw an appropriate `ArgumentException`
|
||||
otherwise.
|
||||
|
||||
## Conversions from CloudEvent to protocol message types
|
||||
|
||||
There are two patterns here, depending on whether it's appropriate
|
||||
for the conversion to construct a new instance or whether it should
|
||||
populate an existing instance:
|
||||
|
||||
```csharp
|
||||
public static ProtocolMessage ToProtocolMessage(
|
||||
this CloudEvent cloudEvent,
|
||||
ContentMode contentMode,
|
||||
CloudEventFormatter formatter);
|
||||
|
||||
public static void CopyToProtocolMessage(
|
||||
this CloudEvent cloudEvent,
|
||||
ProtocolMessage destination,
|
||||
ContentMode contentMode,
|
||||
CloudEventFormatter formatter);
|
||||
```
|
||||
|
||||
Here the `ProtocolMessage` part of the message name varies by target
|
||||
type. Where the type involved is sufficiently clear, that can be
|
||||
used directly. If the type involved only has a generic name such as
|
||||
`Message` or `Request`, it is useful to qualify it with the protocol
|
||||
name. Examples:
|
||||
|
||||
```csharp
|
||||
// HttpContent is already unambiguous.
|
||||
public static HttpContent ToHttpContent(
|
||||
this CloudEvent cloudEvent,
|
||||
ContentMode contentMode,
|
||||
CloudEventFormatter formatter);
|
||||
|
||||
// Message is ambiguous, so clarify it in the method name.
|
||||
public static Message ToAmqpMessage(
|
||||
this CloudEvent cloudEvent,
|
||||
ContentMode contentMode,
|
||||
CloudEventFormatter formatter);
|
||||
```
|
||||
|
||||
Again, where asynchrony is required, make the methods async with
|
||||
appropriate changes to the return type and method name.
|
||||
|
||||
Even if only a single content mode is currently supported, the
|
||||
inclusion of the `contentMode` parameter provides consistency and a
|
||||
seamless path for change later. If the protocol inherently means
|
||||
that `contentMode` is meaningless (so can *never* be supported), it
|
||||
can be omitted.
|
||||
|
||||
Any additional parameters required for the conversion should be
|
||||
added after the `formatter` parameter.
|
||||
|
||||
The conversion should follow the following steps of pseudo-code:
|
||||
|
||||
- Parameter validation (which may be completed in any order):
|
||||
- `cloudEvent` and `formatter` should be non-null
|
||||
- In a `CopyTo...` method, `destination` should be non-null
|
||||
- The `contentMode` should be a known, supported value
|
||||
- Call `cloudEvent.ValidateForConversion(nameof(cloudEvent))`
|
||||
for validation of the original CloudEvent.
|
||||
- For structured mode encoding:
|
||||
- Call `formatter.EncodeStructuredModeMessage` to encode
|
||||
the CloudEvent and indicate the resulting content type
|
||||
for the protocol message.
|
||||
- For binary mode encoding:
|
||||
- Call `formatter.EncodeBinaryModeEventData` to encode
|
||||
the data within the CloudEvent
|
||||
- Populate metadata in the message from the attributes in the
|
||||
CloudEvent.
|
||||
- For `To...` methods, return the resulting protocol message.
|
||||
This must not be null. (`CopyTo...` messages do not return
|
||||
anything.)
|
||||
|
||||
Note that depending on the protocol binding, when encoding a CloudEvent
|
||||
in binary mode, it *may* still be worth populating metadata in the
|
||||
message from the CloudEvent attributes.
|
Loading…
Reference in New Issue