From c6f4c33d0d91a9614fa1fc1e28b161e9e3e85849 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 20 Nov 2023 13:59:38 -0800 Subject: [PATCH 1/6] Replace - with _ when generating namespace for generated types. --- sdk/Sdk.Generators/FunctionsUtil.cs | 2 +- .../FunctionExecutorGeneratorTests.cs | 68 +++++++++++++++++++ .../HttpTriggerTests.cs | 5 +- test/Sdk.Generator.Tests/TestHelpers.cs | 5 +- 4 files changed, 75 insertions(+), 5 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionsUtil.cs b/sdk/Sdk.Generators/FunctionsUtil.cs index 7f5046aed..b08e5ccab 100644 --- a/sdk/Sdk.Generators/FunctionsUtil.cs +++ b/sdk/Sdk.Generators/FunctionsUtil.cs @@ -90,7 +90,7 @@ internal static string GetNamespaceForGeneratedCode(GeneratorExecutionContext co // Get the "RootNamespace" msbuild property value.(This gets populated in Microsoft.NET.Sdk.props and can be overridden by user in their function app) context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(Constants.BuildProperties.MSBuildRootNamespace, out var rootNamespaceValue); - return rootNamespaceValue!; + return rootNamespaceValue!.Replace(" ", "_").Replace("-", "_"); } } } diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index 46b4a461b..7aa83d19b 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -173,6 +173,74 @@ public async ValueTask ExecuteAsync(FunctionContext context) expectedOutput); } + [Fact] + public async Task FunctionsWithProjectNameHavingInvalidCharacters() + { + const string inputSourceCode = @" +using System; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +namespace MyCompany +{ + public class MyHttpTriggers + { + [Function(""FunctionA"")] + public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r) + => throw new Exception(); + } +} +"; + var expectedOutput = $@"// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace FOO_BAR_BAZ +{{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + internal class DirectFunctionExecutor : IFunctionExecutor + {{ + private readonly IFunctionActivator _functionActivator; + private readonly Dictionary types = new() + {{ + {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }} + }}; + + public DirectFunctionExecutor(IFunctionActivator functionActivator) + {{ + _functionActivator = functionActivator ?? throw new ArgumentNullException(nameof(functionActivator)); + }} + + /// + public async ValueTask ExecuteAsync(FunctionContext context) + {{ + var inputBindingFeature = context.Features.Get()!; + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Foo"", StringComparison.Ordinal)) + {{ + var instanceType = types[""MyCompany.MyHttpTriggers""]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); + }} + }} + }} +{GetExpectedExtensionMethodCode()} +}}".Replace("'", "\""); + + await TestHelpers.RunTestAsync( + _referencedAssemblies, + inputSourceCode, + Constants.FileNames.GeneratedFunctionExecutor, + expectedOutput, + testProjectName:"FOO-BAR-BAZ"); + } + [Fact] public async Task MultipleFunctionsDependencyInjection() { diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs index 67ad7fadd..4bbb0c6c6 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs @@ -165,7 +165,7 @@ public static void HttpTrigger([HttpTrigger(AuthorizationLevel.Admin, "get", "po using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; - namespace TestProject + namespace FOO_BAR { /// /// Custom implementation that returns function metadata definitions for the current worker."/> @@ -219,7 +219,8 @@ await TestHelpers.RunTestAsync( _referencedExtensionAssemblies, inputCode, expectedGeneratedFileName, - expectedOutput); + expectedOutput, + testProjectName: "FOO-BAR"); } [Fact] diff --git a/test/Sdk.Generator.Tests/TestHelpers.cs b/test/Sdk.Generator.Tests/TestHelpers.cs index c1d015ebc..f177564f7 100644 --- a/test/Sdk.Generator.Tests/TestHelpers.cs +++ b/test/Sdk.Generator.Tests/TestHelpers.cs @@ -26,7 +26,8 @@ public static Task RunTestAsync( string? expectedFileName, string? expectedOutputSource, List? expectedDiagnosticResults = null, - IDictionary? buildPropertiesDictionary = null) where TSourceGenerator : ISourceGenerator, new() + IDictionary? buildPropertiesDictionary = null, + string? testProjectName = null) where TSourceGenerator : ISourceGenerator, new() { CSharpSourceGeneratorVerifier.Test test = new() { @@ -49,7 +50,7 @@ public static Task RunTestAsync( var config = $@"is_global = true build_property.FunctionsEnableExecutorSourceGen = {true} build_property.FunctionsEnableMetadataSourceGen = {true} - build_property.RootNamespace = TestProject"; + build_property.RootNamespace = {testProjectName ?? "TestProject"}"; // Add test specific MSBuild properties. if (buildPropertiesDictionary is not null) From dbce5e5c80f611375475122ee7e949e677e9bc40 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 20 Nov 2023 15:13:06 -0800 Subject: [PATCH 2/6] Generate valid namespace when root namespace contains - --- sdk/release_notes.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sdk/release_notes.md b/sdk/release_notes.md index dd5cf7a3f..cd0c40b88 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -4,6 +4,10 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Sdk 1.16.3 (meta package) +### Microsoft.Azure.Functions.Worker.Sdk 1.16.4 (meta package) -- Update worker.config generation to accurate worker executable name (#1053) +- Update Microsoft.Azure.Functions.Worker.Sdk.Generators dependency to 1.1.5 + +### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.1.5 + +- Generate valid namespace when root namespace contains `-` (#2083) From 6c2fe2b2a5487c83d3f470dbef07dfc1e196f604 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 27 Nov 2023 11:08:06 -0800 Subject: [PATCH 3/6] Moving the logic to use rootnamespace to msbuil target --- sdk/Sdk.Generators/FunctionsUtil.cs | 13 ++-- ...crosoft.Azure.Functions.Worker.Sdk.targets | 1 + .../FunctionExecutorGeneratorTests.cs | 68 ------------------- .../HttpTriggerTests.cs | 2 +- test/Sdk.Generator.Tests/TestHelpers.cs | 4 +- 5 files changed, 8 insertions(+), 80 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionsUtil.cs b/sdk/Sdk.Generators/FunctionsUtil.cs index b08e5ccab..0ba73d454 100644 --- a/sdk/Sdk.Generators/FunctionsUtil.cs +++ b/sdk/Sdk.Generators/FunctionsUtil.cs @@ -80,17 +80,12 @@ internal static string GetFullyQualifiedMethodName(IMethodSymbol method) /// internal static string GetNamespaceForGeneratedCode(GeneratorExecutionContext context) { - // If csproj has the msbuild property specified, use it's value. - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(Constants.BuildProperties.GeneratedCodeNamespace, out var namespaceValue) - && !string.IsNullOrWhiteSpace(namespaceValue)) - { - return namespaceValue; - } + // If user has not provided a custom namespace explicitly, + // our msbuild target will set the RootNamespace msbuild property value as the value of this property. + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(Constants.BuildProperties.GeneratedCodeNamespace, out var namespaceValue); - // Get the "RootNamespace" msbuild property value.(This gets populated in Microsoft.NET.Sdk.props and can be overridden by user in their function app) - context.AnalyzerConfigOptions.GlobalOptions.TryGetValue(Constants.BuildProperties.MSBuildRootNamespace, out var rootNamespaceValue); + return namespaceValue!; - return rootNamespaceValue!.Replace(" ", "_").Replace("-", "_"); } } } diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets index 8ff55d245..89618a1c9 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets @@ -42,6 +42,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and false true true + $(RootNamespace) throw new Exception(); - } -} -"; - var expectedOutput = $@"// -using System; -using System.Threading.Tasks; -using System.Collections.Generic; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Context.Features; -using Microsoft.Azure.Functions.Worker.Invocation; -namespace FOO_BAR_BAZ -{{ - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] - internal class DirectFunctionExecutor : IFunctionExecutor - {{ - private readonly IFunctionActivator _functionActivator; - private readonly Dictionary types = new() - {{ - {{ ""MyCompany.MyHttpTriggers"", Type.GetType(""MyCompany.MyHttpTriggers"")! }} - }}; - - public DirectFunctionExecutor(IFunctionActivator functionActivator) - {{ - _functionActivator = functionActivator ?? throw new ArgumentNullException(nameof(functionActivator)); - }} - - /// - public async ValueTask ExecuteAsync(FunctionContext context) - {{ - var inputBindingFeature = context.Features.Get()!; - var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context)!; - var inputArguments = inputBindingResult.Values; - - if (string.Equals(context.FunctionDefinition.EntryPoint, ""MyCompany.MyHttpTriggers.Foo"", StringComparison.Ordinal)) - {{ - var instanceType = types[""MyCompany.MyHttpTriggers""]; - var i = _functionActivator.CreateInstance(instanceType, context) as global::MyCompany.MyHttpTriggers; - context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0]); - }} - }} - }} -{GetExpectedExtensionMethodCode()} -}}".Replace("'", "\""); - - await TestHelpers.RunTestAsync( - _referencedAssemblies, - inputSourceCode, - Constants.FileNames.GeneratedFunctionExecutor, - expectedOutput, - testProjectName:"FOO-BAR-BAZ"); - } - [Fact] public async Task MultipleFunctionsDependencyInjection() { diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs index 4bbb0c6c6..9253c8990 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs @@ -220,7 +220,7 @@ await TestHelpers.RunTestAsync( inputCode, expectedGeneratedFileName, expectedOutput, - testProjectName: "FOO-BAR"); + generatedCodeNamespace: "FOO_BAR"); } [Fact] diff --git a/test/Sdk.Generator.Tests/TestHelpers.cs b/test/Sdk.Generator.Tests/TestHelpers.cs index f177564f7..71605e43e 100644 --- a/test/Sdk.Generator.Tests/TestHelpers.cs +++ b/test/Sdk.Generator.Tests/TestHelpers.cs @@ -27,7 +27,7 @@ public static Task RunTestAsync( string? expectedOutputSource, List? expectedDiagnosticResults = null, IDictionary? buildPropertiesDictionary = null, - string? testProjectName = null) where TSourceGenerator : ISourceGenerator, new() + string? generatedCodeNamespace = null) where TSourceGenerator : ISourceGenerator, new() { CSharpSourceGeneratorVerifier.Test test = new() { @@ -50,7 +50,7 @@ public static Task RunTestAsync( var config = $@"is_global = true build_property.FunctionsEnableExecutorSourceGen = {true} build_property.FunctionsEnableMetadataSourceGen = {true} - build_property.RootNamespace = {testProjectName ?? "TestProject"}"; + build_property.FunctionsGeneratedCodeNamespace = {generatedCodeNamespace ?? "TestProject"}"; // Add test specific MSBuild properties. if (buildPropertiesDictionary is not null) From d0b0ed8e75c5b3f8459be430dee126132babab7e Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 27 Nov 2023 11:23:03 -0800 Subject: [PATCH 4/6] Replace "-" with "_" when using the RootNamespace if customer app does not have an overridden (fixed) root namespace value. --- sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets index 89618a1c9..a6fa6dd62 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets @@ -42,7 +42,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and false true true - $(RootNamespace) + $(RootNamespace.Replace("-", "_")) Date: Mon, 27 Nov 2023 11:36:04 -0800 Subject: [PATCH 5/6] Reverting a test change which is not relevant anymore. --- .../HttpTriggerTests.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs index 9253c8990..67ad7fadd 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProviderGeneratorTests/HttpTriggerTests.cs @@ -165,7 +165,7 @@ public static void HttpTrigger([HttpTrigger(AuthorizationLevel.Admin, "get", "po using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; - namespace FOO_BAR + namespace TestProject { /// /// Custom implementation that returns function metadata definitions for the current worker."/> @@ -219,8 +219,7 @@ await TestHelpers.RunTestAsync( _referencedExtensionAssemblies, inputCode, expectedGeneratedFileName, - expectedOutput, - generatedCodeNamespace: "FOO_BAR"); + expectedOutput); } [Fact] From 2b6634718da8517275041dabd0921aa5f9d4cf00 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Mon, 27 Nov 2023 11:43:03 -0800 Subject: [PATCH 6/6] Fix release notes --- sdk/release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/release_notes.md b/sdk/release_notes.md index cd0c40b88..375de1d54 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -10,4 +10,4 @@ ### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.1.5 -- Generate valid namespace when root namespace contains `-` (#2083) +- Generate valid namespace when root namespace contains `-` (#2097)