public AmqpCloudEventMessage(CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter)
        {
            ApplicationProperties = new ApplicationProperties();
            MapHeaders(cloudEvent);

            if (contentMode == ContentMode.Structured)
            {
                BodySection = new Data
                {
                    Binary = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType)
                };
                Properties = new Properties {
                    ContentType = contentType.MediaType
                };
                ApplicationProperties = new ApplicationProperties();
                MapHeaders(cloudEvent);
                return;
            }
            else
            {
                BodySection = SerializeData(cloudEvent.Data);
                Properties  = new Properties {
                    ContentType = cloudEvent.DataContentType
                };
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="cloudEvent">CloudEvent</param>
        /// <param name="contentMode">Content mode. Structured or binary.</param>
        /// <param name="formatter">Event formatter</param>
        public CloudEventHttpContent(CloudEvent cloudEvent, ContentMode contentMode, CloudEventFormatter formatter)
        {
            byte[]      content;
            ContentType contentType;

            switch (contentMode)
            {
            case ContentMode.Structured:
                content = formatter.EncodeStructuredModeMessage(cloudEvent, out contentType);
                // This is optional in the specification, but can be useful.
                MapHeaders(cloudEvent, includeDataContentType: true);
                break;

            case ContentMode.Binary:
                content     = formatter.EncodeBinaryModeEventData(cloudEvent);
                contentType = MimeUtilities.CreateContentTypeOrNull(cloudEvent.DataContentType);
                MapHeaders(cloudEvent, includeDataContentType: false);
                break;

            default:
                throw new ArgumentException($"Unsupported content mode: {contentMode}");
            }
            inner = new InnerByteArrayContent(content);
            if (contentType is object)
            {
                Headers.ContentType = contentType.ToMediaTypeHeaderValue();
            }
            else if (content.Length != 0)
            {
                throw new ArgumentException(Strings.ErrorContentTypeUnspecified, nameof(cloudEvent));
            }
        }
Esempio n. 3
0
            internal CloudEvent ConvertToCloudEvent(Request request, CloudEventFormatter formatter)
            {
                MaybeReshapeData(request);

                var context  = request.Context;
                var resource = context.Resource;

                // Default the service name if necessary
                resource.Service = ValidateNotNullOrEmpty(resource.Service ?? _defaultService, "service");
                ValidateNotNullOrEmpty(resource.Name, "resource name");

                var evt = new CloudEvent
                {
                    Type            = _cloudEventType,
                    Id              = context.Id,
                    Time            = context.Timestamp,
                    DataContentType = JsonContentType
                };

                PopulateAttributes(request, evt);
                var bytes = JsonSerializer.SerializeToUtf8Bytes(request.Data);

                formatter.DecodeBinaryModeEventData(bytes, evt);
                return(evt);
            }
Esempio n. 4
0
            internal CloudEvent ConvertToCloudEvent(Request request, CloudEventFormatter formatter)
            {
                MaybeReshapeData(request);

                var context      = request.Context;
                var resource     = context.Resource;
                var service      = ValidateNotNullOrEmpty(resource.Service ?? _defaultService, "service");
                var resourceName = ValidateNotNullOrEmpty(resource.Name, "resource name");

                var(source, subject) = ConvertSourceAndSubject(service, resourceName, request.Data);

                var evt = new CloudEvent
                {
                    Type            = _cloudEventType,
                    Source          = source,
                    Id              = context.Id,
                    Time            = context.Timestamp,
                    DataContentType = JsonContentType,
                    Subject         = subject
                };
                var bytes = JsonSerializer.SerializeToUtf8Bytes(request.Data);

                formatter.DecodeBinaryModeEventData(bytes, evt);
                return(evt);
            }
Esempio n. 5
0
        /// <summary>
        /// Converts this Kafka message into a CloudEvent object.
        /// </summary>
        /// <param name="message">The Kafka message to convert. Must not be null.</param>
        /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param>
        /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param>
        /// <returns>A reference to a validated CloudEvent instance.</returns>
        public static CloudEvent ToCloudEvent(this Message <string, byte[]> message,
                                              CloudEventFormatter formatter, IEnumerable <CloudEventAttribute> extensionAttributes)
        {
            Validation.CheckNotNull(message, nameof(message));
            Validation.CheckNotNull(formatter, nameof(formatter));

            if (!IsCloudEvent(message))
            {
                throw new InvalidOperationException();
            }

            var contentType = ExtractContentType(message);

            CloudEvent cloudEvent;

            // Structured mode
            if (MimeUtilities.IsCloudEventsContentType(contentType))
            {
                cloudEvent = formatter.DecodeStructuredModeMessage(message.Value, new ContentType(contentType), extensionAttributes);
            }
            else
            {
                // Binary mode
                if (!(GetHeaderValue(message, SpecVersionKafkaHeader) is byte[] versionIdBytes))
                {
                    throw new ArgumentException("Request is not a CloudEvent");
                }
                string versionId = Encoding.UTF8.GetString(versionIdBytes);
                CloudEventsSpecVersion version = CloudEventsSpecVersion.FromVersionId(versionId)
                                                 ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", nameof(message));

                cloudEvent = new CloudEvent(version, extensionAttributes)
                {
                    DataContentType = contentType
                };

                foreach (var header in message.Headers.Where(h => h.Key.StartsWith(KafkaHeaderPrefix)))
                {
                    var attributeName = header.Key.Substring(KafkaHeaderPrefix.Length).ToLowerInvariant();
                    if (attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                    {
                        continue;
                    }
                    // TODO: Is this feasible?
                    var headerValue = header.GetValueBytes();
                    if (headerValue is null)
                    {
                        continue;
                    }
                    string attributeValue = Encoding.UTF8.GetString(headerValue);

                    cloudEvent.SetAttributeFromString(attributeName, attributeValue);
                }
                formatter.DecodeBinaryModeEventData(message.Value, cloudEvent);
            }

            InitPartitioningKey(message, cloudEvent);
            return(Validation.CheckCloudEventArgument(cloudEvent, nameof(message)));
        }
Esempio n. 6
0
 /// <summary>
 /// Converts this HTTP request message into a CloudEvent batch.
 /// </summary>
 /// <param name="httpRequestMessage">The HTTP request message to convert. Must not be null.</param>
 /// <param name="formatter">The event formatter to use to parse the CloudEvents. Must not be null.</param>
 /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvents. May be null.</param>
 /// <returns>The decoded batch of CloudEvents.</returns>
 public static Task <IReadOnlyList <CloudEvent> > ToCloudEventBatchAsync(
     this HttpRequestMessage httpRequestMessage,
     CloudEventFormatter formatter,
     IEnumerable <CloudEventAttribute>?extensionAttributes)
 {
     Validation.CheckNotNull(httpRequestMessage, nameof(httpRequestMessage));
     return(ToCloudEventBatchInternalAsync(httpRequestMessage.Content, formatter, extensionAttributes, nameof(httpRequestMessage)));
 }
Esempio n. 7
0
 /// <summary>
 /// Converts this HTTP response message into a CloudEvent object
 /// </summary>
 /// <param name="httpResponseMessage">The HTTP response message to convert. Must not be null.</param>
 /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param>
 /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param>
 /// <returns>A reference to a validated CloudEvent instance.</returns>
 public static Task <CloudEvent> ToCloudEventAsync(
     this HttpResponseMessage httpResponseMessage,
     CloudEventFormatter formatter,
     IEnumerable <CloudEventAttribute>?extensionAttributes)
 {
     Validation.CheckNotNull(httpResponseMessage, nameof(httpResponseMessage));
     return(ToCloudEventInternalAsync(httpResponseMessage.Headers, httpResponseMessage.Content, formatter, extensionAttributes, nameof(httpResponseMessage)));
 }
        public static CloudEvent ToCloudEvent(this Message <string, byte[]> message,
                                              CloudEventFormatter eventFormatter, IEnumerable <CloudEventAttribute> extensionAttributes)
        {
            if (!IsCloudEvent(message))
            {
                throw new InvalidOperationException();
            }

            var contentType = ExtractContentType(message);

            CloudEvent cloudEvent;

            // Structured mode
            if (contentType?.StartsWith(CloudEvent.MediaType, StringComparison.InvariantCultureIgnoreCase) == true)
            {
                cloudEvent = eventFormatter.DecodeStructuredModeMessage(message.Value, new ContentType(contentType), extensionAttributes);
            }
            else
            {
                // Binary mode
                if (!(GetHeaderValue(message, KafkaCloudEventMessage.SpecVersionKafkaHeader) is byte[] versionIdBytes))
                {
                    throw new ArgumentException("Request is not a CloudEvent");
                }
                string versionId = Encoding.UTF8.GetString(versionIdBytes);
                CloudEventsSpecVersion version = CloudEventsSpecVersion.FromVersionId(versionId);
                if (version is null)
                {
                    throw new ArgumentException($"Unsupported CloudEvents spec version '{versionId}'");
                }

                cloudEvent = new CloudEvent(version, extensionAttributes)
                {
                    Data            = message.Value,
                    DataContentType = contentType
                };
                foreach (var header in message.Headers.Where(h => h.Key.StartsWith(KafkaCloudEventMessage.KafkaHeaderPrefix)))
                {
                    var attributeName = header.Key.Substring(KafkaCloudEventMessage.KafkaHeaderPrefix.Length).ToLowerInvariant();
                    if (attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                    {
                        continue;
                    }
                    // TODO: Is this feasible?
                    var headerValue = header.GetValueBytes();
                    if (headerValue is null)
                    {
                        continue;
                    }
                    string attributeValue = Encoding.UTF8.GetString(headerValue);

                    cloudEvent.SetAttributeFromString(attributeName, attributeValue);
                }
            }

            InitPartitioningKey(message, cloudEvent);
            return(cloudEvent);
        }
Esempio n. 9
0
        /// <summary>
        /// Converts this MQTT message into a CloudEvent object.
        /// </summary>
        /// <param name="message">The MQTT message to convert. Must not be null.</param>
        /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param>
        /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param>
        /// <returns>A reference to a validated CloudEvent instance.</returns>
        public static CloudEvent ToCloudEvent(this MqttApplicationMessage message,
                                              CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes)
        {
            Validation.CheckNotNull(formatter, nameof(formatter));
            Validation.CheckNotNull(message, nameof(message));

            // TODO: Determine if there's a sensible content type we should apply.
            return(formatter.DecodeStructuredModeMessage(message.Payload, contentType: null, extensionAttributes));
        }
Esempio n. 10
0
        /// <summary>
        /// Constructs a new instance that uses the given formatter for deserialization.
        /// </summary>
        /// <param name="formatter"></param>
        public CloudEventJsonInputFormatter(CloudEventFormatter formatter)
        {
            _formatter = Validation.CheckNotNull(formatter, nameof(formatter));
            SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
            SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/cloudevents+json"));

            SupportedEncodings.Add(Encoding.UTF8);
            SupportedEncodings.Add(Encoding.Unicode);
        }
 public KafkaMessagePublisher(
     IOptions <KafkaPublisherOptions <TOutput> > options,
     CloudEventFormatter cloudEventFormatter,
     IOptions <PublisherOptions> publisherOptions)
 {
     _cloudEventFormatter = cloudEventFormatter;
     _options             = options.Value ?? throw new ArgumentNullException(nameof(options));
     _publisherOptions    = publisherOptions.Value ?? throw new ArgumentNullException(nameof(publisherOptions));
     _producer            = new ProducerBuilder <string?, byte[]>(_options).Build();
 }
 public NATSMessagePublisher(
     IOptions <NATSBaseOptions> options,
     INATSClientFactory natsClientFactory,
     CloudEventFormatter cloudEventFormatter,
     IOptions <PublisherOptions> publisherOptions)
 {
     _options             = options.Value;
     _client              = natsClientFactory.From(_options);
     _cloudEventFormatter = cloudEventFormatter;
     _publisherOptions    = publisherOptions.Value;
 }
Esempio n. 13
0
 public RabbitMQMessagePublisher(
     IRabbitMQConnectionFactory <TOutput> connectionFactory,
     IOptions <RabbitMQPublisherOptions <TOutput> > config,
     IOptions <PublisherOptions> publisherOptions,
     CloudEventFormatter cloudEventFormatter
     )
 {
     ConnectionFactory    = connectionFactory;
     _options             = config.Value;
     _publisherOptions    = publisherOptions.Value;
     _cloudEventFormatter = cloudEventFormatter;
 }
Esempio n. 14
0
        /// <summary>
        /// Copies a <see cref="CloudEvent"/> batch into the specified <see cref="HttpWebRequest"/>.
        /// </summary>
        /// <param name="cloudEvents">CloudEvent batch 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="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 IReadOnlyList <CloudEvent> cloudEvents,
                                                           HttpWebRequest destination,
                                                           CloudEventFormatter formatter)
        {
            Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents));
            Validation.CheckNotNull(destination, nameof(destination));
            Validation.CheckNotNull(formatter, nameof(formatter));

            byte[] content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType);
            destination.ContentType = contentType.ToString();
            await destination.GetRequestStream().WriteAsync(content, 0, content.Length).ConfigureAwait(false);
        }
