From 787c261f7116c654adc999ca4d04620d115d5c2d Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Fri, 21 Oct 2022 11:29:19 -0700 Subject: [PATCH 01/12] wip --- sdk/Sdk.Generators/Constants.cs | 1 + ...unctionMetadataProviderGenerator.Parser.cs | 20 ++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/sdk/Sdk.Generators/Constants.cs b/sdk/Sdk.Generators/Constants.cs index 703485af9..df3a3d57d 100644 --- a/sdk/Sdk.Generators/Constants.cs +++ b/sdk/Sdk.Generators/Constants.cs @@ -12,6 +12,7 @@ internal static class Constants internal const string HttpResponseType = "Microsoft.Azure.Functions.Worker.Http.HttpResponseData"; internal const string EventHubsTriggerType = "Microsoft.Azure.Functions.Worker.EventHubTriggerAttribute"; internal const string BindingPropertyNameAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingPropertyNameAttribute"; + internal const string DefaultValueType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions"; // System types internal const string IEnumerableType = "System.Collections.IEnumerable"; diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index 89262f9c8..d4e521f16 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -516,7 +516,25 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at } } - return true; + // some properties have default values, so if these properties were not already defined in constructor or named arguments, we will auto-add them here + foreach (var member in attributeData.AttributeClass!.GetMembers().Where(a => a is IPropertySymbol)) + { + var defaultValAttrList = member.GetAttributes().Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, Compilation.GetTypeByMetadataName(Constants.DefaultValueType))); + + if (defaultValAttrList.Count() > 0) + { + // list will only be of size one b/c there cannot be duplicates of an attribute on one piece of syntax + // only one constructor argument in DefaultValue attribute + var defaultVal = defaultValAttrList.FirstOrDefault().ConstructorArguments.FirstOrDefault(); + + if (!attrProperties.Keys.Any(a => string.Equals(a, default, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args + { + attrProperties[member.Name.ToString()] = defaultVal; // add property with default value to func metadata, using binding property as the arg name + } + } + } + + return true; } private bool TryLoadConstructorArguments(AttributeData attributeData, IDictionary dict, Location? attribLocation) From 4a30d823e7c3c53309408e28b3de7812219091d3 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Tue, 18 Oct 2022 11:58:06 -0700 Subject: [PATCH 02/12] cleanup --- sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index d4e521f16..21e197712 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -534,7 +534,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at } } - return true; + return true; } private bool TryLoadConstructorArguments(AttributeData attributeData, IDictionary dict, Location? attribLocation) From c073c35c74d3401c90a1c571e37581485281e69b Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Fri, 21 Oct 2022 11:30:55 -0700 Subject: [PATCH 03/12] resolve comment, add event hubs check --- sdk/Sdk.Generators/Constants.cs | 4 ++++ ...unctionMetadataProviderGenerator.Parser.cs | 21 +++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/sdk/Sdk.Generators/Constants.cs b/sdk/Sdk.Generators/Constants.cs index df3a3d57d..3d2974860 100644 --- a/sdk/Sdk.Generators/Constants.cs +++ b/sdk/Sdk.Generators/Constants.cs @@ -11,8 +11,12 @@ internal static class Constants internal const string FunctionNameType = "Microsoft.Azure.Functions.Worker.FunctionAttribute"; internal const string HttpResponseType = "Microsoft.Azure.Functions.Worker.Http.HttpResponseData"; internal const string EventHubsTriggerType = "Microsoft.Azure.Functions.Worker.EventHubTriggerAttribute"; +<<<<<<< HEAD internal const string BindingPropertyNameAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingPropertyNameAttribute"; internal const string DefaultValueType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions"; +======= + internal const string DefaultValueType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.DefaultValueAttribute"; +>>>>>>> 8d65547 (resolve comment, add event hubs check) // System types internal const string IEnumerableType = "System.Collections.IEnumerable"; diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index 21e197712..9cc07f71d 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -521,15 +521,12 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at { var defaultValAttrList = member.GetAttributes().Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, Compilation.GetTypeByMetadataName(Constants.DefaultValueType))); - if (defaultValAttrList.Count() > 0) + if (defaultValAttrList.SingleOrDefault() is { } defaultValAttr) // list will only be of size one b/c there cannot be duplicates of an attribute on one piece of syntax { - // list will only be of size one b/c there cannot be duplicates of an attribute on one piece of syntax // only one constructor argument in DefaultValue attribute - var defaultVal = defaultValAttrList.FirstOrDefault().ConstructorArguments.FirstOrDefault(); - if (!attrProperties.Keys.Any(a => string.Equals(a, default, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args { - attrProperties[member.Name.ToString()] = defaultVal; // add property with default value to func metadata, using binding property as the arg name + attrProperties[member.Name.ToString()] = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // add property with default value to func metadata, using binding property as the arg name } } } @@ -608,7 +605,7 @@ private bool IsEventHubsTriggerValid(IParameterSymbol parameterSymbol, TypeSynta dataType = DataType.Undefined; cardinality = Cardinality.Undefined; - // Check if IsBatched is false (by default it is true and does not appear in the attribute constructor) + // check if IsBatched is defined in the NamedArguments foreach (var arg in attribute.NamedArguments) { if (String.Equals(arg.Key, Constants.IsBatchedKey) && @@ -625,6 +622,18 @@ private bool IsEventHubsTriggerValid(IParameterSymbol parameterSymbol, TypeSynta } } + // Check the default value of IsBatched, if it is by default false, we find the data type and return + var eventHubsAttr = attribute.AttributeClass; + var isBatchedProp = eventHubsAttr!.GetMembers().Where(m => string.Equals(m.Name, Constants.IsBatchedKey, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); + AttributeData defaultValAttr = isBatchedProp.GetAttributes().Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, Compilation.GetTypeByMetadataName(Constants.DefaultValueType))).SingleOrDefault(); + var defaultVal = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // there is only one constructor arg, the default value + if (string.Equals(defaultVal, "false", StringComparison.OrdinalIgnoreCase)) + { + dataType = GetDataType(parameterSymbol.Type); + cardinality = Cardinality.One; + return true; + } + // we check if the param is an array type // we exclude byte arrays (byte[]) b/c we handle that as cardinality one (we handle this simliar to how a char[] is basically a string) if (parameterSymbol.Type is IArrayTypeSymbol && !SymbolEqualityComparer.Default.Equals(parameterSymbol.Type, Compilation.GetTypeByMetadataName(Constants.ByteArrayType))) From 3e3990e5571d4ae2c1e57b96f77d7e8c95dd7ea7 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Fri, 21 Oct 2022 11:33:01 -0700 Subject: [PATCH 04/12] resolve conflict --- sdk/Sdk.Generators/Constants.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdk/Sdk.Generators/Constants.cs b/sdk/Sdk.Generators/Constants.cs index 3d2974860..0d98a856a 100644 --- a/sdk/Sdk.Generators/Constants.cs +++ b/sdk/Sdk.Generators/Constants.cs @@ -11,12 +11,8 @@ internal static class Constants internal const string FunctionNameType = "Microsoft.Azure.Functions.Worker.FunctionAttribute"; internal const string HttpResponseType = "Microsoft.Azure.Functions.Worker.Http.HttpResponseData"; internal const string EventHubsTriggerType = "Microsoft.Azure.Functions.Worker.EventHubTriggerAttribute"; -<<<<<<< HEAD internal const string BindingPropertyNameAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingPropertyNameAttribute"; - internal const string DefaultValueType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions"; -======= internal const string DefaultValueType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.DefaultValueAttribute"; ->>>>>>> 8d65547 (resolve comment, add event hubs check) // System types internal const string IEnumerableType = "System.Collections.IEnumerable"; From 3742484bd34330a2dbe26a33997c3dccc3005bdb Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Fri, 21 Oct 2022 11:34:36 -0700 Subject: [PATCH 05/12] cleanup comments --- .../FunctionMetadataProviderGenerator.Parser.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index 9cc07f71d..061a9fffc 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -523,10 +523,9 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at if (defaultValAttrList.SingleOrDefault() is { } defaultValAttr) // list will only be of size one b/c there cannot be duplicates of an attribute on one piece of syntax { - // only one constructor argument in DefaultValue attribute if (!attrProperties.Keys.Any(a => string.Equals(a, default, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args { - attrProperties[member.Name.ToString()] = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // add property with default value to func metadata, using binding property as the arg name + attrProperties[member.Name.ToString()] = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // only one constructor arg in DefaultValue attribute (the default value) } } } @@ -622,7 +621,7 @@ private bool IsEventHubsTriggerValid(IParameterSymbol parameterSymbol, TypeSynta } } - // Check the default value of IsBatched, if it is by default false, we find the data type and return + // Check the default value of IsBatched var eventHubsAttr = attribute.AttributeClass; var isBatchedProp = eventHubsAttr!.GetMembers().Where(m => string.Equals(m.Name, Constants.IsBatchedKey, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); AttributeData defaultValAttr = isBatchedProp.GetAttributes().Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, Compilation.GetTypeByMetadataName(Constants.DefaultValueType))).SingleOrDefault(); From a31b2706407862d1a21171a264592e40095036bf Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Fri, 21 Oct 2022 15:21:00 -0700 Subject: [PATCH 06/12] resolve tests --- ...unctionMetadataProviderGenerator.Parser.cs | 19 ++++++++++++++++--- .../EventHubsBindingsTests.cs | 16 ++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index 061a9fffc..2156a0c7a 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -503,7 +503,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at { if (namedArgument.Value.Value != null) { - if (String.Equals(namedArgument.Key, Constants.IsBatchedKey) && !attrProperties.Keys.Contains("Cardinality")) + if (string.Equals(namedArgument.Key, Constants.IsBatchedKey) && !attrProperties.Keys.Any(k => string.Equals(k, "Cardinality", StringComparison.OrdinalIgnoreCase))) { var argValue = (bool)namedArgument.Value.Value; // isBatched only takes in booleans and the generator will parse it as a bool so we can type cast this to use in the next line @@ -523,9 +523,22 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at if (defaultValAttrList.SingleOrDefault() is { } defaultValAttr) // list will only be of size one b/c there cannot be duplicates of an attribute on one piece of syntax { - if (!attrProperties.Keys.Any(a => string.Equals(a, default, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args + var argName = member.Name; + var argDefaultVal = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // only one constructor arg in DefaultValue attribute (the default value) + + if (string.Equals(argName, Constants.IsBatchedKey)) { - attrProperties[member.Name.ToString()] = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // only one constructor arg in DefaultValue attribute (the default value) + if (!attrProperties.Keys.Contains("Cardinality")) + { + attrProperties["Cardinality"] = string.Equals(argDefaultVal, "true", StringComparison.OrdinalIgnoreCase) ? "Many" : "One"; + } + } + else + { + if (!attrProperties.Keys.Any(a => string.Equals(a, default, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args + { + attrProperties[argName] = argDefaultVal; + } } } } diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs index 249cb4583..1b5cff132 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs @@ -230,7 +230,8 @@ public Task> GetFunctionMetadataAsync(string d type = ""EventHubTrigger"", direction = ""In"", eventHubName = ""test"", - Connection = ""EventHubConnectionAppSetting""," + Connection = ""EventHubConnectionAppSetting"", + Cardinality = ""Many""," ); if (!string.Equals(dataType, "")) @@ -240,7 +241,6 @@ public Task> GetFunctionMetadataAsync(string d } expectedOutputBuilder.Append(@" - Cardinality = ""Many"", }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); @@ -476,8 +476,8 @@ public Task> GetFunctionMetadataAsync(string d direction = 'In', eventHubName = 'test', Connection = 'EventHubConnectionAppSetting', - dataType = 'String', Cardinality = 'Many', + dataType = 'String', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); @@ -497,8 +497,8 @@ public Task> GetFunctionMetadataAsync(string d direction = 'In', eventHubName = 'test', Connection = 'EventHubConnectionAppSetting', - dataType = 'String', Cardinality = 'Many', + dataType = 'String', }; var Function1binding0JSON = JsonSerializer.Serialize(Function1binding0); Function1RawBindings.Add(Function1binding0JSON); @@ -518,8 +518,8 @@ public Task> GetFunctionMetadataAsync(string d direction = 'In', eventHubName = 'test', Connection = 'EventHubConnectionAppSetting', - dataType = 'String', Cardinality = 'Many', + dataType = 'String', }; var Function2binding0JSON = JsonSerializer.Serialize(Function2binding0); Function2RawBindings.Add(Function2binding0JSON); @@ -539,8 +539,8 @@ public Task> GetFunctionMetadataAsync(string d direction = 'In', eventHubName = 'test', Connection = 'EventHubConnectionAppSetting', - dataType = 'String', Cardinality = 'Many', + dataType = 'String', }; var Function3binding0JSON = JsonSerializer.Serialize(Function3binding0); Function3RawBindings.Add(Function3binding0JSON); @@ -657,8 +657,8 @@ public Task> GetFunctionMetadataAsync(string d direction = 'In', eventHubName = 'test', Connection = 'EventHubConnectionAppSetting', - dataType = 'Binary', Cardinality = 'Many', + dataType = 'Binary', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); @@ -678,8 +678,8 @@ public Task> GetFunctionMetadataAsync(string d direction = 'In', eventHubName = 'test', Connection = 'EventHubConnectionAppSetting', - dataType = 'Binary', Cardinality = 'Many', + dataType = 'Binary', }; var Function1binding0JSON = JsonSerializer.Serialize(Function1binding0); Function1RawBindings.Add(Function1binding0JSON); From 6b1bdd51c9a182146dd39d0f6a401aa92dc0bb84 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Tue, 25 Oct 2022 13:57:56 -0700 Subject: [PATCH 07/12] resolve comments --- ...FunctionMetadataProviderGenerator.Parser.cs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index 2156a0c7a..b9dbb3b29 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -503,7 +503,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at { if (namedArgument.Value.Value != null) { - if (string.Equals(namedArgument.Key, Constants.IsBatchedKey) && !attrProperties.Keys.Any(k => string.Equals(k, "Cardinality", StringComparison.OrdinalIgnoreCase))) + if (String.Equals(namedArgument.Key, Constants.IsBatchedKey) && !attrProperties.Keys.Contains("Cardinality")) { var argValue = (bool)namedArgument.Value.Value; // isBatched only takes in booleans and the generator will parse it as a bool so we can type cast this to use in the next line @@ -524,21 +524,17 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at if (defaultValAttrList.SingleOrDefault() is { } defaultValAttr) // list will only be of size one b/c there cannot be duplicates of an attribute on one piece of syntax { var argName = member.Name; - var argDefaultVal = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // only one constructor arg in DefaultValue attribute (the default value) - - if (string.Equals(argName, Constants.IsBatchedKey)) + object arg = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!; // only one constructor arg in DefaultValue attribute (the default value) + if ( arg is bool b && string.Equals(argName, Constants.IsBatchedKey)) { if (!attrProperties.Keys.Contains("Cardinality")) { - attrProperties["Cardinality"] = string.Equals(argDefaultVal, "true", StringComparison.OrdinalIgnoreCase) ? "Many" : "One"; + attrProperties["Cardinality"] = b ? "Many" : "One"; } } - else + else if (!attrProperties.Keys.Any(a => string.Equals(a, argName, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args { - if (!attrProperties.Keys.Any(a => string.Equals(a, default, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args - { - attrProperties[argName] = argDefaultVal; - } + attrProperties[argName] = arg; } } } @@ -639,7 +635,7 @@ private bool IsEventHubsTriggerValid(IParameterSymbol parameterSymbol, TypeSynta var isBatchedProp = eventHubsAttr!.GetMembers().Where(m => string.Equals(m.Name, Constants.IsBatchedKey, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); AttributeData defaultValAttr = isBatchedProp.GetAttributes().Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, Compilation.GetTypeByMetadataName(Constants.DefaultValueType))).SingleOrDefault(); var defaultVal = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // there is only one constructor arg, the default value - if (string.Equals(defaultVal, "false", StringComparison.OrdinalIgnoreCase)) + if (!bool.Parse(defaultVal)) { dataType = GetDataType(parameterSymbol.Type); cardinality = Cardinality.One; From e2358545a9ff4f5b299afd3c2b72cace4960e96f Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Tue, 1 Nov 2022 10:46:52 -0700 Subject: [PATCH 08/12] standardize casing for bindings --- .../Extensions/StringExtensions.cs | 12 ++ ...unctionMetadataProviderGenerator.Parser.cs | 22 ++-- .../EventHubsBindingsTests.cs | 104 +++++++++--------- .../HttpTriggerTests.cs | 42 +++---- .../IntegratedTriggersAndBindingsTests.cs | 98 ++++++++--------- .../StorageBindingTests.cs | 54 ++++----- 6 files changed, 172 insertions(+), 160 deletions(-) diff --git a/sdk/Sdk.Generators/Extensions/StringExtensions.cs b/sdk/Sdk.Generators/Extensions/StringExtensions.cs index 4d057b0c5..527fd503e 100644 --- a/sdk/Sdk.Generators/Extensions/StringExtensions.cs +++ b/sdk/Sdk.Generators/Extensions/StringExtensions.cs @@ -29,5 +29,17 @@ public static string TrimStringsFromEnd(this string str, IReadOnlyList s return result; } + + public static string UppercaseFirst(this string str) + { + // Check for empty string. + if (string.IsNullOrEmpty(str)) + { + return string.Empty; + } + + // Return char and concat substring. + return char.ToUpper(str[0]) + str.Substring(1); + } } } diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index b9dbb3b29..2ad9fdcb4 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -284,7 +284,7 @@ private bool TryGetParameterInputAndTriggerBindings(MethodDeclarationSyntax meth if (dataType is not DataType.Undefined) { - bindingDict!.Add("dataType", FormatObject(Enum.GetName(typeof(DataType), dataType))); + bindingDict!.Add("DataType", FormatObject(Enum.GetName(typeof(DataType), dataType))); } // special handling for EventHubsTrigger - we need to define a property called "Cardinality" @@ -433,9 +433,9 @@ private IDictionary GetHttpReturnBinding(string returnBindingNam { var httpBinding = new Dictionary(); - httpBinding.Add("name", FormatObject(returnBindingName)); - httpBinding.Add("type", FormatObject("http")); - httpBinding.Add("direction", FormatObject("Out")); + httpBinding.Add("Name", FormatObject(returnBindingName)); + httpBinding.Add("Type", FormatObject("http")); + httpBinding.Add("Direction", FormatObject("Out")); return httpBinding; } @@ -460,9 +460,9 @@ private bool TryCreateBindingDict(AttributeData bindingAttrData, string bindingN var bindingCount = attributeProperties!.Count + 3; bindings = new Dictionary(capacity: bindingCount); - bindings.Add("name", FormatObject(bindingName)); - bindings.Add("type", FormatObject(bindingType)); - bindings.Add("direction", FormatObject(bindingDirection)); + bindings.Add("Name", FormatObject(bindingName)); + bindings.Add("Type", FormatObject(bindingType)); + bindings.Add("Direction", FormatObject(bindingDirection)); // Add additional bindingInfo to the anonymous type because some functions have more properties than others foreach (var prop in attributeProperties!) // attributeProperties won't be null here b/c we would've exited this method earlier if it was during TryGetAttributeProperties check @@ -472,14 +472,14 @@ private bool TryCreateBindingDict(AttributeData bindingAttrData, string bindingN if (prop.Value?.GetType().IsArray ?? false) { string arr = FormatArray((IEnumerable)prop.Value); - bindings[propertyName] = arr; + bindings[propertyName.UppercaseFirst()] = arr; // Uppercase first to use PascalCase in generated file's anonymous type } else { bool isEnum = string.Equals(prop.Key, "authLevel", StringComparison.OrdinalIgnoreCase); // prop keys come from Azure Functions defined attributes so we can check directly for authLevel var propertyValue = FormatObject(prop.Value, isEnum); - bindings[propertyName] = propertyValue; + bindings[propertyName.UppercaseFirst()] = propertyValue; } } @@ -511,7 +511,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at } else { - attrProperties[namedArgument.Key] = namedArgument.Value.Value; + attrProperties[namedArgument.Key.UppercaseFirst()] = namedArgument.Value.Value; } } } @@ -534,7 +534,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at } else if (!attrProperties.Keys.Any(a => string.Equals(a, argName, StringComparison.OrdinalIgnoreCase))) // check if this property has been assigned a value already in constructor or named args { - attrProperties[argName] = arg; + attrProperties[argName.UppercaseFirst()] = arg; } } } diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs index 1b5cff132..736300605 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/EventHubsBindingsTests.cs @@ -97,17 +97,17 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = ""input"", - type = ""EventHubTrigger"", - direction = ""In"", - eventHubName = ""test"", + Name = ""input"", + Type = ""EventHubTrigger"", + Direction = ""In"", + EventHubName = ""test"", Connection = ""EventHubConnectionAppSetting"", Cardinality = ""One"","); if(!string.Equals(dataType, "")) { expectedOutputBuilder.Append(@" - dataType = """ + dataType + "\","); + DataType = """ + dataType + "\","); } expectedOutputBuilder.Append(@" @@ -226,10 +226,10 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = ""input"", - type = ""EventHubTrigger"", - direction = ""In"", - eventHubName = ""test"", + Name = ""input"", + Type = ""EventHubTrigger"", + Direction = ""In"", + EventHubName = ""test"", Connection = ""EventHubConnectionAppSetting"", Cardinality = ""Many""," ); @@ -237,7 +237,7 @@ public Task> GetFunctionMetadataAsync(string d if (!string.Equals(dataType, "")) { expectedOutputBuilder.Append(@" - dataType = """ + dataType + "\","); + DataType = """ + dataType + "\","); } expectedOutputBuilder.Append(@" @@ -327,10 +327,10 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', }; @@ -471,13 +471,13 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', - dataType = 'String', + DataType = 'String', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); @@ -492,13 +492,13 @@ public Task> GetFunctionMetadataAsync(string d metadataList.Add(Function0); var Function1RawBindings = new List(); var Function1binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', - dataType = 'String', + DataType = 'String', }; var Function1binding0JSON = JsonSerializer.Serialize(Function1binding0); Function1RawBindings.Add(Function1binding0JSON); @@ -513,13 +513,13 @@ public Task> GetFunctionMetadataAsync(string d metadataList.Add(Function1); var Function2RawBindings = new List(); var Function2binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', - dataType = 'String', + DataType = 'String', }; var Function2binding0JSON = JsonSerializer.Serialize(Function2binding0); Function2RawBindings.Add(Function2binding0JSON); @@ -534,13 +534,13 @@ public Task> GetFunctionMetadataAsync(string d metadataList.Add(Function2); var Function3RawBindings = new List(); var Function3binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', - dataType = 'String', + DataType = 'String', }; var Function3binding0JSON = JsonSerializer.Serialize(Function3binding0); Function3RawBindings.Add(Function3binding0JSON); @@ -652,13 +652,13 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', - dataType = 'Binary', + DataType = 'Binary', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); @@ -673,13 +673,13 @@ public Task> GetFunctionMetadataAsync(string d metadataList.Add(Function0); var Function1RawBindings = new List(); var Function1binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', - dataType = 'Binary', + DataType = 'Binary', }; var Function1binding0JSON = JsonSerializer.Serialize(Function1binding0); Function1RawBindings.Add(Function1binding0JSON); @@ -778,10 +778,10 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', }; @@ -798,10 +798,10 @@ public Task> GetFunctionMetadataAsync(string d metadataList.Add(Function0); var Function1RawBindings = new List(); var Function1binding0 = new { - name = 'input', - type = 'EventHubTrigger', - direction = 'In', - eventHubName = 'test', + Name = 'input', + Type = 'EventHubTrigger', + Direction = 'In', + EventHubName = 'test', Connection = 'EventHubConnectionAppSetting', Cardinality = 'Many', }; diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs index 1ae845d25..f29c5bece 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs @@ -79,18 +79,18 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'req', - type = 'HttpTrigger', - direction = 'In', - authLevel = (AuthorizationLevel)0, - methods = new List { 'get','post' }, + Name = 'req', + Type = 'HttpTrigger', + Direction = 'In', + AuthLevel = (AuthorizationLevel)0, + Methods = new List { 'get','post' }, }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = '$return', - type = 'http', - direction = 'Out', + Name = '$return', + Type = 'http', + Direction = 'Out', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); @@ -175,11 +175,11 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'req', - type = 'HttpTrigger', - direction = 'In', - authLevel = (AuthorizationLevel)4, - methods = new List { 'get','post' }, + Name = 'req', + Type = 'HttpTrigger', + Direction = 'In', + AuthLevel = (AuthorizationLevel)4, + Methods = new List { 'get','post' }, Route = ""/api2"", }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); @@ -270,18 +270,18 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'req', - type = 'HttpTrigger', - direction = 'In', - methods = new List { 'get' }, - dataType = 'String', + Name = 'req', + Type = 'HttpTrigger', + Direction = 'In', + Methods = new List { 'get' }, + DataType = 'String', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = ""httpResponseProp"", - type = ""http"", - direction = ""Out"", + Name = ""httpResponseProp"", + Type = ""http"", + Direction = ""Out"", }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs index e49f4b89c..aa9f3f0c2 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs @@ -102,27 +102,27 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'req', - type = 'HttpTrigger', - direction = 'In', - authLevel = (AuthorizationLevel)0, - methods = new List { 'get','post' }, + Name = 'req', + Type = 'HttpTrigger', + Direction = 'In', + AuthLevel = (AuthorizationLevel)0, + Methods = new List { 'get','post' }, }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = 'Name', - type = 'Queue', - direction = 'Out', - queueName = 'functionstesting2', + Name = 'Name', + Type = 'Queue', + Direction = 'Out', + QueueName = 'functionstesting2', Connection = 'AzureWebJobsStorage', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); var Function0binding2 = new { - name = 'HttpResponse', - type = 'http', - direction = 'Out', + Name = 'HttpResponse', + Type = 'http', + Direction = 'Out', }; var Function0binding2JSON = JsonSerializer.Serialize(Function0binding2); Function0RawBindings.Add(Function0binding2JSON); @@ -233,37 +233,37 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'req', - type = 'HttpTrigger', - direction = 'In', - authLevel = (AuthorizationLevel)0, - methods = new List { 'get','post' }, + Name = 'req', + Type = 'HttpTrigger', + Direction = 'In', + AuthLevel = (AuthorizationLevel)0, + Methods = new List { 'get','post' }, }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = 'myBlob', - type = 'Blob', - direction = 'In', - blobPath = 'test-samples/sample1.txt', + Name = 'myBlob', + Type = 'Blob', + Direction = 'In', + BlobPath = 'test-samples/sample1.txt', Connection = 'AzureWebJobsStorage', - dataType = 'String', + DataType = 'String', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); var Function0binding2 = new { - name = 'Book', - type = 'Queue', - direction = 'Out', - queueName = 'functionstesting2', + Name = 'Book', + Type = 'Queue', + Direction = 'Out', + QueueName = 'functionstesting2', Connection = 'AzureWebJobsStorage', }; var Function0binding2JSON = JsonSerializer.Serialize(Function0binding2); Function0RawBindings.Add(Function0binding2JSON); var Function0binding3 = new { - name = 'HttpResponse', - type = 'http', - direction = 'Out', + Name = 'HttpResponse', + Type = 'http', + Direction = 'Out', }; var Function0binding3JSON = JsonSerializer.Serialize(Function0binding3); Function0RawBindings.Add(Function0binding3JSON); @@ -361,18 +361,18 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'req', - type = 'HttpTrigger', - direction = 'In', - authLevel = (AuthorizationLevel)0, - methods = new List { 'get' }, + Name = 'req', + Type = 'HttpTrigger', + Direction = 'In', + AuthLevel = (AuthorizationLevel)0, + Methods = new List { 'get' }, }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = '$return', - type = 'http', - direction = 'Out', + Name = '$return', + Type = 'http', + Direction = 'Out', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); @@ -457,10 +457,10 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'timer', - type = 'TimerTrigger', - direction = 'In', - schedule = '0 0 0 * * *', + Name = 'timer', + Type = 'TimerTrigger', + Direction = 'In', + Schedule = '0 0 0 * * *', RunOnStartup = 'False', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); @@ -547,19 +547,19 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = 'myReq', - type = 'HttpTrigger', - direction = 'In', - authLevel = (AuthorizationLevel)4, - methods = new List { 'get','Post' }, + Name = 'myReq', + Type = 'HttpTrigger', + Direction = 'In', + AuthLevel = (AuthorizationLevel)4, + Methods = new List { 'get','Post' }, Route = '/api2', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = 'Result', - type = 'http', - direction = 'Out', + Name = 'Result', + Type = 'http', + Direction = 'Out', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/StorageBindingTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/StorageBindingTests.cs index 9f0113d18..c72b67778 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/StorageBindingTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/StorageBindingTests.cs @@ -86,19 +86,19 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = '$return', - type = 'Queue', - direction = 'Out', - queueName = 'test-output-dotnet-isolated', + Name = '$return', + Type = 'Queue', + Direction = 'Out', + QueueName = 'test-output-dotnet-isolated', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = 'message', - type = 'QueueTrigger', - direction = 'In', - queueName = 'test-input-dotnet-isolated', - dataType = 'String', + Name = 'message', + Type = 'QueueTrigger', + Direction = 'In', + QueueName = 'test-input-dotnet-isolated', + DataType = 'String', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); @@ -194,21 +194,21 @@ public Task> GetFunctionMetadataAsync(string d var metadataList = new List(); var Function0RawBindings = new List(); var Function0binding0 = new { - name = '$return', - type = 'Blob', - direction = 'Out', - blobPath = 'container1/hello.txt', + Name = '$return', + Type = 'Blob', + Direction = 'Out', + BlobPath = 'container1/hello.txt', Connection = 'MyOtherConnection', }; var Function0binding0JSON = JsonSerializer.Serialize(Function0binding0); Function0RawBindings.Add(Function0binding0JSON); var Function0binding1 = new { - name = 'queuePayload', - type = 'QueueTrigger', - direction = 'In', - queueName = 'queueName', + Name = 'queuePayload', + Type = 'QueueTrigger', + Direction = 'In', + QueueName = 'queueName', Connection = 'MyConnection', - dataType = 'String', + DataType = 'String', }; var Function0binding1JSON = JsonSerializer.Serialize(Function0binding1); Function0RawBindings.Add(Function0binding1JSON); @@ -223,19 +223,19 @@ public Task> GetFunctionMetadataAsync(string d metadataList.Add(Function0); var Function1RawBindings = new List(); var Function1binding0 = new { - name = '$return', - type = 'Queue', - direction = 'Out', - queueName = 'queue2', + Name = '$return', + Type = 'Queue', + Direction = 'Out', + QueueName = 'queue2', }; var Function1binding0JSON = JsonSerializer.Serialize(Function1binding0); Function1RawBindings.Add(Function1binding0JSON); var Function1binding1 = new { - name = 'blob', - type = 'BlobTrigger', - direction = 'In', - path = 'container2/%file%', - dataType = 'String', + Name = 'blob', + Type = 'BlobTrigger', + Direction = 'In', + Path = 'container2/%file%', + DataType = 'String', }; var Function1binding1JSON = JsonSerializer.Serialize(Function1binding1); Function1RawBindings.Add(Function1binding1JSON); From 8f6f20022e19b4072246f6cd19c3e97bee9c0013 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Tue, 1 Nov 2022 15:26:05 -0700 Subject: [PATCH 09/12] resolve comments --- .../FunctionMetadataProviderGenerator.Parser.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs index 2ad9fdcb4..f9ad65ec8 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator.Parser.cs @@ -290,7 +290,7 @@ private bool TryGetParameterInputAndTriggerBindings(MethodDeclarationSyntax meth // special handling for EventHubsTrigger - we need to define a property called "Cardinality" if (validEventHubs) { - if (!bindingDict!.Keys.Contains("Cardinality")) + if (!bindingDict!.ContainsKey("Cardinality")) { bindingDict["Cardinality"] = cardinality is Cardinality.Many ? FormatObject("Many") : FormatObject("One"); } @@ -503,7 +503,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at { if (namedArgument.Value.Value != null) { - if (String.Equals(namedArgument.Key, Constants.IsBatchedKey) && !attrProperties.Keys.Contains("Cardinality")) + if (string.Equals(namedArgument.Key, Constants.IsBatchedKey) && !attrProperties.ContainsKey("Cardinality")) { var argValue = (bool)namedArgument.Value.Value; // isBatched only takes in booleans and the generator will parse it as a bool so we can type cast this to use in the next line @@ -525,7 +525,7 @@ private bool TryGetAttributeProperties(AttributeData attributeData, Location? at { var argName = member.Name; object arg = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!; // only one constructor arg in DefaultValue attribute (the default value) - if ( arg is bool b && string.Equals(argName, Constants.IsBatchedKey)) + if (arg is bool b && string.Equals(argName, Constants.IsBatchedKey)) { if (!attrProperties.Keys.Contains("Cardinality")) { @@ -635,7 +635,7 @@ private bool IsEventHubsTriggerValid(IParameterSymbol parameterSymbol, TypeSynta var isBatchedProp = eventHubsAttr!.GetMembers().Where(m => string.Equals(m.Name, Constants.IsBatchedKey, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); AttributeData defaultValAttr = isBatchedProp.GetAttributes().Where(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, Compilation.GetTypeByMetadataName(Constants.DefaultValueType))).SingleOrDefault(); var defaultVal = defaultValAttr.ConstructorArguments.SingleOrDefault().Value!.ToString(); // there is only one constructor arg, the default value - if (!bool.Parse(defaultVal)) + if (!bool.TryParse(defaultVal, out bool b) || !b) { dataType = GetDataType(parameterSymbol.Type); cardinality = Cardinality.One; From 6bf1ae6ed25f956d5d13f526a97d0521bb879cb1 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Wed, 2 Nov 2022 10:19:55 -0700 Subject: [PATCH 10/12] pass arg explicitly in setup script --- setup-e2e-tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-e2e-tests.ps1 b/setup-e2e-tests.ps1 index 1d5106dbf..d9a9bc028 100644 --- a/setup-e2e-tests.ps1 +++ b/setup-e2e-tests.ps1 @@ -97,7 +97,7 @@ if (Test-Path $output) Remove-Item $output -Recurse -Force -ErrorAction Ignore } -.\tools\devpack.ps1 -E2E -AdditionalPackArgs @("-c","Release") +.\tools\devpack.ps1 -E2E -AdditionalPackArgs @("-c","Release", "-p:FunctionsRuntimeVersion=$FunctionsRuntimeVersion") if ($SkipStorageEmulator -And $SkipCosmosDBEmulator) { From 2b9bf1f64f8b9c2a97d0dde5e336628271b832b1 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Wed, 2 Nov 2022 10:21:08 -0700 Subject: [PATCH 11/12] Revert "pass arg explicitly in setup script" This reverts commit 6bf1ae6ed25f956d5d13f526a97d0521bb879cb1. --- setup-e2e-tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-e2e-tests.ps1 b/setup-e2e-tests.ps1 index d9a9bc028..1d5106dbf 100644 --- a/setup-e2e-tests.ps1 +++ b/setup-e2e-tests.ps1 @@ -97,7 +97,7 @@ if (Test-Path $output) Remove-Item $output -Recurse -Force -ErrorAction Ignore } -.\tools\devpack.ps1 -E2E -AdditionalPackArgs @("-c","Release", "-p:FunctionsRuntimeVersion=$FunctionsRuntimeVersion") +.\tools\devpack.ps1 -E2E -AdditionalPackArgs @("-c","Release") if ($SkipStorageEmulator -And $SkipCosmosDBEmulator) { From f4b2ee198335dc1d24346e2a2f1f64f15b99f4f3 Mon Sep 17 00:00:00 2001 From: Sarah Vu Date: Thu, 3 Nov 2022 10:05:51 -0700 Subject: [PATCH 12/12] revise uppercase first --- sdk/Sdk.Generators/Extensions/StringExtensions.cs | 12 ++++++++++-- .../ExtensionsTests.cs | 9 +++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/sdk/Sdk.Generators/Extensions/StringExtensions.cs b/sdk/Sdk.Generators/Extensions/StringExtensions.cs index 527fd503e..9bfaa94db 100644 --- a/sdk/Sdk.Generators/Extensions/StringExtensions.cs +++ b/sdk/Sdk.Generators/Extensions/StringExtensions.cs @@ -38,8 +38,16 @@ public static string UppercaseFirst(this string str) return string.Empty; } - // Return char and concat substring. - return char.ToUpper(str[0]) + str.Substring(1); + if (!char.IsUpper(str[0])) + { + // Return char and concat substring. + return char.ToUpper(str[0]) + str.Substring(1); + } + else + { + return str; + } + } } } diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/ExtensionsTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/ExtensionsTests.cs index e489bb673..8c3ab388d 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/ExtensionsTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/ExtensionsTests.cs @@ -33,6 +33,15 @@ public void TrimStringsFromEndWorks(string input, string expectedOutput) Assert.Equal(expectedOutput, actual); } + + [Theory] + [InlineData("isBatched", "IsBatched")] + [InlineData("MyProperty", "MyProperty")] + [InlineData("myproperty", "Myproperty")] + public void UpperCaseFirstLetter(string input, string expectedOutput) + { + Assert.Equal(input.UppercaseFirst(), expectedOutput); + } } } }