예제 #1
0
        private CloudEvent DecodeGenericRecord(GenericRecord record, IEnumerable <CloudEventAttribute> extensionAttributes)
        {
            if (!record.TryGetValue(AttributeName, out var attrObj))
            {
                throw new ArgumentException($"Record has no '{AttributeName}' field");
            }
            IDictionary <string, object> recordAttributes = (IDictionary <string, object>)attrObj;

            if (!recordAttributes.TryGetValue(CloudEventsSpecVersion.SpecVersionAttribute.Name, out var versionId) ||
                !(versionId is string versionIdString))
            {
                throw new ArgumentException("Specification version attribute is missing");
            }
            CloudEventsSpecVersion version = CloudEventsSpecVersion.FromVersionId(versionIdString);

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

            var cloudEvent = new CloudEvent(version, extensionAttributes);

            cloudEvent.Data = record.TryGetValue(DataName, out var data) ? data : null;

            foreach (var keyValuePair in recordAttributes)
            {
                string key   = keyValuePair.Key;
                object value = keyValuePair.Value;
                if (value is null)
                {
                    continue;
                }

                if (key == CloudEventsSpecVersion.SpecVersionAttribute.Name || key == DataName)
                {
                    continue;
                }

                // The Avro schema allows the value to be a Boolean, integer, string or bytes.
                // Timestamps and URIs are represented as strings, so we just use SetAttributeFromString to handle those.
                // TODO: This does mean that any extensions of these types must have been registered beforehand.
                if (value is bool || value is int || value is byte[])
                {
                    cloudEvent[key] = value;
                }
                else if (value is string)
                {
                    cloudEvent.SetAttributeFromString(key, (string)value);
                }
                else
                {
                    throw new ArgumentException($"Invalid value type from Avro record: {value.GetType()}");
                }
            }

            return(Validation.CheckCloudEventArgument(cloudEvent, nameof(record)));
        }
예제 #2
0
        public CloudEvent DecodeGenericRecord(GenericRecord record, IEnumerable <CloudEventAttribute> extensionAttributes)
        {
            if (!record.TryGetValue("attribute", out var attrObj))
            {
                return(null);
            }
            IDictionary <string, object> recordAttributes = (IDictionary <string, object>)attrObj;

            CloudEventsSpecVersion specVersion = CloudEventsSpecVersion.Default;

            if (recordAttributes.TryGetValue(CloudEventsSpecVersion.SpecVersionAttribute.Name, out var versionId) &&
                versionId is string versionIdString)
            {
                specVersion = CloudEventsSpecVersion.FromVersionId(versionIdString);
            }
            var cloudEvent = new CloudEvent(specVersion, extensionAttributes);

            cloudEvent.Data = record.TryGetValue(DataName, out var data) ? data : null;

            foreach (var keyValuePair in recordAttributes)
            {
                string key   = keyValuePair.Key;
                object value = keyValuePair.Value;
                if (value is null)
                {
                    continue;
                }

                if (key == CloudEventsSpecVersion.SpecVersionAttribute.Name || key == DataName)
                {
                    continue;
                }

                // The Avro schema allows the value to be a Boolean, integer, string or bytes.
                // Timestamps and URIs are represented as strings, so we just use SetAttributeFromString to handle those.
                if (value is bool || value is int || value is byte[])
                {
                    cloudEvent[key] = value;
                }
                else if (value is string)
                {
                    cloudEvent.SetAttributeFromString(key, (string)value);
                }
                else
                {
                    throw new ArgumentException($"Invalid value type from Avro record: {value.GetType()}");
                }
            }

            return(cloudEvent);
        }
예제 #3
0
        /// <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="extensions">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 ValueTask <CloudEvent> ReadCloudEventAsync(this HttpRequest httpRequest,
                                                                       CloudEventFormatter formatter,
                                                                       params CloudEventAttribute[] extensionAttributes)
        {
            if (HasCloudEventsContentType(httpRequest))
            {
                // TODO: Handle formatter being null
                return(await formatter.DecodeStructuredModeMessageAsync(httpRequest.Body, MimeUtilities.CreateContentTypeOrNull(httpRequest.ContentType), extensionAttributes).ConfigureAwait(false));
            }
            else
            {
                var headers = httpRequest.Headers;
                if (!headers.TryGetValue(HttpUtilities.SpecVersionHttpHeader, out var versionId))
                {
                    throw new ArgumentException("Request is not a CloudEvent");
                }
                var version = CloudEventsSpecVersion.FromVersionId(versionId.First());
                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)
                {
                    // TODO: This is a bit ugly. We have code in BinaryDataUtilities to handle this, but
                    // we'd rather not expose it...
                    var memoryStream = new MemoryStream();
                    await body.CopyToAsync(memoryStream).ConfigureAwait(false);

                    formatter.DecodeBinaryModeEventData(memoryStream.ToArray(), cloudEvent);
                }
                return(cloudEvent);
            }
        }