Esempio n. 15
0
        /// <summary>
        /// Copies a <see cref="CloudEvent"/> into an <see cref="HttpResponse" />.
        /// </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 CopyToHttpResponseAsync(this CloudEvent cloudEvent, HttpResponse destination,
                                                         ContentMode contentMode, CloudEventFormatter formatter)
        {
            Validation.CheckCloudEventArgument(cloudEvent, nameof(cloudEvent));
            Validation.CheckNotNull(destination, nameof(destination));
            Validation.CheckNotNull(formatter, nameof(formatter));

            ReadOnlyMemory <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(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("The 'datacontenttype' attribute value must be specified", 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);
                }
            }

            destination.ContentLength = content.Length;
            await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false);
        }
Esempio n. 16
0
        /// <summary>
        /// Copies a <see cref="CloudEvent"/> batch into the specified <see cref="HttpWebRequest"/>.
        /// </summary>
        /// <param name="cloudEvents">CloudEvent batch 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="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 IReadOnlyList <CloudEvent> cloudEvents,
                                                           HttpWebRequest destination,
                                                           CloudEventFormatter formatter)
        {
            Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents));
            Validation.CheckNotNull(destination, nameof(destination));
            Validation.CheckNotNull(formatter, nameof(formatter));

            ReadOnlyMemory <byte> content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType);

            destination.ContentType = contentType.ToString();
            await BinaryDataUtilities.CopyToStreamAsync(content, destination.GetRequestStream()).ConfigureAwait(false);
        }
