diff --git a/sdk/Sdk/Constants.cs b/sdk/Sdk/Constants.cs index 1f3b6dd15..fb3c6140c 100644 --- a/sdk/Sdk/Constants.cs +++ b/sdk/Sdk/Constants.cs @@ -31,7 +31,7 @@ internal static class Constants internal const string ReadOnlyMemoryOfBytes = "System.ReadOnlyMemory`1"; internal const string ReturnBindingName = "$return"; - internal const string HttpTriggerBindingType = "HttpTrigger"; + internal const string HttpTriggerBindingType = "httpTrigger"; internal const string IsBatchedKey = "IsBatched"; } } diff --git a/sdk/Sdk/CustomAttributeExtensions.cs b/sdk/Sdk/Extensions/CustomAttributeExtensions.cs similarity index 99% rename from sdk/Sdk/CustomAttributeExtensions.cs rename to sdk/Sdk/Extensions/CustomAttributeExtensions.cs index 6cd240eb9..817cfed9d 100644 --- a/sdk/Sdk/CustomAttributeExtensions.cs +++ b/sdk/Sdk/Extensions/CustomAttributeExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Mono.Cecil; namespace Microsoft.Azure.Functions.Worker.Sdk diff --git a/sdk/Sdk/Extensions/StringExtensions.cs b/sdk/Sdk/Extensions/StringExtensions.cs new file mode 100644 index 000000000..d91bbb429 --- /dev/null +++ b/sdk/Sdk/Extensions/StringExtensions.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Linq; +using System; + +namespace Microsoft.Azure.Functions.Worker.Sdk +{ + internal static class StringExtensions + { + /// + /// Returns a copy of the string where the first character is in lower case. + /// + public static string ToLowerFirstCharacter(this string str) + { + if (!string.IsNullOrEmpty(str)) + { + return Char.ToLowerInvariant(str.First()) + str.Substring(1); + } + else + { + return str; + } + } + } +} diff --git a/sdk/Sdk/TypeReferenceExtensions.cs b/sdk/Sdk/Extensions/TypeReferenceExtensions.cs similarity index 100% rename from sdk/Sdk/TypeReferenceExtensions.cs rename to sdk/Sdk/Extensions/TypeReferenceExtensions.cs diff --git a/sdk/Sdk/FunctionMetadataGenerator.cs b/sdk/Sdk/FunctionMetadataGenerator.cs index 4d7d925bc..a442ad439 100644 --- a/sdk/Sdk/FunctionMetadataGenerator.cs +++ b/sdk/Sdk/FunctionMetadataGenerator.cs @@ -607,11 +607,16 @@ private static string GetBindingType(CustomAttribute attribute) var attributeType = attribute.AttributeType.Name; // TODO: Should "webjob type" be a property of the "worker types" and come from there? - return attributeType - .Replace("TriggerAttribute", "Trigger") - .Replace("InputAttribute", string.Empty) - .Replace("OutputAttribute", string.Empty) - .Replace("Attribute", string.Empty); + var bindingType = attributeType + .Replace("TriggerAttribute", "Trigger") + .Replace("InputAttribute", string.Empty) + .Replace("OutputAttribute", string.Empty) + .Replace("Attribute", string.Empty); + + // The first character of "Type" property value must be lower case for the scaling infrastructure to work correctly + bindingType = bindingType.ToLowerFirstCharacter(); + + return bindingType; } private static void AddHttpOutputBinding(IList bindingMetadata, string name) diff --git a/test/FunctionMetadataGeneratorTests/FunctionMetadataGeneratorTests.cs b/test/FunctionMetadataGeneratorTests/FunctionMetadataGeneratorTests.cs index abc9ac612..7fb4ceae8 100644 --- a/test/FunctionMetadataGeneratorTests/FunctionMetadataGeneratorTests.cs +++ b/test/FunctionMetadataGeneratorTests/FunctionMetadataGeneratorTests.cs @@ -44,7 +44,7 @@ void ValidateTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "myReq" }, - { "Type", "HttpTrigger" }, + { "Type", "httpTrigger" }, { "Direction", "In" }, { "authLevel", "Admin" }, { "methods", new[] { "get", "Post" } }, @@ -86,7 +86,7 @@ void ValidateTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "myReq" }, - { "Type", "HttpTrigger" }, + { "Type", "httpTrigger" }, { "Direction", "In" }, { "authLevel", "Admin" }, { "methods", new[] { "get", "Post" } }, @@ -119,7 +119,7 @@ void ValidateTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "myReq" }, - { "Type", "HttpTrigger" }, + { "Type", "httpTrigger" }, { "Direction", "In" }, { "authLevel", "Admin" }, { "methods", new[] { "get", "Post" } }, @@ -169,7 +169,7 @@ void ValidateQueueTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "queuePayload" }, - { "Type", "QueueTrigger" }, + { "Type", "queueTrigger" }, { "Direction", "In" }, { "Connection", "MyConnection" }, { "queueName", "queueName" }, @@ -182,7 +182,7 @@ void ValidateBlobOutput(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "$return" }, - { "Type", "Blob" }, + { "Type", "blob" }, { "Direction", "Out" }, { "blobPath", "container1/hello.txt" }, { "Connection", "MyOtherConnection" } @@ -198,7 +198,7 @@ void ValidateBlobTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "blob" }, - { "Type", "BlobTrigger" }, + { "Type", "blobTrigger" }, { "Direction", "In" }, { "blobPath", "container2/%file%" }, { "DataType", "String" } @@ -210,7 +210,7 @@ void ValidateQueueOutput(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "$return" }, - { "Type", "Queue" }, + { "Type", "queue" }, { "Direction", "Out" }, { "queueName", "queue2" }, }); @@ -236,7 +236,7 @@ void ValidateTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "timer" }, - { "Type", "TimerTrigger" }, + { "Type", "timerTrigger" }, { "Direction", "In" }, { "schedule", "0 0 0 * * *" }, { "RunOnStartup", false } @@ -273,7 +273,7 @@ void ValidateQueueTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "queuePayload" }, - { "Type", "QueueTrigger" }, + { "Type", "queueTrigger" }, { "Direction", "In" }, { "Connection", "MyConnection" }, { "queueName", "queueName" }, @@ -286,7 +286,7 @@ void ValidateBlobOutput(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "blobOutput" }, - { "Type", "Blob" }, + { "Type", "blob" }, { "Direction", "Out" }, { "blobPath", "container1/hello.txt" }, { "Connection", "MyOtherConnection" }, @@ -299,7 +299,7 @@ void ValidateQueueOutput(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "queueOutput" }, - { "Type", "Queue" }, + { "Type", "queue" }, { "Direction", "Out" }, { "queueName", "queue2" }, { "DataType", "String" } @@ -335,7 +335,7 @@ void ValidateHttpTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "req" }, - { "Type", "HttpTrigger" }, + { "Type", "httpTrigger" }, { "Direction", "In" }, { "methods", new[] { "get" } }, { "DataType", "String" } @@ -357,7 +357,7 @@ void ValidateQueueOutput(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "queueOutput" }, - { "Type", "Queue" }, + { "Type", "queue" }, { "Direction", "Out" }, { "queueName", "queue2" }, { "DataType", "String" } @@ -389,7 +389,7 @@ void ValidateHttpTrigger(ExpandoObject b) AssertExpandoObject(b, new Dictionary { { "Name", "req" }, - { "Type", "HttpTrigger" }, + { "Type", "httpTrigger" }, { "Direction", "In" }, { "methods", new[] { "get" } }, { "DataType", "String" } @@ -468,7 +468,7 @@ void ValidateTrigger(ExpandoObject b, bool many) var expected = new Dictionary() { { "Name", "input" }, - { "Type", "EventHubTrigger" }, + { "Type", "eventHubTrigger" }, { "Direction", "In" }, { "eventHubName", "test" }, { "Connection", "EventHubConnectionAppSetting" } diff --git a/test/FunctionMetadataGeneratorTests/StringExtensionsTests.cs b/test/FunctionMetadataGeneratorTests/StringExtensionsTests.cs new file mode 100644 index 000000000..36c50ca71 --- /dev/null +++ b/test/FunctionMetadataGeneratorTests/StringExtensionsTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Azure.Functions.Worker.Sdk; +using Xunit; + +namespace Microsoft.Azure.Functions.SdkTests +{ + public class StringExtensionsTests + { + [Theory] + [InlineData("HttpTrigger", "httpTrigger")] + [InlineData("Blob", "blob")] + [InlineData("http", "http")] + [InlineData("", "")] + [InlineData(null, null)] + public void ToLowerFirstCharacterWorks(string input, string expectedOutput) + { + var actual = input.ToLowerFirstCharacter(); + + Assert.Equal(expectedOutput, actual); + } + } +} diff --git a/test/SdkE2ETests/Contents/functions.metadata b/test/SdkE2ETests/Contents/functions.metadata index 18b5a73db..4df05982d 100644 --- a/test/SdkE2ETests/Contents/functions.metadata +++ b/test/SdkE2ETests/Contents/functions.metadata @@ -10,7 +10,7 @@ "bindings": [ { "name": "req", - "type": "HttpTrigger", + "type": "httpTrigger", "direction": "In", "authLevel": "Anonymous", "methods": [ @@ -20,7 +20,7 @@ }, { "name": "myBlob", - "type": "Blob", + "type": "blob", "direction": "In", "dataType": "String", "blobPath": "test-samples/sample1.txt", @@ -28,7 +28,7 @@ }, { "name": "Book", - "type": "Queue", + "type": "queue", "direction": "Out", "queueName": "functionstesting2", "connection": "AzureWebJobsStorage" @@ -51,14 +51,14 @@ "bindings": [ { "name": "myQueueItem", - "type": "QueueTrigger", + "type": "queueTrigger", "direction": "In", "queueName": "functionstesting2", "connection": "AzureWebJobsStorage" }, { "name": "myBlob", - "type": "Blob", + "type": "blob", "direction": "In", "dataType": "String", "blobPath": "test-samples/sample1.txt", @@ -77,7 +77,7 @@ "bindings": [ { "name": "req", - "type": "HttpTrigger", + "type": "httpTrigger", "direction": "In", "authLevel": "Anonymous", "methods": [ @@ -87,7 +87,7 @@ }, { "name": "Name", - "type": "Queue", + "type": "queue", "direction": "Out", "dataType": "String", "queueName": "functionstesting2", @@ -111,7 +111,7 @@ "bindings": [ { "name": "req", - "type": "HttpTrigger", + "type": "httpTrigger", "direction": "In", "authLevel": "Anonymous", "methods": [ @@ -137,7 +137,7 @@ "bindings": [ { "name": "req", - "type": "HttpTrigger", + "type": "httpTrigger", "direction": "In", "authLevel": "Anonymous", "methods": [