mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-19 17:23:55 -05:00
288 lines
13 KiB
C#
288 lines
13 KiB
C#
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
//
|
|
|
|
#nullable disable
|
|
using Microsoft.SqlServer.Dac;
|
|
using Microsoft.SqlTools.Utility;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
|
|
{
|
|
/// <summary>
|
|
/// DeploymentOptionProperty class to define deployment options default value, description, and displayNames
|
|
/// </summary>
|
|
/// <typeparam name="T"></typeparam>
|
|
public class DeploymentOptionProperty<T>
|
|
{
|
|
public DeploymentOptionProperty(T value, string description = "", string displayName = "")
|
|
{
|
|
this.Value = value;
|
|
this.Description = description;
|
|
this.DisplayName = displayName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Default and selected value of the deployment option
|
|
/// </summary>
|
|
public T Value { get; set; }
|
|
|
|
/// <summary>
|
|
/// Description of the deployment option
|
|
/// </summary>
|
|
public string Description { get; set; }
|
|
|
|
/// <summary>
|
|
/// Display name of the deployment option
|
|
/// </summary>
|
|
public string DisplayName { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to define deployment options.
|
|
/// These property names will be given to the DeploymentOptions interface defined in ADS 'azuredatastudio\extensions\mssql\src\mssql.d.ts' and 'azuredatastudio\extensions\types\vscode-mssql.d.ts'
|
|
/// BooleanOptionsDictionary will automatically gets the newly added boolean properties from DacFx, All other types should be added here and ADS
|
|
/// </summary>
|
|
public class DeploymentOptions
|
|
{
|
|
#region Properties
|
|
|
|
/// <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 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)
|
|
}
|
|
);
|
|
|
|
/// <summary>
|
|
/// BooleanOptionsDictionary contains all boolean type deployment options
|
|
/// </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()
|
|
{
|
|
DacDeployOptions options = new DacDeployOptions();
|
|
|
|
// Adding these defaults to ensure behavior similarity with other tools. Dacfx and SSMS import/export wizards use these defaults.
|
|
// Tracking the full fix : https://github.com/microsoft/azuredatastudio/issues/5599
|
|
options.AllowDropBlockingAssemblies = true;
|
|
options.AllowIncompatiblePlatform = true;
|
|
options.DropObjectsNotInSource = true;
|
|
options.DropPermissionsNotInSource = true;
|
|
options.DropRoleMembersNotInSource = true;
|
|
options.IgnoreKeywordCasing = false;
|
|
options.IgnoreSemicolonBetweenStatements = false;
|
|
|
|
// Initializing the default boolean type options to the BooleanOptionsDictionary
|
|
// Not considering DacFx default ExcludeObjectTypes, as it has some STS defaults which needs to be considered here, DacFx defaults are only considered for InitializeFromProfile(), where options are loading from profile
|
|
InitializeBooleanTypeOptions(options);
|
|
}
|
|
|
|
public DeploymentOptions(DacDeployOptions options)
|
|
{
|
|
SetOptions(options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// initialize deployment options from the options in a publish profile.xml
|
|
/// </summary>
|
|
/// <param name="options">options created from the profile</param>
|
|
/// <param name="profilePath"></param>
|
|
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("<AllowDropBlockingAssemblies>"))
|
|
{
|
|
options.AllowDropBlockingAssemblies = true;
|
|
}
|
|
if (!contents.Contains("<AllowIncompatiblePlatform>"))
|
|
{
|
|
options.AllowIncompatiblePlatform = true;
|
|
}
|
|
if (!contents.Contains("<DropObjectsNotInSource>"))
|
|
{
|
|
options.DropObjectsNotInSource = true;
|
|
}
|
|
if (!contents.Contains("<DropPermissionsNotInSource>"))
|
|
{
|
|
options.DropPermissionsNotInSource = true;
|
|
}
|
|
if (!contents.Contains("<DropRoleMembersNotInSource>"))
|
|
{
|
|
options.DropRoleMembersNotInSource = true;
|
|
}
|
|
if (!contents.Contains("<IgnoreKeywordCasing>"))
|
|
{
|
|
options.IgnoreKeywordCasing = false;
|
|
}
|
|
if (!contents.Contains("<IgnoreSemicolonBetweenStatements>"))
|
|
{
|
|
options.IgnoreSemicolonBetweenStatements = false;
|
|
}
|
|
|
|
SetOptions(options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Populates BooleanOptionsDictionary with the boolean type properties
|
|
/// </summary>
|
|
/// <param name="options"></param>
|
|
public void InitializeBooleanTypeOptions(DacDeployOptions options)
|
|
{
|
|
PropertyInfo[] dacDeploymentOptionsProperties = options.GetType().GetProperties();
|
|
foreach (PropertyInfo prop in dacDeploymentOptionsProperties)
|
|
{
|
|
if (prop != null && prop.PropertyType == typeof(System.Boolean))
|
|
{
|
|
object setProp = GetDeploymentOptionProp(prop, options);
|
|
this.BooleanOptionsDictionary[prop.Name] = (DeploymentOptionProperty<bool>)setProp;
|
|
}
|
|
}
|
|
|
|
// Preparing object types dictionary
|
|
InitializeObjectTypesDictionary();
|
|
}
|
|
|
|
public void SetOptions(DacDeployOptions options)
|
|
{
|
|
// Set the default options properties
|
|
InitializeBooleanTypeOptions(options);
|
|
InitializeNonBooleanTypeOptions(options);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Preparing all non boolean properties (except BooleanOptionsDictionary)
|
|
/// </summary>
|
|
/// <param name="options"></param>
|
|
public void InitializeNonBooleanTypeOptions(DacDeployOptions options)
|
|
{
|
|
// preparing remaining properties
|
|
PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties();
|
|
foreach (PropertyInfo deployOptionsProp in deploymentOptionsProperties)
|
|
{
|
|
if (deployOptionsProp.Name != nameof(DeploymentOptions.BooleanOptionsDictionary))
|
|
{
|
|
PropertyInfo prop = options.GetType().GetProperty(deployOptionsProp.Name);
|
|
object setProp = GetDeploymentOptionProp(prop, options);
|
|
deployOptionsProp.SetValue(this, setProp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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.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>
|
|
/// <param name="prop"></param>
|
|
/// <param name="options"></param>
|
|
public object GetDeploymentOptionProp(PropertyInfo prop, DacDeployOptions options)
|
|
{
|
|
var val = prop.GetValue(options);
|
|
DescriptionAttribute descriptionAttribute = prop.GetCustomAttributes<DescriptionAttribute>().FirstOrDefault();
|
|
DisplayNameAttribute displayNameAttribute = prop.GetCustomAttributes<DisplayNameAttribute>().FirstOrDefault();
|
|
Type type = val != null ? typeof(DeploymentOptionProperty<>).MakeGenericType(val.GetType())
|
|
: typeof(DeploymentOptionProperty<>).MakeGenericType(prop.PropertyType);
|
|
|
|
// 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()
|
|
{
|
|
return new DeploymentOptions();
|
|
}
|
|
|
|
public static DeploymentOptions GetDefaultPublishOptions()
|
|
{
|
|
DeploymentOptions result = new DeploymentOptions();
|
|
|
|
result.ExcludeObjectTypes.Value = result.ExcludeObjectTypes.Value.Where(x => x != Enum.GetName(ObjectType.DatabaseScopedCredentials)).ToArray(); // re-include database-scoped credentials
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|