예제 #4
0
        /// <summary>
        /// Converts this HTTP request into a CloudEvent object, with the given extensions,
        /// overriding the formatter.
        /// </summary>
        /// <param name="httpRequest">HTTP request</param>
        /// <param name="formatter">The event formatter to use to process the request body.</param>
        /// <param name="extensions">List of extension instances</param>
        /// <returns>A CloudEvent instance or 'null' if the request message doesn't hold a CloudEvent</returns>
        public static async ValueTask <CloudEvent> ReadCloudEventAsync(this HttpRequest httpRequest,
                                                                       ICloudEventFormatter formatter,
                                                                       params CloudEventAttribute[] extensionAttributes)
        {
            if (HasCloudEventsContentType(httpRequest))
            {
                // TODO: Handle formatter being null
                return(await formatter.DecodeStructuredEventAsync(httpRequest.Body, extensionAttributes).ConfigureAwait(false));
            }
            else
            {
                var headers = httpRequest.Headers;
                CloudEventsSpecVersion version = CloudEventsSpecVersion.Default;
                if (headers.TryGetValue(HttpUtilities.SpecVersionHttpHeader, out var values))
                {
                    string versionId = values.First();
                    version = CloudEventsSpecVersion.FromVersionId(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);
                }

                cloudEvent.DataContentType = httpRequest.ContentType;
                if (httpRequest.Body is Stream body)
                {
                    // TODO: This is a bit ugly.
                    var memoryStream = new MemoryStream();
                    await body.CopyToAsync(memoryStream).ConfigureAwait(false);

                    if (memoryStream.Length != 0)
                    {
                        cloudEvent.Data = formatter.DecodeData(memoryStream.ToArray(), cloudEvent.DataContentType);
                    }
                }
                return(cloudEvent);
            }
        }
예제 #5
0
        public async Task SetsTraceParentExtension(bool inclTraceparent, bool inclTracestate)
        {
            var mockTransport = new MockTransport(new MockResponse(200));
            var options       = new EventGridPublisherClientOptions
            {
                Transport = mockTransport
            };
            EventGridPublisherClient client =
                new EventGridPublisherClient(
                    new Uri("http://localHost"),
                    new AzureKeyCredential("fakeKey"),
                    options);
            var activity = new Activity($"{nameof(EventGridPublisherClient)}.{nameof(EventGridPublisherClient.SendEvents)}");

            activity.SetW3CFormat();
            activity.Start();
            List <CloudEvent> inputEvents = new List <CloudEvent>();

            for (int i = 0; i < 10; i++)
            {
                var cloudEvent =
                    new CloudEvent
                {
                    Subject = "record",
                    Source  = new Uri("http://localHost"),
                    Id      = Guid.NewGuid().ToString(),
                    Time    = DateTime.Now,
                    Type    = "test"
                };

                if (inclTraceparent && inclTracestate && i % 2 == 0)
                {
                    cloudEvent.SetAttributeFromString("traceparent", "traceparentValue");
                }
                if (inclTracestate && i % 2 == 0)
                {
                    cloudEvent.SetAttributeFromString("tracestate", "param:value");
                }
                inputEvents.Add(cloudEvent);
            }
            await client.SendCloudNativeCloudEventsAsync(inputEvents);

            activity.Stop();
            List <CloudEvent>        endEvents = DeserializeRequest(mockTransport.SingleRequest);
            IEnumerator <CloudEvent> inputEnum = inputEvents.GetEnumerator();

            foreach (CloudEvent cloudEvent in endEvents)
            {
                inputEnum.MoveNext();
                var inputAttributes = inputEnum.Current.GetPopulatedAttributes().Select(pair => pair.Key.Name).ToList();
                if (inputAttributes.Contains(TraceParentHeaderName) &&
                    inputAttributes.Contains(TraceStateHeaderName))
                {
                    Assert.AreEqual(
                        inputEnum.Current[TraceParentHeaderName],
                        cloudEvent[TraceParentHeaderName]);

                    Assert.AreEqual(
                        inputEnum.Current[TraceStateHeaderName],
                        cloudEvent[TraceStateHeaderName]);
                }
                else if (inputAttributes.Contains(TraceParentHeaderName))
                {
                    Assert.AreEqual(
                        inputEnum.Current[TraceParentHeaderName],
                        cloudEvent[TraceParentHeaderName]);
                }
                else if (inputAttributes.Contains(TraceStateHeaderName))
                {
                    Assert.AreEqual(
                        inputEnum.Current[TraceStateHeaderName],
                        cloudEvent[TraceStateHeaderName]);
                }
                else
                {
                    Assert.AreEqual(
                        activity.Id,
                        cloudEvent[TraceParentHeaderName]);
                }
            }
        }