Create remote file browser service (#448)

* code refactoring

* Add filebrowser service and tests

* change dataset reference

* Address pr comments

* add more tests

* address pr comments

* address pr comments and added more tests

* minor change

* minor fix

* Fix test break and add dataset result check
This commit is contained in:
Kate Shin
2017-09-08 13:57:56 -07:00
committed by GitHub
parent 784f4c5d05
commit 14ec5be961
29 changed files with 1942 additions and 59 deletions

View File

@@ -0,0 +1,45 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Parameters to pass to close file browser
/// </summary>
public class FileBrowserCloseParams
{
/// <summary>
/// Connection uri
/// </summary>
public string OwnerUri;
}
/// <summary>
/// Response for closing the browser
/// </summary>
public class FileBrowserCloseResponse
{
/// <summary>
/// Result of the operation
/// </summary>
public bool Succeeded;
/// <summary>
/// Error message if any
/// </summary>
public string Message;
}
/// <summary>
/// Requst to close the file browser
/// </summary>
class FileBrowserCloseRequest
{
public static readonly
RequestType<FileBrowserCloseParams, FileBrowserCloseResponse> Type =
RequestType<FileBrowserCloseParams, FileBrowserCloseResponse>.Create("filebrowser/close");
}
}

View File

@@ -0,0 +1,40 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Event params for expanding a node
/// </summary>
public class FileBrowserExpandCompleteParams
{
/// <summary>
/// Expanded node
/// </summary>
public FileTreeNode ExpandedNode;
/// <summary>
/// Result of the operation
/// </summary>
public bool Succeeded;
/// <summary>
/// Error message if any
/// </summary>
public string Message;
}
/// <summary>
/// Notification for expand completion
/// </summary>
public class FileBrowserExpandCompleteNotification
{
public static readonly
EventType<FileBrowserExpandCompleteParams> Type =
EventType<FileBrowserExpandCompleteParams>.Create("filebrowser/expandcomplete");
}
}

View File

@@ -0,0 +1,34 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Parameters for expanding a folder node
/// </summary>
public class FileBrowserExpandParams
{
/// <summary>
/// Connection uri
/// </summary>
public string OwnerUri;
/// <summary>
/// The path to expand the nodes for
/// </summary>
public string ExpandPath;
}
/// <summary>
/// Request to expand a node in the file browser
/// </summary>
public class FileBrowserExpandRequest
{
public static readonly
RequestType<FileBrowserExpandParams, bool> Type =
RequestType<FileBrowserExpandParams, bool>.Create("filebrowser/expand");
}
}

View File

@@ -0,0 +1,42 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Event params for opening a file browser
/// Returns full directory structure on the server side
/// </summary>
public class FileBrowserOpenCompleteParams
{
/// <summary>
/// Entire file/folder tree
/// </summary>
public FileTree FileTree;
/// <summary>
/// Result of the operation
/// </summary>
public bool Succeeded;
/// <summary>
/// Error message
/// </summary>
public string Message;
}
/// <summary>
/// Notification for completing file browser opening
/// </summary>
public class FileBrowserOpenCompleteNotification
{
public static readonly
EventType<FileBrowserOpenCompleteParams> Type =
EventType<FileBrowserOpenCompleteParams>.Create("filebrowser/opencomplete");
}
}

View File

@@ -0,0 +1,40 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Parameters for opening file browser
/// </summary>
public class FileBrowserOpenParams
{
/// <summary>
/// Connection uri
/// </summary>
public string OwnerUri;
/// <summary>
/// The initial path to expand the nodes for (e.g. Backup will set this path to default backup folder)
/// </summary>
public string ExpandPath;
/// <summary>
/// File extension filter (e.g. *.bak)
/// </summary>
public string[] FileFilters;
}
/// <summary>
/// Request to open a file browser
/// </summary>
public class FileBrowserOpenRequest
{
public static readonly
RequestType<FileBrowserOpenParams, bool> Type =
RequestType<FileBrowserOpenParams, bool>.Create("filebrowser/open");
}
}

View File

@@ -0,0 +1,35 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Event params for validation completion
/// </summary>
public class FileBrowserValidateCompleteParams
{
/// <summary>
/// Result of the operation
/// </summary>
public bool Succeeded;
/// <summary>
/// Error message if any
/// </summary>
public string Message;
}
/// <summary>
/// Notification for validation completion
/// </summary>
public class FileBrowserValidateCompleteNotification
{
public static readonly
EventType<FileBrowserValidateCompleteParams> Type =
EventType<FileBrowserValidateCompleteParams>.Create("filebrowser/validatecomplete");
}
}

