From 1342a8a085c0e40b59f4fd04cd8210c6d9a85101 Mon Sep 17 00:00:00 2001
From: Sai Avishkar Sreerama <74571829+ssreerama@users.noreply.github.com>
Date: Fri, 5 Aug 2022 15:28:39 -0500
Subject: [PATCH] Object Types are getting from DacFx ObjectTypes (#1574)
* Include Object Types are getting from DacFx
* All tests are passing now with excludObjectType casting
* Test fix with new ExccvludeObjectType change
* Added test case for the include objects types
* Updated name to objectTypesDictionary and objectType getName from Display attribute
* code Review updates
* Removing Exclude word logic here as the options were updated on DacFx
* updating the null check
* DacFx vBump
* code updates according to review comments
---
Packages.props | 2 +-
.../DacFx/Contracts/DeploymentOptions.cs | 121 +++++++++++++-----
.../SchemaCompare/SchemaCompareUtils.cs | 25 +++-
.../DacFx/DacFxServiceTests.cs | 35 ++++-
.../SchemaCompareServiceOptionsTests.cs | 52 ++++----
.../SchemaCompareServiceTests.cs | 4 +-
.../SchemaCompare/SchemaCompareTestUtils.cs | 2 +-
7 files changed, 170 insertions(+), 71 deletions(-)
diff --git a/Packages.props b/Packages.props
index 35f9defe..2f4c447b 100644
--- a/Packages.props
+++ b/Packages.props
@@ -22,7 +22,7 @@
-
+
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeploymentOptions.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeploymentOptions.cs
index f7d87ff2..1fe08c8d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeploymentOptions.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/DeploymentOptions.cs
@@ -2,14 +2,17 @@
// 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.Utility;
using System;
+using System.Collections.Generic;
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Threading.Tasks;
-using Microsoft.SqlServer.Dac;
using System.Reflection;
-using System.Collections.Generic;
+using System.Threading.Tasks;
namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
{
@@ -50,31 +53,36 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
public class DeploymentOptions
{
#region Properties
- public DeploymentOptionProperty ExcludeObjectTypes { get; set; } = new DeploymentOptionProperty
+
+ ///
+ /// These default exclude options are for schema compare extension, It require some default options to be excluded for SC operations
+ /// Where as the publish operation does not require any defaults, removing all default options for publish is handled in \extensions\sql-database-projects\src\dialogs\publishDatabaseDialog.ts
+ ///
+ public DeploymentOptionProperty ExcludeObjectTypes { get; set; } = new DeploymentOptionProperty
(
- new ObjectType[] {
- ObjectType.ServerTriggers,
- ObjectType.Routes,
- ObjectType.LinkedServerLogins,
- ObjectType.Endpoints,
- ObjectType.ErrorMessages,
- ObjectType.Files,
- ObjectType.Logins,
- ObjectType.LinkedServers,
- ObjectType.Credentials,
- ObjectType.DatabaseScopedCredentials,
- ObjectType.DatabaseEncryptionKeys,
- ObjectType.MasterKeys,
- ObjectType.DatabaseAuditSpecifications,
- ObjectType.Audits,
- ObjectType.ServerAuditSpecifications,
- ObjectType.CryptographicProviders,
- ObjectType.ServerRoles,
- ObjectType.EventSessions,
- ObjectType.DatabaseOptions,
- ObjectType.EventNotifications,
- ObjectType.ServerRoleMembership,
- ObjectType.AssemblyFiles
+ new string[] {
+ Enum.GetName(ObjectType.ServerTriggers),
+ Enum.GetName(ObjectType.Routes),
+ Enum.GetName(ObjectType.LinkedServerLogins),
+ Enum.GetName(ObjectType.Endpoints),
+ Enum.GetName(ObjectType.ErrorMessages),
+ Enum.GetName(ObjectType.Files),
+ Enum.GetName(ObjectType.Logins),
+ Enum.GetName(ObjectType.LinkedServers),
+ Enum.GetName(ObjectType.Credentials),
+ Enum.GetName(ObjectType.DatabaseScopedCredentials),
+ Enum.GetName(ObjectType.DatabaseEncryptionKeys),
+ Enum.GetName(ObjectType.MasterKeys),
+ Enum.GetName(ObjectType.DatabaseAuditSpecifications),
+ Enum.GetName(ObjectType.Audits),
+ Enum.GetName(ObjectType.ServerAuditSpecifications),
+ Enum.GetName(ObjectType.CryptographicProviders),
+ Enum.GetName(ObjectType.ServerRoles),
+ Enum.GetName(ObjectType.EventSessions),
+ Enum.GetName(ObjectType.DatabaseOptions),
+ Enum.GetName(ObjectType.EventNotifications),
+ Enum.GetName(ObjectType.ServerRoleMembership),
+ Enum.GetName(ObjectType.AssemblyFiles)
}
);
@@ -83,6 +91,11 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
///
public Dictionary> BooleanOptionsDictionary { get; set; } = new Dictionary>(StringComparer.InvariantCultureIgnoreCase);
+ ///
+ /// Contains object types enum name and its display name from \Product\Source\DeploymentApi\ObjectTypes.cs Enum
+ /// key: optionName, value:DisplayName
+ ///
+ public Dictionary ObjectTypesDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
#endregion
public DeploymentOptions()
@@ -165,6 +178,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
this.BooleanOptionsDictionary[prop.Name] = (DeploymentOptionProperty)setProp;
}
}
+
+ // Preparing object types dictionary
+ InitializeObjectTypesDictionary();
}
public void SetOptions(DacDeployOptions options)
@@ -193,6 +209,30 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
}
}
+ ///
+ /// Preparing all object types which are considered as boolean options
+ ///
+ public void InitializeObjectTypesDictionary()
+ {
+ Type objectTypeEnum = typeof(ObjectType);
+ foreach (string name in Enum.GetNames(objectTypeEnum))
+ {
+ MemberInfo[] member = objectTypeEnum.GetMember(name);
+ MemberInfo info = member?.FirstOrDefault();
+ string displayName = info?.GetCustomAttribute().GetName();
+ if (string.IsNullOrEmpty(displayName))
+ {
+ // not expecting display name for any options as empty string
+ Logger.Write(TraceEventType.Error, string.Format($"Display name is empty for the Object type enum {0}", name));
+ }
+ else
+ {
+ // Add the property to the Dictionary
+ ObjectTypesDictionary[name] = displayName;
+ }
+ }
+ }
+
///
/// Prepares and returns the value and description of a property
///
@@ -206,10 +246,27 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
Type type = val != null ? typeof(DeploymentOptionProperty<>).MakeGenericType(val.GetType())
: typeof(DeploymentOptionProperty<>).MakeGenericType(prop.PropertyType);
- object setProp = Activator.CreateInstance(type, val,
- (descriptionAttribute != null ? descriptionAttribute.Description : ""),
- (displayNameAttribute != null ? displayNameAttribute.DisplayName : ""));
- return setProp;
+ // DeploymentOptions ExcludeObjectTypes are String[] type and need special casting here
+ if (prop.Name == nameof(this.ExcludeObjectTypes))
+ {
+ type = typeof(DeploymentOptionProperty);
+ val = val != null ? ConvertObjectTypeToStringArray((ObjectType[])val): new string[] { };
+ }
+
+ return Activator.CreateInstance(type, val,
+ (descriptionAttribute != null ? descriptionAttribute.Description : string.Empty),
+ (displayNameAttribute != null ? displayNameAttribute.DisplayName : string.Empty));
+ }
+
+ ///
+ /// Converting ObjectType to String[] as the deployemnt options excludeObjectTypes is string[] but the DacFx DacDeployOptions excludeObjectTypes is of ObjectType[]
+ /// Loading options from profile and schema compare .scmp file should need this conversion
+ ///
+ ///
+ /// string[]
+ public string[] ConvertObjectTypeToStringArray(ObjectType[] excludeObjectTypes)
+ {
+ return excludeObjectTypes.Select(t => t.ToString()).ToArray();
}
public static DeploymentOptions GetDefaultSchemaCompareOptions()
@@ -221,7 +278,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
{
DeploymentOptions result = new DeploymentOptions();
- result.ExcludeObjectTypes.Value = result.ExcludeObjectTypes.Value.Where(x => x != ObjectType.DatabaseScopedCredentials).ToArray(); // re-include database-scoped credentials
+ result.ExcludeObjectTypes.Value = result.ExcludeObjectTypes.Value.Where(x => x != Enum.GetName(ObjectType.DatabaseScopedCredentials)).ToArray(); // re-include database-scoped credentials
return result;
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs
index 5e4d610a..ad3ef6d1 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs
@@ -43,15 +43,30 @@ namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
foreach (PropertyInfo deployOptionsProp in deploymentOptionsProperties)
{
var prop = propType.GetProperty(deployOptionsProp.Name);
- if (prop != null)
+ // Set the excludeObjectTypes values to the DacDeployOptions
+ if (prop != null && deployOptionsProp.Name == nameof(deploymentOptions.ExcludeObjectTypes))
{
+ List finalExcludeObjects = new List { };
var val = deployOptionsProp.GetValue(deploymentOptions);
- var selectedVal = val.GetType().GetProperty("Value").GetValue(val);
+ string[] excludeObjectTypeOptionsArray = (string[])val.GetType().GetProperty("Value").GetValue(val);
- // Set the excludeObjectTypes values to the DacDeployOptions
- if (selectedVal != null && deployOptionsProp.Name == nameof(deploymentOptions.ExcludeObjectTypes))
+ if (excludeObjectTypeOptionsArray != null)
{
- prop.SetValue(dacOptions, selectedVal);
+ foreach(string objectTypeValue in excludeObjectTypeOptionsArray)
+ {
+ ObjectType objectTypeName = new ObjectType();
+
+ if (objectTypeValue != null && Enum.TryParse(objectTypeValue, ignoreCase: true, out objectTypeName))
+ {
+ finalExcludeObjects.Add(objectTypeName);
+ }
+ else
+ {
+ Logger.Write(TraceEventType.Error, string.Format($"{objectTypeValue} is not part of ObjectTypes enum"));
+ }
+ }
+ // set final values to excludeObjectType property
+ prop.SetValue(dacOptions, finalExcludeObjects.ToArray());
}
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxServiceTests.cs
index 70addd64..c460e088 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxServiceTests.cs
@@ -589,7 +589,7 @@ FROM MissingEdgeHubInputStream'";
UpgradeExisting = true,
DeploymentOptions = new DeploymentOptions()
{
- ExcludeObjectTypes = new DeploymentOptionProperty(new[] { ObjectType.Views })
+ ExcludeObjectTypes = new DeploymentOptionProperty(new[] { Enum.GetName(ObjectType.Views) })
}
};
@@ -669,7 +669,7 @@ FROM MissingEdgeHubInputStream'";
DatabaseName = targetDb.DatabaseName,
DeploymentOptions = new DeploymentOptions()
{
- ExcludeObjectTypes = new DeploymentOptionProperty(new[] { ObjectType.Views })
+ ExcludeObjectTypes = new DeploymentOptionProperty(new[] { Enum.GetName(ObjectType.Views) })
}
};
@@ -689,7 +689,7 @@ FROM MissingEdgeHubInputStream'";
DatabaseName = targetDb.DatabaseName,
DeploymentOptions = new DeploymentOptions()
{
- ExcludeObjectTypes = new DeploymentOptionProperty(new[] { ObjectType.Views })
+ ExcludeObjectTypes = new DeploymentOptionProperty(new[] { Enum.GetName(ObjectType.Views) })
}
};
@@ -839,6 +839,33 @@ Streaming query statement contains a reference to missing output stream 'Missing
dacfxRequestContext.VerifyAll();
}
+ ///
+ /// Verify Object Types Dictionary items with ObjectType Enum members
+ ///
+ ///
+ [Test]
+ public void ValidateObjectTypesOptionswithEnum()
+ {
+ DeploymentOptions options = new DeploymentOptions();
+
+ // Verify the object types dictionary should exists
+ Assert.That(options.ObjectTypesDictionary, Is.Not.Null, "Object types dictionary is empty");
+
+ // Verify that the objects dictionary has all the item from Enum
+ Assert.That(options.ObjectTypesDictionary.Count, Is.EqualTo(Enum.GetNames(typeof(ObjectType)).Length), @"ObjectTypesDictionary is missing these objectTypes: {0}",
+ string.Join(", ", Enum.GetNames(typeof(ObjectType)).Except(options.ObjectTypesDictionary.Keys)));
+
+ // Verify the options in the objects dictionary exists in the ObjectType Enum
+ foreach (var objTypeRow in options.ObjectTypesDictionary)
+ {
+ // Verify the option exists in ObjectType Enum
+ Assert.That(Enum.IsDefined(typeof(ObjectType), objTypeRow.Key), Is.True, $"{objTypeRow.Key} is not an enum member");
+
+ // Verify the options display name exists
+ Assert.That(objTypeRow.Value, Is.Not.Empty, $"Display name for the option {objTypeRow.Key} is empty");
+ }
+ }
+
private bool ValidateStreamingJobErrors(ValidateStreamingJobResult expected, ValidateStreamingJobResult actual)
{
return expected.Success == actual.Success
@@ -860,7 +887,7 @@ Streaming query statement contains a reference to missing output stream 'Missing
if (v.Name == nameof(DeploymentOptions.ExcludeObjectTypes))
{
- Assert.True((defaultP as ObjectType[])?.Length == (actualP as ObjectType[])?.Length, "Number of excluded objects is different not equal");
+ Assert.True((defaultP as string[])?.Length == (actualP as string[])?.Length, "Number of excluded objects is different not equal");
}
else
{
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs
index c6d980bf..1ac95a93 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceOptionsTests.cs
@@ -79,33 +79,33 @@ END
private DeploymentOptions GetExcludeTableValuedFunctionOptions()
{
var options = new DeploymentOptions();
- options.ExcludeObjectTypes = new DeploymentOptionProperty
+ options.ExcludeObjectTypes = new DeploymentOptionProperty
(
- new ObjectType[]{
- ObjectType.ServerTriggers,
- ObjectType.Routes,
- ObjectType.LinkedServerLogins,
- ObjectType.Endpoints,
- ObjectType.ErrorMessages,
- ObjectType.Filegroups,
- ObjectType.Files,
- ObjectType.Logins,
- ObjectType.LinkedServers,
- ObjectType.Credentials,
- ObjectType.DatabaseScopedCredentials,
- ObjectType.DatabaseEncryptionKeys,
- ObjectType.MasterKeys,
- ObjectType.DatabaseAuditSpecifications,
- ObjectType.Audits,
- ObjectType.ServerAuditSpecifications,
- ObjectType.CryptographicProviders,
- ObjectType.ServerRoles,
- ObjectType.EventSessions,
- ObjectType.DatabaseOptions,
- ObjectType.EventNotifications,
- ObjectType.ServerRoleMembership,
- ObjectType.AssemblyFiles,
- ObjectType.TableValuedFunctions, //added Functions to excluded types
+ new string[]{
+ Enum.GetName(ObjectType.ServerTriggers),
+ Enum.GetName(ObjectType.Routes),
+ Enum.GetName(ObjectType.LinkedServerLogins),
+ Enum.GetName(ObjectType.Endpoints),
+ Enum.GetName(ObjectType.ErrorMessages),
+ Enum.GetName(ObjectType.Filegroups),
+ Enum.GetName(ObjectType.Files),
+ Enum.GetName(ObjectType.Logins),
+ Enum.GetName(ObjectType.LinkedServers),
+ Enum.GetName(ObjectType.Credentials),
+ Enum.GetName(ObjectType.DatabaseScopedCredentials),
+ Enum.GetName(ObjectType.DatabaseEncryptionKeys),
+ Enum.GetName(ObjectType.MasterKeys),
+ Enum.GetName(ObjectType.DatabaseAuditSpecifications),
+ Enum.GetName(ObjectType.Audits),
+ Enum.GetName(ObjectType.ServerAuditSpecifications),
+ Enum.GetName(ObjectType.CryptographicProviders),
+ Enum.GetName(ObjectType.ServerRoles),
+ Enum.GetName(ObjectType.EventSessions),
+ Enum.GetName(ObjectType.DatabaseOptions),
+ Enum.GetName(ObjectType.EventNotifications),
+ Enum.GetName(ObjectType.ServerRoleMembership),
+ Enum.GetName(ObjectType.AssemblyFiles),
+ Enum.GetName(ObjectType.TableValuedFunctions), //added Functions to excluded types
}
);
return options;
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs
index e28967e5..3b02f2df 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs
@@ -1356,8 +1356,8 @@ WITH VALUES
DeploymentOptions options = new DeploymentOptions();
// ensure that files are excluded seperate from filegroups
- Assert.True(options.ExcludeObjectTypes.Value.Contains(SqlServer.Dac.ObjectType.Files));
- Assert.False(options.ExcludeObjectTypes.Value.Contains(SqlServer.Dac.ObjectType.Filegroups));
+ Assert.True(options.ExcludeObjectTypes.Value.Contains(Enum.GetName(SqlServer.Dac.ObjectType.Files)));
+ Assert.False(options.ExcludeObjectTypes.Value.Contains(Enum.GetName(SqlServer.Dac.ObjectType.Filegroups)));
var schemaCompareParams = new SchemaCompareParams
{
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs
index 51f2a3a2..97b52935 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareTestUtils.cs
@@ -174,7 +174,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare
if (v.Name == nameof(DeploymentOptions.ExcludeObjectTypes))
{
- Assert.That((defaultPValue as ObjectType[]).Length, Is.EqualTo((actualPValue as ObjectType[]).Length), $"Number of excluded objects is different; expected: {(defaultPValue as ObjectType[]).Length} actual: {(actualPValue as ObjectType[]).Length}");
+ Assert.That((defaultPValue as string[]).Length, Is.EqualTo((actualPValue as string[]).Length), $"Number of excluded objects is different.");
}
else
{