diff --git a/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/AzureFunctionsUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/AzureFunctionsUtils.cs
index e0f0b223..3cd81036 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/AzureFunctionsUtils.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/AzureFunctionsUtils.cs
@@ -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
}
///
- /// Gets the route from an HttpTrigger attribute if specified
+ /// Gets the info for a HttpTriggerBinding for the specified method (if such a binding exists)
///
/// The method
- /// The name of the route, or null if no route is specified (or there isn't an HttpTrigger binding)
- public static string? GetHttpRoute(this MethodDeclarationSyntax m)
+ /// The HttpTriggerBinding, or null if none exists
+ public static HttpTriggerBinding? GetHttpTriggerBinding(this MethodDeclarationSyntax m)
+ {
+ var httpTriggerAttribute = m.GetHttpTriggerAttribute();
+ return httpTriggerAttribute == null ? null : new HttpTriggerBinding(httpTriggerAttribute.GetHttpRoute(), httpTriggerAttribute.GetHttpOperations());
+ }
+
+ ///
+ /// 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
+ ///
+ /// The method
+ /// The attribute if such a binding exists
+ 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
+ }
+
+ ///
+ /// 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
+ ///
+ /// The attribute
+ /// The name of the route, or null if no route is specified (or there isn't an HttpTrigger binding)
+ public static string? GetHttpRoute(this AttributeSyntax a)
+ {
+ return a.ArgumentList
?.Arguments
.Where(a => a.ChildNodes().OfType().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();
+ }
+
+ ///
+ /// 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
+ ///
+ /// The attribute
+ /// The operations (methods) specified
+ 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();
}
///
@@ -90,8 +127,19 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
?.Arguments
.FirstOrDefault() // The first argument is the function name
?.ToString()
+ .TrimStringQuotes() ?? "";
+ }
+
+ ///
+ /// Removes the quotes (and $ for interpolated strings) around a string literal
+ ///
+ /// The string
+ /// The string without quotes
+ 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
}
///
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/Contracts/GetAzureFunctionsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/Contracts/GetAzureFunctionsRequest.cs
index 839ded38..7883ca23 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/Contracts/GetAzureFunctionsRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/Contracts/GetAzureFunctionsRequest.cs
@@ -22,17 +22,36 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts
///
/// The name of the function
///
- public string Name { get; set; }
+ public string Name { get; }
///
- /// The route of the HttpTrigger binding if one exists on this function
+ /// The HttpTrigger binding if one is specified
///
- 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
+ {
+ ///
+ /// The route specified
+ ///
+ public string? Route { get; }
+
+ ///
+ /// The operations (methods) specified
+ ///
+ public string[]? Operations { get; }
+
+ public HttpTriggerBinding(string? route, string[]? operations)
+ {
this.Route = route;
+ this.Operations = operations;
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/GetAzureFunctionsOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/GetAzureFunctionsOperation.cs
index 4cf46835..a1c8d60a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/GetAzureFunctionsOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/AzureFunctions/GetAzureFunctionsOperation.cs
@@ -45,7 +45,10 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
// get all the method declarations with the FunctionName attribute
IEnumerable 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);
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/AzureFunctions/AzureFunctionTestFiles/AzureFunctionsOperations.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/AzureFunctions/AzureFunctionTestFiles/AzureFunctionsOperations.cs
new file mode 100644
index 00000000..fd57ba48
--- /dev/null
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/AzureFunctions/AzureFunctionTestFiles/AzureFunctionsOperations.cs
@@ -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
+ {
+ ///
+ /// Tests binding with a single operation and a route specified
+ ///
+ [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