public void SetAttributeFromStringValue_Valid()
        {
            var extensions = new[]
            {
                CloudEventAttribute.CreateExtension("string", CloudEventAttributeType.String),
                CloudEventAttribute.CreateExtension("integer", CloudEventAttributeType.Integer),
                CloudEventAttribute.CreateExtension("binary", CloudEventAttributeType.Binary),
                CloudEventAttribute.CreateExtension("boolean", CloudEventAttributeType.Boolean),
                CloudEventAttribute.CreateExtension("timestamp", CloudEventAttributeType.Timestamp),
                CloudEventAttribute.CreateExtension("uri", CloudEventAttributeType.Uri),
                CloudEventAttribute.CreateExtension("urireference", CloudEventAttributeType.UriReference)
            };
            var cloudEvent = new CloudEvent(extensions);

            cloudEvent.SetAttributeFromString("string", "text");
            cloudEvent.SetAttributeFromString("integer", "10");
            cloudEvent.SetAttributeFromString("binary", "TQ==");
            cloudEvent.SetAttributeFromString("boolean", "true");
            cloudEvent.SetAttributeFromString("timestamp", "2021-02-09T11:58:12.242Z");
            cloudEvent.SetAttributeFromString("uri", "https://cloudevents.io");
            cloudEvent.SetAttributeFromString("urireference", "//auth");

            Assert.Equal("text", cloudEvent["string"]);
            Assert.Equal(10, cloudEvent["integer"]);
            Assert.Equal(new byte[] { 77 }, cloudEvent["binary"]);
            Assert.True((bool)cloudEvent["boolean"]);
            AssertTimestampsEqual("2021-02-09T11:58:12.242Z", (DateTimeOffset)cloudEvent["timestamp"]);
            Assert.Equal(new Uri("https://cloudevents.io"), cloudEvent["uri"]);
            Assert.Equal(new Uri("//auth", UriKind.RelativeOrAbsolute), cloudEvent["urireference"]);
        }
        public void SetAttributeFromStringValue_Validates()
        {
            var attr       = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);
            var cloudEvent = new CloudEvent(new[] { attr });

            Assert.Throws <ArgumentException>(() => cloudEvent.SetAttributeFromString("ext", "garbage"));
        }
        private void ValidateTokenTypeForAttribute(CloudEventAttribute attribute, JsonValueKind valueKind)
        {
            // We can't validate unknown attributes, don't check for extension attributes,
            // and null values will be ignored anyway.
            if (attribute is null || attribute.IsExtension || valueKind == JsonValueKind.Null)
            {
                return;
            }

            // This is deliberately written so that if a new attribute type is added without this being updated, we "fail valid".
            // (That should only happen in major versions anyway, but it's worth being somewhat forgiving here.)
            var valid = CloudEventAttributeTypes.GetOrdinal(attribute.Type) switch
            {
                CloudEventAttributeTypeOrdinal.Binary => valueKind == JsonValueKind.String,
                CloudEventAttributeTypeOrdinal.Boolean => valueKind == JsonValueKind.True || valueKind == JsonValueKind.False,
                CloudEventAttributeTypeOrdinal.Integer => valueKind == JsonValueKind.Number,
                CloudEventAttributeTypeOrdinal.String => valueKind == JsonValueKind.String,
                CloudEventAttributeTypeOrdinal.Timestamp => valueKind == JsonValueKind.String,
                CloudEventAttributeTypeOrdinal.Uri => valueKind == JsonValueKind.String,
                CloudEventAttributeTypeOrdinal.UriReference => valueKind == JsonValueKind.String,
                _ => true
            };

            if (!valid)
            {
                throw new ArgumentException($"Invalid token type '{valueKind}' for CloudEvent attribute '{attribute.Name}' with type '{attribute.Type}'");
            }
        }
            public void Constructor_ExtensionAttributes_IncludesExistingAttributeName()
            {
                var ext        = CloudEventAttribute.CreateExtension("type", CloudEventAttributeType.String);
                var extensions = new[] { ext };

                Assert.Throws <ArgumentException>(() => new CloudEvent(extensions));
            }
        public void Constructor_ExtensionAttributes_Duplicate()
        {
            var ext1       = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);
            var ext2       = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);
            var extensions = new[] { ext1, ext2 };

            Assert.Throws <ArgumentException>(() => new CloudEvent(extensions));
        }
        public void Constructor_SpecVersionAndExtensionAttributes()
        {
            var extension  = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);
            var cloudEvent = new CloudEvent(CloudEventsSpecVersion.V1_0, new[] { extension });

            // This fails if the extension isn't registered.
            cloudEvent["ext"] = 10;
            Assert.Equal(10, cloudEvent[extension]);
        }
