Skip to content

Commit

Permalink
OptionsMapTable preparing from DacFx description,value,displayname at… (
Browse files Browse the repository at this point in the history
#1550)

* OptionsMapTable preparing from DacFx description,value,displayname attributes

* Review Comments addressed for options map table changes

* OptionsMapTable final changes

* Code review comments updated code changes

* Test fix: Adding missing change while splitting the PR

* DacFx vBump

* Reverted to displayName and code updates

* final:prop name changed, references updated, tests fixed, comments addressed

* Code review comments updated for name,exception etc

* updates method names

* property name changes to BooleanOptionsDictionary and comment updates

* Removed the unused properties, null cases handeled, hardcoded values are replaced
  • Loading branch information
ssreerama authored Jul 7, 2022
1 parent 6558f20 commit 5663ddb
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 440 deletions.
427 changes: 64 additions & 363 deletions src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeploymentOptions.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
Expand All @@ -14,6 +15,7 @@
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;

namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
{
Expand All @@ -23,31 +25,60 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
/// </summary>
internal static class SchemaCompareUtils
{
/// <summary>
/// Converts DeploymentOptions used in STS and ADS to DacDeployOptions which can be passed to the DacFx apis
/// </summary>
/// <param name="deploymentOptions"></param>
/// <returns>DacDeployOptions</returns
internal static DacDeployOptions CreateSchemaCompareOptions(DeploymentOptions deploymentOptions)
{
PropertyInfo[] deploymentOptionsProperties = deploymentOptions.GetType().GetProperties();

DacDeployOptions dacOptions = new DacDeployOptions();
foreach (var deployOptionsProp in deploymentOptionsProperties)
try
{
var prop = dacOptions.GetType().GetProperty(deployOptionsProp.Name);
if (prop != null)
PropertyInfo[] deploymentOptionsProperties = deploymentOptions.GetType().GetProperties();

DacDeployOptions dacOptions = new DacDeployOptions();
Type propType = dacOptions.GetType();
Dictionary<string, DeploymentOptionProperty<bool>> booleanOptionsDictionary = new Dictionary<string, DeploymentOptionProperty<bool>>();

foreach (PropertyInfo deployOptionsProp in deploymentOptionsProperties)
{
var val = deployOptionsProp.GetValue(deploymentOptions);
var selectedVal = val.GetType().GetProperty("Value").GetValue(val);
var prop = propType.GetProperty(deployOptionsProp.Name);
if (prop != null)
{
var val = deployOptionsProp.GetValue(deploymentOptions);
var selectedVal = val.GetType().GetProperty("Value").GetValue(val);

// Set the excludeObjectTypes values to the DacDeployOptions
if (selectedVal != null && deployOptionsProp.Name == nameof(deploymentOptions.ExcludeObjectTypes))
{
prop.SetValue(dacOptions, selectedVal);
}
}

// JSON.NET by default reads Number type as Int64, deserializing an object type to dacOptions of Int32 type required to convert into Int32 from Int64.
// If not converted setting value(Int64) to dacOption(Int32) will throw {"Object of type 'System.Int64' cannot be converted to type 'System.Int32'."}.
// As these integer type options are non-editable and are not availbale in ADS to update, integer overflow exception will not be happening here.
if (selectedVal != null && selectedVal.GetType() == typeof(System.Int64))
// BooleanOptionsDictionary has all the deployment options and is being processed separately in the second iteration by collecting here
if (deployOptionsProp.Name == nameof(deploymentOptions.BooleanOptionsDictionary))
{
selectedVal = Convert.ToInt32(selectedVal);
booleanOptionsDictionary = deploymentOptions.BooleanOptionsDictionary as Dictionary<string, DeploymentOptionProperty<bool>>;
}
}

prop.SetValue(dacOptions, selectedVal);
// Iterating through the updated boolean options coming from the booleanOptionsDictionary and assigning them to DacDeployOptions
foreach (KeyValuePair<string, DeploymentOptionProperty<bool>> deployOptionsProp in booleanOptionsDictionary)
{
var prop = propType.GetProperty(deployOptionsProp.Key);
if (prop != null)
{
var selectedVal = deployOptionsProp.Value.Value;
prop.SetValue(dacOptions, selectedVal);
}
}
return dacOptions;
}
catch (Exception e)
{
Logger.Write(TraceEventType.Error, string.Format("Schema compare create options model failed: {0}", e.Message));
throw;
}
return dacOptions;
}

internal static DiffEntry CreateDiffEntry(SchemaDifference difference, DiffEntry parent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using NUnit.Framework;
using Moq;
using System.Reflection;

namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
{
Expand Down Expand Up @@ -586,11 +587,13 @@ public async Task DeployWithOptions()
UpgradeExisting = true,
DeploymentOptions = new DeploymentOptions()
{
DropObjectsNotInSource = new DeploymentOptionProperty<bool>(false),
ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>(new[] { ObjectType.Views })
}
};

// Updating the BooleanOptionsDictionary since it has all the boolean type options
deployParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.DropObjectsNotInSource)].Value = false;

// expect table3 to not have been dropped and view1 to not have been created
await VerifyDeployWithOptions(deployParams, targetDb, service, result.ConnectionInfo, expectedTableResult: "table3", expectedViewResult: null);

Expand Down Expand Up @@ -664,11 +667,13 @@ public async Task GenerateDeployScriptWithOptions()
DatabaseName = targetDb.DatabaseName,
DeploymentOptions = new DeploymentOptions()
{
DropObjectsNotInSource = new DeploymentOptionProperty<bool>(false),
ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>(new[] { ObjectType.Views })
}
};

// Updating the BooleanOptionsDictionary since it has all the boolean type properties
generateScriptFalseOptionParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.DropObjectsNotInSource)].Value = false;