View File

@@ -0,0 +1,39 @@
//
// 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.FileBrowser.Contracts
{
/// <summary>
/// Parameters for validating selected file paths
/// </summary>
public class FileBrowserValidateParams
{
/// <summary>
/// Connection uri
/// </summary>
public string OwnerUri;
/// <summary>
/// Type of service that uses the file browser
/// </summary>
public string ServiceType;
/// <summary>
/// Selected files
/// </summary>
public string[] SelectedFiles;
}
/// <summary>
/// Requst to validate the selected file paths
/// </summary>
class FileBrowserValidateRequest
{
public static readonly
RequestType<FileBrowserValidateParams, bool> Type =
RequestType<FileBrowserValidateParams, bool>.Create("filebrowser/validate");
}
}

View File

@@ -0,0 +1,31 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts
{
/// <summary>
/// Tree to represent file/folder structure
/// </summary>
public class FileTree
{
/// <summary>
/// Root node of the tree
/// </summary>
public FileTreeNode RootNode { get; private set; }
/// <summary>
/// Selected node of the tree
/// </summary>
public FileTreeNode SelectedNode { get; set; }
/// <summary>
/// Constructor
/// </summary>
public FileTree()
{
this.RootNode = new FileTreeNode();
}
}
}

View File

@@ -0,0 +1,55 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System.Collections.Generic;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts
{
/// <summary>
/// Tree node to represent file or folder
/// </summary>
public class FileTreeNode
{
/// <summary>
/// Constructor
/// </summary>
public FileTreeNode()
{
this.Children = new List<FileTreeNode>();
}
/// <summary>
/// Parent node
/// </summary>
public FileTreeNode Parent { get; private set; }
/// <summary>
/// List of children nodes
/// </summary>
public List<FileTreeNode> Children { get; private set; }
// Indicates if the node is expanded, applicable to a folder.
public bool IsExpanded { get; set; }
// Indicates if the node is file or folder
public bool IsFile { get; set; }
/// <summary>
/// File or folder name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Full path
/// </summary>
public string FullPath { get; set; }
public void AddChildNode(FileTreeNode item)
{
item.Parent = this;
this.Children.Add(item);
}
}
}

View File