Beispiel #7
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 void CreateExtension_InvalidName(string name)
        {
            var exception = Assert.Throws <ArgumentException>(() => CloudEventAttribute.CreateExtension(name, CloudEventAttributeType.String));

            if (!string.IsNullOrEmpty(name))
            {
                Assert.Contains($"'{name}'", exception.Message);
            }
        }
        public void Indexer_SetUsingExtensionAttributeWithSameType()
        {
            var extension  = CloudEventAttribute.CreateExtension("subject", CloudEventAttributeType.String);
            var cloudEvent = new CloudEvent();

            cloudEvent.Subject    = "normal subject";
            cloudEvent[extension] = "extended subject";
            Assert.Equal("extended subject", cloudEvent.Subject);
        }
        public void Validate_WithValidator_InvalidValue()
        {
            var attr      = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer, ValidateNonNegative);
            var exception = Assert.Throws <ArgumentException>(() => attr.Validate(-5));

            Assert.Contains("Custom validation message", exception.Message);
            Assert.IsType <Exception>(exception.InnerException);
            Assert.Equal("Custom validation message", exception.InnerException.Message);
        }
Beispiel #11
0
            public void ClearNewExtensionAttributeRetainsAttributeType()
            {
                var cloudEvent = new CloudEvent();
                var attr       = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);

                cloudEvent[attr]  = null; // Doesn't set any value, but remembers the extension...
                cloudEvent["ext"] = 10;   // Which means it can be set as the integer later.
                Assert.Same(attr, cloudEvent.GetAttribute("ext"));
            }
        public void Properties_ExtensionAttribute()
        {
            var attr = CloudEventAttribute.CreateExtension("test", CloudEventAttributeType.Uri);

            Assert.Equal("test", attr.Name);
            Assert.Equal("test", attr.ToString());
            Assert.Equal(CloudEventAttributeType.Uri, attr.Type);
            Assert.False(attr.IsRequired);
            Assert.True(attr.IsExtension);
        }
Beispiel #13
0
        public void ConvertFromProto_SpecifiedExtensionAttributes_Valid()
        {
            var attribute = CloudEventAttribute.CreateExtension("xyz", CloudEventAttributeType.String);
            var proto     = CreateMinimalCloudEventProto();

            proto.Attributes.Add(attribute.Name, StringAttribute("abc"));
            var cloudEvent = new ProtobufEventFormatter().ConvertFromProto(proto, new[] { attribute });

            Assert.Equal("abc", cloudEvent[attribute]);
        }
Beispiel #14
0
            public void FetchIntegerExtensionSetImplicitlyWithString_Throws()
            {
                var cloudEvent = new CloudEvent();

                cloudEvent["ext"] = "10";

                var attr = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);

                Assert.Throws <ArgumentException>(() => cloudEvent[attr]);
            }
Beispiel #15
0
        public void ConvertFromProto_SpecifiedExtensionAttributes_UnexpectedType()
        {
            var attribute = CloudEventAttribute.CreateExtension("xyz", CloudEventAttributeType.UriReference);
            var proto     = CreateMinimalCloudEventProto();

            proto.Attributes.Add(attribute.Name, UriAttribute("https://xyz"));
            // Even though the value would be valid as a URI reference, we fail because
            // the type in the proto message is not the same as the type we've specified in the method argument.
            Assert.Throws <ArgumentException>(() => new ProtobufEventFormatter().ConvertFromProto(proto, new[] { attribute }));
        }
        public void StructuredParseWithExtensionsSuccess10()
        {
            // Register comexampleextension2 as an integer extension before parsing.
            var extension = CloudEventAttribute.CreateExtension("comexampleextension2", CloudEventAttributeType.Integer);

            var jsonFormatter = new JsonEventFormatter();
            var cloudEvent    = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonv10), extension);

            // Instead of getting it as a string (as before), we now have it as an integer.
            Assert.Equal(10, cloudEvent["comexampleextension2"]);
        }
Beispiel #17
0
        public void ExtensionAttributesProperty()
        {
            var ext1       = CloudEventAttribute.CreateExtension("ext1", CloudEventAttributeType.Integer);
            var ext2       = CloudEventAttribute.CreateExtension("ext2", CloudEventAttributeType.Integer);
            var cloudEvent = new CloudEvent(new[] { ext1 });

            cloudEvent[ext2] = 10;
            var extensions = cloudEvent.ExtensionAttributes.OrderBy(attr => attr.Name).ToList();

            Assert.Equal(new[] { ext1, ext2 }, extensions);
        }
Beispiel #18
0
 private void ValidateTokenTypeForAttribute(CloudEventAttribute attribute, JTokenType tokenType)
 {
     // We can't validate unknown attributes, don't check for extension attributes,
     // and null values will be ignored anyway.
     if (attribute is null || attribute.IsExtension || tokenType == JTokenType.Null)
     {
         return;
     }
     // We use TryGetValue so that if a new attribute type is added without this being updated, we "fail valid".
     // (That should only happen in major versions anyway, but it's worth being somewhat forgiving here.)
     if (expectedTokenTypesForReservedAttributes.TryGetValue(attribute.Type, out JTokenType expectedTokenType) &&
         tokenType != expectedTokenType)
     {
         throw new ArgumentException($"Invalid token type '{tokenType}' for CloudEvent attribute '{attribute.Name}' with type '{attribute.Type}'");
     }
 }
