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
This commit is contained in:
Sai Avishkar Sreerama
2022-08-05 15:28:39 -05:00
committed by GitHub
parent d78ff94b31
commit 1342a8a085
7 changed files with 170 additions and 71 deletions

View File

@@ -22,7 +22,7 @@
<PackageReference Update="Microsoft.Data.SqlClient" Version="3.1.0" />
<PackageReference Update="Microsoft.SqlServer.SqlManagementObjects" Version="161.47021.0" />
<PackageReference Update="Microsoft.SqlServer.Management.SmoMetadataProvider" Version="161.47008.0" />
<PackageReference Update="Microsoft.SqlServer.DACFx" Version="160.6232.0-preview" GeneratePathProperty="true" />
<PackageReference Update="Microsoft.SqlServer.DACFx" Version="160.6253.0-preview" GeneratePathProperty="true" />
<PackageReference Update="Microsoft.Azure.Kusto.Data" Version="9.0.4" />
<PackageReference Update="Microsoft.Azure.Kusto.Language" Version="9.0.4" />
<PackageReference Update="Microsoft.SqlServer.Assessment" Version="[1.1.9]" />

View File

@@ -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<ObjectType[]> ExcludeObjectTypes { get; set; } = new DeploymentOptionProperty<ObjectType[]>
/// <summary>
/// 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 <azuredatastudio>\extensions\sql-database-projects\src\dialogs\publishDatabaseDialog.ts
/// </summary>
public DeploymentOptionProperty<string[]> ExcludeObjectTypes { get; set; } = new DeploymentOptionProperty<string[]>
(
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
/// </summary>
public Dictionary<string, DeploymentOptionProperty<bool>> BooleanOptionsDictionary { get; set; } = new Dictionary<string, DeploymentOptionProperty<bool>>(StringComparer.InvariantCultureIgnoreCase);
/// <summary>
/// Contains object types enum name and its display name from <DacFx>\Product\Source\DeploymentApi\ObjectTypes.cs Enum
/// key: optionName, value:DisplayName
/// </summary>
public Dictionary<string, string> ObjectTypesDictionary = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
#endregion
public DeploymentOptions()
@@ -165,6 +178,9 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
this.BooleanOptionsDictionary[prop.Name] = (DeploymentOptionProperty<bool>)setProp;
}
}
// Preparing object types dictionary
InitializeObjectTypesDictionary();
}
public void SetOptions(DacDeployOptions options)
@@ -193,6 +209,30 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
}
}
/// <summary>
/// Preparing all object types which are considered as boolean options
/// </summary>
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<DisplayAttribute>().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;
}
}
}
/// <summary>
/// Prepares and returns the value and description of a property
/// </summary>
@@ -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<string[]>);
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));
}
/// <summary>
/// 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
/// </summary>
/// <param name="excludeObjectTypes"></param>
/// <returns>string[]</returns>
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;
}

View File

@@ -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<ObjectType> finalExcludeObjects = new List<ObjectType> { };
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());
}
}

View File

@@ -589,7 +589,7 @@ FROM MissingEdgeHubInputStream'";
UpgradeExisting = true,
DeploymentOptions = new DeploymentOptions()
{
ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>(new[] { ObjectType.Views })
ExcludeObjectTypes = new DeploymentOptionProperty<string[]>(new[] { Enum.GetName(ObjectType.Views) })
}
};
@@ -669,7 +669,7 @@ FROM MissingEdgeHubInputStream'";
DatabaseName = targetDb.DatabaseName,
DeploymentOptions = new DeploymentOptions()
{
ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>(new[] { ObjectType.Views })
ExcludeObjectTypes = new DeploymentOptionProperty<string[]>(new[] { Enum.GetName(ObjectType.Views) })
}
};
@@ -689,7 +689,7 @@ FROM MissingEdgeHubInputStream'";
DatabaseName = targetDb.DatabaseName,
DeploymentOptions = new DeploymentOptions()
{
ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>(new[] { ObjectType.Views })
ExcludeObjectTypes = new DeploymentOptionProperty<string[]>(new[] { Enum.GetName(ObjectType.Views) })
}
};
@@ -839,6 +839,33 @@ Streaming query statement contains a reference to missing output stream 'Missing
dacfxRequestContext.VerifyAll();
}
/// <summary>
/// Verify Object Types Dictionary items with ObjectType Enum members
/// </summary>
/// <returns></returns>
[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
{

View File

@@ -79,33 +79,33 @@ END
private DeploymentOptions GetExcludeTableValuedFunctionOptions()
{
var options = new DeploymentOptions();
options.ExcludeObjectTypes = new DeploymentOptionProperty<ObjectType[]>
options.ExcludeObjectTypes = new DeploymentOptionProperty<string[]>
(
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;

View File

@@ -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
{

View File

@@ -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
{