@@ -0,0 +1,195 @@
//
// 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.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Sdk.Sfc;
using System.Data.SqlClient;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
public struct FileInfo
{
// Empty for folder
public string fileName;
public string folderName;
public string path;
// Includes file name in the path. Empty for folder.
public string fullPath;
}
/// <summary>
/// Base class for file browser
/// </summary>
public abstract class FileBrowserBase
{
private Enumerator enumerator = null;
protected SqlConnection sqlConnection = null;
protected Enumerator Enumerator
{
get
{
return this.enumerator = (this.enumerator ?? new Enumerator());
}
}
/// <summary>
/// Separator string for components of the file path. Defaults to \ for Windows and / for Linux
/// </summary>
internal char PathSeparator { get; set; }
/// <summary>
/// Returns the PathSeparator values of the Server.
/// </summary>
/// <returns>PathSeparator</returns>
internal static char GetPathSeparator(Enumerator enumerator, SqlConnection connection)
{
var req = new Request();
req.Urn = "Server";
req.Fields = new[] { "PathSeparator" };
string pathSeparator = string.Empty;
using (DataSet ds = enumerator.Process(connection, req))
{
if (IsValidDataSet(ds))
{
pathSeparator = Convert.ToString(ds.Tables[0].Rows[0][0], System.Globalization.CultureInfo.InvariantCulture);
}
}
if (string.IsNullOrEmpty(pathSeparator))
{
pathSeparator = @"\";
}
return pathSeparator[0];
}
/// <summary>
/// Enumerates the FileInfo objects associated with drives
/// </summary>
/// <param name="enumerator"></param>
/// <param name="connection"></param>
/// <returns></returns>
internal static IEnumerable<FileInfo> EnumerateDrives(Enumerator enumerator, SqlConnection connection)
{
// if not supplied, server name will be obtained from urn
Request req = new Request();
bool clustered = false;
req.Urn = "Server/Information";
req.Fields = new string[] { "IsClustered", "PathSeparator", "HostPlatform" };
var pathSeparator = @"\";
var hostPlatform = HostPlatformNames.Windows;
try
{
using (DataSet ds = enumerator.Process(connection, req))
{
if (IsValidDataSet(ds))
{
clustered = Convert.ToBoolean(ds.Tables[0].Rows[0][0],
CultureInfo.InvariantCulture);
pathSeparator = Convert.ToString(ds.Tables[0].Rows[0][1], CultureInfo.InvariantCulture);
hostPlatform = Convert.ToString(ds.Tables[0].Rows[0][2], CultureInfo.InvariantCulture);
}
}
}
catch (UnknownPropertyEnumeratorException)
{
//there can be no clusters on 7.0 server
}
// we need to issue different queries to get all fixed drives on a normal server, and
// shared drives on a cluster
req.Urn = clustered ? "Server/AvailableMedia[@SharedDrive=true()]" : "Server/Drive";
req.Fields = new[] { "Name" };
using (DataSet ds = enumerator.Process(connection, req))
{
if (IsValidDataSet(ds))
{
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
var fileInfo = new FileInfo
{
fileName = string.Empty,
path = Convert.ToString(ds.Tables[0].Rows[i][0], System.Globalization.CultureInfo.InvariantCulture)
};
// if we're looking at shared devices on a clustered server
// they already have \ on the drive
// sys.dm_os_enumerate_fixed_drives appends a \ on Windows for sql17+
if (!clustered && hostPlatform == HostPlatformNames.Windows && !fileInfo.path.EndsWith(pathSeparator))
{
fileInfo.path += pathSeparator;
}
yield return fileInfo;
}
}
}
}
/// <summary>
/// Enumerates files and folders that are immediate children of the given path on the server
/// </summary>
/// <param name="enumerator"></param>
/// <param name="connection"></param>
/// <param name="path"></param>
/// <returns></returns>
internal static IEnumerable<FileInfo> EnumerateFilesInFolder(Enumerator enumerator, SqlConnection connection, string path)
{
var request = new Request
{
Urn = "Server/File[@Path='" + Urn.EscapeString(path) + "']",
Fields = new[] { "Name", "IsFile", "FullName" },
OrderByList = new[]
{
new OrderBy
{
Field = "IsFile"
},
new OrderBy
{
Field = "Name"
}
}
};
using (DataSet ds = enumerator.Process(connection, request))
{
if (IsValidDataSet(ds))
{
foreach (DataRow row in ds.Tables[0].Rows)
{
bool isFile = Convert.ToBoolean((object)row[1], CultureInfo.InvariantCulture);
yield return new FileInfo
{
path = isFile ? path : Convert.ToString((object)row[2], CultureInfo.InvariantCulture),
fileName = isFile ? Convert.ToString((object)row[0], CultureInfo.InvariantCulture) : String.Empty,
folderName = isFile ? String.Empty : Convert.ToString((object)row[0], CultureInfo.InvariantCulture),
fullPath = isFile ? Convert.ToString((object)row[2], CultureInfo.InvariantCulture) : String.Empty
};
}
}
}
}
internal static bool IsValidDataSet(DataSet ds)
{
return (ds != null
&& ds.Tables != null
&& ds.Tables.Count > 0
&& ds.Tables[0].Rows != null
&& ds.Tables[0].Rows.Count > 0) ;
}
}
}

View File

@@ -0,0 +1,19 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
/// <summary>
/// Exception raised from file browser operation
/// </summary>
internal sealed class FileBrowserException : Exception
{
internal FileBrowserException(string m) : base(m)
{
}
}
}

View File

