private CloudEventsSpecVersion( string versionId, CloudEventAttribute idAttribute, CloudEventAttribute sourceAttribute, CloudEventAttribute typeAttribute, CloudEventAttribute dataContentTypeAttribute, CloudEventAttribute dataSchemaAttribute, CloudEventAttribute subjectAttribute, CloudEventAttribute timeAttribute) { VersionId = versionId; IdAttribute = idAttribute; SourceAttribute = sourceAttribute; TypeAttribute = typeAttribute; DataContentTypeAttribute = dataContentTypeAttribute; DataSchemaAttribute = dataSchemaAttribute; SubjectAttribute = subjectAttribute; TimeAttribute = timeAttribute; var allAttributes = new[] { idAttribute, sourceAttribute, typeAttribute, dataContentTypeAttribute, dataSchemaAttribute, subjectAttribute, timeAttribute }; RequiredAttributes = allAttributes.Where(a => a.IsRequired).ToList().AsReadOnly(); OptionalAttributes = allAttributes.Where(a => !a.IsRequired).ToList().AsReadOnly(); AllAttributes = RequiredAttributes.Concat(OptionalAttributes).ToList().AsReadOnly(); attributesByName = AllAttributes.ToDictionary(attr => attr.Name); allVersions.Add(this); }
/// <summary> /// Sets or fetches the value associated with the given attribute. /// If the attribute is not known in this event, fetching the value always returns null, and /// setting the value adds the attribute, which must be an extension attribute with a name which is /// not otherwise present known to the event. /// </summary> /// <remarks> /// <para> /// If non-null, the value must be compatible with the type of the attribute. For example, an attempt /// to store a Time context attribute with a string value will fail with an <see cref="ArgumentException"/>. /// </para> /// <para> /// The the value being set is null, any existing value is removed from the event. /// </para> /// </remarks> /// <param name="attribute">The attribute whose value should be set or fetched.</param> /// <returns>The fetched attribute value, or null if the attribute has no value in this event.</returns> public object this[CloudEventAttribute attribute] { get { Validation.CheckNotNull(attribute, nameof(attribute)); Validation.CheckArgument(attribute.Name != CloudEventsSpecVersion.SpecVersionAttributeName, nameof(attribute), Strings.ErrorCannotIndexBySpecVersionAttribute); // TODO: Is this validation definitely useful? It does mean we never return something // that's invalid for the attribute, which is potentially good... var value = attributeValues.GetValueOrDefault(attribute.Name); if (value is object) { attribute.Validate(value); } return(value); } set { Validation.CheckNotNull(attribute, nameof(attribute)); Validation.CheckArgument(attribute.Name != CloudEventsSpecVersion.SpecVersionAttributeName, nameof(attribute), Strings.ErrorCannotIndexBySpecVersionAttribute); string name = attribute.Name; var knownAttribute = GetAttribute(name); // TODO: Are we happy to add the extension in even if the value is null? Validation.CheckArgument(knownAttribute is object || attribute.IsExtension, nameof(attribute), "Cannot add an unknown non-extension attribute to an event."); // If the attribute is new, or we previously had an extension attribute, replace it with our new information. // TODO: Alternatively, we could validate that it's got the same type... but what if it has // different validation criteria? if (knownAttribute is null || (knownAttribute.IsExtension && knownAttribute != attribute)) { extensionAttributes[name] = attribute; } if (value is null) { attributeValues.Remove(name); return; } // TODO: We could convert the attribute value here instead? Or is that a bit too much "magic"? attributeValues[name] = attribute.Validate(value); } }
/// <summary> /// Sets or fetches the value associated with the given attribute name. /// Setting a value of null removes the value from the event, if it exists. /// If the attribute is not known in this event, fetching the value always returns null, and /// setting the value add a new extension attribute with the given name, and a type of string. /// (The value for an unknown attribute must be a string or null.) /// </summary> public object this[string attributeName] { get { // TODO: Validate the attribute name is valid (e.g. not upper case)? Seems overkill. Validation.CheckNotNull(attributeName, nameof(attributeName)); Validation.CheckArgument(attributeName != CloudEventsSpecVersion.SpecVersionAttributeName, nameof(attributeName), Strings.ErrorCannotIndexBySpecVersionAttribute); return(attributeValues.GetValueOrDefault(Validation.CheckNotNull(attributeName, nameof(attributeName)))); } set { Validation.CheckNotNull(attributeName, nameof(attributeName)); Validation.CheckArgument(attributeName != CloudEventsSpecVersion.SpecVersionAttributeName, nameof(attributeName), Strings.ErrorCannotIndexBySpecVersionAttribute); var knownAttribute = GetAttribute(attributeName); // TODO: Are we happy to add the extension in even if the value is null? // (It's a simple way of populating extensions after the fact...) if (knownAttribute is null) { Validation.CheckArgument(value is null || value is string, nameof(value), "Cannot assign value of type {0} to unknown attribute '{1}'", value.GetType(), attributeName); knownAttribute = CloudEventAttribute.CreateExtension(attributeName, CloudEventAttributeType.String); extensionAttributes[attributeName] = knownAttribute; } if (value is null) { attributeValues.Remove(attributeName); return; } // TODO: We could convert the attribute value here instead? Or is that a bit too much "magic"? attributeValues[attributeName] = knownAttribute.Validate(value); } }