Merge branch 'main' into v-chrcan/issue392

This commit is contained in:
Christopher C
2021-08-18 21:38:44 -07:00
10 changed files with 201 additions and 16 deletions

View File

@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -87,13 +88,10 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
Success = true
};
}
catch (Exception e)
catch (Exception ex)
{
return new ResultStatus()
{
Success = false,
ErrorMessage = e.ToString()
};
Logger.Write(TraceEventType.Information, $"Failed to add sql binding. Error: {ex.Message}");
throw ex;
}
}

View File

@@ -33,6 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
public void InitializeService(ServiceHost serviceHost)
{
serviceHost.SetRequestHandler(AddSqlBindingRequest.Type, this.HandleAddSqlBindingRequest);
serviceHost.SetRequestHandler(GetAzureFunctionsRequest.Type, this.HandleGetAzureFunctionsRequest);
}
/// <summary>
@@ -52,5 +53,23 @@ namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to get the names of the Azure functions in a file
/// </summary>
public async Task HandleGetAzureFunctionsRequest(GetAzureFunctionsParams parameters, RequestContext<GetAzureFunctionsResult> requestContext)
{
try
{
GetAzureFunctionsOperation operation = new GetAzureFunctionsOperation(parameters);
GetAzureFunctionsResult result = operation.GetAzureFunctions();
await requestContext.SendResult(result);
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
}
}

View File

@@ -0,0 +1,39 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts
{
/// <summary>
/// Parameters for getting the Azure functions in a file
/// </summary>
public class GetAzureFunctionsParams
{
/// <summary>
/// Gets or sets the filePath
/// </summary>
public string filePath { get; set; }
}
/// <summary>
/// Parameters returned from a get Azure functions request
/// </summary>
public class GetAzureFunctionsResult : ResultStatus
{
public string[] azureFunctions { get; set; }
}
/// <summary>
/// Defines the get Azure functions request
/// </summary>
class GetAzureFunctionsRequest
{
public static readonly RequestType<GetAzureFunctionsParams, GetAzureFunctionsResult> Type =
RequestType<GetAzureFunctionsParams, GetAzureFunctionsResult>.Create("azureFunctions/getAzureFunctions");
}
}

View File

@@ -0,0 +1,76 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts;
using Microsoft.SqlTools.Utility;
using Microsoft.CodeAnalysis.CSharp;
namespace Microsoft.SqlTools.ServiceLayer.AzureFunctions
{
/// <summary>
/// Class to represent getting the Azure Functions in a file
/// </summary>
class GetAzureFunctionsOperation
{
const string functionAttributeText = "FunctionName";
public GetAzureFunctionsParams Parameters { get; }
public GetAzureFunctionsOperation(GetAzureFunctionsParams parameters)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
}
/// <summary>
/// Gets the names of all the azure functions in a file
/// </summary>
/// <returns>the result of trying to get the names of all the Azure functions in a file</returns>
public GetAzureFunctionsResult GetAzureFunctions()
{
try
{
string text = File.ReadAllText(Parameters.filePath);
SyntaxTree tree = CSharpSyntaxTree.ParseText(text);
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
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);
// Get FunctionName attributes
IEnumerable<AttributeSyntax> functionNameAttributes = methodsWithFunctionAttributes.Select(md => md.AttributeLists.Select(a => a.Attributes.Where(attr => attr.Name.ToString().Contains(functionAttributeText)).First()).First());
// 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
};
}
catch (Exception ex)
{
Logger.Write(TraceEventType.Information, $"Failed to get Azure functions. Error: {ex.Message}");
throw ex;
}
}
}
}

View File