Esempio n. 17
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));

            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(cloudEvent.DataContentType);
                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 destination.GetRequestStream().WriteAsync(content, 0, content.Length).ConfigureAwait(false);
        }
        /// <summary>
        /// Converts this HTTP request into a CloudEvent object.
        /// </summary>
        /// <param name="httpRequest">The HTTP request to decode. Must not be null.</param>
        /// <param name="formatter">The event formatter to use to process the request body. Must not be null.</param>
        /// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
        /// <returns>The decoded CloudEvent.</returns>
        /// <exception cref="ArgumentException">The request does not contain a CloudEvent.</exception>
        public static async Task <CloudEvent> ToCloudEventAsync(
            this HttpRequest httpRequest,
            CloudEventFormatter formatter,
            IEnumerable <CloudEventAttribute>?extensionAttributes)
        {
            Validation.CheckNotNull(httpRequest, nameof(httpRequest));
            Validation.CheckNotNull(formatter, nameof(formatter));
            if (HasCloudEventsContentType(httpRequest))
            {
                var contentType = MimeUtilities.CreateContentTypeOrNull(httpRequest.ContentType);
                return(await formatter.DecodeStructuredModeMessageAsync(httpRequest.Body, contentType, extensionAttributes).ConfigureAwait(false));
            }
            else
            {
                var headers = httpRequest.Headers;
                headers.TryGetValue(HttpUtilities.SpecVersionHttpHeader, out var versionId);
                if (versionId.Count == 0)
                {
                    throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpRequest));
                }
                var version = CloudEventsSpecVersion.FromVersionId(versionId.FirstOrDefault())
                              ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", nameof(httpRequest));

                if (version is null)
                {
                    throw new ArgumentException($"Unsupported CloudEvents spec version '{versionId.First()}'");
                }

                var cloudEvent = new CloudEvent(version, extensionAttributes);
                foreach (var header in headers)
                {
                    string?attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
                    if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                    {
                        continue;
                    }
                    string attributeValue = HttpUtilities.DecodeHeaderValue(header.Value.First());

                    cloudEvent.SetAttributeFromString(attributeName, attributeValue);
                }

                cloudEvent.DataContentType = httpRequest.ContentType;
                if (httpRequest.Body is Stream body)
                {
                    ReadOnlyMemory <byte> data = await BinaryDataUtilities.ToReadOnlyMemoryAsync(body).ConfigureAwait(false);

                    formatter.DecodeBinaryModeEventData(data, cloudEvent);
                }
                return(Validation.CheckCloudEventArgument(cloudEvent, nameof(httpRequest)));
            }
        }
 /// <summary>
 /// Constructs a new instance based on the given CloudEvent Function.
 /// </summary>
 /// <param name="function">The CloudEvent Function to invoke.</param>
 /// <param name="formatter">The CloudEvent formatter to use to deserialize the CloudEvent. This is expected to be a single-type formatter,
 /// targeting <typeparamref name="TData"/>.</param>
 /// <param name="logger">The logger to use to report errors.</param>
 public CloudEventAdapter(ICloudEventFunction <TData> function, CloudEventFormatter formatter, ILogger <CloudEventAdapter <TData> > logger)
 {
     _function = Preconditions.CheckNotNull(function, nameof(function));
     if (formatter is null)
     {
         throw new ArgumentNullException(nameof(formatter),
                                         "No CloudEventFormatter configured for the CloudEvent function. " +
                                         "The Functions Framework hosting will provide an event formatter if the target " +
                                         "data type is decorated with CloudEventFormatterAttribute; otherwise, the formatter " +
                                         "should be configured via dependency injection.");
     }
     _formatter = Preconditions.CheckNotNull(formatter, nameof(formatter));
     _logger    = Preconditions.CheckNotNull(logger, nameof(logger));
 }
 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));
     }
 }
 private static CloudEvent StructuredToCloudEvent(
     Message message,
     string contentType,
     CloudEventFormatter formatter,
     IEnumerable <CloudEventAttribute>?extensionAttributes
     )
 {
     using (var stream = new MemoryStream(message.Body))
     {
         var cloudEvent = formatter.DecodeStructuredModeMessage(stream, new ContentType(contentType), extensionAttributes);
         cloudEvent.Id = message.MessageId;
         return(cloudEvent);
     }
 }
        private async static Task <CloudEvent> ToCloudEventAsyncImpl(HttpListenerRequest httpListenerRequest,
                                                                     CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes, bool async)
        {
            Validation.CheckNotNull(httpListenerRequest, nameof(httpListenerRequest));
            Validation.CheckNotNull(formatter, nameof(formatter));
            var stream = httpListenerRequest.InputStream;

            if (HasCloudEventsContentType(httpListenerRequest))
            {
                var contentType = MimeUtilities.CreateContentTypeOrNull(httpListenerRequest.ContentType);
                return(async
                    ? await formatter.DecodeStructuredModeMessageAsync(stream, contentType, extensionAttributes).ConfigureAwait(false)
                    : formatter.DecodeStructuredModeMessage(stream, contentType, extensionAttributes));
            }
            else
            {
                string versionId = httpListenerRequest.Headers[HttpUtilities.SpecVersionHttpHeader];
                if (versionId is null)
                {
                    throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(httpListenerRequest));
                }
                var version = CloudEventsSpecVersion.FromVersionId(versionId)
                              ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", nameof(httpListenerRequest));

                var cloudEvent = new CloudEvent(version, extensionAttributes);
                var headers    = httpListenerRequest.Headers;
                foreach (var key in headers.AllKeys)
                {
                    string?attributeName = HttpUtilities.GetAttributeNameFromHeaderName(key);
                    if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                    {
                        continue;
                    }
                    string attributeValue = HttpUtilities.DecodeHeaderValue(headers[key]);
                    cloudEvent.SetAttributeFromString(attributeName, attributeValue);
                }

                // The data content type should not have been set via a "ce-" header; instead,
                // it's in the regular content type.
                cloudEvent.DataContentType = httpListenerRequest.ContentType;

                ReadOnlyMemory <byte> data = async
                    ? await BinaryDataUtilities.ToReadOnlyMemoryAsync(stream).ConfigureAwait(false)
                    : BinaryDataUtilities.ToReadOnlyMemory(stream);

                formatter.DecodeBinaryModeEventData(data, cloudEvent);
                return(Validation.CheckCloudEventArgument(cloudEvent, nameof(httpListenerRequest)));
            }
        }
 public KafkaMessageConsumer(ILogger <KafkaMessageConsumer <TData> > logger,
                             IOptions <KafkaConsumerOptions <TData> > config,
                             IMetricsFactory <KafkaMessageConsumer <TData> >?metricsFactory,
                             IApplicationNameService applicationNameService,
                             CloudEventFormatter cloudEventFormatter)
 {
     _logger = logger ?? throw new ArgumentNullException(nameof(logger));
     _applicationNameService = applicationNameService ?? throw new ArgumentNullException(nameof(config));
     _cloudEventFormatter    = cloudEventFormatter;
     _options            = config.Value ?? throw new ArgumentNullException(nameof(config));
     _consumerLagSummary = metricsFactory?.CreateSummary("consumer_lag_distribution",
                                                         "Contains a summary of current consumer lag of each partition", new[] { "topic", "partition" });
     _consumerLagGauge = metricsFactory?.CreateGauge("consumer_lag",
                                                     "Contains current number consumer lag of each partition", false, "topic", "partition");
 }
