diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionInfo.cs index 82542721..8388da10 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionInfo.cs @@ -18,6 +18,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection /// public class ConnectionInfo { + private static readonly DateTime UnixEpochUtc = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + /// /// Constructor /// @@ -27,7 +29,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection OwnerUri = ownerUri; ConnectionDetails = details; ConnectionId = Guid.NewGuid(); - IntellisenseMetrics = new InteractionMetrics(new int[] {50, 100, 200, 500, 1000, 2000}); + IntellisenseMetrics = new InteractionMetrics(new int[] { 50, 100, 200, 500, 1000, 2000 }); } /// @@ -72,11 +74,17 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection /// Returns true if the db connection is to a SQL db instance /// public bool IsSqlDb { get; set; } - + + /// /// Returns true if the sql connection is to a DW instance /// public bool IsSqlDW { get; set; } + /// + /// Returns true if Authentication mode is AzureMFA, determines if Access token is in use. + /// + public bool IsAzureAuth { get; set; } + /// /// Returns the connection Engine Edition /// @@ -158,8 +166,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection public void RemoveConnection(string connectionType) { Validate.IsNotNullOrEmptyString("Connection Type", connectionType); - DbConnection connection; - ConnectionTypeToConnectionMap.TryRemove(connectionType, out connection); + ConnectionTypeToConnectionMap.TryRemove(connectionType, out _); } /// @@ -169,18 +176,44 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection { foreach (var type in AllConnectionTypes) { - DbConnection connection; - ConnectionTypeToConnectionMap.TryRemove(type, out connection); + ConnectionTypeToConnectionMap.TryRemove(type, out _); } - } + } /// /// Updates the Auth Token and Expires On fields /// - public void UpdateAuthToken(string token, int expiresOn) + public bool TryUpdateAccessToken(SecurityToken? securityToken) { - ConnectionDetails.AzureAccountToken = token; - ConnectionDetails.ExpiresOn = expiresOn; + if (securityToken != null && !string.IsNullOrEmpty(securityToken.Token) && IsAzureAuth && IsAccessTokenExpired) + { + ConnectionDetails.AzureAccountToken = securityToken.Token; + ConnectionDetails.ExpiresOn = securityToken.ExpiresOn; + return true; + } + return false; + } + + /// + /// Returns true if Access token saved in connection details is expired or about to expire in 2 minutes. + /// + private bool IsAccessTokenExpired + { + get + { + if (IsAzureAuth && ConnectionDetails.ExpiresOn != null && double.TryParse(ConnectionDetails.ExpiresOn.ToString(), out var expiresOn)) + { + DateTime dateTime = UnixEpochUtc.AddSeconds(expiresOn); + + // Check if access token is already expired or shall expire in 2 minutes. + if (dateTime <= DateTime.UtcNow.AddMinutes(2)) + { + Logger.Verbose("Access token found expired."); + return true; + } + } + return false; + } } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs index 0792e0b8..256f5923 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs @@ -304,7 +304,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection return; } this.TokenUpdateUris.Remove(tokenRefreshedParams.Uri, out var result); - connection.UpdateAuthToken(tokenRefreshedParams.Token, tokenRefreshedParams.ExpiresOn); + connection.TryUpdateAccessToken(new SecurityToken() { Token = tokenRefreshedParams.Token, ExpiresOn = tokenRefreshedParams.ExpiresOn }); } /// @@ -585,6 +585,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection connectionInfo.MajorVersion = serverInfo.ServerMajorVersion; connectionInfo.IsSqlDb = serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDatabase; connectionInfo.IsSqlDW = (serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDataWarehouse); + // Determines that access token is used for creating connection. + connectionInfo.IsAzureAuth = connectionInfo.ConnectionDetails.AuthenticationType == "AzureMFA"; connectionInfo.EngineEdition = (DatabaseEngineEdition)serverInfo.EngineEditionId; // Azure Data Studio supports SQL Server 2014 and later releases. response.IsSupportedVersion = serverInfo.IsCloud || serverInfo.ServerMajorVersion >= 12; diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/RefreshTokenNotification.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/RefreshTokenNotification.cs index 9cf02e81..e41498ef 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/RefreshTokenNotification.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/RefreshTokenNotification.cs @@ -7,7 +7,7 @@ using Microsoft.SqlTools.Hosting.Protocol.Contracts; namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts { - class RefreshTokenParams + public class RefreshTokenParams { /// /// ID of the tenant @@ -39,14 +39,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts /// /// Refresh token request mapping entry /// - class RefreshTokenNotification + public class RefreshTokenNotification { public static readonly EventType Type = EventType.Create("account/refreshToken"); } - class TokenRefreshedParams + public class TokenRefreshedParams { /// /// Gets or sets the refresh token. @@ -64,7 +64,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts public string Uri { get; set; } } - class TokenRefreshedNotification + public class TokenRefreshedNotification { public static readonly EventType Type = diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityToken.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityToken.cs new file mode 100644 index 00000000..622a4460 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityToken.cs @@ -0,0 +1,25 @@ +// +// 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.Connection.Contracts +{ + public class SecurityToken + { + /// + /// Gets or sets the refresh token. + /// + public string Token { get; set; } + + /// + /// Gets or sets the token expiration, a Unix epoch + /// + public int? ExpiresOn { get; set; } + + /// + /// Gets or sets the token type, e.g. 'Bearer' + /// + public string? TokenType { get; set; } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityTokenRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityTokenRequest.cs index bbcefd1c..25608c96 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityTokenRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/Contracts/SecurityTokenRequest.cs @@ -7,7 +7,7 @@ using Microsoft.SqlTools.Hosting.Protocol.Contracts; namespace Microsoft.SqlTools.ServiceLayer.Connection.Contracts { - class RequestSecurityTokenParams + class RequestSecurityTokenParams { /// /// Gets or sets the address of the authority to issue token. diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs index 3db9f926..34056387 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/ExpandRequest.cs @@ -4,6 +4,7 @@ // using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts { @@ -49,6 +50,11 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts /// Path identifying the node to expand. See for details /// public string NodePath { get; set; } + + /// + /// Security token for AzureMFA authentication for refresing access token on connection. + /// + public SecurityToken? SecurityToken { get; set; } } /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs index 15fb4c20..5601295a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Contracts/RefreshRequest.cs @@ -10,7 +10,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Contracts /// /// Parameters to the . /// - public class RefreshParams: ExpandParams + public class RefreshParams : ExpandParams { } diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs index 54f98e2a..2a91d8b7 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/Nodes/TreeNode.cs @@ -240,14 +240,14 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes /// Expands this node and returns its children /// /// Children as an IList. This is the raw children collection, not a copy - public IList Expand(string name, CancellationToken cancellationToken) + public IList Expand(string name, CancellationToken cancellationToken, string? accessToken = null) { // TODO consider why solution explorer has separate Children and Items options if (children.IsInitialized) { return children; } - PopulateChildren(false, name, cancellationToken); + PopulateChildren(false, name, cancellationToken, accessToken); return children; } @@ -255,19 +255,19 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes /// Expands this node and returns its children /// /// Children as an IList. This is the raw children collection, not a copy - public IList Expand(CancellationToken cancellationToken) + public IList Expand(CancellationToken cancellationToken, string? accessToken = null) { - return Expand(null, cancellationToken); + return Expand(null, cancellationToken, accessToken); } /// /// Refresh this node and returns its children /// /// Children as an IList. This is the raw children collection, not a copy - public virtual IList Refresh(CancellationToken cancellationToken) + public virtual IList Refresh(CancellationToken cancellationToken, string? accessToken = null) { // TODO consider why solution explorer has separate Children and Items options - PopulateChildren(true, null, cancellationToken); + PopulateChildren(true, null, cancellationToken, accessToken); return children; } @@ -319,7 +319,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes return Parent as T; } - protected virtual void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken) + protected virtual void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken, string? accessToken = null) { Logger.Write(TraceEventType.Verbose, string.Format(CultureInfo.InvariantCulture, "Populating oe node :{0}", this.GetNodePath())); Debug.Assert(IsAlwaysLeaf == false); @@ -335,6 +335,9 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.Nodes children.Clear(); BeginChildrenInit(); + // Update access token for future queries + context.UpdateAccessToken(accessToken); + try { ErrorMessage = null; diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs index 9a95825f..678df5cc 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/ObjectExplorerService.cs @@ -212,7 +212,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer } else { - RunExpandTask(session, expandParams); + bool refreshNeeded = session.ConnectionInfo.TryUpdateAccessToken(expandParams.SecurityToken); + RunExpandTask(session, expandParams, refreshNeeded); return true; } }; @@ -239,6 +240,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer } else { + session.ConnectionInfo.TryUpdateAccessToken(refreshParams.SecurityToken); RunExpandTask(session, refreshParams, true); } await context.SendResult(true); @@ -373,12 +375,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer } - internal Task ExpandNode(ObjectExplorerSession session, string nodePath, bool forceRefresh = false) + internal Task ExpandNode(ObjectExplorerSession session, string nodePath, bool forceRefresh = false, SecurityToken? securityToken = null) { - return Task.Run(() => QueueExpandNodeRequest(session, nodePath, forceRefresh)); + return Task.Run(() => QueueExpandNodeRequest(session, nodePath, forceRefresh, securityToken)); } - internal ExpandResponse QueueExpandNodeRequest(ObjectExplorerSession session, string nodePath, bool forceRefresh = false) + internal ExpandResponse QueueExpandNodeRequest(ObjectExplorerSession session, string nodePath, bool forceRefresh = false, SecurityToken? securityToken = null) { NodeInfo[] nodes = null; TreeNode node = session.Root.FindNodeByPath(nodePath); @@ -432,15 +434,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer waitForLockTimeout: timeout, bindOperation: (bindingContext, cancelToken) => { + if (!session.ConnectionInfo.IsAzureAuth) + { + // explicitly set null here to prevent setting access token for non-Azure auth modes. + securityToken = null; + } + if (forceRefresh) { Logger.Verbose($"Forcing refresh for {nodePath}"); - nodes = node.Refresh(cancelToken).Select(x => x.ToNodeInfo()).ToArray(); + nodes = node.Refresh(cancelToken, securityToken?.Token).Select(x => x.ToNodeInfo()).ToArray(); } else { Logger.Verbose($"Expanding {nodePath}"); - nodes = node.Expand(cancelToken).Select(x => x.ToNodeInfo()).ToArray(); + nodes = node.Expand(cancelToken, securityToken?.Token).Select(x => x.ToNodeInfo()).ToArray(); } response.Nodes = nodes; response.ErrorMessage = node.ErrorMessage; @@ -635,7 +643,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer private async Task ExpandNodeAsync(ObjectExplorerSession session, ExpandParams expandParams, CancellationToken cancellationToken, bool forceRefresh = false) { ExpandResponse response = null; - response = await ExpandNode(session, expandParams.NodePath, forceRefresh); + response = await ExpandNode(session, expandParams.NodePath, forceRefresh, expandParams.SecurityToken); if (cancellationToken.IsCancellationRequested) { Logger.Write(TraceEventType.Verbose, "OE expand canceled"); diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs index 3c98ca9e..04b9ca47 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/DatabaseTreeNode.cs @@ -16,7 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { internal partial class DatabaseTreeNode { - public DatabaseTreeNode(ServerNode serverNode, string databaseName): this() + public DatabaseTreeNode(ServerNode serverNode, string databaseName) : this() { Parent = serverNode; NodeValue = databaseName; @@ -52,12 +52,12 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel } } - protected override void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken) + protected override void PopulateChildren(bool refresh, string name, CancellationToken cancellationToken, string? accessToken = null) { var smoQueryContext = this.GetContextAs(); if (IsAccessible(smoQueryContext)) { - base.PopulateChildren(refresh, name, cancellationToken); + base.PopulateChildren(refresh, name, cancellationToken, accessToken); } else { diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoExtensions.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoExtensions.cs new file mode 100644 index 00000000..6bc5bf8b --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoExtensions.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +using Microsoft.SqlServer.Management.Smo; +using Microsoft.SqlTools.ServiceLayer.Connection; + +namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel +{ + internal static class SmoExtensions + { + /// + /// Updates access token on the connection context of instance. + /// + /// (this) SMO SQL Object containing connection context. + /// Access token + public static void UpdateAccessToken(this SqlSmoObject sqlObj, string accessToken) + { + if (sqlObj != null && !string.IsNullOrEmpty(accessToken) + && sqlObj.ExecutionManager != null + && sqlObj.ExecutionManager.ConnectionContext != null) + { + sqlObj.ExecutionManager.ConnectionContext.AccessToken = new AzureAccessToken(accessToken); + } + } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryContext.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryContext.cs index 1752588b..059c65b0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryContext.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoQueryContext.cs @@ -46,17 +46,19 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel /// /// The server SMO will query against /// - public Server Server { + public Server Server + { get { return GetObjectWithOpenedConnection(server); - } + } } /// /// Optional Database context object to query against /// - public Database Database { + public Database Database + { get { return GetObjectWithOpenedConnection(database); @@ -70,7 +72,8 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel /// /// Parent of a give node to use for queries /// - public SmoObjectBase Parent { + public SmoObjectBase Parent + { get { return GetObjectWithOpenedConnection(parent); @@ -86,7 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel /// for specific SMO types /// public IMultiServiceProvider ServiceProvider { get; private set; } - + /// /// Helper method to cast a parent to a specific type /// @@ -116,7 +119,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel ObjectExplorerService service = ServiceProvider.GetService(); if (service == null) { - throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, + throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SqlTools.Hosting.SR.ServiceNotFound, nameof(ObjectExplorerService))); } @@ -147,7 +150,7 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel { get { - if(validFor == 0) + if (validFor == 0) { validFor = ServerVersionHelper.GetValidForFlag(SqlServerType, Database); } @@ -169,6 +172,31 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel return smoObj; } + /// + /// Updates access token on parent connection context. + /// + /// Acquired access token + public void UpdateAccessToken(string? accessToken) + { + if (!string.IsNullOrEmpty(accessToken)) + { + // Update all applicable nodes that could contain access token + // to prevent stale token from being re-used. + if (server != null) + { + (server as SqlSmoObject).UpdateAccessToken(accessToken); + } + if (database != null) + { + (database as SqlSmoObject).UpdateAccessToken(accessToken); + } + if (parent != null) + { + (parent as SqlSmoObject).UpdateAccessToken(accessToken); + } + } + } + /// /// Ensures the server objects connection context is open. This is used by all child objects, and /// the only way to easily access is via the server object. This should be called during access of diff --git a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoWrapper.cs b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoWrapper.cs index 7adf74e3..773a81d7 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoWrapper.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/ObjectExplorer/SmoModel/SmoWrapper.cs @@ -14,11 +14,21 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel /// internal class SmoWrapper { + /// + /// Creates instance of from provided instance. + /// + /// Server connection instance. + /// Server instance. public virtual Server CreateServer(ServerConnection serverConn) { return serverConn == null ? null : new Server(serverConn); } + /// + /// Checks if connection is open on the instance. + /// + /// SMO Object containing connection context. + /// True if connection is open, otherwise false. public virtual bool IsConnectionOpen(SmoObjectBase smoObj) { SqlSmoObject sqlObj = smoObj as SqlSmoObject; @@ -28,6 +38,10 @@ namespace Microsoft.SqlTools.ServiceLayer.ObjectExplorer.SmoModel && sqlObj.ExecutionManager.ConnectionContext.IsOpen; } + /// + /// Opens connection on the connection context of instance. + /// + /// SMO Object containing connection context. public virtual void OpenConnection(SmoObjectBase smoObj) { SqlSmoObject sqlObj = smoObj as SqlSmoObject; diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/LiveConnectionHelper.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/LiveConnectionHelper.cs index 7d8ce12f..3e960c1d 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/LiveConnectionHelper.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Utility/LiveConnectionHelper.cs @@ -18,9 +18,10 @@ using System.Threading; namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility { - public class LiveConnectionException : Exception { - public LiveConnectionException(string message) - : base(message) { } + public class LiveConnectionException : Exception + { + public LiveConnectionException(string message) + : base(message) { } } public class LiveConnectionHelper @@ -48,7 +49,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility public static TestConnectionResult InitLiveConnectionInfo(string databaseName = null, string ownerUri = null) => InitLiveConnectionInfoAsync(databaseName, ownerUri, ServiceLayer.Connection.ConnectionType.Default).ConfigureAwait(false).GetAwaiter().GetResult(); - public static async Task InitLiveConnectionInfoAsync(string databaseName = "master", string ownerUri = null, + public static async Task InitLiveConnectionInfoAsync(string databaseName = "master", string ownerUri = null, string connectionType = ServiceLayer.Connection.ConnectionType.Default, TestServerType serverType = TestServerType.OnPrem) { ScriptFile scriptFile = null; @@ -58,7 +59,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility scriptFile = TestServiceProvider.Instance.WorkspaceService.Workspace.GetFile(ownerUri); ownerUri = scriptFile.ClientUri; } - if (string.IsNullOrEmpty(databaseName)) + if (string.IsNullOrEmpty(databaseName)) { databaseName = "master"; } diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs index aa7b6667..4ffdb32b 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/ObjectExplorer/ObjectExplorerServiceTests.cs @@ -295,7 +295,7 @@ namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ObjectExplorer public void FindNodeCanExpandParentNodes() { var mockTreeNode = new Mock(); - object[] populateChildrenArguments = { ItExpr.Is(x => x == false), ItExpr.IsNull(), new CancellationToken() }; + object[] populateChildrenArguments = { ItExpr.Is(x => x == false), ItExpr.IsNull(), new CancellationToken(), ItExpr.IsNull() }; mockTreeNode.Protected().Setup("PopulateChildren", populateChildrenArguments); mockTreeNode.Object.IsAlwaysLeaf = false;