mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Add support for getting DacFx deploy options from a publish profile (#995)
* add support for getting options from a publish profile * update comments * set values for default options if they aren't specified in the publish profile * addressing comments
This commit is contained in:
@@ -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.DacFx.Contracts
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters for a DacFx get options from profile request.
|
||||||
|
/// </summary>
|
||||||
|
public class GetOptionsFromProfileParams
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the profile path
|
||||||
|
/// </summary>
|
||||||
|
public string ProfilePath { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parameters returned from a DacFx get options from profile request.
|
||||||
|
/// </summary>
|
||||||
|
public class DacFxOptionsResult : ResultStatus
|
||||||
|
{
|
||||||
|
public DeploymentOptions DeploymentOptions { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the DacFx get options from profile request type
|
||||||
|
/// </summary>
|
||||||
|
class GetOptionsFromProfileRequest
|
||||||
|
{
|
||||||
|
public static readonly RequestType<GetOptionsFromProfileParams, DacFxOptionsResult> Type =
|
||||||
|
RequestType<GetOptionsFromProfileParams, DacFxOptionsResult>.Create("dacfx/getOptionsFromProfile");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,16 +2,16 @@
|
|||||||
// Copyright (c) Microsoft. All rights reserved.
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlServer.Dac;
|
using Microsoft.SqlServer.Dac;
|
||||||
using Microsoft.SqlTools.Hosting.Protocol;
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using Microsoft.Data.SqlClient;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
||||||
{
|
{
|
||||||
@@ -46,6 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
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);
|
serviceHost.SetRequestHandler(GenerateDeployPlanRequest.Type, this.HandleGenerateDeployPlanRequest);
|
||||||
|
serviceHost.SetRequestHandler(GetOptionsFromProfileRequest.Type, this.HandleGetOptionsFromProfileRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -223,6 +224,38 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles request to get the options from a publish profile
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task HandleGetOptionsFromProfileRequest(GetOptionsFromProfileParams parameters, RequestContext<DacFxOptionsResult> requestContext)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DeploymentOptions options = null;
|
||||||
|
if (parameters.ProfilePath != null)
|
||||||
|
{
|
||||||
|
DacProfile profile = DacProfile.Load(parameters.ProfilePath);
|
||||||
|
if (profile.DeployOptions != null)
|
||||||
|
{
|
||||||
|
options = new DeploymentOptions();
|
||||||
|
await options.InitializeFromProfile(profile.DeployOptions, parameters.ProfilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await requestContext.SendResult(new DacFxOptionsResult()
|
||||||
|
{
|
||||||
|
DeploymentOptions = options,
|
||||||
|
Success = true,
|
||||||
|
ErrorMessage = string.Empty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await requestContext.SendError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ExecuteOperation(DacFxOperation operation, DacFxParams parameters, string taskName, RequestContext<DacFxResult> requestContext)
|
private void ExecuteOperation(DacFxOperation operation, DacFxParams parameters, string taskName, RequestContext<DacFxResult> requestContext)
|
||||||
{
|
{
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
|
|||||||
@@ -2,12 +2,9 @@
|
|||||||
// Copyright (c) Microsoft. All rights reserved.
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
//
|
//
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.SqlServer.Dac;
|
using Microsoft.SqlServer.Dac;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
||||||
{
|
{
|
||||||
@@ -241,6 +238,52 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DeploymentOptions(DacDeployOptions options)
|
public DeploymentOptions(DacDeployOptions options)
|
||||||
|
{
|
||||||
|
SetOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// initialize deployment options from the options in a publish profile.xml
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">options created from the profile</param>
|
||||||
|
/// <param name="profilePath"></param>
|
||||||
|
public async Task InitializeFromProfile(DacDeployOptions options, string profilePath)
|
||||||
|
{
|
||||||
|
// check if defaults need to be set if they aren't specified in the profile
|
||||||
|
string contents = await File.ReadAllTextAsync(profilePath);
|
||||||
|
if (!contents.Contains("<AllowDropBlockingAssemblies>"))
|
||||||
|
{
|
||||||
|
options.AllowDropBlockingAssemblies = true;
|
||||||
|
}
|
||||||
|
if (!contents.Contains("<AllowIncompatiblePlatform>"))
|
||||||
|
{
|
||||||
|
options.AllowIncompatiblePlatform = true;
|
||||||
|
}
|
||||||
|
if (!contents.Contains("<DropObjectsNotInSource>"))
|
||||||
|
{
|
||||||
|
options.DropObjectsNotInSource = true;
|
||||||
|
}
|
||||||
|
if (!contents.Contains("<DropPermissionsNotInSource>"))
|
||||||
|
{
|
||||||
|
options.DropPermissionsNotInSource = true;
|
||||||
|
}
|
||||||
|
if (!contents.Contains("<DropRoleMembersNotInSource>"))
|
||||||
|
{
|
||||||
|
options.DropRoleMembersNotInSource = true;
|
||||||
|
}
|
||||||
|
if (!contents.Contains("<IgnoreKeywordCasing>"))
|
||||||
|
{
|
||||||
|
options.IgnoreKeywordCasing = false;
|
||||||
|
}
|
||||||
|
if (!contents.Contains("<IgnoreSemicolonBetweenStatements>"))
|
||||||
|
{
|
||||||
|
options.IgnoreSemicolonBetweenStatements = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetOptions(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetOptions(DacDeployOptions options)
|
||||||
{
|
{
|
||||||
System.Reflection.PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties();
|
System.Reflection.PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties();
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,25 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Data.SqlClient;
|
using Microsoft.Data.SqlClient;
|
||||||
using Microsoft.SqlServer.Dac;
|
using Microsoft.SqlServer.Dac;
|
||||||
|
using Microsoft.SqlTools.Hosting.Protocol;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||||
using Microsoft.SqlTools.ServiceLayer.DacFx;
|
using Microsoft.SqlTools.ServiceLayer.DacFx;
|
||||||
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
|
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||||
|
using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
|
||||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
|
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
|
||||||
{
|
{
|
||||||
public class DacFxServiceTests
|
public class DacFxServiceTests
|
||||||
{
|
{
|
||||||
|
private string publishProfileFolder = Path.Combine("..", "..", "..", "DacFx", "PublishProfiles");
|
||||||
private const string SourceScript = @"CREATE TABLE [dbo].[table1]
|
private const string SourceScript = @"CREATE TABLE [dbo].[table1]
|
||||||
(
|
(
|
||||||
[ID] INT NOT NULL PRIMARY KEY,
|
[ID] INT NOT NULL PRIMARY KEY,
|
||||||
@@ -532,6 +537,80 @@ RETURN 0
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
/// Verify that options can get retrieved from publish profile
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async void GetOptionsFromProfile()
|
||||||
|
{
|
||||||
|
DeploymentOptions expectedResults = new DeploymentOptions()
|
||||||
|
{
|
||||||
|
ExcludeObjectTypes = null,
|
||||||
|
IncludeCompositeObjects = true,
|
||||||
|
BlockOnPossibleDataLoss = true,
|
||||||
|
AllowIncompatiblePlatform = true
|
||||||
|
};
|
||||||
|
|
||||||
|
var dacfxRequestContext = new Mock<RequestContext<DacFxOptionsResult>>();
|
||||||
|
dacfxRequestContext.Setup((RequestContext<DacFxOptionsResult> x) => x.SendResult(It.Is<DacFxOptionsResult>((result) => ValidateOptions(expectedResults, result.DeploymentOptions) == true))).Returns(Task.FromResult(new object()));
|
||||||
|
|
||||||
|
DacFxService service = new DacFxService();
|
||||||
|
string file = Path.Combine(publishProfileFolder, "profileWithOptions.publish.xml");
|
||||||
|
|
||||||
|
var getOptionsFromProfileParams = new GetOptionsFromProfileParams
|
||||||
|
{
|
||||||
|
ProfilePath = file
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.HandleGetOptionsFromProfileRequest(getOptionsFromProfileParams, dacfxRequestContext.Object);
|
||||||
|
dacfxRequestContext.VerifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
/// Verify that default options are returned if a profile doesn't specify any options
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async void GetOptionsFromProfileWithoutOptions()
|
||||||
|
{
|
||||||
|
DeploymentOptions expectedResults = new DeploymentOptions();
|
||||||
|
expectedResults.ExcludeObjectTypes = null;
|
||||||
|
|
||||||
|
var dacfxRequestContext = new Mock<RequestContext<DacFxOptionsResult>>();
|
||||||
|
dacfxRequestContext.Setup((RequestContext<DacFxOptionsResult> x) => x.SendResult(It.Is<DacFxOptionsResult>((result) => ValidateOptions(expectedResults, result.DeploymentOptions) == true))).Returns(Task.FromResult(new object()));
|
||||||
|
|
||||||
|
DacFxService service = new DacFxService();
|
||||||
|
string file = Path.Combine(publishProfileFolder, "profileNoOptions.publish.xml");
|
||||||
|
|
||||||
|
var getOptionsFromProfileParams = new GetOptionsFromProfileParams
|
||||||
|
{
|
||||||
|
ProfilePath = file
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.HandleGetOptionsFromProfileRequest(getOptionsFromProfileParams, dacfxRequestContext.Object);
|
||||||
|
dacfxRequestContext.VerifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ValidateOptions(DeploymentOptions expected, DeploymentOptions actual)
|
||||||
|
{
|
||||||
|
System.Reflection.PropertyInfo[] deploymentOptionsProperties = expected.GetType().GetProperties();
|
||||||
|
foreach (var v in deploymentOptionsProperties)
|
||||||
|
{
|
||||||
|
var defaultP = v.GetValue(expected);
|
||||||
|
var actualP = v.GetValue(actual);
|
||||||
|
|
||||||
|
if (v.Name == "ExcludeObjectTypes")
|
||||||
|
{
|
||||||
|
Assert.True((defaultP as ObjectType[])?.Length == (actualP as ObjectType[])?.Length, "Number of excluded objects is different not equal");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.True((defaultP == null && actualP == null) || (defaultP == null && (actualP as string) == string.Empty) || defaultP.Equals(actualP), $"Actual Property from Service is not equal to default property for {v.Name}, Actual value: {actualP} and Default value: {defaultP}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private string InitialExtract(DacFxService service, SqlTestDb sourceDb, LiveConnectionHelper.TestConnectionResult result)
|
private string InitialExtract(DacFxService service, SqlTestDb sourceDb, LiveConnectionHelper.TestConnectionResult result)
|
||||||
{
|
{
|
||||||
string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
|
string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetDatabaseName>testdb</TargetDatabaseName>
|
||||||
|
<DeployScriptFileName>DatabaseProjectTestdb.sql</DeployScriptFileName>
|
||||||
|
<ProfileVersionNumber>1</ProfileVersionNumber>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<SqlCmdVariable Include="ProdDatabaseName">
|
||||||
|
<DefaultValue>prodname</DefaultValue>
|
||||||
|
<Value>$(SqlCmdVar__1)</Value>
|
||||||
|
</SqlCmdVariable>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<IncludeCompositeObjects>True</IncludeCompositeObjects>
|
||||||
|
<TargetDatabaseName>testdb</TargetDatabaseName>
|
||||||
|
<DeployScriptFileName>DatabaseProjectTestdb.sql</DeployScriptFileName>
|
||||||
|
<ProfileVersionNumber>1</ProfileVersionNumber>
|
||||||
|
<BlockOnPossibleDataLoss>True</BlockOnPossibleDataLoss>
|
||||||
|
<AllowIncompatiblePlatform>True</AllowIncompatiblePlatform>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<SqlCmdVariable Include="ProdDatabaseName">
|
||||||
|
<DefaultValue>prodname</DefaultValue>
|
||||||
|
<Value>$(SqlCmdVar__1)</Value>
|
||||||
|
</SqlCmdVariable>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
Reference in New Issue
Block a user