Esempio n. 24
0
        private static async Task <CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content,
                                                                         CloudEventFormatter formatter, IEnumerable <CloudEventAttribute> extensionAttributes)
        {
            if (HasCloudEventsContentType(content))
            {
                // FIXME: Handle no formatter being specified.
                var stream = await content.ReadAsStreamAsync().ConfigureAwait(false);

                return(await formatter.DecodeStructuredModeMessageAsync(stream, content.Headers.ContentType.ToContentType(), extensionAttributes).ConfigureAwait(false));
            }
            else
            {
                string versionId = headers.Contains(HttpUtilities.SpecVersionHttpHeader)
                    ? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
                    : null;
                if (versionId is null)
                {
                    throw new ArgumentException("Request is not a CloudEvent");
                }
                var version = CloudEventsSpecVersion.FromVersionId(versionId);
                if (version is null)
                {
                    throw new ArgumentException($"Unsupported CloudEvents spec version '{versionId}'");
                }

                var cloudEvent = new CloudEvent(version, extensionAttributes);
                foreach (var header in headers)
                {
                    string attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
                    if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                    {
                        continue;
                    }
                    string attributeValue = HttpUtilities.DecodeHeaderValue(header.Value.First());

                    cloudEvent.SetAttributeFromString(attributeName, attributeValue);
                }
                if (content is object)
                {
                    // TODO: Should this just be the media type? We probably need to take a full audit of this...
                    cloudEvent.DataContentType = content.Headers?.ContentType?.ToString();
                    var data = await content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    formatter.DecodeBinaryModeEventData(data, cloudEvent);
                }
                return(cloudEvent);
            }
        }
