示例#1
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"]);
            }
示例#2
0
        public void SetAttributeFromStringValue_NewAttribute()
        {
            var cloudEvent = new CloudEvent();

            cloudEvent.SetAttributeFromString("ext", "text");
            Assert.Equal("text", cloudEvent["ext"]);
            Assert.Equal(CloudEventAttributeType.String, cloudEvent.GetAttribute("ext").Type);
        }
示例#3
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"));
            }
示例#4
0
        /// <summary>
        /// Attempts to get the specified attribute
        /// </summary>
        /// <param name="e">The <see cref="CloudEvent"/> to check</param>
        /// <param name="name">The name of the attribute to get</param>
        /// <param name="value">The value of the attribute, if any</param>
        /// <returns>A boolean indicating whether or not the specified attribute is defined in the specified <see cref="CloudEvent"/></returns>
        public static bool TryGetAttribute(this CloudEvent e, string name, out string value)
        {
            value = null !;
            if (string.IsNullOrWhiteSpace(name))
            {
                throw new ArgumentNullException(nameof(name));
            }
            var attribute = e.GetAttribute(name);

            if (attribute == null)
            {
                return(false);
            }
            value = attribute.Format(e[attribute]);
            return(true);
        }
        private void PopulateAttributesFromStructuredEvent(CloudEvent cloudEvent, JsonElement element)
        {
            foreach (var jsonProperty in element.EnumerateObject())
            {
                var name  = jsonProperty.Name;
                var value = jsonProperty.Value;

                // Skip the spec version attribute, which we've already taken account of.
                // Data is handled later, when everything else (importantly, the data content type)
                // has been populated.
                if (name == CloudEventsSpecVersion.SpecVersionAttribute.Name ||
                    name == DataBase64PropertyName ||
                    name == DataPropertyName)
                {
                    continue;
                }

                // For non-extension attributes, validate that the token type is as expected.
                // We're more forgiving for extension attributes: if an integer-typed extension attribute
                // has a value of "10" (i.e. as a string), that's fine. (If it has a value of "garbage",
                // that will throw in SetAttributeFromString.)
                ValidateTokenTypeForAttribute(cloudEvent.GetAttribute(name), value.ValueKind);

                // TODO: This currently performs more conversions than it really should, in the cause of simplicity.
                // We basically need a matrix of "attribute type vs token type" but that's rather complicated.

                string attributeValue = value.ValueKind switch
                {
                    JsonValueKind.String => value.GetString(),
                    JsonValueKind.True => CloudEventAttributeType.Boolean.Format(true),
                    JsonValueKind.False => CloudEventAttributeType.Boolean.Format(false),
                    JsonValueKind.Null => null,
                    // Note: this will fail if the value isn't an integer, or is out of range for Int32.
                    JsonValueKind.Number => CloudEventAttributeType.Integer.Format(value.GetInt32()),
                    _ => throw new ArgumentException($"Invalid token type '{value.ValueKind}' for CloudEvent attribute")
                };
                if (attributeValue is null)
                {
                    continue;
                }
                // Note: we *could* infer an extension type of integer and Boolean, but not other extension types.
                // (We don't want to assume that everything that looks like a timestamp is a timestamp, etc.)
                // Stick to strings for consistency.
                cloudEvent.SetAttributeFromString(name, attributeValue);
            }
        }
