mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-17 17:23:48 -05:00
Merge branch 'main' into v-chrcan/issue392
This commit is contained in:
@@ -50,6 +50,7 @@ This document provides the protocol specification for all the service's JSON-RPC
|
||||
* :leftwards_arrow_with_hook: [query/subset](#query_subset)
|
||||
* :leftwards_arrow_with_hook: [query/dispose](#query_dispose)
|
||||
* :leftwards_arrow_with_hook: [query/cancel](#query_cancel)
|
||||
* :arrow_right: [query/connectionUriChanged](#query_connectionUriChanged)
|
||||
* :leftwards_arrow_with_hook: [query/saveCsv](#query_saveCsv)
|
||||
* :leftwards_arrow_with_hook: [query/saveExcel](#query_saveExcel)
|
||||
* :leftwards_arrow_with_hook: [query/saveJson](#query_saveJson)
|
||||
@@ -889,6 +890,18 @@ Cancel the query in progress for the owner uri.
|
||||
public string Messages { get; set; }
|
||||
}
|
||||
```
|
||||
### <a name="query_connectionUriChanged"></a>`query/connectionUriChanged`
|
||||
|
||||
Change the uri associated with a query.
|
||||
|
||||
#### Notification
|
||||
```csharp
|
||||
public class ConnectionUriChangedParams
|
||||
{
|
||||
public string NewOwnerUri { get; set; }
|
||||
public string OriginalOwnerUri { get; set;
|
||||
}
|
||||
```
|
||||
|
||||
### <a name="query_saveCsv"></a>`query/saveCsv`
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -52,6 +53,17 @@ namespace Microsoft.Kusto.ServiceLayer.DataSource.Monitor
|
||||
};
|
||||
|
||||
_metadata = JsonSerializer.Deserialize<WorkspaceResponse>(results, options);
|
||||
|
||||
if (_metadata?.Tables is null && _metadata?.Workspaces is null && _metadata?.TableGroups is null)
|
||||
{
|
||||
var errorMessage = JsonSerializer.Deserialize<ErrorResponse>(results, options);
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(
|
||||
"The Log Analytics Workspace can not be reached. Please validate the Workspace ID, the correct tenant is selected, and that you have access to the workspace. ");
|
||||
builder.AppendLine($"Error Message: {errorMessage?.Error?.Message}");
|
||||
throw new Exception(builder.ToString());
|
||||
}
|
||||
|
||||
return _metadata;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
/// <summary>
|
||||
/// URI identifying the owner/user of the connection. Could be a file, service, resource, etc.
|
||||
/// </summary>
|
||||
public string OwnerUri { get; private set; }
|
||||
public string OwnerUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Factory used for creating the SQL connection associated with the connection info.
|
||||
|
||||
@@ -766,6 +766,22 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reassign the uri associated with a connection info with a new uri.
|
||||
/// </summary>
|
||||
public bool ReplaceUri(string originalOwnerUri, string newOwnerUri)
|
||||
{
|
||||
// Lookup the ConnectionInfo owned by the URI
|
||||
ConnectionInfo info;
|
||||
if (!OwnerToConnectionMap.TryGetValue(originalOwnerUri, out info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
OwnerToConnectionMap.Remove(originalOwnerUri);
|
||||
OwnerToConnectionMap.Add(newOwnerUri, info);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close a connection with the specified connection details.
|
||||
/// </summary>
|
||||
|
||||
@@ -30,6 +30,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Metadata.Contracts
|
||||
public string Schema { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string ParentName { get; set; }
|
||||
|
||||
public string ParentTypeName { get; set; }
|
||||
|
||||
public string Urn { get; set; }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
//
|
||||
|
||||
using System.Globalization;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes;
|
||||
|
||||
@@ -56,13 +57,13 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
{
|
||||
SmoObject = smoObject;
|
||||
NodeValue = smoObject.Name;
|
||||
ScriptSchemaObjectBase schemaBasecObject = smoObject as ScriptSchemaObjectBase;
|
||||
ScriptSchemaObjectBase schemaBaseObject = smoObject as ScriptSchemaObjectBase;
|
||||
ObjectMetadata = new Metadata.Contracts.ObjectMetadata();
|
||||
ObjectMetadata.Name = smoObject.Name;
|
||||
|
||||
try
|
||||
{
|
||||
if(smoObject.Urn != null)
|
||||
if (smoObject.Urn != null)
|
||||
{
|
||||
ObjectMetadata.MetadataTypeName = smoObject.Urn.Type;
|
||||
}
|
||||
@@ -72,16 +73,27 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
//Ignore the exception, sometimes the urn returns exception and I' not sure why
|
||||
}
|
||||
|
||||
if (schemaBasecObject != null)
|
||||
if (schemaBaseObject != null)
|
||||
{
|
||||
ObjectMetadata.Schema = schemaBasecObject.Schema;
|
||||
ObjectMetadata.Schema = schemaBaseObject.Schema;
|
||||
if (!string.IsNullOrEmpty(ObjectMetadata.Schema))
|
||||
{
|
||||
NodeValue = $"{ObjectMetadata.Schema}.{smoObject.Name}";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to read the schema from the parent object
|
||||
var parent = smoObject?.ParentCollection?.ParentInstance as ScriptSchemaObjectBase;
|
||||
if (parent != null)
|
||||
{
|
||||
ObjectMetadata.Schema = parent.Schema;
|
||||
ObjectMetadata.ParentName = parent.Name;
|
||||
ObjectMetadata.ParentTypeName = parent.Urn.Type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public virtual NamedSmoObject GetParentSmoObject()
|
||||
{
|
||||
if (SmoObject != null)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters for the connection uri changed notification.
|
||||
/// </summary>
|
||||
public class ConnectionUriChangedParams
|
||||
{
|
||||
public string NewOwnerUri { get; set; }
|
||||
public string OriginalOwnerUri { get; set; }
|
||||
}
|
||||
public class ConnectionUriChangedNotification
|
||||
{
|
||||
public static readonly
|
||||
EventType<ConnectionUriChangedParams> Type =
|
||||
EventType<ConnectionUriChangedParams>.Create("query/connectionUriChanged");
|
||||
}
|
||||
}
|
||||
@@ -385,6 +385,20 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
Batches[saveParams.BatchIndex].SaveAs(saveParams, fileFactory, successHandler, failureHandler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the OwnerURI for the editor connection.
|
||||
/// </summary>
|
||||
public String ConnectionOwnerURI {
|
||||
get
|
||||
{
|
||||
return this.editorConnection.OwnerUri;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.editorConnection.OwnerUri = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Helpers
|
||||
|
||||
@@ -170,6 +170,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
serviceHost.SetRequestHandler(SubsetRequest.Type, HandleResultSubsetRequest);
|
||||
serviceHost.SetRequestHandler(QueryDisposeRequest.Type, HandleDisposeRequest);
|
||||
serviceHost.SetRequestHandler(QueryCancelRequest.Type, HandleCancelRequest);
|
||||
serviceHost.SetEventHandler(ConnectionUriChangedNotification.Type, HandleConnectionUriChangedNotification);
|
||||
serviceHost.SetRequestHandler(SaveResultsAsCsvRequest.Type, HandleSaveResultsAsCsvRequest);
|
||||
serviceHost.SetRequestHandler(SaveResultsAsExcelRequest.Type, HandleSaveResultsAsExcelRequest);
|
||||
serviceHost.SetRequestHandler(SaveResultsAsJsonRequest.Type, HandleSaveResultsAsJsonRequest);
|
||||
@@ -351,6 +352,33 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles a request to change the uri associated with an active query and connection info.
|
||||
/// </summary>
|
||||
internal Task HandleConnectionUriChangedNotification(ConnectionUriChangedParams changeUriParams,
|
||||
EventContext eventContext)
|
||||
{
|
||||
try {
|
||||
string OriginalOwnerUri = changeUriParams.OriginalOwnerUri;
|
||||
string NewOwnerUri = changeUriParams.NewOwnerUri;
|
||||
// Attempt to load the query
|
||||
Query query;
|
||||
if(!ActiveQueries.TryRemove(OriginalOwnerUri, out query)){
|
||||
throw new Exception("Uri: " + OriginalOwnerUri + " is not associated with an active query.");
|
||||
}
|
||||
ConnectionService.ReplaceUri(OriginalOwnerUri, NewOwnerUri);
|
||||
query.ConnectionOwnerURI = NewOwnerUri;
|
||||
ActiveQueries.TryAdd(NewOwnerUri, query);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(TraceEventType.Error, "Error encountered " + ex.ToString());
|
||||
return Task.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles a request to get a subset of the results of this query
|
||||
/// </summary>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
|
||||
{
|
||||
@@ -52,13 +51,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent object name
|
||||
/// </summary>
|
||||
public string ParentName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent object type name, such as Table, View, etc.
|
||||
/// </summary>
|
||||
public string ParentTypeName { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string objectName = string.IsNullOrEmpty(this.Schema)
|
||||
? this.Name
|
||||
: this.Schema + "." + this.Name;
|
||||
|
||||
return objectName;
|
||||
string objectName = string.Empty;
|
||||
if (!string.IsNullOrEmpty(this.Schema))
|
||||
{
|
||||
objectName += this.Schema + ".";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(this.ParentName))
|
||||
{
|
||||
objectName += this.ParentName + ".";
|
||||
}
|
||||
return objectName + this.Name;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
@@ -66,6 +80,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
|
||||
return
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Type ?? string.Empty) ^
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Schema ?? string.Empty) ^
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.ParentName ?? string.Empty) ^
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.ParentTypeName ?? string.Empty) ^
|
||||
StringComparer.OrdinalIgnoreCase.GetHashCode(this.Name ?? string.Empty);
|
||||
}
|
||||
|
||||
@@ -87,6 +103,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting.Contracts
|
||||
return
|
||||
string.Equals(this.Type, other.Type, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.Schema, other.Schema, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.ParentName, other.ParentName, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.ParentTypeName, other.ParentTypeName, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.ParentTypeName, other.ParentTypeName, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.SqlScriptPublish;
|
||||
@@ -100,15 +101,35 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
Validate.IsNotNullOrWhitespaceString("scriptingObject.Type", scriptingObject.Type);
|
||||
|
||||
// Leaving the server name blank will automatically match whatever the server SMO is running against.
|
||||
string urn = string.Format(
|
||||
"Server[@Name='{0}']/Database[@Name='{1}']/{2}[@Name='{3}' {4}]",
|
||||
server.ToUpper(),
|
||||
Urn.EscapeString(database),
|
||||
scriptingObject.Type,
|
||||
Urn.EscapeString(scriptingObject.Name),
|
||||
scriptingObject.Schema != null ? string.Format("and @Schema = '{0}'", Urn.EscapeString(scriptingObject.Schema)) : string.Empty);
|
||||
StringBuilder urnBuilder = new StringBuilder();
|
||||
urnBuilder.AppendFormat("Server[@Name='{0}']/", server.ToUpper());
|
||||
urnBuilder.AppendFormat("Database[@Name='{0}']/", Urn.EscapeString(database));
|
||||
|
||||
return new Urn(urn);
|
||||
bool hasParentObject = !string.IsNullOrWhiteSpace(scriptingObject.ParentName)
|
||||
&& !string.IsNullOrWhiteSpace(scriptingObject.ParentTypeName);
|
||||
if (hasParentObject)
|
||||
{
|
||||
urnBuilder.AppendFormat("{0}[@Name='{1}'", scriptingObject.ParentTypeName, Urn.EscapeString(scriptingObject.ParentName));
|
||||
if (!string.IsNullOrWhiteSpace(scriptingObject.Schema))
|
||||
{
|
||||
urnBuilder.AppendFormat(" and @Schema = '{0}'", Urn.EscapeString(scriptingObject.Schema));
|
||||
}
|
||||
urnBuilder.Append("]/");
|
||||
}
|
||||
|
||||
urnBuilder.AppendFormat("{0}[@Name='{1}'", scriptingObject.Type, Urn.EscapeString(scriptingObject.Name));
|
||||
|
||||
// add schema to object only if there is no parent object specified
|
||||
// the parent object field is only set for objects that don't have schema themselves
|
||||
// so if parent is not null then the schema filter will already be set that part of the urn above
|
||||
if (!string.IsNullOrWhiteSpace(scriptingObject.Schema) && !hasParentObject)
|
||||
{
|
||||
urnBuilder.AppendFormat(" and @Schema = '{0}'", Urn.EscapeString(scriptingObject.Schema));
|
||||
}
|
||||
|
||||
urnBuilder.Append("]");
|
||||
|
||||
return new Urn(urnBuilder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts.ExecuteRequests;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common.RequestContextMocking;
|
||||
using Microsoft.SqlTools.ServiceLayer.Workspace;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.QueryExecution
|
||||
{
|
||||
public class ConnectionUriChangedTests
|
||||
{
|
||||
[Test]
|
||||
public async Task ChangeUriForExecutedQuery()
|
||||
{
|
||||
// If:
|
||||
// ... I request a query (doesn't matter what kind)
|
||||
var workspaceService = Common.GetPrimedWorkspaceService(Constants.StandardQuery);
|
||||
var queryService = Common.GetPrimedExecutionService(null, true, false, false, workspaceService);
|
||||
var executeParams = new ExecuteDocumentSelectionParams {QuerySelection = null, OwnerUri = Constants.OwnerUri};
|
||||
var executeRequest = RequestContextMocks.Create<ExecuteRequestResult>(null);
|
||||
await queryService.HandleExecuteRequest(executeParams, executeRequest.Object);
|
||||
await queryService.WorkTask;
|
||||
await queryService.ActiveQueries[Constants.OwnerUri].ExecutionTask;
|
||||
|
||||
const string newOwnerUri = "newTestFile";
|
||||
Query query;
|
||||
queryService.ActiveQueries.TryGetValue(Constants.OwnerUri, out query);
|
||||
|
||||
// ... And then I change the uri for the query
|
||||
var changeUriParams = new ConnectionUriChangedParams {
|
||||
OriginalOwnerUri = Constants.OwnerUri,
|
||||
NewOwnerUri = newOwnerUri
|
||||
};
|
||||
|
||||
|
||||
await queryService.HandleConnectionUriChangedNotification(changeUriParams, new TestEventContext());
|
||||
|
||||
// Then:
|
||||
// ... And the active queries should have the new query.
|
||||
Assert.That(queryService.ActiveQueries.TryGetValue(newOwnerUri, out query), "Query with newOwnerUri not found.");
|
||||
Assert.That(Equals(query.ConnectionOwnerURI, newOwnerUri), "OwnerUri was not changed!");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChangeUriForMissingQuery()
|
||||
{
|
||||
// If:
|
||||
// ... I attempt to change the uri a query that doesn't exist
|
||||
var workspaceService = new Mock<WorkspaceService<SqlToolsSettings>>();
|
||||
var queryService = Common.GetPrimedExecutionService(null, false, false, false, workspaceService.Object);
|
||||
const string newOwnerUri = "newTestFile";
|
||||
var changeUriParams = new ConnectionUriChangedParams {
|
||||
OriginalOwnerUri = Constants.OwnerUri,
|
||||
NewOwnerUri = newOwnerUri
|
||||
};
|
||||
|
||||
Assert.ThrowsAsync<Exception>(async () => await queryService.HandleConnectionUriChangedNotification(changeUriParams, new TestEventContext()));
|
||||
|
||||
Query query;
|
||||
Assert.False(queryService.ActiveQueries.TryGetValue(Constants.OwnerUri, out query), "Query was removed from Active Queries");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user