private void MapHeaders(CloudEvent cloudEvent) { var properties = ApplicationProperties.Map; properties.Add(AmqpClientExtensions.SpecVersionAmqpHeader, cloudEvent.SpecVersion.VersionId); foreach (var pair in cloudEvent.GetPopulatedAttributes()) { var attribute = pair.Key; // The content type is specified elsewhere. if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute) { continue; } string propKey = AmqpClientExtensions.AmqpHeaderPrefix + attribute.Name; // TODO: Check that AMQP can handle byte[], bool and int values object propValue = pair.Value switch { Uri uri => uri.ToString(), // AMQPNetLite doesn't support DateTimeOffset values, so convert to UTC. // That means we can't roundtrip events with non-UTC timestamps, but that's not awful. DateTimeOffset dto => dto.UtcDateTime, _ => pair.Value }; properties.Add(propKey, propValue); } }
private static void MapHeaders(CloudEvent cloudEvent, Message message) { message.MessageId = cloudEvent.Id; var properties = message.UserProperties; properties.Add(Constants.SpecVersionPropertyKey, cloudEvent.SpecVersion.VersionId); foreach (var pair in cloudEvent.GetPopulatedAttributes()) { var attribute = pair.Key; // The content type and id is specified elsewhere. if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute || attribute == cloudEvent.SpecVersion.IdAttribute) { continue; } string propertyKey = Constants.PropertyKeyPrefix + attribute.Name; var propertyValue = pair.Value switch { Uri uri => uri.ToString(), // Compat with CloudEvents.Amqp implemenation DateTimeOffset dto => dto.UtcDateTime, _ => pair.Value, }; properties.Add(propertyKey, propertyValue); } }
/// <summary> /// Converts a CloudEvent to <see cref="HttpContent"/>. /// </summary> /// <param name="cloudEvent">The CloudEvent to convert. Must not be null, and must be a valid CloudEvent.</param> /// <param name="contentMode">Content mode. Structured or binary.</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> public static HttpContent ToHttpContent(this CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter) { Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); Validation.CheckNotNull(formatter, nameof(formatter)); ReadOnlyMemory <byte> content; // The content type to include in the ContentType header - may be the data content type, or the formatter's content type. ContentType?contentType; switch (contentMode) { case ContentMode.Structured: content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); break; case ContentMode.Binary: content = formatter.EncodeBinaryModeEventData(cloudEvent); contentType = MimeUtilities.CreateContentTypeOrNull(formatter.GetOrInferDataContentType(cloudEvent)); break; default: throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); } ByteArrayContent ret = ToByteArrayContent(content); if (contentType is object) { ret.Headers.ContentType = MimeUtilities.ToMediaTypeHeaderValue(contentType); } else if (content.Length != 0) { throw new ArgumentException(Strings.ErrorContentTypeUnspecified, nameof(cloudEvent)); } // Map headers in either mode. // Including the headers in structured mode is optional in the spec (as they're already within the body) but // can be useful. ret.Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { CloudEventAttribute attribute = attributeAndValue.Key; string headerName = HttpUtilities.HttpHeaderPrefix + attribute.Name; object value = attributeAndValue.Value; // Skip the data content type attribute in binary mode, because it's already in the content type header. if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute && contentMode == ContentMode.Binary) { continue; } else { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); ret.Headers.Add(headerName, headerValue); } } return(ret); }
public ActionResult <IEnumerable <string> > ReceiveCloudEvent([FromBody] CloudEvent cloudEvent) { var attributeMap = new JObject(); foreach (var(attribute, value) in cloudEvent.GetPopulatedAttributes()) { attributeMap[attribute.Name] = attribute.Format(value); } return(Ok($"Received event with ID {cloudEvent.Id}, attributes: {attributeMap}")); }
/// <summary> /// Copies a <see cref="CloudEvent"/> into an <see cref="HttpListenerResponse" />. /// </summary> /// <param name="cloudEvent">The CloudEvent to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The response to copy the CloudEvent to. Must not be null.</param> /// <param name="contentMode">Content mode (structured or binary)</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpListenerResponseAsync(this CloudEvent cloudEvent, HttpListenerResponse destination, ContentMode contentMode, CloudEventFormatter formatter) { Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); byte[] content; ContentType contentType; switch (contentMode) { case ContentMode.Structured: content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); break; case ContentMode.Binary: content = formatter.EncodeBinaryModeEventData(cloudEvent); contentType = MimeUtilities.CreateContentTypeOrNull(cloudEvent.DataContentType); destination.ContentType = cloudEvent.DataContentType?.ToString() ?? "application/json"; break; default: throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); } if (contentType is object) { destination.ContentType = contentType.ToString(); } else if (content.Length != 0) { throw new ArgumentException(Strings.ErrorContentTypeUnspecified, nameof(cloudEvent)); } // Map headers in either mode. // Including the headers in structured mode is optional in the spec (as they're already within the body) but // can be useful. destination.Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { var attribute = attributeAndValue.Key; var value = attributeAndValue.Value; // The content type is already handled based on the content mode. if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); destination.Headers.Add(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); } } await destination.OutputStream.WriteAsync(content, 0, content.Length).ConfigureAwait(false); }
private static void MapAttributesToListenerResponse(CloudEvent cloudEvent, HttpListenerResponse httpListenerResponse) { foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { var attribute = attributeAndValue.Key; var value = attributeAndValue.Value; if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); httpListenerResponse.Headers.Add(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); } } }
// TODO: HttpWebResponse as well? /// <summary> /// Copies a <see cref="CloudEvent"/> into the specified <see cref="HttpWebRequest"/>. /// </summary> /// <param name="cloudEvent">CloudEvent to copy. Must not be null, and must be a valid CloudEvent.</param> /// <param name="destination">The request to populate. Must not be null.</param> /// <param name="contentMode">Content mode (structured or binary)</param> /// <param name="formatter">The formatter to use within the conversion. Must not be null.</param> /// <returns>A task representing the asynchronous operation.</returns> public static async Task CopyToHttpWebRequestAsync(this CloudEvent cloudEvent, HttpWebRequest destination, ContentMode contentMode, CloudEventFormatter formatter) { Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); Validation.CheckNotNull(destination, nameof(destination)); Validation.CheckNotNull(formatter, nameof(formatter)); ReadOnlyMemory <byte> content; // The content type to include in the ContentType header - may be the data content type, or the formatter's content type. ContentType?contentType; switch (contentMode) { case ContentMode.Structured: content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType); break; case ContentMode.Binary: content = formatter.EncodeBinaryModeEventData(cloudEvent); contentType = MimeUtilities.CreateContentTypeOrNull(formatter.GetOrInferDataContentType(cloudEvent)); break; default: throw new ArgumentOutOfRangeException(nameof(contentMode), $"Unsupported content mode: {contentMode}"); } if (contentType is object) { destination.ContentType = contentType.ToString(); } else if (content.Length != 0) { throw new ArgumentException(Strings.ErrorContentTypeUnspecified, nameof(cloudEvent)); } // Map headers in either mode. // Including the headers in structured mode is optional in the spec (as they're already within the body) but // can be useful. destination.Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { var attribute = attributeAndValue.Key; var value = attributeAndValue.Value; if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); destination.Headers.Add(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); } } await BinaryDataUtilities.CopyToStreamAsync(content, destination.GetRequestStream()).ConfigureAwait(false); }
private void MapHeaders(CloudEvent cloudEvent, CloudEventFormatter formatter) { foreach (var pair in cloudEvent.GetPopulatedAttributes()) { var attribute = pair.Key; if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute || attribute.Name == Partitioning.PartitionKeyAttribute.Name) { continue; } var value = attribute.Format(pair.Value); Headers.Add(KafkaHeaderPrefix + attribute.Name, Encoding.UTF8.GetBytes(value)); } }
static void MapAttributesToWebRequest(CloudEvent cloudEvent, HttpWebRequest httpWebRequest) { httpWebRequest.Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { var attribute = attributeAndValue.Key; var value = attributeAndValue.Value; if (attribute != cloudEvent.SpecVersion.DataContentTypeAttribute) { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); httpWebRequest.Headers.Add(HttpUtilities.HttpHeaderPrefix + attribute.Name, headerValue); } } }
// TODO: Use this more widely internal static void AssertCloudEventsEqual(CloudEvent expected, CloudEvent actual) { Assert.Equal(expected.SpecVersion, actual.SpecVersion); var expectedAttributes = expected.GetPopulatedAttributes().ToList(); var actualAttributes = actual.GetPopulatedAttributes().ToList(); Assert.Equal(expectedAttributes.Count, actualAttributes.Count); foreach (var expectedAttribute in expectedAttributes) { var actualAttribute = actualAttributes.FirstOrDefault(actual => actual.Key.Name == expectedAttribute.Key.Name); Assert.NotNull(actualAttribute.Key); Assert.Equal(actualAttribute.Key.Type, expectedAttribute.Key.Type); Assert.Equal(actualAttribute.Value, expectedAttribute.Value); } }
public void Indexer_NullValue_Removes() { var cloudEvent = new CloudEvent { Id = "id", Type = "eventtype", Time = DateTimeOffset.UtcNow }; cloudEvent["id"] = null; cloudEvent[CloudEventsSpecVersion.V1_0.TimeAttribute] = null; // We only have a single attribute left. var attributeAndValue = Assert.Single(cloudEvent.GetPopulatedAttributes()); Assert.Equal("type", attributeAndValue.Key.Name); Assert.Equal("eventtype", attributeAndValue.Value); }
private static Headers MapHeaders(CloudEvent cloudEvent) { var headers = new Headers { { SpecVersionKafkaHeader, Encoding.UTF8.GetBytes(cloudEvent.SpecVersion.VersionId) } }; foreach (var pair in cloudEvent.GetPopulatedAttributes()) { var attribute = pair.Key; if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute || attribute.Name == Partitioning.PartitionKeyAttribute.Name) { continue; } var value = attribute.Format(pair.Value); headers.Add(KafkaHeaderPrefix + attribute.Name, Encoding.UTF8.GetBytes(value)); } return(headers); }
private void MapHeaders(CloudEvent cloudEvent, bool includeDataContentType) { Headers.Add(HttpUtilities.SpecVersionHttpHeader, HttpUtilities.EncodeHeaderValue(cloudEvent.SpecVersion.VersionId)); foreach (var attributeAndValue in cloudEvent.GetPopulatedAttributes()) { CloudEventAttribute attribute = attributeAndValue.Key; string headerName = HttpUtilities.HttpHeaderPrefix + attribute.Name; object value = attributeAndValue.Value; // Only map the data content type attribute to a header if we've been asked to if (attribute == cloudEvent.SpecVersion.DataContentTypeAttribute && !includeDataContentType) { continue; } else { string headerValue = HttpUtilities.EncodeHeaderValue(attribute.Format(value)); Headers.Add(headerName, headerValue); } } }
private void WriteCloudEventForBatchOrStructuredMode(Utf8JsonWriter writer, CloudEvent cloudEvent) { Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent)); writer.WriteStartObject(); writer.WritePropertyName(CloudEventsSpecVersion.SpecVersionAttribute.Name); writer.WriteStringValue(cloudEvent.SpecVersion.VersionId); var attributes = cloudEvent.GetPopulatedAttributes(); foreach (var keyValuePair in attributes) { var attribute = keyValuePair.Key; var value = keyValuePair.Value; writer.WritePropertyName(attribute.Name); switch (CloudEventAttributeTypes.GetOrdinal(attribute.Type)) { case CloudEventAttributeTypeOrdinal.Integer: writer.WriteNumberValue((int)value); break; case CloudEventAttributeTypeOrdinal.Boolean: writer.WriteBooleanValue((bool)value); break; default: writer.WriteStringValue(attribute.Type.Format(value)); break; } } if (cloudEvent.Data is object) { EncodeStructuredModeData(cloudEvent, writer); } writer.WriteEndObject(); }
public byte[] EncodeStructuredEvent(CloudEvent cloudEvent, out ContentType contentType) { contentType = new ContentType("application/cloudevents+json") { CharSet = Encoding.UTF8.WebName }; JObject jObject = new JObject(); var attributes = cloudEvent.GetPopulatedAttributes(); foreach (var keyValuePair in attributes) { jObject[keyValuePair.Key.Name] = JToken.FromObject(keyValuePair.Value); } // FIXME: This is all a bit arbitrary. if (cloudEvent.Data is object) { // FIXME: This assumes there's nothing beyond the media type... if (cloudEvent.DataContentType == "application/json") { jObject[Data] = JToken.FromObject(cloudEvent.Data); } else if (cloudEvent.Data is string text && cloudEvent.DataContentType?.StartsWith("text/") == true) { jObject[Data] = text; } else if (cloudEvent.Data is byte[] binary) { jObject[DataBase64] = Convert.ToBase64String(binary); } else { throw new ArgumentException($"{nameof(JsonEventFormatter)} cannot serialize data of type {cloudEvent.Data.GetType()} with content type {cloudEvent.DataContentType}"); } }