mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-29 09:35:38 -05:00
Add scripting API implemented by the SqlScriptPublishModel (#316)
Update the ScriptingService to expose new scripting JSON-RPC APIs that use the SqlScriptPublishModel for script generation. The SqlScriptPublishModel is the model behind the SSMS scripting wizard. To enable scripting for CLI tools, we've ported SqlScriptPublishModel to .NET Core. The SqlScriptPublishModel wraps the SMO scripting APIs for .sql script generation. 1) Added three new requests to the ScriptingService: ScriptingRequest, ScriptingListObjectsRequest, ScriptingCancelRequest. 2) Generating scripts are long running operations, so the ScriptingRequest and ScriptingListObjectsRequest kick off a long running scripting task and return immediately. 3) Long running scripting task reports progress and completion, and can be cancelled by a ScriptingCancelRequest request. 4) Bumped the SMO nuget package to 140.17049.0. This new version contains a signed SSMS_Rel build of SMO with the SqlScriptPublishModel. 5) For testing, adding the Northwind database schema TODO (in later pull requests) 1) Integrate the new ScriptingService APIs with the ConnectionService 2) Integrate with the metadata support recently added
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
"System.Runtime.Serialization.Primitives": "4.1.1",
|
||||
"System.Data.Common": "4.1.0",
|
||||
"System.Data.SqlClient": "4.4.0-sqltools-24613-04",
|
||||
"Microsoft.SqlServer.Smo": "14.0.17028",
|
||||
"Microsoft.SqlServer.Smo": "140.17049.0",
|
||||
"System.Security.SecureString": "4.0.0",
|
||||
"System.Collections.Specialized": "4.0.1",
|
||||
"System.ComponentModel.TypeConverter": "4.1.0",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
}
|
||||
},
|
||||
"runtimes": {
|
||||
"win7-x64": {}
|
||||
"win7-x64": {},
|
||||
"win7-x86": {}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -43,10 +43,12 @@ END
|
||||
|
||||
public static string CreateDatabaseQuery { get { return CreateDatabaseQueryInstance.Value; } }
|
||||
|
||||
public static string TestDbComplexSelectQueries { get { return TestDbSelectQueriesInstance.Value; } }
|
||||
|
||||
public static string TestDbComplexSelectQueries { get { return TestDbSelectQueriesInstance.Value; } }
|
||||
|
||||
public static string AdventureWorksScript { get { return AdventureWorksScriptInstance.Value; } }
|
||||
|
||||
public static string CreateNorthwindSchema { get { return CreateNorthwindSchemaInstance.Value; } }
|
||||
|
||||
private static readonly Lazy<string> CreateDatabaseObjectsQueryInstance = new Lazy<string>(() =>
|
||||
{
|
||||
return GetScriptFileContent(ResourceNameRefix + "CreateTestDatabaseObjects.sql");
|
||||
@@ -60,13 +62,18 @@ END
|
||||
private static readonly Lazy<string> TestDbSelectQueriesInstance = new Lazy<string>(() =>
|
||||
{
|
||||
return GetScriptFileContent(ResourceNameRefix + "TestDbTableQueries.sql");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
private static readonly Lazy<string> AdventureWorksScriptInstance = new Lazy<string>(() =>
|
||||
{
|
||||
return GetScriptFileContent(ResourceNameRefix + "AdventureWorks.sql");
|
||||
});
|
||||
|
||||
private static readonly Lazy<string> CreateNorthwindSchemaInstance = new Lazy<string>(() =>
|
||||
{
|
||||
return GetScriptFileContent(ResourceNameRefix + "CreateNorthwindSchema.sql");
|
||||
});
|
||||
|
||||
private static string GetScriptFileContent(string fileName)
|
||||
{
|
||||
string fileContent = string.Empty;
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Globalization;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Xunit;
|
||||
using System.Data.SqlClient;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
@@ -24,6 +25,31 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
|
||||
public bool DoNotCleanupDb { get; set; }
|
||||
|
||||
public string ConnectionString
|
||||
{
|
||||
get
|
||||
{
|
||||
ConnectParams connectParams = TestConnectionProfileService.Instance.GetConnectionParameters(this.ServerType, this.DatabaseName);
|
||||
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder
|
||||
{
|
||||
DataSource = connectParams.Connection.ServerName,
|
||||
InitialCatalog = connectParams.Connection.DatabaseName,
|
||||
};
|
||||
|
||||
if (connectParams.Connection.AuthenticationType == "Integrated")
|
||||
{
|
||||
builder.IntegratedSecurity = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.UserID = connectParams.Connection.UserName;
|
||||
builder.Password = connectParams.Connection.Password;
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the test db if not already exists
|
||||
/// </summary>
|
||||
@@ -63,6 +89,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
return CreateNew(serverType, false, null, query);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the test db if not already exists
|
||||
/// </summary>
|
||||
public static SqlTestDb CreateNew(TestServerType serverType)
|
||||
{
|
||||
return CreateNew(serverType, false, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a mangled name that unique based on Prefix + Machine + Process
|
||||
/// </summary>
|
||||
@@ -115,6 +149,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
return connInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the passed query against the test db.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to execute.</param>
|
||||
/// <param name="throwOnError">If true, throw an exception if the query encounters an error executing a batch statement.</param>
|
||||
public void RunQuery(string query, bool throwOnError = false)
|
||||
{
|
||||
TestServiceProvider.Instance.RunQuery(this.ServerType, this.DatabaseName, query, throwOnError);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
|
||||
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.TestDriver.Driver;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace.Contracts;
|
||||
@@ -422,6 +424,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<ScriptingListObjectsResult> ListScriptingObjects(ScriptingListObjectsParams parameters)
|
||||
{
|
||||
return await Driver.SendRequest(ScriptingListObjectsRequest.Type, parameters);
|
||||
}
|
||||
|
||||
public async Task<ScriptingResult> Script(ScriptingParams parameters)
|
||||
{
|
||||
return await Driver.SendRequest(ScriptingRequest.Type, parameters);
|
||||
}
|
||||
|
||||
public async Task<ScriptingCancelResult> CancelScript(string operationId)
|
||||
{
|
||||
return await Driver.SendRequest(ScriptingCancelRequest.Type, new ScriptingCancelParams { OperationId = operationId });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for a message to be returned by the service
|
||||
/// </summary>
|
||||
@@ -438,5 +455,30 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
System.IO.File.WriteAllText(ownerUri, query);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetEvent<T>(EventType<T> eventType, out T value)
|
||||
{
|
||||
value = default(T);
|
||||
|
||||
try
|
||||
{
|
||||
Task<T> t = this.Driver.WaitForEvent(eventType, TimeSpan.Zero);
|
||||
value = t.Result;
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void AssertEventNotQueued<T>(EventType<T> eventType)
|
||||
{
|
||||
T temp;
|
||||
if (TryGetEvent(eventType, out temp))
|
||||
{
|
||||
Assert.True(false, string.Format("Event of type {0} was found in the queue.", eventType.GetType().FullName, temp.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.Credentials;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
@@ -44,8 +46,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
{
|
||||
return CredentialService.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ObjectExplorerService ObjectExplorerService
|
||||
{
|
||||
get
|
||||
@@ -74,7 +76,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
/// <summary>
|
||||
/// Runs a query by calling the services directly (not using the test driver)
|
||||
/// </summary>
|
||||
public void RunQuery(TestServerType serverType, string databaseName, string queryText)
|
||||
public void RunQuery(TestServerType serverType, string databaseName, string queryText, bool throwOnError = false)
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
@@ -82,6 +84,18 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.Common
|
||||
Query query = new Query(queryText, connInfo, new QueryExecutionSettings(), MemoryFileSystem.GetFileStreamFactory());
|
||||
query.Execute();
|
||||
query.ExecutionTask.Wait();
|
||||
|
||||
if (throwOnError)
|
||||
{
|
||||
IEnumerable<Batch> errorBatches = query.Batches.Where(b => b.HasError);
|
||||
if (errorBatches.Count() > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
string.Format(
|
||||
"The query encountered and error. The batches with errors: {0}",
|
||||
string.Join(Environment.NewLine, errorBatches.Select(b => b.BatchText))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
"includeFiles": [
|
||||
"Scripts/CreateTestDatabaseObjects.sql",
|
||||
"Scripts/CreateTestDatabase.sql",
|
||||
"Scripts/TestDbTableQueries.sql",
|
||||
"Scripts/AdventureWorks.sql"
|
||||
"Scripts/AdventureWorks.sql",
|
||||
"Scripts/CreateNorthwindSchema.sql",
|
||||
"Scripts/TestDbTableQueries.sql"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -17,7 +18,7 @@
|
||||
"System.Runtime.Serialization.Primitives": "4.1.1",
|
||||
"System.Data.Common": "4.1.0",
|
||||
"System.Data.SqlClient": "4.4.0-sqltools-24613-04",
|
||||
"Microsoft.SqlServer.Smo": "14.0.17028",
|
||||
"Microsoft.SqlServer.Smo": "140.17049.0",
|
||||
"System.Security.SecureString": "4.0.0",
|
||||
"System.Collections.Specialized": "4.0.1",
|
||||
"System.ComponentModel.TypeConverter": "4.1.0",
|
||||
|
||||
@@ -50,18 +50,25 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(methodName))
|
||||
try
|
||||
{
|
||||
var methods = type.GetMethods().Where(x => x.CustomAttributes.Any(a => a.AttributeType == typeof(FactAttribute)));
|
||||
foreach (var method in methods)
|
||||
if (string.IsNullOrEmpty(methodName))
|
||||
{
|
||||
await RunTest(type, method, method.Name);
|
||||
var methods = type.GetMethods().Where(x => x.CustomAttributes.Any(a => a.AttributeType == typeof(FactAttribute)));
|
||||
foreach (var method in methods)
|
||||
{
|
||||
await RunTest(type, method, method.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo methodInfo = type.GetMethod(methodName);
|
||||
await RunTest(type, methodInfo, test);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
MethodInfo methodInfo = type.GetMethod(methodName);
|
||||
await RunTest(type, methodInfo, test);
|
||||
RunTestCleanup(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,5 +99,24 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunTestCleanup(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
MethodInfo cleanupMethod = type.GetMethod("Cleanup");
|
||||
if (cleanupMethod != null)
|
||||
{
|
||||
cleanupMethod.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(
|
||||
"An exception occurred running Cleanup for type {0}: {1}",
|
||||
type.FullName,
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Tests
|
||||
{
|
||||
/// <summary>
|
||||
/// Scripting service end-to-end integration tests that use the SqlScriptPublishModel type to generate scripts.
|
||||
/// </summary>
|
||||
public class SqlScriptPublishModelTests : IClassFixture<SqlScriptPublishModelTests.ScriptingFixture>
|
||||
{
|
||||
public SqlScriptPublishModelTests(ScriptingFixture scriptingFixture)
|
||||
{
|
||||
this.Fixture = scriptingFixture;
|
||||
}
|
||||
|
||||
public ScriptingFixture Fixture { get; private set; }
|
||||
|
||||
public SqlTestDb Northwind { get { return this.Fixture.Database; } }
|
||||
|
||||
[Fact]
|
||||
public async Task ListSchemaObjects()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingListObjectsParams requestParams = new ScriptingListObjectsParams
|
||||
{
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
};
|
||||
|
||||
ScriptingListObjectsResult result = await testService.ListScriptingObjects(requestParams);
|
||||
ScriptingListObjectsCompleteParams completeParameters = await testService.Driver.WaitForEvent(ScriptingListObjectsCompleteEvent.Type, TimeSpan.FromSeconds(30));
|
||||
Assert.Equal<int>(ScriptingFixture.ObjectCountWithoutDatabase, completeParameters.DatabaseObjects.Count);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptDatabaseSchema()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaOnly",
|
||||
},
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingPlanNotificationParams planEvent = await testService.Driver.WaitForEvent(ScriptingPlanNotificationEvent.Type, TimeSpan.FromSeconds(30));
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(30));
|
||||
Assert.True(parameters.Success);
|
||||
Assert.Equal<int>(ScriptingFixture.ObjectCountWithDatabase, planEvent.Count);
|
||||
Assert.True(File.Exists(tempFile.FilePath));
|
||||
Assert.True(new FileInfo(tempFile.FilePath).Length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptDatabaseSchemaAndData()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaAndData",
|
||||
},
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingPlanNotificationParams planEvent = await testService.Driver.WaitForEvent(ScriptingPlanNotificationEvent.Type, TimeSpan.FromSeconds(30));
|
||||
ScriptingCompleteParams completeParameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(30));
|
||||
Assert.True(completeParameters.Success);
|
||||
Assert.Equal<int>(ScriptingFixture.ObjectCountWithDatabase, planEvent.Count);
|
||||
Assert.True(File.Exists(tempFile.FilePath));
|
||||
Assert.True(new FileInfo(tempFile.FilePath).Length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptTable()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaOnly",
|
||||
},
|
||||
ScriptingObjects = new List<ScriptingObject>
|
||||
{
|
||||
new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "dbo",
|
||||
Name = "Customers",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingPlanNotificationParams planEvent = await testService.Driver.WaitForEvent(ScriptingPlanNotificationEvent.Type, TimeSpan.FromSeconds(30));
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(30));
|
||||
Assert.True(parameters.Success);
|
||||
Assert.Equal<int>(2, planEvent.Count);
|
||||
Assert.True(File.Exists(tempFile.FilePath));
|
||||
Assert.True(new FileInfo(tempFile.FilePath).Length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptTableUsingIncludeFilter()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaOnly",
|
||||
},
|
||||
IncludeObjectCriteria = new List<ScriptingObject>
|
||||
{
|
||||
new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "dbo",
|
||||
Name = "Customers",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingPlanNotificationParams planEvent = await testService.Driver.WaitForEvent(ScriptingPlanNotificationEvent.Type, TimeSpan.FromSeconds(30));
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(30));
|
||||
Assert.True(parameters.Success);
|
||||
Assert.Equal<int>(2, planEvent.Count);
|
||||
Assert.True(File.Exists(tempFile.FilePath));
|
||||
Assert.True(new FileInfo(tempFile.FilePath).Length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptTableAndData()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaAndData",
|
||||
},
|
||||
ScriptingObjects = new List<ScriptingObject>
|
||||
{
|
||||
new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "dbo",
|
||||
Name = "Customers",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingPlanNotificationParams planEvent = await testService.Driver.WaitForEvent(ScriptingPlanNotificationEvent.Type, TimeSpan.FromSeconds(30));
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(30));
|
||||
Assert.True(parameters.Success);
|
||||
Assert.Equal<int>(2, planEvent.Count);
|
||||
Assert.True(File.Exists(tempFile.FilePath));
|
||||
Assert.True(new FileInfo(tempFile.FilePath).Length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptTableDoesNotExist()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaOnly",
|
||||
},
|
||||
ScriptingObjects = new List<ScriptingObject>
|
||||
{
|
||||
new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "dbo",
|
||||
Name = "TableDoesNotExist",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(15));
|
||||
Assert.True(parameters.HasError);
|
||||
Assert.Equal("An error occurred while scripting the objects.", parameters.ErrorMessage);
|
||||
Assert.Contains("The Table '[dbo].[TableDoesNotExist]' does not exist on the server.", parameters.ErrorDetails);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptSchemaCancel()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = this.Northwind.ConnectionString,
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaAndData",
|
||||
},
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingCancelResult cancelResult = await testService.CancelScript(result.OperationId);
|
||||
ScriptingCompleteParams cancelEvent = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(10));
|
||||
Assert.True(cancelEvent.Canceled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptSchemaInvalidConnectionString()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
using (SelfCleaningTempFile tempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = tempFile.FilePath,
|
||||
ConnectionString = "I'm an invalid connection string",
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaAndData",
|
||||
},
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(10));
|
||||
Assert.True(parameters.HasError);
|
||||
Assert.Equal("Error parsing ScriptingParams.ConnectionString property.", parameters.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ScriptSchemaInvalidFilePath()
|
||||
{
|
||||
using (TestServiceDriverProvider testService = new TestServiceDriverProvider())
|
||||
{
|
||||
ScriptingParams requestParams = new ScriptingParams
|
||||
{
|
||||
FilePath = "This path doesn't event exist",
|
||||
ConnectionString = "Server=Temp;Database=Temp;User Id=Temp;Password=Temp",
|
||||
ScriptOptions = new ScriptOptions
|
||||
{
|
||||
TypeOfDataToScript = "SchemaAndData",
|
||||
},
|
||||
};
|
||||
|
||||
ScriptingResult result = await testService.Script(requestParams);
|
||||
ScriptingCompleteParams parameters = await testService.Driver.WaitForEvent(ScriptingCompleteEvent.Type, TimeSpan.FromSeconds(10));
|
||||
Assert.True(parameters.HasError);
|
||||
Assert.Equal("Invalid directory specified by the ScriptingParams.FilePath property.", parameters.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public class ScriptingFixture : IDisposable
|
||||
{
|
||||
public ScriptingFixture()
|
||||
{
|
||||
this.Database = SqlTestDb.CreateNew(TestServerType.OnPrem);
|
||||
this.Database.RunQuery(Scripts.CreateNorthwindSchema, throwOnError: true);
|
||||
Console.WriteLine("Northwind setup complete, database name: {0}", this.Database.DatabaseName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The count of object when scripting the entire database including the database object.
|
||||
/// </summary>
|
||||
public const int ObjectCountWithDatabase = 46;
|
||||
|
||||
/// <summary>
|
||||
/// The count of objects when scripting the entire database excluding the database object.
|
||||
/// </summary>
|
||||
public const int ObjectCountWithoutDatabase = 45;
|
||||
|
||||
public SqlTestDb Database { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.Database != null)
|
||||
{
|
||||
Console.WriteLine("Northwind cleanup, deleting database name: {0}", this.Database.DatabaseName);
|
||||
this.Database.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.Hosting.Protocol.Channel;
|
||||
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
|
||||
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
||||
{
|
||||
@@ -124,6 +125,9 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
||||
this.QueueEventsForType(IntelliSenseReadyNotification.Type);
|
||||
this.QueueEventsForType(QueryCompleteEvent.Type);
|
||||
this.QueueEventsForType(PublishDiagnosticsNotification.Type);
|
||||
this.QueueEventsForType(ScriptingCompleteEvent.Type);
|
||||
this.QueueEventsForType(ScriptingPlanNotificationEvent.Type);
|
||||
this.QueueEventsForType(ScriptingListObjectsCompleteEvent.Type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -83,6 +83,13 @@ namespace Microsoft.SqlTools.ServiceLayer.TestDriver.Driver
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<TParams> WaitForEvent<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
TimeSpan timeout)
|
||||
{
|
||||
return await WaitForEvent(eventType, (int) timeout.TotalMilliseconds);
|
||||
}
|
||||
|
||||
public async Task<TParams> WaitForEvent<TParams>(
|
||||
EventType<TParams> eventType,
|
||||
int timeoutMilliseconds = 5000)
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
//
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.Scripting;
|
||||
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.Scripting
|
||||
{
|
||||
public class ScriptingMatcherTests
|
||||
{
|
||||
private static ScriptingObject Table_S1_Table1 = new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "S1",
|
||||
Name = "Table1",
|
||||
};
|
||||
|
||||
private static ScriptingObject Table_S1_Table2 = new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "S1",
|
||||
Name = "Table2",
|
||||
};
|
||||
|
||||
private static ScriptingObject Table_S2_Table1 = new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "S2",
|
||||
Name = "Table1",
|
||||
};
|
||||
|
||||
private static ScriptingObject Table_S2_Table2 = new ScriptingObject
|
||||
{
|
||||
Type = "Table",
|
||||
Schema = "S2",
|
||||
Name = "Table2",
|
||||
};
|
||||
|
||||
private static ScriptingObject View_S1_View1 = new ScriptingObject
|
||||
{
|
||||
Type = "View",
|
||||
Schema = "S1",
|
||||
Name = "View1",
|
||||
};
|
||||
|
||||
private static ScriptingObject View_S1_View2 = new ScriptingObject
|
||||
{
|
||||
Type = "View",
|
||||
Schema = "S1",
|
||||
Name = "View2",
|
||||
};
|
||||
|
||||
private static List<ScriptingObject> TestData = new List<ScriptingObject>
|
||||
{
|
||||
Table_S1_Table1,
|
||||
Table_S1_Table2,
|
||||
Table_S2_Table1,
|
||||
Table_S2_Table2,
|
||||
View_S1_View1,
|
||||
View_S1_View2,
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeAll()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject[0],
|
||||
excludeCriteria: new ScriptingObject[0],
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(6, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeNone()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject(),
|
||||
excludeCriteria: new ScriptingObject(),
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(0, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeName()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Name = "Table1"},
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(2, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeNameWildcard()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Name = "*" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(6, results.Count());
|
||||
}
|
||||
|
||||
public void ScriptingMatchIncludeNameWildcardPostfix()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Name = "Tab*" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(4, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeSchema()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Schema = "S2" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(2, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeSchemaWildcard()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Schema = "*" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(6, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeSchemaWildcardPostfix()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Schema = "S*" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(6, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeType()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Type="Table" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(4, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeNameAndSchema()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Schema = "S1", Name = "Table1" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(1, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchIncludeSchemaAndType()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: new ScriptingObject { Type="View", Schema = "S1" },
|
||||
excludeCriteria: null,
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(2, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeName()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Name = "Table1" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(4, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeNameWildcard()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Name = "*" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(0, results.Count());
|
||||
}
|
||||
|
||||
public void ScriptingMatchExcludeNameWildcardPostfix()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Name = "Tab*" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(4, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeSchema()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Schema = "S2" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(4, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeSchemaWildcard()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Schema = "*" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(0, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeSchemaWildcardPostfix()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Schema = "S*" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(0, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeType()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Type = "Table" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(2, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeNameAndSchema()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Schema = "S1", Name = "Table1" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(5, results.Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ScriptingMatchExcludeSchemaAndType()
|
||||
{
|
||||
IEnumerable<ScriptingObject> results = ScriptingObjectMatcher.Match(
|
||||
includeCriteria: null,
|
||||
excludeCriteria: new ScriptingObject { Type = "View", Schema = "S1" },
|
||||
candidates: TestData);
|
||||
|
||||
Assert.Equal<int>(4, results.Count());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
"System.Runtime.Serialization.Primitives": "4.1.1",
|
||||
"System.Data.Common": "4.1.0",
|
||||
"System.Data.SqlClient": "4.4.0-sqltools-24613-04",
|
||||
"Microsoft.SqlServer.Smo": "14.0.17028",
|
||||
"Microsoft.SqlServer.Smo": "140.17049.0",
|
||||
"System.Security.SecureString": "4.0.0",
|
||||
"System.Collections.Specialized": "4.0.1",
|
||||
"System.ComponentModel.TypeConverter": "4.1.0",
|
||||
|
||||
Reference in New Issue
Block a user