diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs index 679310f4..d51390b6 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs @@ -2925,6 +2925,30 @@ namespace Microsoft.SqlTools.ServiceLayer } } + public static string IncludeNodeTaskName + { + get + { + return Keys.GetString(Keys.IncludeNodeTaskName); + } + } + + public static string ExcludeNodeTaskName + { + get + { + return Keys.GetString(Keys.ExcludeNodeTaskName); + } + } + + public static string SchemaCompareExcludeIncludeNodeNotFound + { + get + { + return Keys.GetString(Keys.SchemaCompareExcludeIncludeNodeNotFound); + } + } + public static string ConnectionServiceListDbErrorNotConnected(string uri) { return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri); @@ -4257,6 +4281,15 @@ namespace Microsoft.SqlTools.ServiceLayer public const string PublishChangesTaskName = "PublishChangesTaskName"; + + + public const string IncludeNodeTaskName = "IncludeNodeTaskName"; + + + public const string ExcludeNodeTaskName = "ExcludeNodeTaskName"; + + + public const string SchemaCompareExcludeIncludeNodeNotFound = "SchemaCompareExcludeIncludeNodeNotFound"; private Keys() diff --git a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx index 433bb93f..f28f51b6 100755 --- a/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx +++ b/src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx @@ -1715,4 +1715,16 @@ Apply schema compare changes + + Include schema compare node + + + + Exclude schema compare node + + + + Failed to find the specified change in the model + + diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs index 59cb3b94..5c6e7018 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/DeploymentOptions.cs @@ -41,7 +41,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool IgnoreLockHintsOnIndexes { get; set; } public bool IgnoreKeywordCasing { get; set; } - + public bool IgnoreIndexPadding { get; set; } public bool IgnoreIndexOptions { get; set; } @@ -51,21 +51,21 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool IgnoreIdentitySeed { get; set; } public bool IgnoreUserSettingsObjects { get; set; } - + public bool IgnoreFullTextCatalogFilePath { get; set; } - + public bool IgnoreWhitespace { get; set; } public bool IgnoreWithNocheckOnForeignKeys { get; set; } - + public bool VerifyCollationCompatibility { get; set; } - + public bool UnmodifiableObjectWarnings { get; set; } public bool TreatVerificationErrorsAsWarnings { get; set; } - + public bool ScriptRefreshModule { get; set; } - + public bool ScriptNewConstraintValidation { get; set; } public bool ScriptFileSize { get; set; } @@ -81,7 +81,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool RunDeploymentPlanExecutors { get; set; } public bool RegisterDataTierApplication { get; set; } - + public bool PopulateFilesOnFileGroups { get; set; } public bool NoAlterStatementsToChangeClrTypes { get; set; } @@ -93,17 +93,17 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool AllowUnsafeRowLevelSecurityDataMovement { get; set; } public bool IgnoreWithNocheckOnCheckConstraints { get; set; } - + public bool IgnoreFillFactor { get; set; } - + public bool IgnoreFileSize { get; set; } - + public bool IgnoreFilegroupPlacement { get; set; } - + public bool DoNotAlterReplicatedObjects { get; set; } - + public bool DoNotAlterChangeDataCaptureObjects { get; set; } - + public bool DisableAndReenableDdlTriggers { get; set; } public bool DeployDatabaseInSingleUserMode { get; set; } @@ -113,11 +113,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool CompareUsingTargetCollation { get; set; } public bool CommentOutSetVarDeclarations { get; set; } - + public int CommandTimeout { get; set; } = 120; public bool BlockWhenDriftDetected { get; set; } - + public bool BlockOnPossibleDataLoss { get; set; } public bool BackupDatabaseBeforeChanges { get; set; } @@ -129,15 +129,15 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public string AdditionalDeploymentContributorArguments { get; set; } public string AdditionalDeploymentContributors { get; set; } - + public bool DropConstraintsNotInSource { get; set; } - + public bool DropDmlTriggersNotInSource { get; set; } - + public bool DropExtendedPropertiesNotInSource { get; set; } - + public bool DropIndexesNotInSource { get; set; } - + public bool IgnoreFileAndLogFilePath { get; set; } public bool IgnoreExtendedProperties { get; set; } @@ -151,9 +151,9 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool IgnoreDdlTriggerState { get; set; } public bool IgnoreDdlTriggerOrder { get; set; } - + public bool IgnoreCryptographicProviderFilePath { get; set; } - + public bool VerifyDeployment { get; set; } public bool IgnoreComments { get; set; } @@ -161,17 +161,17 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public bool IgnoreColumnCollation { get; set; } public bool IgnoreAuthorizer { get; set; } - + public bool IgnoreAnsiNulls { get; set; } public bool GenerateSmartDefaults { get; set; } - + public bool DropStatisticsNotInSource { get; set; } public bool DropRoleMembersNotInSource { get; set; } public bool DropPermissionsNotInSource { get; set; } - + public bool DropObjectsNotInSource { get; set; } public bool IgnoreColumnOrder { get; set; } @@ -208,11 +208,13 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts { DacDeployOptions options = new DacDeployOptions(); System.Reflection.PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties(); - + foreach (var deployOptionsProp in deploymentOptionsProperties) { var prop = options.GetType().GetProperty(deployOptionsProp.Name); - if (prop != null) + + // Note that we set excluded object types here since dacfx has this value as null; + if (prop != null && deployOptionsProp.Name != "ExcludeObjectTypes") { deployOptionsProp.SetValue(this, prop.GetValue(options)); } diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareIncludeExcludeNodeRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareIncludeExcludeNodeRequest.cs new file mode 100644 index 00000000..ae9e8071 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareIncludeExcludeNodeRequest.cs @@ -0,0 +1,43 @@ +// +// 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.SchemaCompare.Contracts +{ + /// + /// Parameters for a schema compare include specific node request + /// + public class SchemaCompareNodeParams + { + /// + /// Operation id of the schema compare operation + /// + public string OperationId { get; set; } + + /// + /// Difference to Include or exclude + /// + public DiffEntry DiffEntry { get; set; } + + /// + /// Indicator for include or exclude request + /// + public bool IncludeRequest { get; set; } + + /// + /// Execution mode for the operation. Default is execution + /// + public TaskExecutionMode TaskExecutionMode { get; set; } + } + + class SchemaCompareIncludeExcludeNodeRequest + { + public static readonly RequestType Type = + RequestType.Create("schemaCompare/includeExcludeNode"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareOptionsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareOptionsRequest.cs new file mode 100644 index 00000000..d2073596 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareOptionsRequest.cs @@ -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.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts +{ + /// + /// Defines paramaters for Get default options call + /// No parameters required so far + /// + public class SchemaCompareGetOptionsParams + { + } + + /// + /// Gets or sets the result of get default options call + /// + public class SchemaCompareOptionsResult : ResultStatus + { + public DeploymentOptions DefaultDeploymentOptions { get; set; } + } + + /// + /// Defines the Schema Compare request type + /// + class SchemaCompareGetDefaultOptionsRequest + { + public static readonly RequestType Type = + RequestType.Create("schemaCompare/getDefaultOptions"); + } + + +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs index 4203f127..ffd4e520 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs @@ -79,15 +79,15 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts public class DiffEntry { - public SchemaUpdateAction UpdateAction; - public SchemaDifferenceType DifferenceType; - public string Name; - public string SourceValue; - public string TargetValue; - public DiffEntry Parent; - public List Children; - public string SourceScript; - public string TargetScript; + public SchemaUpdateAction UpdateAction { get; set; } + public SchemaDifferenceType DifferenceType { get; set; } + public string Name { get; set; } + public string SourceValue { get; set; } + public string TargetValue { get; set; } + public DiffEntry Parent { get; set; } + public List Children { get; set; } + public string SourceScript { get; set; } + public string TargetScript { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareGenerateScriptOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareGenerateScriptOperation.cs index ad13ba8c..c7d064a0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareGenerateScriptOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareGenerateScriptOperation.cs @@ -78,5 +78,17 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare public void Cancel() { } + + /// + /// Disposes the operation. + /// + public void Dispose() + { + if (!disposed) + { + this.Cancel(); + disposed = true; + } + } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareIncludeExcludeNodeOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareIncludeExcludeNodeOperation.cs new file mode 100644 index 00000000..c890c43c --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareIncludeExcludeNodeOperation.cs @@ -0,0 +1,128 @@ +// +// 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.Compare; +using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.Utility; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; + +namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare +{ + /// + /// Class to represent an in-progress schema compare include/exclude Node operation + /// + class SchemaCompareIncludeExcludeNodeOperation : ITaskOperation + { + private CancellationTokenSource cancellation = new CancellationTokenSource(); + private bool disposed = false; + + /// + /// Gets the unique id associated with this instance. + /// + public string OperationId { get; private set; } + + public SchemaCompareNodeParams Parameters { get; } + + protected CancellationToken CancellationToken { get { return this.cancellation.Token; } } + + public string ErrorMessage { get; set; } + + public SqlTask SqlTask { get; set; } + + public SchemaComparisonResult ComparisonResult { get; set; } + + public bool Success { get; set; } + + public SchemaCompareIncludeExcludeNodeOperation(SchemaCompareNodeParams parameters, SchemaComparisonResult comparisonResult) + { + Validate.IsNotNull("parameters", parameters); + this.Parameters = parameters; + Validate.IsNotNull("comparisonResult", comparisonResult); + this.ComparisonResult = comparisonResult; + } + + public void Execute(TaskExecutionMode mode) + { + this.CancellationToken.ThrowIfCancellationRequested(); + + try + { + SchemaDifference node = this.FindDifference(this.ComparisonResult.Differences, this.Parameters.DiffEntry); + if (node == null) + { + throw new InvalidOperationException(SR.SchemaCompareExcludeIncludeNodeNotFound); + } + + this.Success = this.Parameters.IncludeRequest ? this.ComparisonResult.Include(node) : this.ComparisonResult.Exclude(node); + } + catch (Exception e) + { + ErrorMessage = e.Message; + Logger.Write(TraceEventType.Error, string.Format("Schema compare publish changes operation {0} failed with exception {1}", this.OperationId, e.Message)); + throw; + } + } + + private SchemaDifference FindDifference(IEnumerable differences, DiffEntry diffEntry) + { + foreach (var difference in differences) + { + if (IsEqual(difference, diffEntry)) + { + return difference; + } + else + { + var childDiff = FindDifference(difference.Children, diffEntry); + if (childDiff != null) + { + return childDiff; + } + } + } + return null; + } + + private bool IsEqual(SchemaDifference difference, DiffEntry diffEntry) + { + bool result = true; + // Create a diff entry from difference and check if it matches the diff entry passed + DiffEntry entryFromDifference = SchemaCompareOperation.CreateDiffEntry(difference, null); + + System.Reflection.PropertyInfo[] properties = diffEntry.GetType().GetProperties(); + foreach (var prop in properties) + { + result = result && + ((prop.GetValue(diffEntry) == null && + prop.GetValue(entryFromDifference) == null) || + prop.GetValue(diffEntry).SafeToString().Equals(prop.GetValue(entryFromDifference).SafeToString())); + } + + return result; + } + + // The schema compare public api doesn't currently take a cancellation token so the operation can't be cancelled + public void Cancel() + { + } + + /// + /// Disposes the operation. + /// + public void Dispose() + { + if (!disposed) + { + this.Cancel(); + disposed = true; + } + } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs index 1de38af5..2d05e9d5 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs @@ -41,8 +41,6 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare public List Differences; - public DacDeployOptions DefaultOptions; - public SchemaCompareOperation(SchemaCompareParams parameters, ConnectionInfo sourceConnInfo, ConnectionInfo targetConnInfo) { Validate.IsNotNull("parameters", parameters); @@ -134,8 +132,13 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare return dacOptions; } - private DiffEntry CreateDiffEntry(SchemaDifference difference, DiffEntry parent) + internal static DiffEntry CreateDiffEntry(SchemaDifference difference, DiffEntry parent) { + if(difference == null) + { + return null; + } + DiffEntry diffEntry = new DiffEntry(); diffEntry.UpdateAction = difference.UpdateAction; diffEntry.DifferenceType = difference.DifferenceType; @@ -207,13 +210,13 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare return ConnectionService.BuildConnectionString(connInfo.ConnectionDetails); } - private string RemoveExcessWhitespace(string script) + private static string RemoveExcessWhitespace(string script) { // replace all multiple spaces with single space return Regex.Replace(script, " {2,}", " "); } - private string GetName(string name) + private static string GetName(string name) { // remove brackets from name return Regex.Replace(name, @"[\[\]]", ""); diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaComparePublishChangesOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaComparePublishChangesOperation.cs index bc046a1e..940106b2 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaComparePublishChangesOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaComparePublishChangesOperation.cs @@ -19,7 +19,6 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare class SchemaComparePublishChangesOperation : ITaskOperation { private CancellationTokenSource cancellation = new CancellationTokenSource(); - private bool disposed = false; /// /// Gets the unique id associated with this instance. diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs index 3a3a908b..9d7630b4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs @@ -44,6 +44,8 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare serviceHost.SetRequestHandler(SchemaCompareRequest.Type, this.HandleSchemaCompareRequest); serviceHost.SetRequestHandler(SchemaCompareGenerateScriptRequest.Type, this.HandleSchemaCompareGenerateScriptRequest); serviceHost.SetRequestHandler(SchemaComparePublishChangesRequest.Type, this.HandleSchemaComparePublishChangesRequest); + serviceHost.SetRequestHandler(SchemaCompareIncludeExcludeNodeRequest.Type, this.HandleSchemaCompareIncludeExcludeNodeRequest); + serviceHost.SetRequestHandler(SchemaCompareGetDefaultOptionsRequest.Type, this.HandleSchemaCompareGetDefaultOptionsRequest); } /// @@ -174,6 +176,61 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare } } + public async Task HandleSchemaCompareIncludeExcludeNodeRequest(SchemaCompareNodeParams parameters, RequestContext requestContext) + { + SchemaCompareIncludeExcludeNodeOperation operation = null; + try + { + SchemaComparisonResult compareResult = schemaCompareResults.Value[parameters.OperationId]; + operation = new SchemaCompareIncludeExcludeNodeOperation(parameters, compareResult); + SqlTask sqlTask = null; + TaskMetadata metadata = new TaskMetadata(); + metadata.TaskOperation = operation; + metadata.Name = parameters.IncludeRequest ? SR.IncludeNodeTaskName : SR.ExcludeNodeTaskName; + + sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata); + + await requestContext.SendResult(new ResultStatus() + { + Success = true, + ErrorMessage = operation.ErrorMessage + }); + } + catch (Exception e) + { + await requestContext.SendResult(new ResultStatus() + { + Success = false, + ErrorMessage = operation == null ? e.Message : operation.ErrorMessage, + }); + } + } + + public async Task HandleSchemaCompareGetDefaultOptionsRequest(SchemaCompareGetOptionsParams parameters, RequestContext requestContext) + { + try + { + // this does not need to be an async operation since this only creates and resturn the default opbject + DeploymentOptions options = new DeploymentOptions(); + + await requestContext.SendResult(new SchemaCompareOptionsResult() + { + DefaultDeploymentOptions = options, + Success = true, + ErrorMessage = null + }); + } + catch (Exception e) + { + await requestContext.SendResult(new SchemaCompareOptionsResult() + { + DefaultDeploymentOptions = null, + Success = false, + ErrorMessage = e.Message + }); + } + } + private SqlTaskManager SqlTaskManagerInstance { get diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs index 6f2bd153..a63fa439 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs @@ -3,9 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using Microsoft.SqlTools.Hosting.Protocol; -using Microsoft.SqlTools.ServiceLayer.DacFx; -using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; -using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; using Microsoft.SqlTools.ServiceLayer.SchemaCompare; using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; using Microsoft.SqlTools.ServiceLayer.TaskServices; @@ -16,6 +13,7 @@ using System; using System.IO; using System.Threading.Tasks; using Xunit; +using Microsoft.SqlTools.ServiceLayer.SchemaCopmare; namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare { @@ -111,7 +109,6 @@ END private async Task>> SendAndValidateSchemaCompareRequestDacpacToDacpacWithOptions(string sourceScript, string targetScript, DeploymentOptions nodiffOption, DeploymentOptions shouldDiffOption) { - var result = SchemaCompareTestUtils.GetLiveAutoCompleteTestObjects(); var schemaCompareRequestContext = new Mock>(); schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); @@ -373,17 +370,21 @@ END } /// - /// Verify the schema compare script generation comparing dacpac and db with and excluding table valued function + /// Verify the schema compare default creation test /// [Fact] - public void ValidateSchemaCompareOptionsDefault() + public void ValidateSchemaCompareOptionsDefaultAgainstDacFx() { DeploymentOptions deployOptions = new DeploymentOptions(); DacDeployOptions dacOptions = new DacDeployOptions(); System.Reflection.PropertyInfo[] deploymentOptionsProperties = deployOptions.GetType().GetProperties(); System.Reflection.PropertyInfo[] ddProperties = dacOptions.GetType().GetProperties(); - + + // Note that DatabaseSpecification and sql cmd variables list is not present in Sqltools service - its not settable and is not used by ADS options. + // TODO : update this test if the above options are added later + Assert.True(deploymentOptionsProperties.Length == ddProperties.Length - 2 , $"Number of properties is not same Deployment options : {deploymentOptionsProperties.Length} DacFx options : {ddProperties.Length}"); + foreach (var deployOptionsProp in deploymentOptionsProperties) { var dacProp = dacOptions.GetType().GetProperty(deployOptionsProp.Name); @@ -391,10 +392,49 @@ END var deployOptionsValue = deployOptionsProp.GetValue(deployOptions); var dacValue = dacProp.GetValue(dacOptions); - - Assert.True((deployOptionsValue == null && dacValue == null) || deployOptionsValue.Equals(dacValue), $"DacFx DacDeploy property not equal to Tools Service DeploymentOptions for { deployOptionsProp.Name}, SchemaCompareOptions value: {deployOptionsValue} and DacDeployOptions value: {dacValue} "); + + if (deployOptionsProp.Name != "ExcludeObjectTypes") // do not compare for ExcludeObjectTypes because it will be different + { + Assert.True((deployOptionsValue == null && dacValue == null) || deployOptionsValue.Equals(dacValue), $"DacFx DacDeploy property not equal to Tools Service DeploymentOptions for { deployOptionsProp.Name}, SchemaCompareOptions value: {deployOptionsValue} and DacDeployOptions value: {dacValue} "); + } } } + /// + /// Verify the schema compare default creation test + /// + [Fact] + public async void ValidateSchemaCompareGetDefaultOptionsCallFromService() + { + DeploymentOptions deployOptions = new DeploymentOptions(); + var schemaCompareRequestContext = new Mock>(); + schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object())); + schemaCompareRequestContext.Setup((RequestContext x) => x.SendResult(It.Is((options) => this.OptionsEqualsDefault(options) == true))).Returns(Task.FromResult(new object())); + + SchemaCompareGetOptionsParams p = new SchemaCompareGetOptionsParams(); + await SchemaCompareService.Instance.HandleSchemaCompareGetDefaultOptionsRequest(p, schemaCompareRequestContext.Object); + } + + private bool OptionsEqualsDefault(SchemaCompareOptionsResult options) + { + DeploymentOptions defaultOpt = new DeploymentOptions(); + DeploymentOptions actualOpt = options.DefaultDeploymentOptions; + + System.Reflection.PropertyInfo[] deploymentOptionsProperties = defaultOpt.GetType().GetProperties(); + foreach(var v in deploymentOptionsProperties) + { + var defaultP = v.GetValue(defaultOpt); + var actualP = v.GetValue(actualOpt); + if (v.Name == "ExcludeObjectTypes") + { + Assert.True((defaultP as ObjectType[]).Length == (actualP as ObjectType[]).Length, $"Number of excluded objects is different; expected: {(defaultP as ObjectType[]).Length} actual: {(actualP as ObjectType[]).Length}"); + } + else + { + Assert.True((defaultP == null && actualP == null) || 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; + } } } diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs index baa0b71b..3dacc9a1 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs @@ -3,9 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using Microsoft.SqlTools.Hosting.Protocol; -using Microsoft.SqlTools.ServiceLayer.DacFx; -using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; -using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; using Microsoft.SqlTools.ServiceLayer.SchemaCompare; using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts; using Microsoft.SqlTools.ServiceLayer.TaskServices; @@ -13,6 +10,7 @@ using Microsoft.SqlTools.ServiceLayer.Test.Common; using Moq; using System; using System.IO; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -73,11 +71,7 @@ CREATE TABLE [dbo].[table3] }; SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, null, null); - schemaCompareOperation.Execute(TaskExecutionMode.Execute); - - Assert.True(schemaCompareOperation.ComparisonResult.IsValid); - Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); - Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); + ValidateSchemaCompareWithExcludeIncludeResults(schemaCompareOperation); // cleanup SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); @@ -120,11 +114,7 @@ CREATE TABLE [dbo].[table3] }; SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, result.ConnectionInfo); - schemaCompareOperation.Execute(TaskExecutionMode.Execute); - - Assert.True(schemaCompareOperation.ComparisonResult.IsValid); - Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); - Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); + ValidateSchemaCompareWithExcludeIncludeResults(schemaCompareOperation); } finally { @@ -163,11 +153,7 @@ CREATE TABLE [dbo].[table3] }; SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, null); - schemaCompareOperation.Execute(TaskExecutionMode.Execute); - - Assert.True(schemaCompareOperation.ComparisonResult.IsValid); - Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); - Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); + ValidateSchemaCompareWithExcludeIncludeResults(schemaCompareOperation); // cleanup SchemaCompareTestUtils.VerifyAndCleanup(targetDacpacFilePath); @@ -208,13 +194,8 @@ CREATE TABLE [dbo].[table3] }; SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, result.ConnectionInfo); - schemaCompareOperation.Execute(TaskExecutionMode.Execute); - - Assert.True(schemaCompareOperation.ComparisonResult.IsValid); - Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); - Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); - - // generate script + + // generate script params var generateScriptParams = new SchemaCompareGenerateScriptParams { TargetDatabaseName = targetDb.DatabaseName, @@ -222,8 +203,7 @@ CREATE TABLE [dbo].[table3] ScriptFilePath = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "Update.publish.sql")) }; - SchemaCompareGenerateScriptOperation generateScriptOperation = new SchemaCompareGenerateScriptOperation(generateScriptParams, schemaCompareOperation.ComparisonResult); - generateScriptOperation.Execute(TaskExecutionMode.Execute); + ValidateSchemaCompareScriptGenerationWithExcludeIncludeResults(schemaCompareOperation, generateScriptParams); // cleanup SchemaCompareTestUtils.VerifyAndCleanup(generateScriptParams.ScriptFilePath); @@ -266,11 +246,6 @@ CREATE TABLE [dbo].[table3] }; SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, result.ConnectionInfo); - schemaCompareOperation.Execute(TaskExecutionMode.Execute); - - Assert.True(schemaCompareOperation.ComparisonResult.IsValid); - Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); - Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); // generate script var generateScriptParams = new SchemaCompareGenerateScriptParams @@ -280,9 +255,8 @@ CREATE TABLE [dbo].[table3] ScriptFilePath = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "Update.publish.sql")) }; - SchemaCompareGenerateScriptOperation generateScriptOperation = new SchemaCompareGenerateScriptOperation(generateScriptParams, schemaCompareOperation.ComparisonResult); - generateScriptOperation.Execute(TaskExecutionMode.Execute); - + ValidateSchemaCompareScriptGenerationWithExcludeIncludeResults(schemaCompareOperation, generateScriptParams); + // cleanup SchemaCompareTestUtils.VerifyAndCleanup(generateScriptParams.ScriptFilePath); SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); @@ -354,7 +328,7 @@ CREATE TABLE [dbo].[table3] Assert.True(schemaCompareOperation.ComparisonResult.IsValid); Assert.True(schemaCompareOperation.ComparisonResult.IsEqual); Assert.Empty(schemaCompareOperation.ComparisonResult.Differences); - + // cleanup SchemaCompareTestUtils.VerifyAndCleanup(sourceDacpacFilePath); } @@ -429,7 +403,108 @@ CREATE TABLE [dbo].[table3] } return schemaCompareRequestContext; } + + private void ValidateSchemaCompareWithExcludeIncludeResults(SchemaCompareOperation schemaCompareOperation) + { + schemaCompareOperation.Execute(TaskExecutionMode.Execute); + Assert.True(schemaCompareOperation.ComparisonResult.IsValid); + Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); + + // create Diff Entry from Difference + + DiffEntry diff = SchemaCompareOperation.CreateDiffEntry(schemaCompareOperation.ComparisonResult.Differences.First(), null); + + int initial = schemaCompareOperation.ComparisonResult.Differences.Count(); + SchemaCompareNodeParams schemaCompareExcludeNodeParams = new SchemaCompareNodeParams() + { + OperationId = schemaCompareOperation.OperationId, + DiffEntry = diff, + IncludeRequest = false, + TaskExecutionMode = TaskExecutionMode.Execute + }; + SchemaCompareIncludeExcludeNodeOperation nodeExcludeOperation = new SchemaCompareIncludeExcludeNodeOperation(schemaCompareExcludeNodeParams, schemaCompareOperation.ComparisonResult); + nodeExcludeOperation.Execute(TaskExecutionMode.Execute); + + int afterExclude = schemaCompareOperation.ComparisonResult.Differences.Count(); + + Assert.True(initial == afterExclude, $"Changes should be same again after excluding/including, before {initial}, now {afterExclude}"); + + SchemaCompareNodeParams schemaCompareincludeNodeParams = new SchemaCompareNodeParams() + { + OperationId = schemaCompareOperation.OperationId, + DiffEntry = diff, + IncludeRequest = true, + TaskExecutionMode = TaskExecutionMode.Execute + }; + + SchemaCompareIncludeExcludeNodeOperation nodeIncludeOperation = new SchemaCompareIncludeExcludeNodeOperation(schemaCompareincludeNodeParams, schemaCompareOperation.ComparisonResult); + nodeIncludeOperation.Execute(TaskExecutionMode.Execute); + int afterInclude = schemaCompareOperation.ComparisonResult.Differences.Count(); + + + Assert.True(initial == afterInclude, $"Changes should be same again after excluding/including, before:{initial}, now {afterInclude}"); + } + + private void ValidateSchemaCompareScriptGenerationWithExcludeIncludeResults(SchemaCompareOperation schemaCompareOperation, SchemaCompareGenerateScriptParams generateScriptParams) + { + schemaCompareOperation.Execute(TaskExecutionMode.Execute); + + Assert.True(schemaCompareOperation.ComparisonResult.IsValid); + Assert.False(schemaCompareOperation.ComparisonResult.IsEqual); + Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences); + + SchemaCompareGenerateScriptOperation generateScriptOperation = new SchemaCompareGenerateScriptOperation(generateScriptParams, schemaCompareOperation.ComparisonResult); + generateScriptOperation.Execute(TaskExecutionMode.Execute); + + string initialScript = File.ReadAllText(generateScriptParams.ScriptFilePath); + + // create Diff Entry from on Difference + DiffEntry diff = SchemaCompareOperation.CreateDiffEntry(schemaCompareOperation.ComparisonResult.Differences.First(), null); + + int initial = schemaCompareOperation.ComparisonResult.Differences.Count(); + SchemaCompareNodeParams schemaCompareExcludeNodeParams = new SchemaCompareNodeParams() + { + OperationId = schemaCompareOperation.OperationId, + DiffEntry = diff, + IncludeRequest = false, + TaskExecutionMode = TaskExecutionMode.Execute + }; + SchemaCompareIncludeExcludeNodeOperation nodeExcludeOperation = new SchemaCompareIncludeExcludeNodeOperation(schemaCompareExcludeNodeParams, schemaCompareOperation.ComparisonResult); + nodeExcludeOperation.Execute(TaskExecutionMode.Execute); + + int afterExclude = schemaCompareOperation.ComparisonResult.Differences.Count(); + + Assert.True(initial == afterExclude, $"Changes should be same again after excluding/including, before {initial}, now {afterExclude}"); + + generateScriptOperation = new SchemaCompareGenerateScriptOperation(generateScriptParams, schemaCompareOperation.ComparisonResult); + generateScriptOperation.Execute(TaskExecutionMode.Execute); + + string afterExcludeScript = File.ReadAllText(generateScriptParams.ScriptFilePath); + Assert.True(initialScript.Length > afterExcludeScript.Length, $"Script should be affected (less statements) exclude operation, before {initialScript}, now {afterExcludeScript}"); + + SchemaCompareNodeParams schemaCompareincludeNodeParams = new SchemaCompareNodeParams() + { + OperationId = schemaCompareOperation.OperationId, + DiffEntry = diff, + IncludeRequest = true, + TaskExecutionMode = TaskExecutionMode.Execute + }; + + SchemaCompareIncludeExcludeNodeOperation nodeIncludeOperation = new SchemaCompareIncludeExcludeNodeOperation(schemaCompareincludeNodeParams, schemaCompareOperation.ComparisonResult); + nodeIncludeOperation.Execute(TaskExecutionMode.Execute); + int afterInclude = schemaCompareOperation.ComparisonResult.Differences.Count(); + + Assert.True(initial == afterInclude, $"Changes should be same again after excluding/including:{initial}, now {afterInclude}"); + + generateScriptOperation = new SchemaCompareGenerateScriptOperation(generateScriptParams, schemaCompareOperation.ComparisonResult); + generateScriptOperation.Execute(TaskExecutionMode.Execute); + + string afterIncludeScript = File.ReadAllText(generateScriptParams.ScriptFilePath); + Assert.True(initialScript.Length == afterIncludeScript.Length, $"Changes should be same as inital since we included what we excluded, before {initialScript}, now {afterIncludeScript}"); + } + /// /// Verify the schema compare request comparing two dacpacs /// diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs index d9c7a0b6..f764304d 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs @@ -10,6 +10,7 @@ using Microsoft.SqlTools.ServiceLayer.Test.Common; using NUnit.Framework; using System; using System.IO; +using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper; namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare { @@ -50,7 +51,21 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare internal static LiveConnectionHelper.TestConnectionResult GetLiveAutoCompleteTestObjects() { - var result = LiveConnectionHelper.InitLiveConnectionInfo(); + // Adding retry for reliability - otherwise it caused test to fail in lab + TestConnectionResult result = null; + int retry = 3; + + while (retry > 0) + { + result = LiveConnectionHelper.InitLiveConnectionInfo(); + if (result != null && result.ConnectionInfo != null) + { + return result; + } + System.Threading.Thread.Sleep(1000); + retry--; + } + return result; } }