Add error for sql bindings when .net 5 (#1259)

* add error for sql bindings when .net 5

* add test

* cleanup linq stuff and move out common code
This commit is contained in:
Kim Santiago
2021-10-07 16:13:53 -07:00
committed by GitHub
parent f2da10f23a
commit 0f0df25119
10 changed files with 121 additions and 19 deletions

View File

@@ -23,8 +23,6 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
/// </summary> /// </summary>
class AddSqlBindingOperation class AddSqlBindingOperation
{ {
const string functionAttributeText = "FunctionName";
public AddSqlBindingParams Parameters { get; } public AddSqlBindingParams Parameters { get; }
public AddSqlBindingOperation(AddSqlBindingParams parameters) public AddSqlBindingOperation(AddSqlBindingParams parameters)
@@ -43,12 +41,10 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
// look for Azure Function to update // look for Azure Function to update
IEnumerable<MethodDeclarationSyntax> azureFunctionMethods = from methodDeclaration in root.DescendantNodes().OfType<MethodDeclarationSyntax>() IEnumerable<MethodDeclarationSyntax> azureFunctionMethods = AzureFunctionsUtils.GetMethodsWithFunctionAttributes(root);
where methodDeclaration.AttributeLists.Count > 0 IEnumerable<MethodDeclarationSyntax> matchingMethods = azureFunctionMethods.Where(md => md.AttributeLists.Where(a => a.Attributes.Where(attr => attr.ArgumentList.Arguments.First().ToString().Equals($"\"{Parameters.functionName}\"")).Any()).Any());
where methodDeclaration.AttributeLists.Where(a => a.Attributes.Where(attr => attr.Name.ToString().Contains(functionAttributeText) && attr.ArgumentList.Arguments.First().ToString().Equals($"\"{Parameters.functionName}\"")).Count() == 1).Count() == 1
select methodDeclaration;
if (azureFunctionMethods.Count() == 0) if (matchingMethods.Count() == 0)
{ {
return new ResultStatus() return new ResultStatus()
{ {
@@ -56,7 +52,7 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
ErrorMessage = SR.CouldntFindAzureFunction(Parameters.functionName, Parameters.filePath) ErrorMessage = SR.CouldntFindAzureFunction(Parameters.functionName, Parameters.filePath)
}; };
} }
else if (azureFunctionMethods.Count() > 1) else if (matchingMethods.Count() > 1)
{ {
return new ResultStatus() return new ResultStatus()
{ {
@@ -65,7 +61,7 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
}; };
} }
MethodDeclarationSyntax azureFunction = azureFunctionMethods.First(); MethodDeclarationSyntax azureFunction = matchingMethods.First();
var newParam = this.Parameters.bindingType == BindingType.input ? this.GenerateInputBinding() : this.GenerateOutputBinding(); var newParam = this.Parameters.bindingType == BindingType.input ? this.GenerateInputBinding() : this.GenerateOutputBinding();
// Generate updated method with the new parameter // Generate updated method with the new parameter

View File

@@ -0,0 +1,46 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
{
internal static class AzureFunctionsUtils
{
public const string functionAttributeText = "FunctionName";
public const string net5FunctionAttributeText = "Function";
/// <summary>
/// Gets all the methods in the syntax tree with an Azure Function attribute
/// </summary>
public static IEnumerable<MethodDeclarationSyntax> GetMethodsWithFunctionAttributes(CompilationUnitSyntax root)
{
// Look for Azure Functions in the file
// Get all method declarations
IEnumerable<MethodDeclarationSyntax> methodDeclarations = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
// .NET 5 is not currently supported for sql bindings, so an error should be returned if this file has .NET 5 style Azure Functions
if (HasNet5StyleAzureFunctions(methodDeclarations))
{
throw new Exception(SR.SqlBindingsNet5NotSupported);
}
// get all the method declarations with the FunctionName attribute
IEnumerable<MethodDeclarationSyntax> methodsWithFunctionAttributes = methodDeclarations.Where(md => md.AttributeLists.Where(a => a.Attributes.Where(attr => attr.Name.ToString().Equals(functionAttributeText)).Any()).Any());
return methodsWithFunctionAttributes;
}
/// <summary>
/// Checks if any of the method declarations have .NET 5 style Azure Function attributes
/// .NET 5 AFs use the Function attribute, while .NET 3.1 AFs use FunctionName attritube
/// </summary>
public static bool HasNet5StyleAzureFunctions(IEnumerable<MethodDeclarationSyntax> methodDeclarations)
{
// get all the method declarations with the Function attribute
return methodDeclarations.Any(md => md.AttributeLists.Any(al => al.Attributes.Any(attr => attr.Name.ToString().Equals(net5FunctionAttributeText))));
}
}
}

View File

@@ -21,8 +21,6 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
/// </summary> /// </summary>
class GetAzureFunctionsOperation class GetAzureFunctionsOperation
{ {
const string functionAttributeText = "FunctionName";
public GetAzureFunctionsParams Parameters { get; } public GetAzureFunctionsParams Parameters { get; }
public GetAzureFunctionsOperation(GetAzureFunctionsParams parameters) public GetAzureFunctionsOperation(GetAzureFunctionsParams parameters)
@@ -44,15 +42,11 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
SyntaxTree tree = CSharpSyntaxTree.ParseText(text); SyntaxTree tree = CSharpSyntaxTree.ParseText(text);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot(); CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
// Look for Azure Functions in the file
// Get all method declarations
IEnumerable<MethodDeclarationSyntax> methodDeclarations = root.DescendantNodes().OfType<MethodDeclarationSyntax>();
// get all the method declarations with the FunctionName attribute // get all the method declarations with the FunctionName attribute
IEnumerable<MethodDeclarationSyntax> methodsWithFunctionAttributes = methodDeclarations.Where(md => md.AttributeLists.Count > 0).Where(md => md.AttributeLists.Where(a => a.Attributes.Where(attr => attr.Name.ToString().Contains(functionAttributeText)).Count() == 1).Count() == 1); IEnumerable<MethodDeclarationSyntax> methodsWithFunctionAttributes = AzureFunctionsUtils.GetMethodsWithFunctionAttributes(root);
// Get FunctionName attributes // Get FunctionName attributes
IEnumerable<AttributeSyntax> functionNameAttributes = methodsWithFunctionAttributes.Select(md => md.AttributeLists.Select(a => a.Attributes.Where(attr => attr.Name.ToString().Contains(functionAttributeText)).First()).First()); IEnumerable<AttributeSyntax> functionNameAttributes = methodsWithFunctionAttributes.Select(md => md.AttributeLists.Select(a => a.Attributes.Where(attr => attr.Name.ToString().Equals(AzureFunctionsUtils.functionAttributeText)).First()).First());
// Get the function names in the FunctionName attributes // Get the function names in the FunctionName attributes
IEnumerable<AttributeArgumentSyntax> nameArgs = functionNameAttributes.Select(a => a.ArgumentList.Arguments.First()); IEnumerable<AttributeArgumentSyntax> nameArgs = functionNameAttributes.Select(a => a.ArgumentList.Arguments.First());

View File

@@ -3053,6 +3053,14 @@ namespace Microsoft.SqlTools.ServiceLayer
} }
} }
public static string SqlBindingsNet5NotSupported
{
get
{
return Keys.GetString(Keys.SqlBindingsNet5NotSupported);
}
}
public static string ConnectionServiceListDbErrorNotConnected(string uri) public static string ConnectionServiceListDbErrorNotConnected(string uri)
{ {
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri); return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
@@ -4571,6 +4579,9 @@ namespace Microsoft.SqlTools.ServiceLayer
public const string MoreThanOneAzureFunctionWithName = "MoreThanOneAzureFunctionWithName"; public const string MoreThanOneAzureFunctionWithName = "MoreThanOneAzureFunctionWithName";
public const string SqlBindingsNet5NotSupported = "SqlBindingsNet5NotSupported";
private Keys() private Keys()
{ } { }

View File

@@ -1864,4 +1864,8 @@
<comment>. <comment>.
Parameters: 0 - functionName (string), 1 - fileName (string) </comment> Parameters: 0 - functionName (string), 1 - fileName (string) </comment>
</data> </data>
<data name="SqlBindingsNet5NotSupported" xml:space="preserve">
<value>Adding SQL bindings is not supported for .NET 5</value>
<comment></comment>
</data>
</root> </root>

View File

@@ -853,4 +853,5 @@ SqlAssessmentUnsuppoertedEdition(int editionCode) = Unsupported engine edition {
############################################################################ ############################################################################
# Azure Functions # Azure Functions
CouldntFindAzureFunction(string functionName, string fileName) = Couldn't find Azure function with FunctionName '{0}' in {1} CouldntFindAzureFunction(string functionName, string fileName) = Couldn't find Azure function with FunctionName '{0}' in {1}
MoreThanOneAzureFunctionWithName(string functionName, string fileName) = More than one Azure function found with the FunctionName '{0}' in {1} MoreThanOneAzureFunctionWithName(string functionName, string fileName) = More than one Azure function found with the FunctionName '{0}' in {1}
SqlBindingsNet5NotSupported = Adding SQL bindings is not supported for .NET 5

View File

@@ -2173,6 +2173,11 @@
<note>. <note>.
Parameters: 0 - functionName (string), 1 - fileName (string) </note> Parameters: 0 - functionName (string), 1 - fileName (string) </note>
</trans-unit> </trans-unit>
<trans-unit id="SqlBindingsNet5NotSupported">
<source>Adding SQL bindings is not supported for .NET 5</source>
<target state="new">Adding SQL bindings is not supported for .NET 5</target>
<note></note>
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
namespace Company.Function
{
public static class HttpTrigger1
{
[Function("HttpTrigger1")]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("HttpTrigger1");
logger.LogInformation("C# HTTP trigger function processed a request.");
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString("Welcome to Azure Functions!");
return response;
}
}
}

View File

@@ -165,7 +165,6 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
string testFile = Path.Join(Path.GetTempPath(), $"NoAzureFunctions-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs"); string testFile = Path.Join(Path.GetTempPath(), $"NoAzureFunctions-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
FileStream fstream = File.Create(testFile); FileStream fstream = File.Create(testFile);
fstream.Close(); fstream.Close();
GetAzureFunctionsParams parameters = new GetAzureFunctionsParams GetAzureFunctionsParams parameters = new GetAzureFunctionsParams
{ {
filePath = testFile filePath = testFile
@@ -178,5 +177,24 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
Assert.Null(result.ErrorMessage); Assert.Null(result.ErrorMessage);
Assert.AreEqual(0, result.azureFunctions.Length); Assert.AreEqual(0, result.azureFunctions.Length);
} }
/// <summary>
/// Verify there are no errors when a file doesn't have any Azure functions
/// </summary>
[Test]
public void GetAzureFunctionsNet5()
{
string testFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNet5.cs");
GetAzureFunctionsParams parameters = new GetAzureFunctionsParams
{
filePath = testFile
};
GetAzureFunctionsOperation operation = new GetAzureFunctionsOperation(parameters);
Exception ex = Assert.Throws<Exception>(() => { operation.GetAzureFunctions(); });
Assert.AreEqual(SR.SqlBindingsNet5NotSupported, ex.Message);
}
} }
} }

View File

@@ -43,6 +43,7 @@
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsMultipleSameFunction.cs" /> <Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsMultipleSameFunction.cs" />
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsNoBindings.cs" /> <Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsNoBindings.cs" />
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsOutputBinding.cs" /> <Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsOutputBinding.cs" />
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsNet5.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="DacFx\Dacpacs\" /> <Folder Include="DacFx\Dacpacs\" />