mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-27 09:35:38 -05:00
Insert sql bindings into Azure Functions (#1224)
* getting table name from a script * add InsertSqlInputBindingOperation * cleanup * move azure functions stuff out of dacfx service * cleanup * add tests * add another test * cleanup * add comments and connection string setting * addressing comments * change name to use add instead of insert
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
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, [Sql("select * from [dbo].[table1]", CommandType = System.Data.CommandType.Text, ConnectionStringSetting = "SqlConnectionString")] IEnumerable<Object> result)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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("GetArtists_get")]
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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, [Sql("[dbo].[table1]", ConnectionStringSetting = "SqlConnectionString")] out 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.AzureFunctions;
|
||||
using Microsoft.SqlTools.ServiceLayer.AzureFunctions.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.AzureFunctions
|
||||
{
|
||||
class AzureFunctionsServiceTests
|
||||
{
|
||||
private string testAzureFunctionsFolder = Path.Combine("..", "..", "..", "AzureFunctions", "AzureFunctionTestFiles");
|
||||
|
||||
/// <summary>
|
||||
/// Verify input binding gets added
|
||||
/// </summary>
|
||||
[Test]
|
||||
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")));
|
||||
File.Copy(originalFile, testFile, true);
|
||||
|
||||
AddSqlBindingParams parameters = new AddSqlBindingParams
|
||||
{
|
||||
bindingType = BindingType.input,
|
||||
filePath = testFile,
|
||||
functionName = "GetArtists_get",
|
||||
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, "AzureFunctionsInputBinding.ts"));
|
||||
string actualFileText = File.ReadAllText(testFile);
|
||||
Assert.AreEqual(expectedFileText, actualFileText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify output binding gets added
|
||||
/// </summary>
|
||||
[Test]
|
||||
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")));
|
||||
File.Copy(originalFile, testFile, true);
|
||||
|
||||
AddSqlBindingParams parameters = new AddSqlBindingParams
|
||||
{
|
||||
bindingType = BindingType.output,
|
||||
filePath = testFile,
|
||||
functionName = "NewArtist_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, "AzureFunctionsOutputBinding.ts"));
|
||||
string actualFileText = File.ReadAllText(testFile);
|
||||
Assert.AreEqual(expectedFileText, actualFileText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify what happens when specified azure function isn't found
|
||||
/// </summary>
|
||||
[Test]
|
||||
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")));
|
||||
File.Copy(originalFile, testFile, true);
|
||||
|
||||
AddSqlBindingParams parameters = new AddSqlBindingParams
|
||||
{
|
||||
bindingType = BindingType.input,
|
||||
filePath = testFile,
|
||||
functionName = "noExistingFunction",
|
||||
objectName = "[dbo].[table1]",
|
||||
connectionStringSetting = "SqlConnectionString"
|
||||
};
|
||||
|
||||
AddSqlBindingOperation operation = new AddSqlBindingOperation(parameters);
|
||||
ResultStatus result = operation.AddBinding();
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.NotNull(result.ErrorMessage);
|
||||
Assert.True(result.ErrorMessage.Equals(SR.CouldntFindAzureFunction("noExistingFunction", testFile)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify what happens when there's more than one Azure function with the specified name in the file
|
||||
/// </summary>
|
||||
[Test]
|
||||
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")));
|
||||
File.Copy(originalFile, testFile, true);
|
||||
|
||||
AddSqlBindingParams parameters = new AddSqlBindingParams
|
||||
{
|
||||
bindingType = BindingType.input,
|
||||
filePath = testFile,
|
||||
functionName = "GetArtists_get",
|
||||
objectName = "[dbo].[table1]",
|
||||
connectionStringSetting = "SqlConnectionString"
|
||||
};
|
||||
|
||||
AddSqlBindingOperation operation = new AddSqlBindingOperation(parameters);
|
||||
ResultStatus result = operation.AddBinding();
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.NotNull(result.ErrorMessage);
|
||||
Assert.True(result.ErrorMessage.Equals(SR.MoreThanOneAzureFunctionWithName("GetArtists_get", testFile)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user