mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Check if Azure Function method is async before adding output binding (#1297)
* check async before adding output binding * nit
This commit is contained in:
@@ -64,7 +64,7 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
MethodDeclarationSyntax azureFunction = matchingMethods.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(azureFunction);
|
||||||
|
|
||||||
// Generate updated method with the new parameter
|
// Generate updated method with the new parameter
|
||||||
// normalizewhitespace gets rid of any newline whitespace in the leading trivia, so we add that back
|
// normalizewhitespace gets rid of any newline whitespace in the leading trivia, so we add that back
|
||||||
@@ -128,8 +128,12 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a parameter for the sql output binding that looks like
|
/// Generates a parameter for the sql output binding that looks like
|
||||||
/// [Sql("[dbo].[table1]", ConnectionStringSetting = "SqlConnectionString")] out Object output
|
/// [Sql("[dbo].[table1]", ConnectionStringSetting = "SqlConnectionString")] out Object output
|
||||||
|
/// if the Azure Function method is not async and
|
||||||
|
/// [Sql("[dbo].[table1]", ConnectionStringSetting = "SqlConnectionString")] IAsyncCollector<Object> output
|
||||||
|
/// if the Azure Function method is async.
|
||||||
|
/// <param name="azureFunction"> Azure Function method. </param>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ParameterSyntax GenerateOutputBinding()
|
private ParameterSyntax GenerateOutputBinding(MethodDeclarationSyntax azureFunction)
|
||||||
{
|
{
|
||||||
// Create arguments for the Sql Output Binding attribute
|
// Create arguments for the Sql Output Binding attribute
|
||||||
var argumentList = SyntaxFactory.AttributeArgumentList();
|
var argumentList = SyntaxFactory.AttributeArgumentList();
|
||||||
@@ -140,9 +144,22 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
|||||||
attributesList = attributesList.Add(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList<AttributeSyntax>(SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("Sql")).WithArgumentList(argumentList))));
|
attributesList = attributesList.Add(SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList<AttributeSyntax>(SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("Sql")).WithArgumentList(argumentList))));
|
||||||
|
|
||||||
var syntaxTokenList = new SyntaxTokenList();
|
var syntaxTokenList = new SyntaxTokenList();
|
||||||
syntaxTokenList = syntaxTokenList.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword));
|
TypeSyntax typeSyntax;
|
||||||
|
|
||||||
ParameterSyntax newParam = SyntaxFactory.Parameter(attributesList, syntaxTokenList, SyntaxFactory.ParseTypeName(typeof(Object).Name), SyntaxFactory.Identifier("output"), null);
|
// Check if Azure Function method is async
|
||||||
|
IEnumerable<SyntaxToken> asyncModifier = azureFunction.Modifiers.Where(m => m.ToString() == "async");
|
||||||
|
bool asyncMethod = asyncModifier.Count() > 0;
|
||||||
|
if (asyncMethod)
|
||||||
|
{
|
||||||
|
typeSyntax = SyntaxFactory.ParseTypeName("IAsyncCollector<Object>");
|
||||||
|
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
syntaxTokenList = syntaxTokenList.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword));
|
||||||
|
typeSyntax = SyntaxFactory.ParseTypeName(typeof(Object).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterSyntax newParam = SyntaxFactory.Parameter(attributesList, syntaxTokenList, typeSyntax, SyntaxFactory.Identifier("output"), null);
|
||||||
return newParam;
|
return newParam;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,5 +55,20 @@ namespace Company.Namespace
|
|||||||
// Spec Defines: HTTP 400
|
// Spec Defines: HTTP 400
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Lets a user post new artists. </summary>
|
||||||
|
/// <param name="body"> The Artists to use. </param>
|
||||||
|
/// <param name="req"> Raw HTTP Request. </param>
|
||||||
|
/// <param name="cancellationToken"> The cancellation token provided on Function shutdown. </param>
|
||||||
|
/// <exception cref="ArgumentNullException"> <paramref name="body"/> is null. </exception>
|
||||||
|
[FunctionName("NewArtists_post")]
|
||||||
|
public async IActionResult NewArtists([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "artists")] Artist body, HttpRequest req)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("HTTP trigger function processed a request.");
|
||||||
|
// TODO: Handle Documented Responses.
|
||||||
|
// Spec Defines: HTTP 200
|
||||||
|
// Spec Defines: HTTP 400
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,5 +54,20 @@ namespace Company.Namespace
|
|||||||
// Spec Defines: HTTP 400
|
// Spec Defines: HTTP 400
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Lets a user post new artists. </summary>
|
||||||
|
/// <param name="body"> The Artists to use. </param>
|
||||||
|
/// <param name="req"> Raw HTTP Request. </param>
|
||||||
|
/// <param name="cancellationToken"> The cancellation token provided on Function shutdown. </param>
|
||||||
|
/// <exception cref="ArgumentNullException"> <paramref name="body"/> is null. </exception>
|
||||||
|
[FunctionName("NewArtists_post")]
|
||||||
|
public async IActionResult NewArtists([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "artists")] Artist body, HttpRequest req)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("HTTP trigger function processed a request.");
|
||||||
|
// TODO: Handle Documented Responses.
|
||||||
|
// Spec Defines: HTTP 200
|
||||||
|
// Spec Defines: HTTP 400
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,5 +54,20 @@ namespace Company.Namespace
|
|||||||
// Spec Defines: HTTP 400
|
// Spec Defines: HTTP 400
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary> Lets a user post new artists. </summary>
|
||||||
|
/// <param name="body"> The Artists to use. </param>
|
||||||
|
/// <param name="req"> Raw HTTP Request. </param>
|
||||||
|
/// <param name="cancellationToken"> The cancellation token provided on Function shutdown. </param>
|
||||||
|
/// <exception cref="ArgumentNullException"> <paramref name="body"/> is null. </exception>
|
||||||
|
[FunctionName("NewArtists_post")]
|
||||||
|
public async IActionResult NewArtists([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "artists")] Artist body, HttpRequest req)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("HTTP trigger function processed a request.");
|
||||||
|
// TODO: Handle Documented Responses.
|
||||||
|
// Spec Defines: HTTP 200
|
||||||
|
// Spec Defines: HTTP 400
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Company.Namespace.Models;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Azure.WebJobs;
|
||||||
|
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Company.Namespace
|
||||||
|
{
|
||||||
|
public class ArtistsApi
|
||||||
|
{
|
||||||
|
private ILogger<ArtistsApi> _logger;
|
||||||
|
|
||||||
|
/// <summary> Initializes a new instance of ArtistsApi. </summary>
|
||||||
|
/// <param name="logger"> Class logger. </param>
|
||||||
|
/// <exception cref="ArgumentNullException"> <paramref name="logger"/> is null. </exception>
|
||||||
|
public ArtistsApi(ILogger<ArtistsApi> logger)
|
||||||
|
{
|
||||||
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Returns a list of artists. </summary>
|
||||||
|
/// <param name="req"> Raw HTTP Request. </param>
|
||||||
|
/// <param name="cancellationToken"> The cancellation token provided on Function shutdown. </param>
|
||||||
|
[FunctionName("GetArtists_get")]
|
||||||
|
public IActionResult GetArtists([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "artists")] HttpRequest req)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("HTTP trigger function processed a request.");
|
||||||
|
// TODO: Handle Documented Responses.
|
||||||
|
// Spec Defines: HTTP 200
|
||||||
|
// Spec Defines: HTTP 400
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Lets a user post a new artist. </summary>
|
||||||
|
/// <param name="body"> The Artist to use. </param>
|
||||||
|
/// <param name="req"> Raw HTTP Request. </param>
|
||||||
|
/// <param name="cancellationToken"> The cancellation token provided on Function shutdown. </param>
|
||||||
|
/// <exception cref="ArgumentNullException"> <paramref name="body"/> is null. </exception>
|
||||||
|
[FunctionName("NewArtist_post")]
|
||||||
|
public IActionResult NewArtist([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "artists")] Artist body, HttpRequest req)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("HTTP trigger function processed a request.");
|
||||||
|
// TODO: Handle Documented Responses.
|
||||||
|
// Spec Defines: HTTP 200
|
||||||
|
// Spec Defines: HTTP 400
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Lets a user post new artists. </summary>
|
||||||
|
/// <param name="body"> The Artists to use. </param>
|
||||||
|
/// <param name="req"> Raw HTTP Request. </param>
|
||||||
|
/// <param name="cancellationToken"> The cancellation token provided on Function shutdown. </param>
|
||||||
|
/// <exception cref="ArgumentNullException"> <paramref name="body"/> is null. </exception>
|
||||||
|
[FunctionName("NewArtists_post")]
|
||||||
|
public async IActionResult NewArtists([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "artists")] Artist body, HttpRequest req, [Sql("[dbo].[table1]", ConnectionStringSetting = "SqlConnectionString")] IAsyncCollector<Object> output)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("HTTP trigger function processed a request.");
|
||||||
|
// TODO: Handle Documented Responses.
|
||||||
|
// Spec Defines: HTTP 200
|
||||||
|
// Spec Defines: HTTP 400
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,37 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
|
|||||||
Assert.AreEqual(expectedFileText, actualFileText);
|
Assert.AreEqual(expectedFileText, actualFileText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify output binding gets added for an async method
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AddSqlOutputBindingAsync()
|
||||||
|
{
|
||||||
|
// copy the original file because the output binding will be inserted into the file
|
||||||
|
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.cs");
|
||||||
|
string testFile = Path.Join(Path.GetTempPath(), $"InsertSqlOutputBindingAsync-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
|
||||||
|
File.Copy(originalFile, testFile, true);
|
||||||
|
|
||||||
|
AddSqlBindingParams parameters = new AddSqlBindingParams
|
||||||
|
{
|
||||||
|
bindingType = BindingType.output,
|
||||||
|
filePath = testFile,
|
||||||
|
functionName = "NewArtists_post",
|
||||||
|
objectName = "[dbo].[table1]",
|
||||||
|
connectionStringSetting = "SqlConnectionString"
|
||||||
|
};
|
||||||
|
|
||||||
|
AddSqlBindingOperation operation = new AddSqlBindingOperation(parameters);
|
||||||
|
ResultStatus result = operation.AddBinding();
|
||||||
|
|
||||||
|
Assert.True(result.Success);
|
||||||
|
Assert.IsNull(result.ErrorMessage);
|
||||||
|
|
||||||
|
string expectedFileText = File.ReadAllText(Path.Join(testAzureFunctionsFolder, "AzureFunctionsOutputBindingAsync.cs"));
|
||||||
|
string actualFileText = File.ReadAllText(testFile);
|
||||||
|
Assert.AreEqual(expectedFileText, actualFileText);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verify what happens when specified azure function isn't found
|
/// Verify what happens when specified azure function isn't found
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -150,9 +181,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
|
|||||||
|
|
||||||
Assert.True(result.Success);
|
Assert.True(result.Success);
|
||||||
Assert.Null(result.ErrorMessage);
|
Assert.Null(result.ErrorMessage);
|
||||||
Assert.AreEqual(2, result.azureFunctions.Length);
|
Assert.AreEqual(3, result.azureFunctions.Length);
|
||||||
Assert.AreEqual(result.azureFunctions[0], "GetArtists_get");
|
Assert.AreEqual(result.azureFunctions[0], "GetArtists_get");
|
||||||
Assert.AreEqual(result.azureFunctions[1], "NewArtist_post");
|
Assert.AreEqual(result.azureFunctions[1], "NewArtist_post");
|
||||||
|
Assert.AreEqual(result.azureFunctions[2], "NewArtists_post");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
<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" />
|
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsNet5.cs" />
|
||||||
|
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsOutputBindingAsync.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="DacFx\Dacpacs\" />
|
<Folder Include="DacFx\Dacpacs\" />
|
||||||
|
|||||||
Reference in New Issue
Block a user