Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema Compare open SCMP file #825

Merged
merged 6 commits into from
Jun 13, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
using Microsoft.SqlTools.ServiceLayer.Metadata;
using Microsoft.SqlTools.ServiceLayer.Profiler;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.SchemaCopmare;
using Microsoft.SqlTools.ServiceLayer.SchemaCompare;
using Microsoft.SqlTools.ServiceLayer.Scripting;
using Microsoft.SqlTools.ServiceLayer.Security;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
Expand Down Expand Up @@ -119,7 +119,7 @@ private static void InitializeRequestHandlersAndServices(ServiceHost serviceHost
CmsService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(CmsService.Instance);

SchemaCopmare.SchemaCompareService.Instance.InitializeService(serviceHost);
SchemaCompare.SchemaCompareService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(SchemaCompareService.Instance);

InitializeHostedServices(serviceProvider, serviceHost);
Expand Down
11 changes: 11 additions & 0 deletions src/Microsoft.SqlTools.ServiceLayer/Localization/sr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2933,6 +2933,14 @@ public static string SchemaCompareExcludeIncludeNodeNotFound
}
}

public static string OpenScmpConnectionBasedModelParsingError
{
get
{
return Keys.GetString(Keys.OpenScmpConnectionBasedModelParsingError);
}
}

public static string ConnectionServiceListDbErrorNotConnected(string uri)
{
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
Expand Down Expand Up @@ -4270,6 +4278,9 @@ public class Keys
public const string SchemaCompareExcludeIncludeNodeNotFound = "SchemaCompareExcludeIncludeNodeNotFound";


public const string OpenScmpConnectionBasedModelParsingError = "OpenScmpConnectionBasedModelParsingError";


private Keys()
{ }

Expand Down
4 changes: 4 additions & 0 deletions src/Microsoft.SqlTools.ServiceLayer/Localization/sr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1719,4 +1719,8 @@
<value>Failed to find the specified change in the model</value>
<comment></comment>
</data>
<data name="OpenScmpConnectionBasedModelParsingError" xml:space="preserve">
<value>Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}'</value>
<comment></comment>
</data>
</root>
3 changes: 2 additions & 1 deletion src/Microsoft.SqlTools.ServiceLayer/Localization/sr.strings
Original file line number Diff line number Diff line change
Expand Up @@ -796,4 +796,5 @@ ExtractInvalidVersion = Invalid version '{0}' passed. Version must be in the for
############################################################################
# Schema Compare
PublishChangesTaskName = Apply schema compare changes
SchemaCompareExcludeIncludeNodeNotFound = Failed to find the specified change in the model
SchemaCompareExcludeIncludeNodeNotFound = Failed to find the specified change in the model
OpenScmpConnectionBasedModelParsingError = Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}'
5 changes: 5 additions & 0 deletions src/Microsoft.SqlTools.ServiceLayer/Localization/sr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -1996,6 +1996,11 @@
<target state="new">Failed to find the specified change in the model</target>
<note></note>
</trans-unit>
<trans-unit id="OpenScmpConnectionBasedModelParsingError">
<source>Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}'</source>
<target state="new">Error encountered while trying to parse connection information for endpoint '{0}' with error message '{1}'</target>
<note></note>
</trans-unit>
</body>
</file>
</xliff>
Original file line number Diff line number Diff line change
Expand Up @@ -232,5 +232,20 @@ public DeploymentOptions()
}
}
}

