Exemplo n.º 1
0
        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);
            }
        }
Exemplo n.º 3
0
        /// <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);
        }
Exemplo n.º 6
0
 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);
         }
     }
 }
Exemplo n.º 7
0
        // 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);
        }
Exemplo n.º 8
0
 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));
     }
 }
Exemplo n.º 9
0
 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);
         }
     }
 }
Exemplo n.º 10
0
        // 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);
            }
        }
Exemplo n.º 11
0
        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);
        }
Exemplo n.º 12
0
        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);
        }
Exemplo n.º 13
0
        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);
                }
            }
        }
Exemplo n.º 14
0
        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();
        }
Exemplo n.º 15
0
        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}");
                }
            }