mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-10 10:12:39 -05:00
Add route to GetAzureFunctions request + cleanup (#1521)
* Add route to GetAzureFunctions request + cleanup * update comments * comments * Add comment
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -12,8 +13,10 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
{
|
||||
internal static class AzureFunctionsUtils
|
||||
{
|
||||
public const string functionAttributeText = "FunctionName";
|
||||
public const string net5FunctionAttributeText = "Function";
|
||||
private const string FUNCTION_NAME_ATTRIBUTE_NAME = "FunctionName";
|
||||
private const string NET5_FUNCTION_ATTRIBUTE_NAME = "Function";
|
||||
private const string HTTP_TRIGGER_ATTRIBUTE_NAME = "HttpTrigger";
|
||||
private const string ROUTE_ARGUMENT_NAME = "Route";
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the methods in the syntax tree with an Azure Function attribute
|
||||
@@ -31,19 +34,74 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
}
|
||||
|
||||
// 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());
|
||||
IEnumerable<MethodDeclarationSyntax> methodsWithFunctionAttributes = methodDeclarations.Where(md => md.AttributeLists.Where(a => a.Attributes.Where(attr => attr.Name.ToString().Equals(FUNCTION_NAME_ATTRIBUTE_NAME)).Any()).Any());
|
||||
|
||||
return methodsWithFunctionAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the route from an HttpTrigger attribute if specified
|
||||
/// </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)
|
||||
{
|
||||
return m
|
||||
.ParameterList
|
||||
.Parameters // Get all the parameters for the method
|
||||
.SelectMany(p =>
|
||||
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
|
||||
?.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()
|
||||
?.ChildNodes()
|
||||
.OfType<ExpressionSyntax>() // Find the child identifier node with our value
|
||||
.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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the function name from the FunctionName attribute on a method
|
||||
/// </summary>
|
||||
/// <param name="m">The method</param>
|
||||
/// <returns>The function name, or an empty string if the name attribute doesn't exist</returns>
|
||||
public static string GetFunctionName(this MethodDeclarationSyntax m)
|
||||
{
|
||||
// Note that we return an empty string as the default because a null name isn't valid - every function
|
||||
// should have a name. So we should never actually hit that scenario, but just to be safe we return the
|
||||
// empty string as the default in case we hit some unexpected edge case.
|
||||
return m
|
||||
.AttributeLists // Get all the attribute lists on the method
|
||||
.Select(a =>
|
||||
a.Attributes.Where(
|
||||
attr => attr.Name.ToString().Equals(AzureFunctionsUtils.FUNCTION_NAME_ATTRIBUTE_NAME) // Find any FunctionName attributes
|
||||
).FirstOrDefault() // Get the first one available - there should only ever be 0 or 1
|
||||
).Where(a => // Filter out any that didn't have a FunctionName attribute
|
||||
a != null
|
||||
).FirstOrDefault() // Get the first one available - there should only ever be 0 or 1
|
||||
?.ArgumentList // Get all the arguments for the attribute
|
||||
?.Arguments
|
||||
.FirstOrDefault() // The first argument is the function name
|
||||
?.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
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// .NET 5 AFs use the Function attribute, while .NET 3.1 AFs use FunctionName attribute
|
||||
/// </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))));
|
||||
return methodDeclarations.Any(md => md.AttributeLists.Any(al => al.Attributes.Any(attr => attr.Name.ToString().Equals(NET5_FUNCTION_ATTRIBUTE_NAME))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts
|
||||
{
|
||||
@@ -15,15 +14,39 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts
|
||||
/// <summary>
|
||||
/// Gets or sets the filePath
|
||||
/// </summary>
|
||||
public string filePath { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
}
|
||||
|
||||
public class AzureFunction
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the function
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The route of the HttpTrigger binding if one exists on this function
|
||||
/// </summary>
|
||||
public string? Route { get; set; }
|
||||
|
||||
public AzureFunction(string name, string? route)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Route = route;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parameters returned from a get Azure functions request
|
||||
/// </summary>
|
||||
public class GetAzureFunctionsResult : ResultStatus
|
||||
public class GetAzureFunctionsResult
|
||||
{
|
||||
public string[] azureFunctions { get; set; }
|
||||
public AzureFunction[] AzureFunctions { get; set; }
|
||||
|
||||
public GetAzureFunctionsResult(AzureFunction[] azureFunctions)
|
||||
{
|
||||
this.AzureFunctions = azureFunctions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
{
|
||||
try
|
||||
{
|
||||
string text = File.ReadAllText(Parameters.filePath);
|
||||
string text = File.ReadAllText(Parameters.FilePath);
|
||||
|
||||
SyntaxTree tree = CSharpSyntaxTree.ParseText(text);
|
||||
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
|
||||
@@ -45,20 +45,9 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
|
||||
// get all the method declarations with the FunctionName attribute
|
||||
IEnumerable<MethodDeclarationSyntax> methodsWithFunctionAttributes = AzureFunctionsUtils.GetMethodsWithFunctionAttributes(root);
|
||||
|
||||
// Get FunctionName attributes
|
||||
IEnumerable<AttributeSyntax> functionNameAttributes = methodsWithFunctionAttributes.Select(md => md.AttributeLists.Select(a => a.Attributes.Where(attr => attr.Name.ToString().Equals(AzureFunctionsUtils.functionAttributeText)).First()).First());
|
||||
var azureFunctions = methodsWithFunctionAttributes.Select(m => new AzureFunction(m.GetFunctionName(), m.GetHttpRoute())).ToArray();
|
||||
|
||||
// Get the function names in the FunctionName attributes
|
||||
IEnumerable<AttributeArgumentSyntax> nameArgs = functionNameAttributes.Select(a => a.ArgumentList.Arguments.First());
|
||||
|
||||
// Remove quotes from around the names
|
||||
string[] aFNames = nameArgs.Select(ab => ab.ToString().Trim('\"')).ToArray();
|
||||
|
||||
return new GetAzureFunctionsResult()
|
||||
{
|
||||
Success = true,
|
||||
azureFunctions = aFNames
|
||||
};
|
||||
return new GetAzureFunctionsResult(azureFunctions);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user