public DeploymentOptions(DacDeployOptions options)
{
System.Reflection.PropertyInfo[] deploymentOptionsProperties = this.GetType().GetProperties();

foreach (var deployOptionsProp in deploymentOptionsProperties)
{
var prop = options.GetType().GetProperty(deployOptionsProp.Name);

if (prop != null)
{
deployOptionsProp.SetValue(this, prop.GetValue(options));
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// 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;
using System.Collections.Generic;

namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
{
public class SchemaCompareObjectId
{
/// <summary>
/// Name to create object identifier
/// </summary>
public string Name;
kisantia marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// sql object type
/// </summary>
public string SqlObjectType;
}

/// <summary>
/// Parameters for a schema compare open scmp file request.
/// </summary>
public class SchemaCompareOpenScmpParams
{
/// <summary>
/// filepath of scmp
/// </summary>
public string filePath { get; set; }
}

/// <summary>
/// Parameters returned from a schema compare open scmp request.
/// </summary>
public class SchemaCompareOpenScmpResult : ResultStatus
{
/// <summary>
/// Gets or sets the current source endpoint info
/// </summary>
public SchemaCompareEndpointInfo SourceEndpointInfo { get; set; }

/// <summary>
/// Gets or sets the current target endpoint info
/// </summary>
public SchemaCompareEndpointInfo TargetEndpointInfo { get; set; }

/// <summary>
/// Gets or sets the original target name. This is the initial target name, not necessarily the same as TargetEndpointInfo if they were swapped
/// The original target name is used to determine whether to use ExcludedSourceElements or ExcludedTargetElements if source and target were swapped
/// </summary>
public string OriginalTargetName { get; set; }

/// <summary>
/// Gets or sets the original target connection string. This is the initial target connection string, not necessarily the same as TargetEndpointInfo if they were swapped
/// The target connection string is necessary if the source and target are a dacpac and db with the same name
/// </summary>
public string OriginalTargetConnectionString { get; set; }

/// <summary>
/// Gets or sets the deployment options
/// </summary>
public DeploymentOptions DeploymentOptions { get; set; }

/// <summary>
/// Gets or sets the excluded source elements. This is based on the initial source, not necessarily the same as SourceEndpointInfo if they were swapped
/// </summary>
public List<SchemaCompareObjectId> ExcludedSourceElements { get; set; }

/// <summary>
/// Gets or sets the excluded target elements. This is based on the initial target, not necessarily the same as TargetEndpointInfo if they were swapped
/// </summary>
public List<SchemaCompareObjectId> ExcludedTargetElements { get; set; }
}

/// <summary>
/// Defines the Schema Compare open scmp request type
/// </summary>
class SchemaCompareOpenScmpRequest
{
public static readonly RequestType<SchemaCompareOpenScmpParams, SchemaCompareOpenScmpResult> Type =
RequestType<SchemaCompareOpenScmpParams, SchemaCompareOpenScmpResult>.Create("schemaCompare/openScmp");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//
using Microsoft.SqlServer.Dac.Compare;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.ServiceLayer.Utility;
using System.Collections.Generic;
Expand Down Expand Up @@ -37,6 +38,11 @@ public class SchemaCompareEndpointInfo
/// Connection uri
/// </summary>
public string OwnerUri { get; set; }

/// <summary>
/// Connection details
/// </summary>
public ConnectionDetails ConnectionDetails { get; set; }
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
//
// 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.SqlServer.Dac.Compare;
using Microsoft.SqlTools.ServiceLayer.Connection;
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.Text.RegularExpressions;
using System.Threading;
using System.Xml;

namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
{
/// <summary>
/// Schema compare load scmp operation
/// </summary>
class SchemaCompareOpenScmpOperation : ITaskOperation
{
private CancellationTokenSource cancellation = new CancellationTokenSource();
private bool disposed = false;

public SqlTask SqlTask { get; set; }

public SchemaCompareOpenScmpParams Parameters { get; set; }

public SchemaCompareOpenScmpResult Result { get; private set; }

private XmlDocument scmpInfo { get; set; }

public SchemaCompareOpenScmpOperation(SchemaCompareOpenScmpParams parameters)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
}

protected CancellationToken CancellationToken { get { return this.cancellation.Token; } }

/// <summary>
/// The error occurred during operation
/// </summary>
public string ErrorMessage { get; set; }

// The schema compare public api doesn't currently take a cancellation token so the operation can't be cancelled
public void Cancel()
{
}

/// <summary>
/// Disposes the operation.
/// </summary>
public void Dispose()
{
if (!disposed)
{
this.Cancel();
disposed = true;
}
}

public void Execute(TaskExecutionMode mode)
{
if (this.CancellationToken.IsCancellationRequested)
{
throw new OperationCanceledException(this.CancellationToken);
}

try
{
SchemaComparison compare = new SchemaComparison(this.Parameters.filePath);

// load xml file because some parsing still needs to be done
this.scmpInfo = new XmlDocument();
this.scmpInfo.Load(this.Parameters.filePath);

this.Result = new SchemaCompareOpenScmpResult()
{
DeploymentOptions = new DeploymentOptions(compare.Options),
Success = true,
SourceEndpointInfo = this.GetEndpointInfo(true, compare.Source),
TargetEndpointInfo = this.GetEndpointInfo(false, compare.Target),
OriginalTargetName = this.GetOriginalTargetName(),
udeeshagautam marked this conversation as resolved.
Show resolved Hide resolved
OriginalTargetConnectionString = this.GetOriginalTargetConnectionString(),
ExcludedSourceElements = this.GetExcludedElements(compare.ExcludedSourceObjects),
ExcludedTargetElements = this.GetExcludedElements(compare.ExcludedTargetObjects)
};
}
catch (Exception e)
{
ErrorMessage = e.Message;
Logger.Write(TraceEventType.Error, string.Format("Schema compare open scmp operation failed with exception {0}", e.Message));
kisantia marked this conversation as resolved.
Show resolved Hide resolved
throw;
}
}

private SchemaCompareEndpointInfo GetEndpointInfo(bool source, SchemaCompareEndpoint endpoint)
{
SchemaCompareEndpointInfo endpointInfo = new SchemaCompareEndpointInfo();

// if the endpoint is a dacpac we don't need to parse the xml
SchemaCompareDacpacEndpoint dacpacEndpoint = endpoint as SchemaCompareDacpacEndpoint;
if (dacpacEndpoint != null)
{
endpointInfo.EndpointType = SchemaCompareEndpointType.Dacpac;
endpointInfo.PackageFilePath = dacpacEndpoint.FilePath;
}
else
{
// need to parse xml to get connection string of database
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pensivebrian should we make the connection string getter to be public in DacFx SchemaCompareDatabaseEndpoint - so that we don't have to do parsing here? What do you think? I am worried that parsing might need constant maintenance if things around this change in DacFx.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have enough context to have an opinion. I'll leave it up to you two.

XmlNodeList connectionBasedModelProviderNodes = this.scmpInfo.DocumentElement.SelectNodes("descendant::ConnectionBasedModelProvider");
kisantia marked this conversation as resolved.
Show resolved Hide resolved
string searchingFor = source ? "Source" : "Target";

try
{
kisantia marked this conversation as resolved.
Show resolved Hide resolved
if (connectionBasedModelProviderNodes != null)
{
foreach (XmlNode node in connectionBasedModelProviderNodes)
{
if (node.ParentNode.Name.Contains(searchingFor))
{
endpointInfo.ConnectionDetails = SchemaCompareService.ConnectionServiceInstance.ParseConnectionString(node.InnerText);
endpointInfo.ConnectionDetails.ConnectionString = node.InnerText;
endpointInfo.DatabaseName = endpointInfo.ConnectionDetails.DatabaseName;
endpointInfo.EndpointType = SchemaCompareEndpointType.Database;
}
}
}
}
catch (Exception e)
{
ErrorMessage = string.Format(SR.OpenScmpConnectionBasedModelParsingError, ((SchemaCompareDatabaseEndpoint)endpoint).DatabaseName,e.Message);
Logger.Write(TraceEventType.Error, string.Format("Schema compare open scmp operation failed during xml parsing with exception {0}", e.Message));
throw;
}
}

return endpointInfo;
}

private List<SchemaCompareObjectId> GetExcludedElements(IList<SchemaComparisonExcludedObjectId> excludedObjects)
{
List<SchemaCompareObjectId> excludedElements = new List<SchemaCompareObjectId>();

foreach (SchemaComparisonExcludedObjectId entry in excludedObjects)
{
excludedElements.Add(new SchemaCompareObjectId()
{
Name = SchemaCompareOperation.GetName(entry.Identifier.ToString()),
kisantia marked this conversation as resolved.
Show resolved Hide resolved
SqlObjectType = entry.TypeName
});
}

return excludedElements;
}


// The original target name is used to determine whether to use ExcludedSourceElements or ExcludedTargetElements if source and target were swapped
private string GetOriginalTargetName()
{
XmlNode node = this.scmpInfo.DocumentElement.SelectSingleNode("//PropertyElementName[Name='TargetDatabaseName']");
return node != null ? node.LastChild.InnerText : string.Empty;
}

// The original target connection string is used if comparing a dacpac and db with the same name
private string GetOriginalTargetConnectionString()
{
XmlNode node = this.scmpInfo.DocumentElement.SelectSingleNode("//PropertyElementName[Name='TargetConnectionString']");
return node != null ? node.LastChild.InnerText : string.Empty;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ public static string FormatScript(string script)
return script;
}

private static string GetName(string name)
public static string GetName(string name)
kisantia marked this conversation as resolved.
Show resolved Hide resolved
{
// remove brackets from name
return Regex.Replace(name, @"[\[\]]", "");
Expand Down
Loading