From 1a93404e543ed56c1f64a5e8a76ab4e080887c9e Mon Sep 17 00:00:00 2001 From: Kim Santiago <31145923+kisantia@users.noreply.github.com> Date: Mon, 27 Jul 2020 17:52:15 -0700 Subject: [PATCH] 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 --- .../Contracts/GetOptionsFromProfileRequest.cs | 39 +++++++++ .../DacFx/DacFxService.cs | 41 +++++++++- .../Contracts/DeploymentOptions.cs | 53 +++++++++++-- .../DacFx/DacFxserviceTests.cs | 79 +++++++++++++++++++ .../profileNoOptions.publish.xml | 14 ++++ .../profileWithOptions.publish.xml | 17 ++++ 6 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GetOptionsFromProfileRequest.cs create mode 100644 test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileNoOptions.publish.xml create mode 100644 test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileWithOptions.publish.xml diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GetOptionsFromProfileRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GetOptionsFromProfileRequest.cs new file mode 100644 index 00000000..809b11e6 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GetOptionsFromProfileRequest.cs @@ -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 +{ + /// + /// Parameters for a DacFx get options from profile request. + /// + public class GetOptionsFromProfileParams + { + /// + /// Gets or sets the profile path + /// + public string ProfilePath { get; set; } + } + + /// + /// Parameters returned from a DacFx get options from profile request. + /// + public class DacFxOptionsResult : ResultStatus + { + public DeploymentOptions DeploymentOptions { get; set; } + } + + /// + /// Defines the DacFx get options from profile request type + /// + class GetOptionsFromProfileRequest + { + public static readonly RequestType Type = + RequestType.Create("dacfx/getOptionsFromProfile"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs index 954427df..a4717ba3 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs @@ -2,16 +2,16 @@ // 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.Concurrent; +using System.Threading.Tasks; using Microsoft.SqlServer.Dac; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; using Microsoft.SqlTools.ServiceLayer.TaskServices; -using System; -using System.Collections.Concurrent; -using Microsoft.Data.SqlClient; -using System.Threading.Tasks; namespace Microsoft.SqlTools.ServiceLayer.DacFx { @@ -46,6 +46,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest); serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest); serviceHost.SetRequestHandler(GenerateDeployPlanRequest.Type, this.HandleGenerateDeployPlanRequest); + serviceHost.SetRequestHandler(GetOptionsFromProfileRequest.Type, this.HandleGetOptionsFromProfileRequest); } /// @@ -223,6 +224,38 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx } } + /// + /// Handles request to get the options from a publish profile + /// + /// + public async Task HandleGetOptionsFromProfileRequest(GetOptionsFromProfileParams parameters, RequestContext 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 requestContext) { Task.Run(async () => diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs index c6283447..33f1e89d 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs @@ -2,12 +2,9 @@ // Copyright (c) Microsoft. All rights reserved. // 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 System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Text; namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts { @@ -241,6 +238,52 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts } public DeploymentOptions(DacDeployOptions options) + { + SetOptions(options); + } + + /// + /// initialize deployment options from the options in a publish profile.xml + /// + /// options created from the profile + /// + 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("")) + { + options.AllowDropBlockingAssemblies = true; + } + if (!contents.Contains("")) + { + options.AllowIncompatiblePlatform = true; + } + if (!contents.Contains("")) + { + options.DropObjectsNotInSource = true; + } + if (!contents.Contains("")) + { + options.DropPermissionsNotInSource = true; + } + if (!contents.Contains("")) + { + options.DropRoleMembersNotInSource = true; + } + if (!contents.Contains("")) + { + options.IgnoreKeywordCasing = false; + } + if (!contents.Contains("")) + { + options.IgnoreSemicolonBetweenStatements = false; + } + + SetOptions(options); + } + + public void SetOptions(DacDeployOptions options) { System.Reflection.PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties(); diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs index fa771ba3..5a8b7eba 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs @@ -5,20 +5,25 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Microsoft.SqlServer.Dac; +using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; using Microsoft.SqlTools.ServiceLayer.DacFx; using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; using Microsoft.SqlTools.ServiceLayer.TaskServices; using Microsoft.SqlTools.ServiceLayer.Test.Common; +using Moq; using Xunit; namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx { public class DacFxServiceTests { + private string publishProfileFolder = Path.Combine("..", "..", "..", "DacFx", "PublishProfiles"); private const string SourceScript = @"CREATE TABLE [dbo].[table1] ( [ID] INT NOT NULL PRIMARY KEY, @@ -532,6 +537,80 @@ RETURN 0 } } + // + /// Verify that options can get retrieved from publish profile + /// + [Fact] + public async void GetOptionsFromProfile() + { + DeploymentOptions expectedResults = new DeploymentOptions() + { + ExcludeObjectTypes = null, + IncludeCompositeObjects = true, + BlockOnPossibleDataLoss = true, + AllowIncompatiblePlatform = true + }; + + var dacfxRequestContext = new Mock>(); + dacfxRequestContext.Setup((RequestContext x) => x.SendResult(It.Is((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(); + } + + // + /// Verify that default options are returned if a profile doesn't specify any options + /// + [Fact] + public async void GetOptionsFromProfileWithoutOptions() + { + DeploymentOptions expectedResults = new DeploymentOptions(); + expectedResults.ExcludeObjectTypes = null; + + var dacfxRequestContext = new Mock>(); + dacfxRequestContext.Setup((RequestContext x) => x.SendResult(It.Is((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) { string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest"); diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileNoOptions.publish.xml b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileNoOptions.publish.xml new file mode 100644 index 00000000..50ac3a6e --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileNoOptions.publish.xml @@ -0,0 +1,14 @@ + + + + testdb + DatabaseProjectTestdb.sql + 1 + + + + prodname + $(SqlCmdVar__1) + + + \ No newline at end of file diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileWithOptions.publish.xml b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileWithOptions.publish.xml new file mode 100644 index 00000000..1f9843c9 --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/PublishProfiles/profileWithOptions.publish.xml @@ -0,0 +1,17 @@ + + + + True + testdb + DatabaseProjectTestdb.sql + 1 + True + True + + + + prodname + $(SqlCmdVar__1) + + + \ No newline at end of file