@@ -0,0 +1,239 @@
//
// 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.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
/// <summary>
/// Implementation for file browser operation
/// </summary>
internal class FileBrowserOperation : FileBrowserBase
{
private FileTree fileTree;
private string expandPath;
private string[] fileFilters;
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="FileBrowser"/> class.
/// </summary>
public FileBrowserOperation()
{
this.fileTree = new FileTree();
}
/// <summary>
/// Initializes a new instance of the <see cref="FileBrowser"/> class.
/// </summary>
/// <param name="connectionInfo">The connection info</param>
/// <param name="fileFilters">The file extension filters</param>
public FileBrowserOperation(SqlConnection connectionInfo, string expandPath, string[] fileFilters = null): this()
{
this.sqlConnection = connectionInfo;
this.expandPath = expandPath;
if (fileFilters == null)
{
this.fileFilters = new string[1] { "*" };
}
else
{
this.fileFilters = fileFilters;
}
}
#endregion
#region public properties and methods
public FileTree FileTree
{
get
{
return this.fileTree;
}
}
internal string[] FileFilters
{
get
{
return this.fileFilters;
}
}
public void PopulateFileTree()
{
this.PathSeparator = GetPathSeparator(this.Enumerator, this.sqlConnection);
PopulateDrives();
ExpandSelectedNode(this.expandPath);
}
/// <summary>
/// Expand nodes for the selected path.
/// </summary>
public void ExpandSelectedNode(string expandPath)
{
this.expandPath = expandPath;
if (!string.IsNullOrEmpty(this.expandPath))
{
var dirs = this.expandPath.TrimEnd(this.PathSeparator).Split(this.PathSeparator);
List<FileTreeNode> currentChildren = this.fileTree.RootNode.Children;
FileTreeNode lastNode = null;
string pathSeparatorString = Convert.ToString(this.PathSeparator);
foreach (string dir in dirs)
{
FileTreeNode currentNode = null;
foreach (FileTreeNode node in currentChildren)
{
if (node.Name == pathSeparatorString || string.Equals(node.Name, dir, StringComparison.OrdinalIgnoreCase))
{
currentNode = node;
break;
}
}
if (currentNode != null)
{
currentNode.IsExpanded = true;
if (!currentNode.IsFile)
{
PopulateFileNode(currentNode);
}
currentChildren = currentNode.Children;
lastNode = currentNode;
}
else
{
if (lastNode != null)
{
this.fileTree.SelectedNode = lastNode;
}
throw new FileBrowserException(string.Format(SR.InvalidPathError, this.expandPath));
}
}
if (lastNode != null)
{
this.fileTree.SelectedNode = lastNode;
}
}
}
#endregion
private void PopulateDrives()
{
bool first = true;
foreach (var fileInfo in EnumerateDrives(Enumerator, sqlConnection))
{
// Windows drive letter paths have a '\' at the end. Linux drive paths won't have a '\'.
var node = new FileTreeNode
{
Name = Convert.ToString(fileInfo.path, CultureInfo.InvariantCulture).TrimEnd('\\'),
FullPath = fileInfo.path
};
this.fileTree.RootNode.AddChildNode(node);
if (first)
{
this.fileTree.SelectedNode = node;
first = false;
}
PopulateFileNode(node);
}
}
private void PopulateFileNode(FileTreeNode parentNode)
{
string parentPath = parentNode.FullPath;
parentNode.Children.Clear();
foreach (var file in EnumerateFilesInFolder(Enumerator, sqlConnection, parentPath))
{
bool isFile = !string.IsNullOrEmpty(file.fileName);
FileTreeNode treeNode = new FileTreeNode();
if (isFile)
{
treeNode.Name = file.fileName;
treeNode.FullPath = file.fullPath;
}
else
{
treeNode.Name = file.folderName;
treeNode.FullPath = file.path;
}
treeNode.IsFile = isFile;
// if the node is a directory, or if we are browsing for files and the file name is allowed,
// add the node to the tree
if (!isFile || (this.FilterFile(treeNode.Name, this.fileFilters)))
{
parentNode.AddChildNode(treeNode);
}
}
}
/// <summary>
/// Filter a filename based on the full mask provide. The full mask may be a collection a masks seperated by semi-colons.
/// For example: *; *.txt
/// </summary>
internal bool FilterFile(string fileName, string[] masks)
{
for (int index = 0; index < masks.Length; index++)
{
if (MatchFileToSubMask(fileName, masks[index]))
{
return true;
}
}
return false;
}
/// <summary>
/// Compares a file name to the user specified mask using a regular expression
/// </summary>
/// <param name="fileName"></param>
/// <param name="mask"></param>
/// <returns></returns>
private bool MatchFileToSubMask(string fileName, string mask)
{
Regex regex;
// If this mask is for all files (*) then just return true.
if (mask == "*")
{
return true;
}
mask = mask.Replace(".", "\\.");
mask = mask.Replace("*", ".*");
mask = mask.Replace("?", ".");
// Perform case insensitive RegEx
{
regex = new Regex(mask, RegexOptions.IgnoreCase);
}
if (!regex.IsMatch(fileName))
{
return false;
}
else
{
return true;
}
}
}
}