Esempio n. 25
0
        /// <summary>
        /// Copies a <see cref="CloudEvent"/> batch into an <see cref="HttpResponse" />.
        /// </summary>
        /// <param name="cloudEvents">The CloudEvent batch 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="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 CopyToHttpResponseAsync(this IReadOnlyList <CloudEvent> cloudEvents,
                                                         HttpResponse destination, CloudEventFormatter formatter)
        {
            Validation.CheckCloudEventBatchArgument(cloudEvents, nameof(cloudEvents));
            Validation.CheckNotNull(destination, nameof(destination));
            Validation.CheckNotNull(formatter, nameof(formatter));

            // TODO: Validate that all events in the batch have the same version?
            // See https://github.com/cloudevents/spec/issues/807

            ReadOnlyMemory <byte> content = formatter.EncodeBatchModeMessage(cloudEvents, out var contentType);

            destination.ContentType   = contentType.ToString();
            destination.ContentLength = content.Length;
            await BinaryDataUtilities.CopyToStreamAsync(content, destination.Body).ConfigureAwait(false);
        }
Esempio n. 26
0
        // TODO: HttpWebResponse as well?
        // TODO: Change to a CopyTo extension method on CloudEvent?

        /// <summary>
        /// Copies a CloudEvent into the specified HttpWebRequest instance.
        /// </summary>
        /// <param name="httpWebRequest">The request to populate.</param>
        /// <param name="cloudEvent">CloudEvent to copy</param>
        /// <param name="contentMode">Content mode (structured or binary)</param>
        /// <param name="formatter">Formatter</param>
        /// <returns>Task</returns>
        public static async Task CopyFromAsync(this HttpWebRequest httpWebRequest, CloudEvent cloudEvent,
                                               ContentMode contentMode, CloudEventFormatter formatter)
        {
            if (contentMode == ContentMode.Structured)
            {
                var buffer = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType);
                httpWebRequest.ContentType = contentType.ToString();
                await httpWebRequest.GetRequestStream().WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false);

                return;
            }

            httpWebRequest.ContentType = cloudEvent.DataContentType?.ToString() ?? "application/json";
            MapAttributesToWebRequest(cloudEvent, httpWebRequest);
            byte[] content = formatter.EncodeBinaryModeEventData(cloudEvent);
            await httpWebRequest.GetRequestStream().WriteAsync(content, 0, content.Length).ConfigureAwait(false);
        }
 /// <summary>
 /// Converts this <see cref="Message"/> into a CloudEvent object.
 /// </summary>
 /// <param name="message">The message to convert. Must not be null.</param>
 /// <param name="formatter">The event formatter to use to parse the CloudEvent. Must not be null.</param>
 /// <param name="extensionAttributes">The extension attributes to use when parsing the CloudEvent. May be null.</param>
 /// <returns>A validated CloudEvent.</returns>
 public static CloudEvent ToCloudEvent(
     this Message message,
     CloudEventFormatter formatter,
     IEnumerable <CloudEventAttribute>?extensionAttributes
     )
 {
     Validation.CheckNotNull(message, nameof(message));
     Validation.CheckNotNull(formatter, nameof(formatter));
     if (HasCloudEventsContentType(message, out var contentType))
     {
         return(StructuredToCloudEvent(message, contentType, formatter, extensionAttributes));
     }
     else
     {
         return(BinaryToCloudEvent(message, formatter, extensionAttributes));
     }
 }
        // TODO: Check the pattern here. I suspect that cloudEvent.CopyToAsync(response) would be more natural.

        /// <summary>
        /// Copies the CloudEvent into this HttpListenerResponse instance
        /// </summary>
        /// <param name="httpListenerResponse">this</param>
        /// <param name="cloudEvent">CloudEvent to copy</param>
        /// <param name="contentMode">Content mode (structured or binary)</param>
        /// <param name="formatter">Formatter</param>
        /// <returns>Task</returns>
        public static Task CopyFromAsync(this HttpListenerResponse httpListenerResponse, CloudEvent cloudEvent,
                                         ContentMode contentMode, CloudEventFormatter formatter)
        {
            if (contentMode == ContentMode.Structured)
            {
                var buffer = formatter.EncodeStructuredModeMessage(cloudEvent, out var contentType);
                httpListenerResponse.ContentType = contentType.ToString();
                MapAttributesToListenerResponse(cloudEvent, httpListenerResponse);
                return(httpListenerResponse.OutputStream.WriteAsync(buffer, 0, buffer.Length));
            }

            // TODO: Check the defaulting to JSON here...
            httpListenerResponse.ContentType = cloudEvent.DataContentType?.ToString() ?? "application/json";
            MapAttributesToListenerResponse(cloudEvent, httpListenerResponse);
            byte[] content = formatter.EncodeBinaryModeEventData(cloudEvent);
            return(httpListenerResponse.OutputStream.WriteAsync(content, 0, content.Length));
        }