var generateScriptFalseOptionOperation = new GenerateDeployScriptOperation(generateScriptFalseOptionParams, result.ConnectionInfo);
service.PerformOperation(generateScriptFalseOptionOperation, TaskExecutionMode.Execute);

Expand All @@ -682,7 +687,6 @@ public async Task GenerateDeployScriptWithOptions()
DatabaseName = targetDb.DatabaseName,
DeploymentOptions = new DeploymentOptions()
{
DropObjectsNotInSource = new DeploymentOptionProperty<bool>(true),
ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>( new[] { ObjectType.Views })
}
};
Expand Down Expand Up @@ -727,10 +731,10 @@ public async Task GetOptionsFromProfile()
DeploymentOptions expectedResults = DeploymentOptions.GetDefaultPublishOptions();

expectedResults.ExcludeObjectTypes = null;
expectedResults.IncludeCompositeObjects = new DeploymentOptionProperty<bool>(true);
expectedResults.BlockOnPossibleDataLoss = new DeploymentOptionProperty<bool>(true);
expectedResults.AllowIncompatiblePlatform = new DeploymentOptionProperty<bool>(true);
expectedResults.DisableIndexesForDataPhase = new DeploymentOptionProperty<bool>(false);
expectedResults.BooleanOptionsDictionary[nameof(DacDeployOptions.IncludeCompositeObjects)].Value = true;
expectedResults.BooleanOptionsDictionary[nameof(DacDeployOptions.BlockOnPossibleDataLoss)].Value = true;
expectedResults.BooleanOptionsDictionary[nameof(DacDeployOptions.AllowIncompatiblePlatform)].Value = true;
expectedResults.BooleanOptionsDictionary[nameof(DacDeployOptions.DisableIndexesForDataPhase)].Value = false;

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()));
Expand All @@ -755,7 +759,7 @@ public async Task GetOptionsFromProfileWithoutOptions()
{
DeploymentOptions expectedResults = DeploymentOptions.GetDefaultPublishOptions();
expectedResults.ExcludeObjectTypes = null;
expectedResults.DisableIndexesForDataPhase = new DeploymentOptionProperty<bool>(false);
expectedResults.BooleanOptionsDictionary["DisableIndexesForDataPhase"].Value = false;

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()));
Expand Down Expand Up @@ -842,26 +846,37 @@ private bool ValidateStreamingJobErrors(ValidateStreamingJobResult expected, Val
private bool ValidateOptions(DeploymentOptions expected, DeploymentOptions actual)
{
System.Reflection.PropertyInfo[] deploymentOptionsProperties = expected.GetType().GetProperties();
foreach (var v in deploymentOptionsProperties)
var booleanOptionsDictionary = new Dictionary<string, DeploymentOptionProperty<bool>>();
foreach (PropertyInfo v in deploymentOptionsProperties)
{
var defaultP = v.GetValue(expected);
var defaultPValue = defaultP != null ? defaultP.GetType().GetProperty("Value").GetValue(defaultP): defaultP;
var actualP = v.GetValue(actual);
var actualPValue = actualP.GetType().GetProperty("Value").GetValue(actualP);

if (v.Name == "ExcludeObjectTypes")
if (v.Name != nameof(DeploymentOptions.BooleanOptionsDictionary))
{
Assert.True((defaultP as ObjectType[])?.Length == (actualP as ObjectType[])?.Length, "Number of excluded objects is different not equal");
var defaultP = v.GetValue(expected);
var defaultPValue = defaultP != null ? defaultP.GetType().GetProperty("Value").GetValue(defaultP) : defaultP;
var actualP = v.GetValue(actual);
var actualPValue = actualP.GetType().GetProperty("Value").GetValue(actualP);

if (v.Name == nameof(DeploymentOptions.ExcludeObjectTypes))
{
Assert.True((defaultP as ObjectType[])?.Length == (actualP as ObjectType[])?.Length, "Number of excluded objects is different not equal");
}
else
{
//Verifying expected and actual deployment options properties are equal
Assert.True((defaultPValue == null && String.IsNullOrEmpty(actualPValue as string))
|| (defaultPValue).Equals(actualPValue)
, $"Actual Property from Service is not equal to default property for {v.Name}, Actual value: {actualPValue} and Default value: {defaultPValue}");
}
}
else
{
//Verifying expected and actual deployment options properties are equal
Assert.True((defaultPValue == null && String.IsNullOrEmpty(actualPValue as string))
|| (defaultPValue).Equals(actualPValue)
, $"Actual Property from Service is not equal to default property for {v.Name}, Actual value: {actualPValue} and Default value: {defaultPValue}");
booleanOptionsDictionary = v.GetValue(expected) as Dictionary<string, DeploymentOptionProperty<bool>>;
}
}

// Verify expected and actual DeploymentOptions BooleanOptionsDictionary
VerifyExpectedAndActualBooleanOptionsDictionary(booleanOptionsDictionary, actual.BooleanOptionsDictionary);

return true;
}

Expand Down Expand Up @@ -895,5 +910,21 @@ private void VerifyAndCleanup(string filePath)
File.Delete(filePath);
}
}