@@ -21,8 +21,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
public void AddSqlInputBinding()
{
// copy the original file because the input binding will be inserted into the file
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.ts");
string testFile = Path.Join(Path.GetTempPath(), string.Format("InsertSqlInputBinding-{0}.ts", DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss")));
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.cs");
string testFile = Path.Join(Path.GetTempPath(), $"InsertSqlInputBinding-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
File.Copy(originalFile, testFile, true);
AddSqlBindingParams parameters = new AddSqlBindingParams
@@ -40,7 +40,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
Assert.True(result.Success);
Assert.IsNull(result.ErrorMessage);
string expectedFileText = File.ReadAllText(Path.Join(testAzureFunctionsFolder, "AzureFunctionsInputBinding.ts"));
string expectedFileText = File.ReadAllText(Path.Join(testAzureFunctionsFolder, "AzureFunctionsInputBinding.cs"));
string actualFileText = File.ReadAllText(testFile);
Assert.AreEqual(expectedFileText, actualFileText);
}
@@ -52,8 +52,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
public void AddSqlOutputBinding()
{
// copy the original file because the output binding will be inserted into the file
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.ts");
string testFile = Path.Join(Path.GetTempPath(), string.Format("InsertSqlOutputBinding-{0}.ts", DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss")));
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.cs");
string testFile = Path.Join(Path.GetTempPath(), $"InsertSqlOutputBinding-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
File.Copy(originalFile, testFile, true);
AddSqlBindingParams parameters = new AddSqlBindingParams
@@ -71,7 +71,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
Assert.True(result.Success);
Assert.IsNull(result.ErrorMessage);
string expectedFileText = File.ReadAllText(Path.Join(testAzureFunctionsFolder, "AzureFunctionsOutputBinding.ts"));
string expectedFileText = File.ReadAllText(Path.Join(testAzureFunctionsFolder, "AzureFunctionsOutputBinding.cs"));
string actualFileText = File.ReadAllText(testFile);
Assert.AreEqual(expectedFileText, actualFileText);
}
@@ -83,8 +83,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
public void NoAzureFunctionForSqlBinding()
{
// copy the original file because the input binding will be inserted into the file
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.ts");
string testFile = Path.Join(Path.GetTempPath(), string.Format("NoAzureFunctionForSqlBinding-{0}.ts", DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss")));
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.cs");
string testFile = Path.Join(Path.GetTempPath(), $"NoAzureFunctionForSqlBinding-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
File.Copy(originalFile, testFile, true);
AddSqlBindingParams parameters = new AddSqlBindingParams
@@ -111,8 +111,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
public void MoreThanOneAzureFunctionWithSpecifiedName()
{
// copy the original file because the input binding will be inserted into the file
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsMultipleSameFunction.ts");
string testFile = Path.Join(Path.GetTempPath(), string.Format("MoreThanOneAzureFunctionWithSpecifiedName-{0}.ts", DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss")));
string originalFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsMultipleSameFunction.cs");
string testFile = Path.Join(Path.GetTempPath(), $"MoreThanOneAzureFunctionWithSpecifiedName-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
File.Copy(originalFile, testFile, true);
AddSqlBindingParams parameters = new AddSqlBindingParams
@@ -131,5 +131,52 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
Assert.NotNull(result.ErrorMessage);
Assert.True(result.ErrorMessage.Equals(SR.MoreThanOneAzureFunctionWithName("GetArtists_get", testFile)));
}
/// <summary>
/// Verify getting the names of Azure functions in a file
/// </summary>
[Test]
public void GetAzureFunctions()
{
string testFile = Path.Join(testAzureFunctionsFolder, "AzureFunctionsNoBindings.cs");
GetAzureFunctionsParams parameters = new GetAzureFunctionsParams
{
filePath = testFile
};
GetAzureFunctionsOperation operation = new GetAzureFunctionsOperation(parameters);
GetAzureFunctionsResult result = operation.GetAzureFunctions();
Assert.True(result.Success);
Assert.Null(result.ErrorMessage);
Assert.AreEqual(2, result.azureFunctions.Length);
Assert.AreEqual(result.azureFunctions[0], "GetArtists_get");
Assert.AreEqual(result.azureFunctions[1], "NewArtist_post");
}
/// <summary>
/// Verify there are no errors when a file doesn't have any Azure functions
/// </summary>
[Test]
public void GetAzureFunctionsWhenNoFunctions()
{
// make blank file
string testFile = Path.Join(Path.GetTempPath(), $"NoAzureFunctions-{DateTime.Now.ToString("yyyy - dd - MM--HH - mm - ss")}.cs");
FileStream fstream = File.Create(testFile);
fstream.Close();
GetAzureFunctionsParams parameters = new GetAzureFunctionsParams
{
filePath = testFile
};
GetAzureFunctionsOperation operation = new GetAzureFunctionsOperation(parameters);
GetAzureFunctionsResult result = operation.GetAzureFunctions();
Assert.True(result.Success);
Assert.Null(result.ErrorMessage);
Assert.AreEqual(0, result.azureFunctions.Length);
}
}
}

View File

@@ -38,6 +38,12 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsInputBinding.cs" />
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsMultipleSameFunction.cs" />
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsNoBindings.cs" />
<Compile Remove="AzureFunctions\AzureFunctionTestFiles\AzureFunctionsOutputBinding.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="DacFx\Dacpacs\" />
</ItemGroup>