Skip to content

Commit

Permalink
Refactor deferred binding attributes (#1723)
Browse files Browse the repository at this point in the history
  • Loading branch information
liliankasem committed Jul 11, 2023
1 parent f67098d commit 0995527
Show file tree
Hide file tree
Showing 18 changed files with 138 additions and 113 deletions.
2 changes: 1 addition & 1 deletion sdk/Sdk.Generators/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal static class Types
internal const string FixedDelayRetryAttribute = "Microsoft.Azure.Functions.Worker.FixedDelayRetryAttribute";
internal const string ExponentialBackoffRetryAttribute = "Microsoft.Azure.Functions.Worker.ExponentialBackoffRetryAttribute";
internal const string InputConverterAttributeType = "Microsoft.Azure.Functions.Worker.Converters.InputConverterAttribute";
internal const string SupportedConverterTypeAttributeType = "Microsoft.Azure.Functions.Worker.Converters.SupportedConverterTypeAttribute";
internal const string SupportedTargetTypeAttributeType = "Microsoft.Azure.Functions.Worker.Converters.SupportedTargetTypeAttribute";
internal const string SupportsDeferredBindingAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.SupportsDeferredBindingAttribute";

// System types
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ private bool DoesConverterSupportDeferredBinding(TypedConstant converter, string
if (converterAdvertisesDeferredBindingSupport)
{
bool converterAdvertisesTypes = converterAdvertisedAttributes.Any(
a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, _knownFunctionMetadataTypes.SupportedConverterTypeAttributeType));
a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, _knownFunctionMetadataTypes.SupportedTargetTypeAttributeType));

if (!converterAdvertisesTypes)
{
Expand All @@ -399,7 +399,7 @@ private bool DoesConverterSupportTargetType(List<AttributeData> converterAdverti
{
foreach (AttributeData attribute in converterAdvertisedAttributes)
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, _knownFunctionMetadataTypes.SupportedConverterTypeAttributeType))
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, _knownFunctionMetadataTypes.SupportedTargetTypeAttributeType))
{
foreach (var element in attribute.ConstructorArguments)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
private readonly Lazy<INamedTypeSymbol?> _fixedDelayRetryAttribute;
private readonly Lazy<INamedTypeSymbol?> _exponentialBackoffRetryAttribute;
private readonly Lazy<INamedTypeSymbol?> _inputConverterAttributeType;
private readonly Lazy<INamedTypeSymbol?> _supportedConverterTypeAttributeType;
private readonly Lazy<INamedTypeSymbol?> _supportedTargetTypeAttributeType;
private readonly Lazy<INamedTypeSymbol?> _supportsDeferredBindingAttributeType;

internal KnownFunctionMetadataTypes(Compilation compilation)
Expand All @@ -38,7 +38,7 @@ internal KnownFunctionMetadataTypes(Compilation compilation)
_fixedDelayRetryAttribute = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.FixedDelayRetryAttribute));
_exponentialBackoffRetryAttribute = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.ExponentialBackoffRetryAttribute));
_inputConverterAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.InputConverterAttributeType));
_supportedConverterTypeAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.SupportedConverterTypeAttributeType));
_supportedTargetTypeAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.SupportedTargetTypeAttributeType));
_supportsDeferredBindingAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.SupportsDeferredBindingAttributeType));
}

Expand Down Expand Up @@ -66,7 +66,7 @@ internal KnownFunctionMetadataTypes(Compilation compilation)

public INamedTypeSymbol? InputConverterAttributeType { get => _inputConverterAttributeType.Value; }

public INamedTypeSymbol? SupportedConverterTypeAttributeType { get => _supportedConverterTypeAttributeType.Value; }
public INamedTypeSymbol? SupportedTargetTypeAttributeType { get => _supportedTargetTypeAttributeType.Value; }

public INamedTypeSymbol? SupportsDeferredBindingAttributeType { get => _supportsDeferredBindingAttributeType.Value; }
}
Expand Down
2 changes: 1 addition & 1 deletion sdk/Sdk/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ internal static class Constants
internal const string BindingPropertyNameAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingPropertyNameAttribute";
internal const string SupportsDeferredBindingAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.SupportsDeferredBindingAttribute";
internal const string InputConverterAttributeType = "Microsoft.Azure.Functions.Worker.Converters.InputConverterAttribute";
internal const string SupportedConverterTypeAttributeType = "Microsoft.Azure.Functions.Worker.Converters.SupportedConverterTypeAttribute";
internal const string SupportedTargetTypeAttributeType = "Microsoft.Azure.Functions.Worker.Converters.SupportedTargetTypeAttribute";