/// <summary>
/// Verify expected and actual DeploymentOptions BooleanOptionsDictionary values
/// </summary>
/// <param name="expectedBooleanOptionsDictionary"></param>
/// <param name="actualBooleanOptionsDictionary"></param>
public void VerifyExpectedAndActualBooleanOptionsDictionary(Dictionary<string, DeploymentOptionProperty<bool>> expectedBooleanOptionsDictionary, Dictionary<string, DeploymentOptionProperty<bool>> actualBooleanOptionsDictionary)
{
foreach (KeyValuePair<string, DeploymentOptionProperty<bool>> optionRow in expectedBooleanOptionsDictionary)
{
var expectedValue = optionRow.Value.Value;
var actualValue = actualBooleanOptionsDictionary[optionRow.Key].Value;

Assert.That(actualValue, Is.EqualTo(expectedValue), $"Actual Property from Service is not equal to default property for {optionRow.Key}");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ INSERT @returntable
private DeploymentOptions GetIgnoreColumnOptions()
{
var options = new DeploymentOptions();
options.IgnoreColumnOrder = new DeploymentOptionProperty<bool>(true);
options.BooleanOptionsDictionary[nameof(DacDeployOptions.IgnoreColumnOrder)].Value = true;
return options;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using NUnit.Framework;
using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
using System.Collections.Generic;
using Microsoft.SqlServer.Dac;

namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare
{
Expand Down Expand Up @@ -1823,22 +1824,21 @@ private void CreateAndValidateScmpFile(SchemaCompareEndpointInfo sourceInfo, Sch
{
SourceEndpointInfo = sourceInfo,
TargetEndpointInfo = targetInfo,
DeploymentOptions = new DeploymentOptions()
{
// change some random ones explicitly
AllowDropBlockingAssemblies = new DeploymentOptionProperty<bool>(true),
DropConstraintsNotInSource = new DeploymentOptionProperty<bool>(true),
IgnoreAnsiNulls = new DeploymentOptionProperty<bool>(true),
NoAlterStatementsToChangeClrTypes = new DeploymentOptionProperty<bool>(false),
PopulateFilesOnFileGroups = new DeploymentOptionProperty<bool>(false),
VerifyDeployment = new DeploymentOptionProperty<bool>(false),
DisableIndexesForDataPhase = new DeploymentOptionProperty<bool>(false)
},
DeploymentOptions = new DeploymentOptions(),
ScmpFilePath = filePath,
ExcludedSourceObjects = schemaCompareObjectIds,
ExcludedTargetObjects = null,
};

// change some random ones explicitly
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.AllowDropBlockingAssemblies)].Value = true;
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.DropConstraintsNotInSource)].Value = true;
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.IgnoreAnsiNulls)].Value = true;
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.NoAlterStatementsToChangeClrTypes)].Value = false;
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.PopulateFilesOnFileGroups)].Value = false;
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.VerifyDeployment)].Value = false;
schemaCompareParams.DeploymentOptions.BooleanOptionsDictionary[nameof(DacDeployOptions.DisableIndexesForDataPhase)].Value = false;

SchemaCompareSaveScmpOperation schemaCompareOperation = new SchemaCompareSaveScmpOperation(schemaCompareParams, result.ConnectionInfo, result.ConnectionInfo);
schemaCompareOperation.Execute(TaskExecutionMode.Execute);

Expand Down
Loading

0 comments on commit 5663ddb

Please sign in to comment.