-
Notifications
You must be signed in to change notification settings - Fork 82
/
CloudEventFormatter.cs
204 lines (191 loc) · 14.4 KB
/
CloudEventFormatter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright (c) Cloud Native Foundation.
// Licensed under the Apache 2.0 license.
// See LICENSE file in the project root for full license information.
using CloudNative.CloudEvents.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Mime;
using System.Threading.Tasks;
namespace CloudNative.CloudEvents
{
/// <summary>
/// Performs CloudEvent conversions as part of encoding and decoding messages for protocol bindings.
/// </summary>
/// <remarks>
/// <para>
/// Event formatters are responsible for complete CloudEvent encoding and decoding for structured-mode messages (where
/// all the CloudEvent information is represented within the message body), and data-only encoding and decoding
/// for binary-mode messages (where CloudEvent attributes are represented in message metadata, and the CloudEvent data
/// is represented in the message body).
/// </para>
/// <para>
/// Each event formatter type is responsible for documenting what types of value are acceptable for the <see cref="CloudEvent.Data"/>
/// property in CloudEvents it is asked to encode, and likewise what types of value will be present in the same property
/// when it is asked to decode a message. Event formatters should aim to be as consistent as possible with respect to data handling
/// between structured and binary modes, although this is not always possible as the structured mode representation may contain
/// more hints around how to interpret the data than the binary mode representation. Inconsistencies should be carefully
/// noted so that consumers can write robust code.
/// </para>
/// <para>
/// An event format is often naturally associated with a particular kind of data, but it is not limited to working with
/// that kind. For example, the JSON event format allows JSON data to be stored particularly naturally within the structured-mode
/// message body (which is itself JSON), but it is still able to handle arbitrary binary or text data.
/// </para>
/// </remarks>
public abstract class CloudEventFormatter
{
/// <summary>
/// Decodes a CloudEvent from a structured-mode message body, represented as a read-only memory segment.
/// </summary>
/// <param name="body">The message body (content).</param>
/// <param name="contentType">The content type of the message, or null if no content type is known.
/// Typically this is a content type with a media type of "application/cloudevents"; the additional
/// information such as the charset parameter may be needed in order to decode the message body.</param>
/// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
/// <returns>The CloudEvent derived from the structured message body.</returns>
public abstract CloudEvent DecodeStructuredModeMessage(ReadOnlyMemory<byte> body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes);
/// <summary>
/// Decodes a CloudEvent from a structured-mode message body, represented as a stream. The default implementation copies the
/// content of the stream into a read-only memory segment before passing it to <see cref="DecodeStructuredModeMessage(ReadOnlyMemory{byte}, ContentType, IEnumerable{CloudEventAttribute})"/>
/// but this can be overridden by event formatters that can decode a stream more efficiently.
/// </summary>
/// <param name="messageBody">The message body (content). Must not be null.</param>
/// <param name="contentType">The content type of the message, or null if no content type is known.
/// Typically this is a content type with a media type of "application/cloudevents"; the additional
/// information such as the charset parameter may be needed in order to decode the message body.</param>
/// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
/// <returns>The decoded CloudEvent.</returns>
public virtual CloudEvent DecodeStructuredModeMessage(Stream messageBody, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes)
{
var bytes = BinaryDataUtilities.ToReadOnlyMemory(messageBody);
return DecodeStructuredModeMessage(bytes, contentType, extensionAttributes);
}
/// <summary>
/// Asynchronously decodes a CloudEvent from a structured-mode message body, represented as a stream. The default implementation asynchronously copies the
/// content of the stream into a read-only memory segment before passing it to <see cref="DecodeStructuredModeMessage(ReadOnlyMemory{byte}, ContentType, IEnumerable{CloudEventAttribute})"/>
/// but this can be overridden by event formatters that can decode a stream more efficiently.
/// </summary>
/// <param name="body">The message body (content). Must not be null.</param>
/// <param name="contentType">The content type of the message, or null if no content type is known.
/// Typically this is a content type with a media type of "application/cloudevents"; the additional
/// information such as the charset parameter may be needed in order to decode the message body.</param>
/// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
/// <returns>The CloudEvent derived from the structured message body.</returns>
public virtual async Task<CloudEvent> DecodeStructuredModeMessageAsync(Stream body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes)
{
var bytes = await BinaryDataUtilities.ToReadOnlyMemoryAsync(body).ConfigureAwait(false);
return DecodeStructuredModeMessage(bytes, contentType, extensionAttributes);
}
/// <summary>
/// Encodes a CloudEvent as the body of a structured-mode message.
/// </summary>
/// <param name="cloudEvent">The CloudEvent to encode. Must not be null.</param>
/// <param name="contentType">On successful return, the content type of the structured-mode message body.
/// Must not be null (on return).</param>
/// <returns>The structured-mode representation of the CloudEvent.</returns>
public abstract ReadOnlyMemory<byte> EncodeStructuredModeMessage(CloudEvent cloudEvent, out ContentType contentType);
/// <summary>
/// Decodes the given data obtained from a binary-mode message, populating the <see cref="CloudEvent.Data"/>
/// property of <paramref name="cloudEvent"/>. Other attributes within the CloudEvent may be used to inform
/// the interpretation of the message body. This method is expected to be called after all other aspects of the CloudEvent
/// have been populated.
/// </summary>
/// <param name="body">The message body (content). Must not be null, but may be empty.</param>
/// <param name="cloudEvent">The CloudEvent whose Data property should be populated. Must not be null.</param>
/// <exception cref="ArgumentException">The data in the given CloudEvent cannot be decoded by this
/// event formatter.</exception>
public abstract void DecodeBinaryModeEventData(ReadOnlyMemory<byte> body, CloudEvent cloudEvent);
/// <summary>
/// Encodes the data from <paramref name="cloudEvent"/> in a manner suitable for a binary mode message.
/// </summary>
/// <exception cref="ArgumentException">The data in the given CloudEvent cannot be encoded by this
/// event formatter.</exception>
/// <returns>The binary-mode representation of the CloudEvent.</returns>
public abstract ReadOnlyMemory<byte> EncodeBinaryModeEventData(CloudEvent cloudEvent);
/// <summary>
/// Decodes a collection CloudEvents from a batch-mode message body, represented as a read-only memory segment.
/// </summary>
/// <param name="body">The message body (content).</param>
/// <param name="contentType">The content type of the message, or null if no content type is known.
/// Typically this is a content type with a media type with a prefix of "application/cloudevents-batch"; the additional
/// information such as the charset parameter may be needed in order to decode the message body.</param>
/// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
/// <returns>The collection of CloudEvents derived from the batch message body.</returns>
public abstract IReadOnlyList<CloudEvent> DecodeBatchModeMessage(ReadOnlyMemory<byte> body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes);
/// <summary>
/// Decodes a collection CloudEvents from a batch-mode message body, represented as a stream. The default implementation copies the
/// content of the stream into a read-only memory segment before passing it to <see cref="DecodeBatchModeMessage(ReadOnlyMemory{byte}, ContentType, IEnumerable{CloudEventAttribute})"/>
/// but this can be overridden by event formatters that can decode a stream more efficiently.
/// </summary>
/// <param name="body">The message body (content). Must not be null.</param>
/// <param name="contentType">The content type of the message, or null if no content type is known.
/// Typically this is a content type with a media type with a prefix of "application/cloudevents"; the additional
/// information such as the charset parameter may be needed in order to decode the message body.</param>
/// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
/// <returns>The collection of CloudEvents derived from the batch message body.</returns>
public virtual IReadOnlyList<CloudEvent> DecodeBatchModeMessage(Stream body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes)
{
var bytes = BinaryDataUtilities.ToReadOnlyMemory(body);
return DecodeBatchModeMessage(bytes, contentType, extensionAttributes);
}
/// <summary>
/// Asynchronously decodes a collection CloudEvents from a batch-mode message body, represented as a stream. The default implementation asynchronously copies the
/// content of the stream into a read-only memory segment before passing it to <see cref="DecodeBatchModeMessage(ReadOnlyMemory{byte}, ContentType, IEnumerable{CloudEventAttribute})"/>
/// but this can be overridden by event formatters that can decode a stream more efficiently.
/// </summary>
/// <param name="body">The message body (content). Must not be null.</param>
/// <param name="contentType">The content type of the message, or null if no content type is known.
/// Typically this is a content type with a media type with a prefix of "application/cloudevents"; the additional
/// information such as the charset parameter may be needed in order to decode the message body.</param>
/// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
/// <returns>The collection of CloudEvents derived from the batch message body.</returns>
public virtual async Task<IReadOnlyList<CloudEvent>> DecodeBatchModeMessageAsync(Stream body, ContentType? contentType, IEnumerable<CloudEventAttribute>? extensionAttributes)
{
var bytes = await BinaryDataUtilities.ToReadOnlyMemoryAsync(body).ConfigureAwait(false);
return DecodeBatchModeMessage(bytes, contentType, extensionAttributes);
}
/// <summary>
/// Encodes a sequence of CloudEvents as the body of a message.
/// </summary>
/// <param name="cloudEvents">The CloudEvents to encode. Must not be null.</param>
/// <param name="contentType">On successful return, the content type of the batch message body.
/// Must not be null (on return).</param>
/// <returns>The batch representation of the CloudEvent.</returns>
public abstract ReadOnlyMemory<byte> EncodeBatchModeMessage(IEnumerable<CloudEvent> cloudEvents, out ContentType contentType);
/// <summary>
/// Determines the effective data content type of the given CloudEvent.
/// </summary>
/// <remarks>
/// <para>
/// This implementation validates that <paramref name="cloudEvent"/> is not null,
/// returns the existing <see cref="CloudEvent.DataContentType"/> if that's not null,
/// and otherwise returns null if <see cref="CloudEvent.Data"/> is null or
/// delegates to <see cref="InferDataContentType(object)"/> to infer the data content type
/// from the actual data.
/// </para>
/// <para>
/// Derived classes may override this if additional information is needed from the CloudEvent
/// in order to determine the effective data content type, but most cases can be handled by
/// simply overriding <see cref="InferDataContentType(object)"/>.
/// </para>
/// </remarks>
/// <param name="cloudEvent">The CloudEvent to get or infer the data content type from. Must not be null.</param>
/// <returns>The data content type of the CloudEvent, or null for no data content type.</returns>
public virtual string? GetOrInferDataContentType(CloudEvent cloudEvent)
{
Validation.CheckNotNull(cloudEvent, nameof(cloudEvent));
return cloudEvent.DataContentType is string dataContentType ? dataContentType
: cloudEvent.Data is not object data ? null
: InferDataContentType(data);
}
/// <summary>
/// Infers the effective data content type based on the actual data. This base implementation
/// always returns null, but derived classes may override this method to effectively provide
/// a default data content type based on the in-memory data type.
/// </summary>
/// <param name="data">The data within a CloudEvent. Should not be null.</param>
/// <returns>The inferred content type, or null if no content type is inferred.</returns>
protected virtual string? InferDataContentType(object data) => null;
}
}