diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs index f8c01a50a..a79434a72 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs @@ -434,7 +434,7 @@ private bool TryGetReturnTypeBindings(IMethodSymbol method, bool hasHttpTrigger, } } - if (SymbolEqualityComparer.Default.Equals(returnTypeSymbol, _knownFunctionMetadataTypes.HttpResponse)) // If return type is HttpResponseData + if (SymbolEqualityComparer.Default.Equals(returnTypeSymbol, _knownFunctionMetadataTypes.HttpResponse) && !hasMethodOutputBinding) // If return type is HttpResponseData { bindingsList.Add(GetHttpReturnBinding(Constants.FunctionMetadataBindingProps.ReturnBindingName)); } diff --git a/sdk/release_notes.md b/sdk/release_notes.md index 848febf3b..1cf59d3df 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -18,3 +18,4 @@ - Generate valid namespace when root namespace contains `-` (#2097) - Bug fix for scenarios with `$return` output binding and `HttpTrigger` breaking output-binding rules (#2098) - Add `CompilerGeneratedAttribute`` to generated code (#2104) +- Add test and fix for scenario with `HttpTrigger` function that returns `HttpResponseData` along with method-level output binding (#2111) \ No newline at end of file diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs index f4d482c29..b624a9d4a 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/IntegratedTriggersAndBindingsTests.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using Microsoft.Azure.Functions.Worker.Sdk.Generators; @@ -731,6 +732,108 @@ await TestHelpers.RunTestAsync( expectedGeneratedFileName, expectedOutput); } + + [Fact] + public async void HttpTriggerWithHttpResponseAndOutputBinding() + { + string inputCode = """ + using System; + using System.Collections.Generic; + using Microsoft.Azure.Functions.Worker.Http; + using Microsoft.Azure.Functions.Worker; + + namespace Foo + { + public class HttpTriggerSimple + { + [Function("Function")] + [QueueOutput("myqueue", Connection = "Con")] + public HttpResponseData Run2([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req) + { + throw new NotImplementedException(); + } + } + } + """; + + + string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; + string expectedOutput = """ + // + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Text.Json; + using System.Threading.Tasks; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + + namespace MyCompany.MyProject.MyApp + { + /// + /// Custom implementation that returns function metadata definitions for the current worker."/> + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""$return"",""type"":""queue"",""direction"":""Out"",""queueName"":""myqueue"",""connection"":""Con""}"); + Function0RawBindings.Add(@"{""name"":""req"",""type"":""httpTrigger"",""direction"":""In"",""authLevel"":""Function"",""methods"":[""get"",""post""]}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "Function", + EntryPoint = "Foo.HttpTriggerSimple.Run2", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + + return Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } + } + """; + // override the namespace value for generated types using msbuild property. + var buildPropertiesDict = new Dictionary() + { + { Constants.BuildProperties.GeneratedCodeNamespace, "MyCompany.MyProject.MyApp"} + }; + + await TestHelpers.RunTestAsync( + _referencedExtensionAssemblies, + inputCode, + expectedGeneratedFileName, + expectedOutput, + buildPropertiesDictionary: buildPropertiesDict); + } } } }