private static ExpandoObject BuildBindingMetadataFromAttribute()

in sdk/Sdk/FunctionMetadataGenerator.cs [476:579]


        private static ExpandoObject BuildBindingMetadataFromAttribute(CustomAttribute attribute, string bindingType, TypeReference parameterType, string? parameterName)
        {
            ExpandoObject binding = new ExpandoObject();

            var bindingDict = (IDictionary<string, object>)binding;
            var bindingProperties = new Dictionary<string, object>();

            if (!string.IsNullOrEmpty(parameterName))
            {
                bindingDict["Name"] = parameterName!;
            }

            var direction = GetBindingDirection(attribute);
            bindingDict["Direction"] = direction;
            bindingDict["Type"] = bindingType;

            // For extensions that support deferred binding, set the supportsDeferredBinding property so parameters are bound by the worker
            // Only use deferred binding for input and trigger bindings, output is out not currently supported
            if (SupportsDeferredBinding(attribute, parameterType) && direction != Constants.OutputBindingDirection)
            {
                bindingProperties.Add(Constants.SupportsDeferredBindingProperty, Boolean.TrueString);
            }
            else
            {
                // Is string parameter type
                if (IsStringType(parameterType.FullName))
                {
                    bindingDict["DataType"] = nameof(DataType.String);
                }
                // Is binary parameter type
                else if (IsBinaryType(parameterType.FullName))
                {
                    bindingDict["DataType"] = nameof(DataType.Binary);
                }
            }

            var bindingNameAliasDict = GetPropertyNameAliasMapping(attribute);

            foreach (var property in attribute.GetAllDefinedProperties())
            {
                var propName = property.Key;


                var propertyName = bindingNameAliasDict.TryGetValue(property.Key, out var overriddenPropertyName)
                    ? overriddenPropertyName
                    : property.Key;

                bindingDict.Add(propertyName, property.Value);
            }

            // Determine if we should set the "Cardinality" property based on
            // the presence of "IsBatched." This is a property that is from the
            // attributes that implement the ISupportCardinality interface.
            //
            // Note that we are directly looking for "IsBatched" today while we
            // are not actually instantiating the Attribute type and instead relying
            // on type inspection via Mono.Cecil.
            // TODO: Do not hard-code "IsBatched" as the property to set cardinality.
            // We should rely on the interface
            //
            // Conversion rule
            //     "IsBatched": true => "Cardinality": "Many"
            //     "IsBatched": false => "Cardinality": "One"
            if (bindingDict.TryGetValue(Constants.IsBatchedKey, out object isBatchedValue)
                && isBatchedValue is bool isBatched)
            {
                // Batching set to true
                if (isBatched)
                {
                    bindingDict["Cardinality"] = "Many";
                    // Throw if parameter type is *definitely* not a collection type.
                    // Note that this logic doesn't dictate what we can/can't do, and
                    // we can be more restrictive in the future because today some
                    // scenarios result in runtime failures.
                    if (IsIterableCollection(parameterType, out DataType dataType))
                    {
                        if (dataType.Equals(DataType.String))
                        {
                            bindingDict["DataType"] = nameof(DataType.String);
                        }
                        else if (dataType.Equals(DataType.Binary))
                        {
                            bindingDict["DataType"] = nameof(DataType.Binary);
                        }
                    }
                    else
                    {
                        throw new FunctionsMetadataGenerationException("Function is configured to process events in batches but parameter type is not iterable. " +
                            $"Change parameter named '{parameterName}' to be an IEnumerable type or set 'IsBatched' to false on your '{attribute.AttributeType.Name.Replace("Attribute", "")}' attribute.");
                    }
                }
                // Batching set to false
                else
                {
                    bindingDict["Cardinality"] = "One";
                }

                bindingDict.Remove(Constants.IsBatchedKey);
            }

            bindingDict["Properties"] = bindingProperties;

            return binding;
        }