Beispiel #19
0
            public void SetIntegerExtensionSetImplicitlyWithString_Updates()
            {
                var cloudEvent = new CloudEvent();

                cloudEvent["ext"] = "10";
                Assert.Equal(CloudEventAttributeType.String, cloudEvent.GetAttribute("ext").Type);

                var attr = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);

                // Setting the event with the attribute updates the extension registry...
                cloudEvent[attr] = 10;
                Assert.Equal(attr, cloudEvent.GetAttribute("ext"));
                // So we can fetch the value by string or attribute.
                Assert.Equal(10, cloudEvent[attr]);
                Assert.Equal(10, cloudEvent["ext"]);
            }
Beispiel #20
0
        public void ConvertFromProto_SpecifiedExtensionAttributes_InvalidValue()
        {
            var attribute = CloudEventAttribute.CreateExtension("xyz", CloudEventAttributeType.Integer, ValidateValue);
            var proto     = CreateMinimalCloudEventProto();

            proto.Attributes.Add(attribute.Name, IntegerAttribute(1000));
            var exception = Assert.Throws <ArgumentException>(() => new ProtobufEventFormatter().ConvertFromProto(proto, new[] { attribute }));

            Assert.Equal("Boom!", exception !.InnerException !.Message);

            void ValidateValue(object value)
            {
                if ((int)value > 100)
                {
                    throw new Exception("Boom!");
                }
            }
        }
Beispiel #21
0
        public void CreateEventWithExtension()
        {
            var extension = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer);

            var cloudEvent = new CloudEvent(new[] { extension })
            {
                Type        = "com.github.pull.create",
                Id          = "A234-1234-1234",
                Time        = sampleTimestamp,
                [extension] = 10
            };

            Assert.Equal(10, cloudEvent[extension]);
            Assert.Equal(10, cloudEvent["ext"]);

            Assert.Throws <ArgumentException>(() => cloudEvent.SetAttributeFromString("ext", "not an integer"));
            cloudEvent.SetAttributeFromString("ext", "20");
            Assert.Equal(20, cloudEvent[extension]);
        }
        public void StructuredParseWithExtensionsSuccess()
        {
            var jsonFormatter      = new JsonEventFormatter();
            var avroFormatter      = new AvroEventFormatter();
            var extensionAttribute = CloudEventAttribute.CreateExtension("comexampleextension1", CloudEventAttributeType.String);
            var cloudEventJ        = jsonFormatter.DecodeStructuredEvent(Encoding.UTF8.GetBytes(jsonv10), extensionAttribute);
            var avroData           = avroFormatter.EncodeStructuredEvent(cloudEventJ, out var contentType);
            var cloudEvent         = avroFormatter.DecodeStructuredEvent(avroData, extensionAttribute);

            Assert.Equal(CloudEventsSpecVersion.V1_0, cloudEvent.SpecVersion);
            Assert.Equal("com.github.pull.create", cloudEvent.Type);
            Assert.Equal(new Uri("https://github.com/cloudevents/spec/pull/123"), cloudEvent.Source);
            Assert.Equal("A234-1234-1234", cloudEvent.Id);
            AssertTimestampsEqual("2018-04-05T17:31:00Z", cloudEvent.Time.Value);
            Assert.Equal(MediaTypeNames.Text.Xml, cloudEvent.DataContentType);
            Assert.Equal("<much wow=\"xml\"/>", cloudEvent.Data);

            Assert.Equal("value", cloudEvent[extensionAttribute]);
        }
Beispiel #23
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);
                }
            }
        }
        public void Validate_WithValidator_Valid()
        {
            var attr = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer, ValidateNonNegative);

            attr.Validate(10);
        }
        public void Validate_NoValidator_InvalidType()
        {
            var attr = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer, validator: null);

            Assert.Throws <ArgumentException>(() => attr.Validate(10L));
        }
        public void Validate_NoValidator_Valid()
        {
            var attr = CloudEventAttribute.CreateExtension("ext", CloudEventAttributeType.Integer, validator: null);

            attr.Validate(10);
        }
 public void CreateExtension_SpecVersionName() =>
 Assert.Throws <ArgumentException>(() =>
                                   CloudEventAttribute.CreateExtension(CloudEventsSpecVersion.SpecVersionAttributeName, CloudEventAttributeType.String));
 public void CreateExtension_NullType() =>
 Assert.Throws <ArgumentNullException>(() => CloudEventAttribute.CreateExtension("name", null));
 public void CreateExtension_NullName() =>
 Assert.Throws <ArgumentNullException>(() => CloudEventAttribute.CreateExtension(null, CloudEventAttributeType.String));
        public void CreateExtension_ValidName(string name)
        {
            var attr = CloudEventAttribute.CreateExtension(name, CloudEventAttributeType.Uri);

            Assert.Equal(name, attr.Name);
        }