View File

@@ -0,0 +1,282 @@
//
// 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.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
using Microsoft.SqlTools.ServiceLayer.FileBrowser.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
/// <summary>
/// Main class for file browser service
/// </summary>
public sealed class FileBrowserService
{
private static readonly Lazy<FileBrowserService> LazyInstance = new Lazy<FileBrowserService>(() => new FileBrowserService());
public static FileBrowserService Instance => LazyInstance.Value;
// Cache file browser operations for expanding node request
private Dictionary<string, FileBrowserOperation> ownerToFileBrowserMap = new Dictionary<string, FileBrowserOperation>();
private Dictionary<string, ValidatePathsCallback> validatePathsCallbackMap = new Dictionary<string, ValidatePathsCallback>();
private ConnectionService connectionService = null;
/// <summary>
/// Signature for callback method that validates the selected file paths
/// </summary>
/// <param name="eventArgs"></param>
public delegate bool ValidatePathsCallback(FileBrowserValidateEventArgs eventArgs, out string errorMessage);
internal ConnectionService ConnectionServiceInstance
{
get
{
if (connectionService == null)
{
connectionService = ConnectionService.Instance;
}
return connectionService;
}
set
{
connectionService = value;
}
}
/// <summary>
/// Service host object for sending/receiving requests/events.
/// Internal for testing purposes.
/// </summary>
internal IProtocolEndpoint ServiceHost
{
get;
set;
}
/// <summary>
/// Constructor
/// </summary>
public FileBrowserService()
{
}
/// <summary>
/// Register validate path callback
/// </summary>
/// <param name="service"></param>
/// <param name="callback"></param>
public void RegisterValidatePathsCallback(string service, ValidatePathsCallback callback)
{
if (this.validatePathsCallbackMap.ContainsKey(service))
{
this.validatePathsCallbackMap.Remove(service);
}
this.validatePathsCallbackMap.Add(service, callback);
}
/// <summary>
/// Initializes the service instance
/// </summary>
/// <param name="serviceHost"></param>
/// <param name="context"></param>
public void InitializeService(ServiceHost serviceHost)
{
this.ServiceHost = serviceHost;
// Open a file browser
serviceHost.SetRequestHandler(FileBrowserOpenRequest.Type, HandleFileBrowserOpenRequest);
// Expand a folder node
serviceHost.SetRequestHandler(FileBrowserExpandRequest.Type, HandleFileBrowserExpandRequest);
// Validate the selected files
serviceHost.SetRequestHandler(FileBrowserValidateRequest.Type, HandleFileBrowserValidateRequest);
// Close the file browser
serviceHost.SetRequestHandler(FileBrowserCloseRequest.Type, HandleFileBrowserCloseRequest);
}
#region request handlers
internal async Task HandleFileBrowserOpenRequest(
FileBrowserOpenParams fileBrowserParams,
RequestContext<bool> requestContext)
{
try
{
Task.Run(() => RunFileBrowserOpenTask(fileBrowserParams));
await requestContext.SendResult(true);
}
catch
{
await requestContext.SendResult(false);
}
}
internal async Task HandleFileBrowserExpandRequest(
FileBrowserExpandParams fileBrowserParams,
RequestContext<bool> requestContext)
{
try
{
Task.Run(() => RunFileBrowserExpandTask(fileBrowserParams));
await requestContext.SendResult(true);
}
catch
{
await requestContext.SendResult(false);
}
}
internal async Task HandleFileBrowserValidateRequest(
FileBrowserValidateParams fileBrowserParams,
RequestContext<bool> requestContext)
{
try
{
Task.Run(() => RunFileBrowserValidateTask(fileBrowserParams));
await requestContext.SendResult(true);
}
catch
{
await requestContext.SendResult(false);
}
}
internal async Task HandleFileBrowserCloseRequest(
FileBrowserCloseParams fileBrowserParams,
RequestContext<FileBrowserCloseResponse> requestContext)
{
FileBrowserCloseResponse response = new FileBrowserCloseResponse();
if (this.ownerToFileBrowserMap.ContainsKey(fileBrowserParams.OwnerUri))
{
this.ownerToFileBrowserMap.Remove(fileBrowserParams.OwnerUri);
response.Succeeded = true;
}
else
{
response.Succeeded = false;
}
await requestContext.SendResult(response);
}
#endregion
internal async Task RunFileBrowserOpenTask(FileBrowserOpenParams fileBrowserParams)
{
FileBrowserOpenCompleteParams result = new FileBrowserOpenCompleteParams();
try
{
ConnectionInfo connInfo;
this.ConnectionServiceInstance.TryFindConnection(fileBrowserParams.OwnerUri, out connInfo);
SqlConnection conn = null;
if (connInfo != null)
{
DbConnection dbConn;
connInfo.TryGetConnection(ConnectionType.Default, out dbConn);
if (dbConn != null)
{
conn = ReliableConnectionHelper.GetAsSqlConnection((IDbConnection)dbConn);
}
}
if (conn != null)
{
FileBrowserOperation browser = new FileBrowserOperation(conn, fileBrowserParams.ExpandPath, fileBrowserParams.FileFilters);
browser.PopulateFileTree();
this.ownerToFileBrowserMap.Add(fileBrowserParams.OwnerUri, browser);
result.FileTree = browser.FileTree;
result.Succeeded = true;
}
else
{
result.Succeeded = false;
}
}
catch (Exception ex)
{
result.Succeeded = false;
result.Message = ex.Message;
}
await ServiceHost.SendEvent(FileBrowserOpenCompleteNotification.Type, result);
}
internal async Task RunFileBrowserExpandTask(FileBrowserExpandParams fileBrowserParams)
{
FileBrowserExpandCompleteParams result = new FileBrowserExpandCompleteParams();
try
{
if (this.ownerToFileBrowserMap.ContainsKey(fileBrowserParams.OwnerUri))
{
FileBrowserOperation browser = this.ownerToFileBrowserMap[fileBrowserParams.OwnerUri];
browser.ExpandSelectedNode(fileBrowserParams.ExpandPath);
result.ExpandedNode = browser.FileTree.SelectedNode;
result.Succeeded = true;
}
else
{
result.Succeeded = false;
}
}
catch (Exception ex)
{
result.Succeeded = false;
result.Message = ex.Message;
}
await ServiceHost.SendEvent(FileBrowserExpandCompleteNotification.Type, result);
}
internal async Task RunFileBrowserValidateTask(FileBrowserValidateParams fileBrowserParams)
{
FileBrowserValidateCompleteParams result = new FileBrowserValidateCompleteParams();
try
{
if (this.validatePathsCallbackMap.ContainsKey(fileBrowserParams.ServiceType)
&& this.validatePathsCallbackMap[fileBrowserParams.ServiceType] != null
&& fileBrowserParams.SelectedFiles != null
&& fileBrowserParams.SelectedFiles.Length > 0)
{
string errorMessage;
result.Succeeded = this.validatePathsCallbackMap[fileBrowserParams.ServiceType](new FileBrowserValidateEventArgs
{
ServiceType = fileBrowserParams.ServiceType,
OwnerUri = fileBrowserParams.OwnerUri,
FilePaths = fileBrowserParams.SelectedFiles
}, out errorMessage);
if (!string.IsNullOrEmpty(errorMessage))
{
result.Message = errorMessage;
}
}
else
{
result.Succeeded = true;
}
}
catch (Exception ex)
{
result.Succeeded = false;
result.Message = ex.Message;
}
await ServiceHost.SendEvent(FileBrowserValidateCompleteNotification.Type, result);
}
}
}

View File

@@ -0,0 +1,30 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
/// <summary>
/// Event arguments for validating selected files in file browser
/// </summary>
public sealed class FileBrowserValidateEventArgs : EventArgs
{
/// <summary>
/// Connection uri
/// </summary>
public string OwnerUri { get; set; }
/// <summary>
/// Service which provide validation callback
/// </summary>
public string ServiceType { get; set; }
/// <summary>
/// Selected files
/// </summary>
public string[] FilePaths { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
namespace Microsoft.SqlTools.ServiceLayer.FileBrowser
{
/// <summary>
/// List of services that provide file validation callback to file browser service
/// </summary>
public static class FileValidationServiceConstants
{
/// <summary>
/// Backup
/// </summary>
public const string Backup = "Backup";
/// <summary>
/// Restore
/// </summary>
public const string Restore = "Restore";
}
}