mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -05:00
Add Operations to GetAzureFunctions request (#1527)
* Add Operations to GetAzureFunctions request * add comment * comments
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -40,11 +41,23 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route from an HttpTrigger attribute if specified
|
||||
/// Gets the info for a HttpTriggerBinding for the specified method (if such a binding exists)
|
||||
/// </summary>
|
||||
/// <param name="m">The method</param>
|
||||
/// <returns>The name of the route, or null if no route is specified (or there isn't an HttpTrigger binding)</returns>
|
||||
public static string? GetHttpRoute(this MethodDeclarationSyntax m)
|
||||
/// <returns>The HttpTriggerBinding, or null if none exists</returns>
|
||||
public static HttpTriggerBinding? GetHttpTriggerBinding(this MethodDeclarationSyntax m)
|
||||
{
|
||||
var httpTriggerAttribute = m.GetHttpTriggerAttribute();
|
||||
return httpTriggerAttribute == null ? null : new HttpTriggerBinding(httpTriggerAttribute.GetHttpRoute(), httpTriggerAttribute.GetHttpOperations());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HttpTrigger attribute on the parameters for this method if one exists
|
||||
/// https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger
|
||||
/// </summary>
|
||||
/// <param name="m">The method</param>
|
||||
/// <returns>The attribute if such a binding exists</returns>
|
||||
public static AttributeSyntax? GetHttpTriggerAttribute(this MethodDeclarationSyntax m)
|
||||
{
|
||||
return m
|
||||
.ParameterList
|
||||
@@ -53,8 +66,18 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
p.AttributeLists // Get a list of all attributes on any of the parameters
|
||||
.SelectMany(al => al.Attributes)
|
||||
).Where(a => a.Name.ToString().Equals(HTTP_TRIGGER_ATTRIBUTE_NAME) // Find any HttpTrigger attributes
|
||||
).FirstOrDefault() // Get the first one available - there should only ever be 0 or 1
|
||||
?.ArgumentList // Get all the arguments for the attribute
|
||||
).FirstOrDefault(); // Get the first one available - there should only ever be 0 or 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route from the HttpTrigger attribute
|
||||
/// https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp#attributes
|
||||
/// </summary>
|
||||
/// <param name="m">The attribute</param>
|
||||
/// <returns>The name of the route, or null if no route is specified (or there isn't an HttpTrigger binding)</returns>
|
||||
public static string? GetHttpRoute(this AttributeSyntax a)
|
||||
{
|
||||
return a.ArgumentList
|
||||
?.Arguments
|
||||
.Where(a => a.ChildNodes().OfType<NameEqualsSyntax>().Where(nes => nes.Name.ToString().Equals(ROUTE_ARGUMENT_NAME)).Any()) // Find the Route argument - it should always be a named argument
|
||||
.FirstOrDefault()
|
||||
@@ -63,8 +86,22 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
.Where(le => le.Kind() != SyntaxKind.NullLiteralExpression) // Skip the null expressions so they aren't ToString()'d into "null"
|
||||
.FirstOrDefault()
|
||||
?.ToString()
|
||||
.TrimStart('$') // Remove $ from interpolated string, since this will always be outside the quotes we can just trim
|
||||
.Trim('\"'); // Trim off the quotes from the string value - additional quotes at the beginning and end aren't valid syntax so it's fine to trim
|
||||
.TrimStringQuotes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the operations (methods) on an HttpTrigger attribute. These are string params arguments to the attribute
|
||||
/// https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?tabs=in-process%2Cfunctionsv2&pivots=programming-language-csharp#attributes
|
||||
/// </summary>
|
||||
/// <param name="a">The attribute</param>
|
||||
/// <returns>The operations (methods) specified</returns>
|
||||
public static string[]? GetHttpOperations(this AttributeSyntax a)
|
||||
{
|
||||
return a.ArgumentList
|
||||
?.Arguments
|
||||
.Where(a => a.Expression.Kind() == SyntaxKind.StringLiteralExpression && a.NameEquals == null) // Operations are string literals who don't have a name (Route is always a named param)
|
||||
.Select(a => a.ToString().TrimStringQuotes().ToUpper()) // upper case for consistent naming
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -90,8 +127,19 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
?.Arguments
|
||||
.FirstOrDefault() // The first argument is the function name
|
||||
?.ToString()
|
||||
.TrimStringQuotes() ?? "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the quotes (and $ for interpolated strings) around a string literal
|
||||
/// </summary>
|
||||
/// <param name="s">The string</param>
|
||||
/// <returns>The string without quotes</returns>
|
||||
public static string TrimStringQuotes(this string s)
|
||||
{
|
||||
return s
|
||||
.TrimStart('$') // Remove $ from interpolated string, since this will always be outside the quotes we can just trim
|
||||
.Trim('\"') ?? ""; // Trim off the quotes from the string value - additional quotes at the beginning and end aren't valid syntax so it's fine to trim
|
||||
.Trim('\"'); // Trim off the quotes from the string value - additional quotes at the beginning and end aren't valid syntax so it's fine to trim
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -22,17 +22,36 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts
|
||||
/// <summary>
|
||||
/// The name of the function
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The route of the HttpTrigger binding if one exists on this function
|
||||
/// The HttpTrigger binding if one is specified
|
||||
/// </summary>
|
||||
public string? Route { get; set; }
|
||||
public HttpTriggerBinding? HttpTriggerBinding { get; }
|
||||
|
||||
public AzureFunction(string name, string? route)
|
||||
public AzureFunction(string name, HttpTriggerBinding? httpTriggerBinding)
|
||||
{
|
||||
this.Name = name;
|
||||
this.HttpTriggerBinding = httpTriggerBinding;
|
||||
}
|
||||
}
|
||||
|
||||
public class HttpTriggerBinding
|
||||
{
|
||||
/// <summary>
|
||||
/// The route specified
|
||||
/// </summary>
|
||||
public string? Route { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The operations (methods) specified
|
||||
/// </summary>
|
||||
public string[]? Operations { get; }
|
||||
|
||||
public HttpTriggerBinding(string? route, string[]? operations)
|
||||
{
|
||||
this.Route = route;
|
||||
this.Operations = operations;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,10 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
// get all the method declarations with the FunctionName attribute
|
||||
IEnumerable<MethodDeclarationSyntax> methodsWithFunctionAttributes = AzureFunctionsUtils.GetMethodsWithFunctionAttributes(root);
|
||||
|
||||
var azureFunctions = methodsWithFunctionAttributes.Select(m => new AzureFunction(m.GetFunctionName(), m.GetHttpRoute())).ToArray();
|
||||
var azureFunctions = methodsWithFunctionAttributes.Select(m => new AzureFunction(
|
||||
m.GetFunctionName(),
|
||||
m.GetHttpTriggerBinding()))
|
||||
.ToArray();
|
||||
|
||||
return new GetAzureFunctionsResult(azureFunctions);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
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 AzureFunctionsRoute
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests binding with a single operation and a route specified
|
||||
/// </summary>
|
||||
[FunctionName("SingleWithRoute")]
|
||||
public IActionResult SingleWithRoute([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "route")] HttpRequest req, [Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests binding with multiple operations and a route specified
|
||||
/// </summary>
|
||||
[FunctionName("MultipleWithRoute")]
|
||||
public IActionResult MultipleWithRoute([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "route")] HttpRequest req, [Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests binding with no operations and a route specified
|
||||
/// </summary>
|
||||
[FunctionName("NoOperationsWithRoute")]
|
||||
public IActionResult MultipleWithRoute([HttpTrigger(AuthorizationLevel.Anonymous, Route = "route")] HttpRequest req, [Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests binding with no operations and no route
|
||||
/// </summary>
|
||||
[FunctionName("NoOperationsNoRoute")]
|
||||
public IActionResult MultipleWithRoute([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req, [Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests binding without an HttpBinding
|
||||
/// </summary>
|
||||
[FunctionName("NoHttpBinding")]
|
||||
public IActionResult WithRoute([Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,5 +85,14 @@ namespace Company.Namespace
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests binding without an HttpBinding
|
||||
/// </summary>
|
||||
[FunctionName("NoHttpBinding")]
|
||||
public IActionResult WithRoute([Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify getting the routes of Azure Functions in a file
|
||||
/// Verify getting the routes of a HttpTriggerBinding on Azure Functions in a file
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetAzureFunctionsRoute()
|
||||
@@ -243,15 +243,40 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
|
||||
GetAzureFunctionsOperation operation = new GetAzureFunctionsOperation(parameters);
|
||||
GetAzureFunctionsResult result = operation.GetAzureFunctions();
|
||||
|
||||
Assert.That(result.AzureFunctions.Length, Is.EqualTo(8));
|
||||
Assert.That(result.AzureFunctions[0].Route, Is.EqualTo("withRoute"));
|
||||
Assert.That(result.AzureFunctions[1].Route, Is.EqualTo("{interpolated}String"));
|
||||
Assert.That(result.AzureFunctions[2].Route, Is.EqualTo("$withDollarSigns$"));
|
||||
Assert.That(result.AzureFunctions[3].Route, Is.EqualTo("withRouteNoSpaces"));
|
||||
Assert.That(result.AzureFunctions[4].Route, Is.EqualTo("withRouteExtraSpaces"));
|
||||
Assert.That(result.AzureFunctions[5].Route, Is.Null, "Route specified as null should be null");
|
||||
Assert.That(result.AzureFunctions[6].Route, Is.Null, "No route specified should be null");
|
||||
Assert.That(result.AzureFunctions[7].Route, Is.EqualTo(""));
|
||||
Assert.That(result.AzureFunctions.Length, Is.EqualTo(9));
|
||||
Assert.That(result.AzureFunctions[0].HttpTriggerBinding!.Route, Is.EqualTo("withRoute"));
|
||||
Assert.That(result.AzureFunctions[1].HttpTriggerBinding!.Route, Is.EqualTo("{interpolated}String"));
|
||||
Assert.That(result.AzureFunctions[2].HttpTriggerBinding!.Route, Is.EqualTo("$withDollarSigns$"));
|
||||
Assert.That(result.AzureFunctions[3].HttpTriggerBinding!.Route, Is.EqualTo("withRouteNoSpaces"));
|
||||
Assert.That(result.AzureFunctions[4].HttpTriggerBinding!.Route, Is.EqualTo("withRouteExtraSpaces"));
|
||||
Assert.That(result.AzureFunctions[5].HttpTriggerBinding!.Route, Is.Null, "Route specified as null should be null");
|
||||
Assert.That(result.AzureFunctions[6].HttpTriggerBinding!.Route, Is.Null, "No route specified should be null");
|
||||
Assert.That(result.AzureFunctions[7].HttpTriggerBinding!.Route, Is.EqualTo(""));
|
||||
Assert.That(result.AzureFunctions[8].HttpTriggerBinding, Is.Null, "Should not be an HttpTriggerBinding");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify getting the operations of a HttpTriggerBinding on Azure Functions in a file
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetAzureFunctionsOperations()
|
||||
{
|
||||
string testFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsOperations.cs");
|
||||
|
||||
GetAzureFunctionsParams parameters = new GetAzureFunctionsParams
|
||||
{
|
||||
FilePath = testFile
|
||||
};
|
||||
|
||||
GetAzureFunctionsOperation operation = new GetAzureFunctionsOperation(parameters);
|
||||
GetAzureFunctionsResult result = operation.GetAzureFunctions();
|
||||
|
||||
Assert.That(result.AzureFunctions.Length, Is.EqualTo(5));
|
||||
Assert.That(result.AzureFunctions[0].HttpTriggerBinding!.Operations, Is.EqualTo(new string[] { "GET" }));
|
||||
Assert.That(result.AzureFunctions[1].HttpTriggerBinding!.Operations, Is.EqualTo(new string[] { "GET", "POST" }));
|
||||
Assert.That(result.AzureFunctions[2].HttpTriggerBinding!.Operations, Is.EqualTo(Array.Empty<string>()));
|
||||
Assert.That(result.AzureFunctions[3].HttpTriggerBinding!.Operations, Is.EqualTo(Array.Empty<string>()));
|
||||
Assert.That(result.AzureFunctions[4].HttpTriggerBinding, Is.Null, "Should not be an HttpTriggerBinding");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user