示例#6
0
        private void PopulateAttributesFromStructuredEvent(CloudEvent cloudEvent, JObject jObject)
        {
            foreach (var keyValuePair in jObject)
            {
                var key   = keyValuePair.Key;
                var value = keyValuePair.Value;

                // Skip the spec version attribute, which we've already taken account of.
                // Data is handled later, when everything else (importantly, the data content type)
                // has been populated.
                if (key == CloudEventsSpecVersion.SpecVersionAttribute.Name ||
                    key == DataBase64PropertyName ||
                    key == DataPropertyName)
                {
                    continue;
                }

                // For non-extension attributes, validate that the token type is as expected.
                // We're more forgiving for extension attributes: if an integer-typed extension attribute
                // has a value of "10" (i.e. as a string), that's fine. (If it has a value of "garbage",
                // that will throw in SetAttributeFromString.)
                ValidateTokenTypeForAttribute(cloudEvent.GetAttribute(key), value.Type);

                // TODO: This currently performs more conversions than it really should, in the cause of simplicity.
                // We basically need a matrix of "attribute type vs token type" but that's rather complicated.

                string attributeValue = value.Type switch
                {
                    JTokenType.String => (string)value,
                    JTokenType.Boolean => CloudEventAttributeType.Boolean.Format((bool)value),
                    JTokenType.Null => null,
                    JTokenType.Integer => CloudEventAttributeType.Integer.Format((int)value),
                    _ => throw new ArgumentException($"Invalid token type '{value.Type}' for CloudEvent attribute")
                };
                if (attributeValue is null)
                {
                    continue;
                }
                // Note: we *could* infer an extension type of integer and Boolean, but not other extension types.
                // (We don't want to assume that everything that looks like a timestamp is a timestamp, etc.)
                // Stick to strings for consistency.
                cloudEvent.SetAttributeFromString(key, attributeValue);
            }
        }
示例#7
0
        // TODO: If we make this private, we'll have significantly more control over what token types we see.
        // For example, we could turn off date parsing entirely, and we may never get "Uri" tokens either.
        public CloudEvent DecodeJObject(JObject jObject, IEnumerable <CloudEventAttribute> extensionAttributes = null)
        {
            CloudEventsSpecVersion specVersion = CloudEventsSpecVersion.Default;

            if (jObject.TryGetValue(CloudEventsSpecVersion.SpecVersionAttribute.Name, out var specVersionToken))
            {
                string versionId = (string)specVersionToken;
                specVersion = CloudEventsSpecVersion.FromVersionId(versionId);
                // TODO: Throw if specVersion is null?
            }

            var cloudEvent = new CloudEvent(specVersion, extensionAttributes);

            foreach (var keyValuePair in jObject)
            {
                var key   = keyValuePair.Key;
                var value = keyValuePair.Value;

                // Skip the spec version attribute, which we've already taken account of.
                if (key == CloudEventsSpecVersion.SpecVersionAttribute.Name)
                {
                    continue;
                }

                // TODO: Is the data_base64 name version-specific?
                if (specVersion == CloudEventsSpecVersion.V1_0 && key == DataBase64)
                {
                    // Handle base64 encoded binaries
                    cloudEvent.Data = Convert.FromBase64String((string)value);
                    continue;
                }
                if (key == Data)
                {
                    // FIXME: Deserialize where appropriate.
                    // Consider whether there are any options here to consider beyond "string" and "object".
                    // (e.g. arrays, numbers etc).
                    // Note: the cast to "object" is important here, otherwise the string branch is implicitly
                    // converted back to JToken...
                    cloudEvent.Data = value.Type == JTokenType.String ? (string)value : (object)value;
                    continue;
                }

                var attribute = cloudEvent.GetAttribute(key);

                // Set the attribute in the event, taking account of mismatches between the type in the JObject
                // and the attribute type as best we can.

                // TODO: This currently performs more conversions than it really should, in the cause of simplicity.
                // We basically need a matrix of "attribute type vs token type" but that's rather complicated.

                string attributeValue = value.Type switch
                {
                    JTokenType.String => (string)value,
                    JTokenType.Date => CloudEventAttributeType.Timestamp.Format((DateTimeOffset)value),
                    JTokenType.Uri => CloudEventAttributeType.UriReference.Format((Uri)value),
                    JTokenType.Null => null, // TODO: Check we want to do this. It's a bit weird.
                    JTokenType.Integer => CloudEventAttributeType.Integer.Format((int)value),
                    _ => throw new ArgumentException($"Invalid token type '{value.Type}' for CloudEvent attribute")
                };

                cloudEvent.SetAttributeFromString(key, attributeValue);
            }

            return(cloudEvent);
        }