Esempio n. 29
0
        private static async Task <CloudEvent> ToCloudEventInternalAsync(HttpHeaders headers, HttpContent content,
                                                                         CloudEventFormatter formatter, IEnumerable <CloudEventAttribute>?extensionAttributes, string paramName)
        {
            Validation.CheckNotNull(formatter, nameof(formatter));

            if (HasCloudEventsContentType(content))
            {
                var stream = await content.ReadAsStreamAsync().ConfigureAwait(false);

                return(await formatter.DecodeStructuredModeMessageAsync(stream, MimeUtilities.ToContentType(content.Headers.ContentType), extensionAttributes).ConfigureAwait(false));
            }
            else
            {
                string?versionId = headers.Contains(HttpUtilities.SpecVersionHttpHeader)
                    ? headers.GetValues(HttpUtilities.SpecVersionHttpHeader).First()
                    : null;
                if (versionId is null)
                {
                    throw new ArgumentException($"Request does not represent a CloudEvent. It has neither a {HttpUtilities.SpecVersionHttpHeader} header, nor a suitable content type.", nameof(paramName));
                }
                var version = CloudEventsSpecVersion.FromVersionId(versionId)
                              ?? throw new ArgumentException($"Unknown CloudEvents spec version '{versionId}'", paramName);

                var cloudEvent = new CloudEvent(version, extensionAttributes);
                foreach (var header in headers)
                {
                    string?attributeName = HttpUtilities.GetAttributeNameFromHeaderName(header.Key);
                    if (attributeName is null || attributeName == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                    {
                        continue;
                    }
                    string attributeValue = HttpUtilities.DecodeHeaderValue(header.Value.First());

                    cloudEvent.SetAttributeFromString(attributeName, attributeValue);
                }
                if (content is object)
                {
                    // TODO: Should this just be the media type? We probably need to take a full audit of this...
                    cloudEvent.DataContentType = content.Headers?.ContentType?.ToString();
                    var data = await content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    formatter.DecodeBinaryModeEventData(data, cloudEvent);
                }
                return(Validation.CheckCloudEventArgument(cloudEvent, paramName));
            }
        }
Esempio n. 30
0
        /// <summary>
        /// Converts this HTTP request into a batch of CloudEvents.
        /// </summary>
        /// <param name="httpRequest">The HTTP request to decode. Must not be null.</param>
        /// <param name="formatter">The event formatter to use to process the request body. Must not be null.</param>
        /// <param name="extensionAttributes">The extension attributes to use when populating the CloudEvent. May be null.</param>
        /// <returns>The decoded CloudEvent.</returns>
        /// <exception cref="ArgumentException">The request does not contain a CloudEvent.</exception>
        public static async Task <IReadOnlyList <CloudEvent> > ToCloudEventBatchAsync(
            this HttpRequest httpRequest,
            CloudEventFormatter formatter,
            IEnumerable <CloudEventAttribute> extensionAttributes)
        {
            Validation.CheckNotNull(httpRequest, nameof(httpRequest));
            Validation.CheckNotNull(formatter, nameof(formatter));

            if (HasCloudEventsBatchContentType(httpRequest))
            {
                var contentType = MimeUtilities.CreateContentTypeOrNull(httpRequest.ContentType);
                return(await formatter.DecodeBatchModeMessageAsync(httpRequest.Body, contentType, extensionAttributes).ConfigureAwait(false));
            }
            else
            {
                throw new ArgumentException("HTTP message does not represent a CloudEvents batch.", nameof(httpRequest));
            }
        }