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 all 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[] NameParts;

/// <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,183 @@
//
// 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.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.Linq;

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 XDocument 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 = XDocument.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));
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.

var result = this.scmpInfo.Descendants("ConnectionBasedModelProvider");
string searchingFor = source ? "Source" : "Target";

try
{
kisantia marked this conversation as resolved.
Show resolved Hide resolved
if (result != null)
{
foreach (XElement node in result)
{
if (node.Parent.Name.ToString().Contains(searchingFor))
{
endpointInfo.ConnectionDetails = SchemaCompareService.ConnectionServiceInstance.ParseConnectionString(node.Value);
endpointInfo.ConnectionDetails.ConnectionString = node.Value;
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()
{
NameParts = entry.Identifier.Parts.Cast<string>().ToArray(),
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()
{
var result = this.scmpInfo.Descendants("PropertyElementName")
.Where(x => x.Element("Name").Value == "TargetDatabaseName")
.Select(x => x.Element("Value")).FirstOrDefault();

return result != null ? result.Value : string.Empty;
}

// The original target connection string is used if comparing a dacpac and db with the same name
private string GetOriginalTargetConnectionString()
{
var result = this.scmpInfo.Descendants("PropertyElementName")
.Where(x => x.Element("Name").Value == "TargetConnectionString")
.Select(x => x.Element("Value")).FirstOrDefault();

return result != null ? result.Value : string.Empty;
}
}
}
Loading