mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-02 01:25:42 -05:00
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:
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// 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.Data;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using Microsoft.SqlServer.Management.Sdk.Sfc;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
|
||||
using Microsoft.SqlTools.ServiceLayer.FileBrowser;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
/// <summary>
|
||||
/// Validate selected file paths for backup/restore operations
|
||||
/// </summary>
|
||||
public static class DisasterRecoveryFileValidator
|
||||
{
|
||||
internal const string LocalSqlServer = "(local)";
|
||||
internal const string LocalMachineName = ".";
|
||||
|
||||
public static bool ValidatePaths(FileBrowserValidateEventArgs args, out string errorMessage)
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
bool result = true;
|
||||
SqlConnection connection = null;
|
||||
|
||||
if (args != null)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionService.Instance.TryFindConnection(args.OwnerUri, out connInfo);
|
||||
if (connInfo != null)
|
||||
{
|
||||
DbConnection dbConnection = null;
|
||||
connInfo.TryGetConnection(Connection.ConnectionType.Default, out dbConnection);
|
||||
if (dbConnection != null)
|
||||
{
|
||||
connection = ReliableConnectionHelper.GetAsSqlConnection(dbConnection);
|
||||
}
|
||||
}
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
bool isLocal = false;
|
||||
if (string.Compare(GetMachineName(connection.DataSource), Environment.MachineName, StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
isLocal = true;
|
||||
}
|
||||
|
||||
foreach (string filePath in args.FilePaths)
|
||||
{
|
||||
bool isFolder;
|
||||
bool existing = IsPathExisting(connection, filePath, out isFolder);
|
||||
|
||||
if (existing)
|
||||
{
|
||||
if (isFolder)
|
||||
{
|
||||
errorMessage = string.Format(SR.BackupPathIsFolderError, filePath);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the file path doesn't exist, check if the folder exists
|
||||
string folderPath = PathWrapper.GetDirectoryName(filePath);
|
||||
if (isLocal)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(folderPath) && !Directory.Exists(folderPath))
|
||||
{
|
||||
errorMessage = string.Format(SR.InvalidBackupPathError, folderPath);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isFolderOnRemote;
|
||||
bool existsOnRemote = IsPathExisting(connection, folderPath, out isFolderOnRemote);
|
||||
if (!existsOnRemote)
|
||||
{
|
||||
errorMessage = string.Format(SR.InvalidBackupPathError, folderPath);
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#region private methods
|
||||
|
||||
internal static bool IsPathExisting(SqlConnection connection, string path, out bool isFolder)
|
||||
{
|
||||
Request req = new Request
|
||||
{
|
||||
Urn = "Server/File[@FullName='" + Urn.EscapeString(path) + "']",
|
||||
Fields = new[] { "IsFile" }
|
||||
};
|
||||
|
||||
Enumerator en = new Enumerator();
|
||||
bool isExisting = false;
|
||||
isFolder = false;
|
||||
|
||||
using (DataSet ds = en.Process(connection, req))
|
||||
{
|
||||
if (FileBrowserBase.IsValidDataSet(ds))
|
||||
{
|
||||
isFolder = !(Convert.ToBoolean(ds.Tables[0].Rows[0]["IsFile"], CultureInfo.InvariantCulture));
|
||||
isExisting = true;
|
||||
}
|
||||
}
|
||||
|
||||
return isExisting;
|
||||
}
|
||||
|
||||
internal static string GetMachineName(string sqlServerName)
|
||||
{
|
||||
string machineName = string.Empty;
|
||||
if (sqlServerName != null)
|
||||
{
|
||||
string serverName = sqlServerName.ToLowerInvariant().Trim();
|
||||
if ((serverName == LocalSqlServer) || (serverName == LocalMachineName))
|
||||
{
|
||||
machineName = System.Environment.MachineName;
|
||||
}
|
||||
else
|
||||
{
|
||||
machineName = sqlServerName;
|
||||
if (sqlServerName.Trim().Length != 0)
|
||||
{
|
||||
// [0] = machine, [1] = instance
|
||||
return sqlServerName.Split('\\')[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return machineName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,11 @@ using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using System.Threading;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||
using System.Globalization;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.DisasterRecovery.RestoreOperation;
|
||||
using Microsoft.SqlTools.ServiceLayer.FileBrowser;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
{
|
||||
@@ -26,6 +25,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
private static readonly Lazy<DisasterRecoveryService> instance = new Lazy<DisasterRecoveryService>(() => new DisasterRecoveryService());
|
||||
private static ConnectionService connectionService = null;
|
||||
private SqlTaskManager sqlTaskManagerInstance = null;
|
||||
private FileBrowserService fileBrowserService = null;
|
||||
private RestoreDatabaseHelper restoreDatabaseService = new RestoreDatabaseHelper();
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +78,25 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current filebrowser service instance
|
||||
/// </summary>
|
||||
internal FileBrowserService FileBrowserServiceInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (fileBrowserService == null)
|
||||
{
|
||||
fileBrowserService = FileBrowserService.Instance;
|
||||
}
|
||||
return fileBrowserService;
|
||||
}
|
||||
set
|
||||
{
|
||||
fileBrowserService = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the service instance
|
||||
/// </summary>
|
||||
@@ -89,14 +108,18 @@ namespace Microsoft.SqlTools.ServiceLayer.DisasterRecovery
|
||||
// Create backup
|
||||
serviceHost.SetRequestHandler(BackupRequest.Type, HandleBackupRequest);
|
||||
|
||||
// Create respore task
|
||||
// Create restore task
|
||||
serviceHost.SetRequestHandler(RestoreRequest.Type, HandleRestoreRequest);
|
||||
|
||||
// Create respore plan
|
||||
// Create restore plan
|
||||
serviceHost.SetRequestHandler(RestorePlanRequest.Type, HandleRestorePlanRequest);
|
||||
|
||||
// Create respore config
|
||||
// Create restore config
|
||||
serviceHost.SetRequestHandler(RestoreConfigInfoRequest.Type, HandleRestoreConfigInfoRequest);
|
||||
|
||||
// Register file path validation callbacks
|
||||
FileBrowserServiceInstance.RegisterValidatePathsCallback(FileValidationServiceConstants.Backup, DisasterRecoveryFileValidator.ValidatePaths);
|
||||
FileBrowserServiceInstance.RegisterValidatePathsCallback(FileValidationServiceConstants.Restore, DisasterRecoveryFileValidator.ValidatePaths);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -3285,6 +3285,22 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string BackupPathIsFolderError
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.BackupPathIsFolderError);
|
||||
}
|
||||
}
|
||||
|
||||
public static string InvalidBackupPathError
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.InvalidBackupPathError);
|
||||
}
|
||||
}
|
||||
|
||||
public static string TaskInProgress
|
||||
{
|
||||
get
|
||||
@@ -3493,6 +3509,14 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string InvalidPathError
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.InvalidPathError);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ConnectionServiceListDbErrorNotConnected(string uri)
|
||||
{
|
||||
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
|
||||
@@ -4834,6 +4858,12 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string BackupTaskName = "BackupTaskName";
|
||||
|
||||
|
||||
public const string BackupPathIsFolderError = "BackupPathIsFolderError";
|
||||
|
||||
|
||||
public const string InvalidBackupPathError = "InvalidBackupPathError";
|
||||
|
||||
|
||||
public const string TaskInProgress = "TaskInProgress";
|
||||
|
||||
|
||||
@@ -4912,6 +4942,9 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string ScriptTaskName = "ScriptTaskName";
|
||||
|
||||
|
||||
public const string InvalidPathError = "InvalidPathError";
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
|
||||
@@ -1815,6 +1815,14 @@
|
||||
<value>Backup Database</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="BackupPathIsFolderError" xml:space="preserve">
|
||||
<value>The provided path specifies a directory but a file path is required: {0}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="InvalidBackupPathError" xml:space="preserve">
|
||||
<value> Cannot verify the existence of the backup file location: {0}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="TaskInProgress" xml:space="preserve">
|
||||
<value>In progress</value>
|
||||
<comment></comment>
|
||||
@@ -1919,4 +1927,8 @@
|
||||
<value>scripting</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="InvalidPathError" xml:space="preserve">
|
||||
<value>Cannot access the specified path on the server: {0}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -807,6 +807,10 @@ prototype_file_noApplicableFileGroup = No Applicable Filegroup
|
||||
# Backup Service
|
||||
BackupTaskName = Backup Database
|
||||
|
||||
# Backup File Validation Errors
|
||||
BackupPathIsFolderError = The provided path specifies a directory but a file path is required: {0}
|
||||
InvalidBackupPathError = Cannot verify the existence of the backup file location: {0}
|
||||
|
||||
############################################################################
|
||||
# Task Service
|
||||
TaskInProgress = In progress
|
||||
@@ -840,4 +844,8 @@ TheLastBackupTaken = The last backup taken ({0})
|
||||
|
||||
############################################################################
|
||||
# Generate Script
|
||||
ScriptTaskName = scripting
|
||||
ScriptTaskName = scripting
|
||||
|
||||
############################################################################
|
||||
# File Browser Validation Errors
|
||||
InvalidPathError = Cannot access the specified path on the server: {0}
|
||||
@@ -2250,6 +2250,21 @@
|
||||
<target state="new">scripting</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="BackupPathIsFolderError">
|
||||
<source>The provided path specifies a directory but a file path is required: {0}</source>
|
||||
<target state="new">The file name specified is also a directory name: {0}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidBackupPathError">
|
||||
<source> Cannot verify the existence of the backup file location: {0}</source>
|
||||
<target state="new"> Cannot verify the existence of the backup file location: {0}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidPathError">
|
||||
<source>Cannot access the specified path on the server: {0}</source>
|
||||
<target state="new">Cannot access the specified path on the server: {0}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -1,16 +1,16 @@
|
||||
// This file was generated by a T4 Template. Do not modify directly, instead update the SmoQueryModelDefinition.xml file
|
||||
// and re-run the T4 template. This can be done in Visual Studio by right-click in and choosing "Run Custom Tool",
|
||||
// or from the command-line on any platform by running "build.cmd -Target=CodeGen" or "build.sh -Target=CodeGen".
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Smo.Broker;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
{
|
||||
// This file was generated by a T4 Template. Do not modify directly, instead update the SmoQueryModelDefinition.xml file
|
||||
// and re-run the T4 template. This can be done in Visual Studio by right-click in and choosing "Run Custom Tool",
|
||||
// or from the command-line on any platform by running "build.cmd -Target=CodeGen" or "build.sh -Target=CodeGen".
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Composition;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Smo.Broker;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
{
|
||||
|
||||
[Export(typeof(SmoQuerier))]
|
||||
internal partial class SqlDatabaseQuerier: SmoQuerier
|
||||
@@ -1628,5 +1628,5 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel
|
||||
return Enumerable.Empty<SqlSmoObject>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// THIS FILE IS GENERATED BY A CODEGEN TOOL. DO NOT EDIT!!!!
|
||||
// IF YOU NEED TO MAKE CHANGES, EDIT THE .TT FILE!!!
|
||||
|
||||
// To regenerate either (1) save the .TT file from Visual Studio or (2) run the build script codegen task.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
{
|
||||
internal partial class Scripter
|
||||
{
|
||||
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
// THIS FILE IS GENERATED BY A CODEGEN TOOL. DO NOT EDIT!!!!
|
||||
// IF YOU NEED TO MAKE CHANGES, EDIT THE .TT FILE!!!
|
||||
|
||||
// To regenerate either (1) save the .TT file from Visual Studio or (2) run the build script codegen task.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.SqlParser.Intellisense;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
{
|
||||
internal partial class Scripter
|
||||
{
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
AddSupportedType(DeclarationType.Table, GetTableScripts, "Table", "table");
|
||||
@@ -243,6 +243,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Scripting
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user