// System types
internal const string IEnumerableType = "System.Collections.IEnumerable";
Expand Down
6 changes: 3 additions & 3 deletions sdk/Sdk/FunctionMetadataGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,11 +837,11 @@ private static bool DoesConverterSupportDeferredBinding(CustomAttributeArgument

if (converterAdvertisesDeferredBindingSupport)
{
bool converterAdvertisesTypes = typeReferenceCustomAttributes.Any(a => string.Equals(a.AttributeType.FullName, Constants.SupportedConverterTypeAttributeType, StringComparison.Ordinal));
bool converterAdvertisesTypes = typeReferenceCustomAttributes.Any(a => string.Equals(a.AttributeType.FullName, Constants.SupportedTargetTypeAttributeType, StringComparison.Ordinal));

if (!converterAdvertisesTypes)
{
// If a converter advertises deferred binding but does not explictly advertise any types then DeferredBinding will be supported for all the types
// If a converter advertises deferred binding but does not explicitly advertise any types then DeferredBinding will be supported for all the types
return true;
}

Expand All @@ -857,7 +857,7 @@ private static bool DoesConverterSupportTargetType(Collection<CustomAttribute> c
// Parse attributes advertised by converter
foreach (CustomAttribute attribute in customAttributes)
{
if (string.Equals(attribute.AttributeType.FullName, Constants.SupportedConverterTypeAttributeType, StringComparison.Ordinal))
if (string.Equals(attribute.AttributeType.FullName, Constants.SupportedTargetTypeAttributeType, StringComparison.Ordinal))
{
foreach (CustomAttributeArgument element in attribute.ConstructorArguments)
{
Expand Down
4 changes: 2 additions & 2 deletions src/DotNetWorker.Core/CollectionModelBindingData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace Microsoft.Azure.Functions.Worker.Core
public abstract class CollectionModelBindingData
{
/// <summary>
/// Gets the array of ModelBindingData
/// Gets a ModelBindingData array
/// </summary>
public abstract ModelBindingData[] ModelBindingDataArray { get; }
public abstract ModelBindingData[] ModelBindingData { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,11 @@ public async ValueTask<FunctionInputBindingResult> BindFunctionInputAsync(Functi
{
var properties = new Dictionary<string, object>();

// Pass info about specific input converter type defined for this parameter, if present.
if (param.Properties.TryGetValue(PropertyBagKeys.ConverterType, out var converterTypeAssemblyFullName))
{
properties.Add(PropertyBagKeys.ConverterType, converterTypeAssemblyFullName);
}

// Pass info about the flag to allow fallback to default converters defined for this parameter, if present.
if (param.Properties.TryGetValue(PropertyBagKeys.AllowConverterFallback, out var flag))
{
properties.Add(PropertyBagKeys.AllowConverterFallback, flag);
}

// Pass info about input converter types defined for this parameter, if present.
if (param.Properties.TryGetValue(PropertyBagKeys.BindingAttributeSupportedConverters, out var converters))
{
properties.Add(PropertyBagKeys.BindingAttributeSupportedConverters, converters);
}

var converterContext = _converterContextFactory.Create(param.Type, source, context, properties.Count() != 0
AddFunctionParameterPropertyIfPresent(properties, param, PropertyBagKeys.ConverterType);
AddFunctionParameterPropertyIfPresent(properties, param, PropertyBagKeys.ConverterFallbackBehavior);
AddFunctionParameterPropertyIfPresent(properties, param, PropertyBagKeys.BindingAttributeSupportedConverters);

var converterContext = _converterContextFactory.Create(param.Type, source, context, properties.Count() != 0
? properties.ToImmutableDictionary()
: ImmutableDictionary<string, object>.Empty);

Expand Down Expand Up @@ -129,6 +115,14 @@ public async ValueTask<FunctionInputBindingResult> BindFunctionInputAsync(Functi
}
}

private void AddFunctionParameterPropertyIfPresent(IDictionary<string, object> properties, FunctionParameter param, string key)
{
if (param.Properties.TryGetValue(key, out object val))
{
properties.Add(key, val);
}
}

public void Dispose()
{
if (_disposed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,11 @@ private async ValueTask<ConversionResult> AwaitAndReturnConversionTaskResult(Val
/// </summary>
private bool IsConverterFallbackAllowed(ConverterContext context)
{
if (context.Properties.TryGetValue(PropertyBagKeys.AllowConverterFallback, out var result))
if (context.Properties.TryGetValue(PropertyBagKeys.ConverterFallbackBehavior, out var result))
{
if (result is not null && result is bool res)
if (result is not null && result is ConverterFallbackBehavior fallbackBehavior)
{
return res;
return fallbackBehavior != ConverterFallbackBehavior.Disallow;
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;

namespace Microsoft.Azure.Functions.Worker.Converters
{
/// <summary>
/// Specifies the fallback behavior for a converter.
/// The default behavior is <see cref="ConverterFallbackBehavior.Allow"/>.
/// </summary>
public enum ConverterFallbackBehavior
{
/// <summary>
/// Allows fallback to built-in converters. This is the default behavior.
/// </summary>
Allow = 0,

/// <summary>
/// Disallows fallback to built-in converters.
/// </summary>
Disallow = 1,

/// <summary>
/// Specifies the default fallback behavior as <see cref="ConverterFallbackBehavior.Allow"/>
/// </summary>
Default = Allow
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;

namespace Microsoft.Azure.Functions.Worker.Converters
{
/// <summary>
/// An attribute that specifies if converter fallback is allowed or disallowed.
/// Converter fallback refers to the ability to use built-in converters when custom converters
/// cannot handle a given request.
/// The default converter fallback behavior is <see cref="ConverterFallbackBehavior.Allow"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class ConverterFallbackBehaviorAttribute : Attribute
{
/// <summary>
/// Gets the value of the converter fallback behavior.
/// </summary>
public ConverterFallbackBehavior Behavior { get; }

/// <summary>
/// Creates a new instance of <see cref="ConverterFallbackBehaviorAttribute"/>
/// </summary>
/// <param name="fallbackBehavior">The value to indicate if converter fallback is allowed or disallowed.</param>
public ConverterFallbackBehaviorAttribute(ConverterFallbackBehavior fallbackBehavior)
{
Behavior = fallbackBehavior;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Microsoft.Azure.Functions.Worker.Converters
internal static class PropertyBagKeys
{
internal const string ConverterType = "converterType";
internal const string AllowConverterFallback = "allowConverterFallback";
internal const string ConverterFallbackBehavior = "converterFallbackBehavior";
internal const string BindingAttributeSupportedConverters = "bindingAttributeSupportedConverters";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;

namespace Microsoft.Azure.Functions.Worker.Converters
{
/// <summary>
/// An attribute that can specify a target type supported by function input conversion.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class SupportedTargetTypeAttribute : Attribute
{
/// <summary>
/// Gets the input converter supported target type.
/// </summary>
public Type TargetType { get; }

/// <summary>
/// Creates a new instance of <see cref="SupportedTargetTypeAttribute"/>
/// </summary>
/// <param name="targetType">Input converter target type.</param>
/// <exception cref="ArgumentNullException">Thrown when type is null</exception>
public SupportedTargetTypeAttribute(Type targetType)
{
TargetType = targetType ?? throw new ArgumentNullException(nameof(targetType));
}
}
}
5 changes: 2 additions & 3 deletions src/DotNetWorker.Grpc/Definition/GrpcFunctionDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ public GrpcFunctionDefinition(FunctionLoadRequest loadRequest, IMethodInfoLocato
// The dictionary has key of type IInputConverter and value as List of Types supported by the converter.
var converterTypesDictionary = new Dictionary<Type, List<Type>>();


Type type = bindingAttribute.GetType();
var attributes = type.GetCustomAttributes<InputConverterAttribute>();

Expand All @@ -131,7 +130,7 @@ public GrpcFunctionDefinition(FunctionLoadRequest loadRequest, IMethodInfoLocato

if (isInputConverterAttributeAdvertised)
{
output[PropertyBagKeys.AllowConverterFallback] = type.GetCustomAttribute<AllowConverterFallbackAttribute>()?.AllowConverterFallback ?? true;
output[PropertyBagKeys.ConverterFallbackBehavior] = type.GetCustomAttribute<ConverterFallbackBehaviorAttribute>()?.Behavior ?? ConverterFallbackBehavior.Default;
}

return output.ToImmutableDictionary();
Expand All @@ -143,7 +142,7 @@ private List<Type> GetTypesSupportedByConverter(Type converter)

foreach (CustomAttributeData converterAttribute in converter.CustomAttributes)
{
if (converterAttribute.AttributeType == typeof(SupportedConverterTypeAttribute))
if (converterAttribute.AttributeType == typeof(SupportedTargetTypeAttribute))
{
foreach (CustomAttributeTypedArgument supportedType in converterAttribute.ConstructorArguments)
{
Expand Down
6 changes: 3 additions & 3 deletions src/DotNetWorker.Grpc/GrpcCollectionModelBindingData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ namespace Microsoft.Azure.Functions.Worker.Grpc.Messages
{
internal partial class GrpcCollectionModelBindingData : Microsoft.Azure.Functions.Worker.Core.CollectionModelBindingData
{
public GrpcCollectionModelBindingData(CollectionModelBindingData modelBindingDataArray)
public GrpcCollectionModelBindingData(CollectionModelBindingData collectionModelBindingData)
{
ModelBindingDataArray = modelBindingDataArray.ModelBindingData
ModelBindingData = collectionModelBindingData.ModelBindingData
.Select(p => new GrpcModelBindingData(p)).ToArray();
}

public override Core.ModelBindingData[] ModelBindingDataArray { get; }
public override Core.ModelBindingData[] ModelBindingData { get; }
}
}

0 comments on commit 0995527

Please sign in to comment.