mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 10:58:30 -05:00
Add DacFx Generate Deploy Plan Operation (#768)
* initial adding upgrade plan * upgrade plan request from ADS works * now data loss operations are returned * fixes after rebasing * refactoring to use GenerateDeployPlan instead of UpgradePlan * improving test * Addressing comments * changing abstract execute to virtual * changed GenerateDeployPlanOepration to use Execute() * addressing comments * simplyfing deploy options * adding deploy options to deploy and generate script operations
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// 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.TaskServices;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters for a DacFx generate deploy plan request.
|
||||||
|
/// </summary>
|
||||||
|
public class GenerateDeployPlanParams : DacFxParams
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the DacFx generate deploy plan request type
|
||||||
|
/// </summary>
|
||||||
|
class GenerateDeployPlanRequest
|
||||||
|
{
|
||||||
|
public static readonly RequestType<GenerateDeployPlanParams, GenerateDeployPlanRequestResult> Type =
|
||||||
|
RequestType<GenerateDeployPlanParams, GenerateDeployPlanRequestResult>.Create("dacfx/generateDeployPlan");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters returned from a generate deploy script request.
|
||||||
|
/// </summary>
|
||||||
|
public class GenerateDeployPlanRequestResult : DacFxResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An xml string that details the alerts and the operations for deploying the specified dacpac to the database
|
||||||
|
/// </summary>
|
||||||
|
public string Report { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,5 +90,23 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Execute();
|
public abstract void Execute();
|
||||||
|
|
||||||
|
protected DacDeployOptions GetDefaultDeployOptions()
|
||||||
|
{
|
||||||
|
DacDeployOptions options = new DacDeployOptions
|
||||||
|
{
|
||||||
|
AllowDropBlockingAssemblies = true,
|
||||||
|
AllowIncompatiblePlatform = true,
|
||||||
|
BlockOnPossibleDataLoss = false,
|
||||||
|
DropObjectsNotInSource = true,
|
||||||
|
DropPermissionsNotInSource = true,
|
||||||
|
DropRoleMembersNotInSource = true,
|
||||||
|
IgnoreKeywordCasing = false,
|
||||||
|
IgnoreSemicolonBetweenStatements = false,
|
||||||
|
IgnoreWhitespace = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
serviceHost.SetRequestHandler(ExtractRequest.Type, this.HandleExtractRequest);
|
serviceHost.SetRequestHandler(ExtractRequest.Type, this.HandleExtractRequest);
|
||||||
serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest);
|
serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest);
|
||||||
serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest);
|
serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest);
|
||||||
|
serviceHost.SetRequestHandler(GenerateDeployPlanRequest.Type, this.HandleGenerateDeployPlanRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -167,7 +168,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
|
|
||||||
// want to show filepath in task history instead of server and database
|
// want to show filepath in task history instead of server and database
|
||||||
metadata.ServerName = parameters.ScriptFilePath;
|
metadata.ServerName = parameters.ScriptFilePath;
|
||||||
metadata.DatabaseName = "";
|
metadata.DatabaseName = string.Empty;
|
||||||
|
|
||||||
sqlTask = SqlTaskManagerInstance.CreateAndRun<SqlTask>(metadata);
|
sqlTask = SqlTaskManagerInstance.CreateAndRun<SqlTask>(metadata);
|
||||||
|
|
||||||
@@ -175,7 +176,39 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
{
|
{
|
||||||
OperationId = operation.OperationId,
|
OperationId = operation.OperationId,
|
||||||
Success = true,
|
Success = true,
|
||||||
ErrorMessage = ""
|
ErrorMessage = string.Empty
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await requestContext.SendError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles request to generate deploy plan
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task HandleGenerateDeployPlanRequest(GenerateDeployPlanParams parameters, RequestContext<GenerateDeployPlanRequestResult> requestContext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ConnectionInfo connInfo;
|
||||||
|
ConnectionServiceInstance.TryFindConnection(
|
||||||
|
parameters.OwnerUri,
|
||||||
|
out connInfo);
|
||||||
|
if (connInfo != null)
|
||||||
|
{
|
||||||
|
GenerateDeployPlanOperation operation = new GenerateDeployPlanOperation(parameters, connInfo);
|
||||||
|
operation.Execute(parameters.TaskExecutionMode);
|
||||||
|
|
||||||
|
await requestContext.SendResult(new GenerateDeployPlanRequestResult()
|
||||||
|
{
|
||||||
|
OperationId = operation.OperationId,
|
||||||
|
Success = true,
|
||||||
|
ErrorMessage = string.Empty,
|
||||||
|
Report = operation.DeployReport
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,7 +232,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
{
|
{
|
||||||
OperationId = operation.OperationId,
|
OperationId = operation.OperationId,
|
||||||
Success = true,
|
Success = true,
|
||||||
ErrorMessage = ""
|
ErrorMessage = string.Empty
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
public override void Execute()
|
public override void Execute()
|
||||||
{
|
{
|
||||||
DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath);
|
DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath);
|
||||||
this.DacServices.Deploy(dacpac, this.Parameters.DatabaseName, this.Parameters.UpgradeExisting, null, this.CancellationToken);
|
DacDeployOptions options = this.GetDefaultDeployOptions();
|
||||||
|
this.DacServices.Deploy(dacpac, this.Parameters.DatabaseName, this.Parameters.UpgradeExisting, options, this.CancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
using Microsoft.SqlServer.Dac;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
|
using Microsoft.SqlTools.Utility;
|
||||||
|
using System;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class to represent an in-progress generate deploy plan operation
|
||||||
|
/// </summary>
|
||||||
|
class GenerateDeployPlanOperation : DacFxOperation
|
||||||
|
{
|
||||||
|
public GenerateDeployPlanParams Parameters { get; }
|
||||||
|
|
||||||
|
public string DeployReport { get; set; }
|
||||||
|
|
||||||
|
public GenerateDeployPlanOperation(GenerateDeployPlanParams parameters, ConnectionInfo connInfo): base(connInfo)
|
||||||
|
{
|
||||||
|
Validate.IsNotNull("parameters", parameters);
|
||||||
|
this.Parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Execute()
|
||||||
|
{
|
||||||
|
DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath);
|
||||||
|
DacDeployOptions options = GetDefaultDeployOptions();
|
||||||
|
DeployReport = this.DacServices.GenerateDeployReport(dacpac, this.Parameters.DatabaseName, options, this.CancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
PublishOptions publishOptions = new PublishOptions();
|
PublishOptions publishOptions = new PublishOptions();
|
||||||
publishOptions.GenerateDeploymentReport = this.Parameters.GenerateDeploymentReport;
|
publishOptions.GenerateDeploymentReport = this.Parameters.GenerateDeploymentReport;
|
||||||
publishOptions.CancelToken = this.CancellationToken;
|
publishOptions.CancelToken = this.CancellationToken;
|
||||||
|
publishOptions.DeployOptions = this.GetDefaultDeployOptions();
|
||||||
publishOptions.DatabaseScriptPath = this.Parameters.ScriptFilePath;
|
publishOptions.DatabaseScriptPath = this.Parameters.ScriptFilePath;
|
||||||
// master script is only used if the target is Azure SQL db and the script contains all operations that must be done against the master database
|
// master script is only used if the target is Azure SQL db and the script contains all operations that must be done against the master database
|
||||||
publishOptions.MasterDbScriptPath = Path.Combine(Path.GetDirectoryName(this.Parameters.ScriptFilePath), string.Concat("master_", Path.GetFileName(this.Parameters.ScriptFilePath)));
|
publishOptions.MasterDbScriptPath = Path.Combine(Path.GetDirectoryName(this.Parameters.ScriptFilePath), string.Concat("master_", Path.GetFileName(this.Parameters.ScriptFilePath)));
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
|
|||||||
{
|
{
|
||||||
service.PerformOperation(operation);
|
service.PerformOperation(operation);
|
||||||
}
|
}
|
||||||
catch(OperationCanceledException canceledException)
|
catch (OperationCanceledException canceledException)
|
||||||
{
|
{
|
||||||
expectedException = canceledException;
|
expectedException = canceledException;
|
||||||
}
|
}
|
||||||
@@ -260,6 +260,74 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
|
|||||||
return generateScriptRequestContext;
|
return generateScriptRequestContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<Mock<RequestContext<DacFxResult>>> SendAndValidateGenerateDeployPlanRequest()
|
||||||
|
{
|
||||||
|
var result = GetLiveAutoCompleteTestObjects();
|
||||||
|
SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "DacFxGenerateDeployPlanTest");
|
||||||
|
sourceDb.RunQuery(@"CREATE TABLE [dbo].[table1]
|
||||||
|
(
|
||||||
|
[ID] INT NOT NULL PRIMARY KEY,
|
||||||
|
[Date] DATE NOT NULL
|
||||||
|
)
|
||||||
|
CREATE TABLE [dbo].[table2]
|
||||||
|
(
|
||||||
|
[ID] INT NOT NULL PRIMARY KEY,
|
||||||
|
[col1] NCHAR(10) NULL
|
||||||
|
)");
|
||||||
|
DacFxService service = new DacFxService();
|
||||||
|
string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
|
||||||
|
Directory.CreateDirectory(folderPath);
|
||||||
|
|
||||||
|
var extractParams = new ExtractParams
|
||||||
|
{
|
||||||
|
DatabaseName = sourceDb.DatabaseName,
|
||||||
|
PackageFilePath = Path.Combine(folderPath, string.Format("{0}.dacpac", sourceDb.DatabaseName)),
|
||||||
|
ApplicationName = "test",
|
||||||
|
ApplicationVersion = new Version(1, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
ExtractOperation extractOperation = new ExtractOperation(extractParams, result.ConnectionInfo);
|
||||||
|
service.PerformOperation(extractOperation);
|
||||||
|
|
||||||
|
// generate deploy plan for deploying dacpac to targetDb
|
||||||
|
var generateDeployPlanRequestContext = new Mock<RequestContext<DacFxResult>>();
|
||||||
|
generateDeployPlanRequestContext.Setup(x => x.SendResult(It.IsAny<DacFxResult>())).Returns(Task.FromResult(new object()));
|
||||||
|
|
||||||
|
SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "DacFxGenerateDeployPlanTestTarget");
|
||||||
|
targetDb.RunQuery(@"CREATE TABLE [dbo].[table2]
|
||||||
|
(
|
||||||
|
[ID] INT NOT NULL PRIMARY KEY,
|
||||||
|
[col1] NCHAR(10) NULL,
|
||||||
|
[col2] NCHAR(10) NULL
|
||||||
|
)
|
||||||
|
CREATE TABLE [dbo].[table3]
|
||||||
|
(
|
||||||
|
[ID] INT NOT NULL PRIMARY KEY,
|
||||||
|
[col1] INT NULL,
|
||||||
|
)");
|
||||||
|
|
||||||
|
var generateDeployPlanParams = new GenerateDeployPlanParams
|
||||||
|
{
|
||||||
|
PackageFilePath = extractParams.PackageFilePath,
|
||||||
|
DatabaseName = targetDb.DatabaseName,
|
||||||
|
};
|
||||||
|
|
||||||
|
GenerateDeployPlanOperation generateDeployPlanOperation = new GenerateDeployPlanOperation(generateDeployPlanParams, result.ConnectionInfo);
|
||||||
|
service.PerformOperation(generateDeployPlanOperation);
|
||||||
|
string report = generateDeployPlanOperation.DeployReport;
|
||||||
|
Assert.NotNull(report);
|
||||||
|
Assert.Contains("Create", report);
|
||||||
|
Assert.Contains("Drop", report);
|
||||||
|
Assert.Contains("Alter", report);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
VerifyAndCleanup(extractParams.PackageFilePath);
|
||||||
|
sourceDb.Cleanup();
|
||||||
|
targetDb.Cleanup();
|
||||||
|
|
||||||
|
return generateDeployPlanRequestContext;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verify the export bacpac request
|
/// Verify the export bacpac request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -306,7 +374,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Verify the gnerate deploy script request
|
/// Verify the generate deploy script request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Fact]
|
[Fact]
|
||||||
public async void GenerateDeployScript()
|
public async void GenerateDeployScript()
|
||||||
@@ -314,6 +382,15 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
|
|||||||
Assert.NotNull(await SendAndValidateGenerateDeployScriptRequest());
|
Assert.NotNull(await SendAndValidateGenerateDeployScriptRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify the generate deploy plan request
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async void GenerateDeployPlan()
|
||||||
|
{
|
||||||
|
Assert.NotNull(await SendAndValidateGenerateDeployPlanRequest());
|
||||||
|
}
|
||||||
|
|
||||||
private void VerifyAndCleanup(string filePath)
|
private void VerifyAndCleanup(string filePath)
|
||||||
{
|
{
|
||||||
// Verify it was created
|
// Verify it was created
|
||||||
|
|||||||
Reference in New Issue
Block a user