Simplify Object Management APIs (#2015)

* unify requests-wip

* wip

* unify api

* fix test

* add credential handler

* fix credential handler issue.

* generic type update

* fix scripting for user
This commit is contained in:
Alan Ren
2023-04-19 15:43:01 -07:00
committed by GitHub
parent 98ad0197e4
commit e314f839d8
57 changed files with 1802 additions and 2234 deletions

View File

@@ -0,0 +1,46 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
/// <summary>
/// Tests for the Credential management component
/// </summary>
public class CredentialTests
{
/// <summary>
/// TestHandleCreateCredentialRequest
/// </summary>
[Test]
public async Task TestCredentialOperations()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
// setup, drop credential if exists.
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var credential = ObjectManagementTestUtils.GetTestCredentialInfo();
var objUrn = ObjectManagementTestUtils.GetCredentialURN(credential.Name);
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn);
// create and update
var parametersForCreation = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionResult.ConnectionInfo.OwnerUri, "master", true, SqlObjectType.Credential, "", "");
await ObjectManagementTestUtils.SaveObject(parametersForCreation, credential);
var parametersForUpdate = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionResult.ConnectionInfo.OwnerUri, "master", false, SqlObjectType.Credential, "", objUrn);
await ObjectManagementTestUtils.SaveObject(parametersForUpdate, credential);
// cleanup
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn);
}
}
}
}

View File

@@ -0,0 +1,46 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
/// <summary>
/// Tests for the Login management component
/// </summary>
public class LoginTests
{
/// <summary>
/// Test the basic Create Login method handler
/// </summary>
// [Test]
public async Task TestHandleCreateLoginRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
// setup, drop credential if exists.
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var testLogin = ObjectManagementTestUtils.GetTestLoginInfo();
var objUrn = ObjectManagementTestUtils.GetLoginURN(testLogin.Name);
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn);
// create and update
var parametersForCreation = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionResult.ConnectionInfo.OwnerUri, "master", true, SqlObjectType.ServerLevelLogin, "", "");
await ObjectManagementTestUtils.SaveObject(parametersForCreation, testLogin);
var parametersForUpdate = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionResult.ConnectionInfo.OwnerUri, "master", false, SqlObjectType.ServerLevelLogin, "", objUrn);
await ObjectManagementTestUtils.SaveObject(parametersForUpdate, testLogin);
// cleanup
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, objUrn);
}
}
}
}

View File

@@ -11,7 +11,6 @@ using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
@@ -27,22 +26,15 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
private const string TableQuery = @"CREATE TABLE testTable1_RenamingTable (c1 int)";
private const string OwnerUri = "testDB";
private ObjectManagementService objectManagementService;
private SqlTestDb testDb;
private Mock<RequestContext<bool>> requestContextMock;
private Mock<RequestContext<RenameRequestResponse>> requestContextMock;
[SetUp]
public async Task TestInitialize()
{
this.testDb = await SqlTestDb.CreateNewAsync(serverType: TestServerType.OnPrem, query: TableQuery, dbNamePrefix: "RenameTest");
requestContextMock = new Mock<RequestContext<bool>>();
ConnectionService connectionService = LiveConnectionHelper.GetLiveTestConnectionService();
requestContextMock = new Mock<RequestContext<RenameRequestResponse>>();
TestConnectionResult connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(testDb.DatabaseName, OwnerUri, ConnectionType.Default);
ObjectManagementService.ConnectionServiceInstance = connectionService;
this.objectManagementService = new ObjectManagementService();
}
[TearDown]
@@ -55,10 +47,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
public async Task TestRenameTable()
{
//arrange & act
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
await ObjectManagementTestUtils.Service.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
//assert
requestContextMock.Verify(x => x.SendResult(It.Is<bool>(r => r == true)));
requestContextMock.Verify(x => x.SendResult(It.IsAny<RenameRequestResponse>()));
Query queryRenameObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.tables WHERE name='RenamingTable'");
Assert.That(queryRenameObject.HasExecuted, Is.True, "The query to check for the renamed table was not executed");
@@ -75,10 +67,10 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
public async Task TestRenameColumn()
{
//arrange & act
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1']", testDb.DatabaseName)), requestContextMock.Object);
await ObjectManagementTestUtils.Service.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1']", testDb.DatabaseName)), requestContextMock.Object);
//assert
requestContextMock.Verify(x => x.SendResult(It.Is<bool>(r => r == true)));
requestContextMock.Verify(x => x.SendResult(It.IsAny<RenameRequestResponse>()));
Query queryRenameObject = ExecuteQuery("SELECT * FROM " + testDb.DatabaseName + ".sys.columns WHERE name='RenameColumn'");
Assert.That(queryRenameObject.HasExecuted, Is.True, "The query to check for the renamed column was not executed");
@@ -96,7 +88,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
Assert.That(async () =>
{
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1_NOT']", testDb.DatabaseName)), requestContextMock.Object);
await ObjectManagementTestUtils.Service.HandleRenameRequest(this.InitRequestParams("RenameColumn", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_RenamingTable' and @Schema='dbo']/Column[@Name='C1_NOT']", testDb.DatabaseName)), requestContextMock.Object);
}, Throws.Exception.TypeOf<FailedOperationException>(), "Did find the column, which should not have existed");
}
@@ -105,7 +97,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
Assert.That(async () =>
{
await objectManagementService.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_Not' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
await ObjectManagementTestUtils.Service.HandleRenameRequest(this.InitRequestParams("RenamingTable", String.Format("Server/Database[@Name='{0}']/Table[@Name='testTable1_Not' and @Schema='dbo']", testDb.DatabaseName)), requestContextMock.Object);
}, Throws.Exception.TypeOf<FailedOperationException>(), "Did find the table, which should not have existed");
}
@@ -120,7 +112,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
};
Assert.That(async () =>
{
await objectManagementService.HandleRenameRequest(testRenameRequestParams, requestContextMock.Object);
await ObjectManagementTestUtils.Service.HandleRenameRequest(testRenameRequestParams, requestContextMock.Object);
}, Throws.Exception.TypeOf<Exception>(), "Did find the connection, which should not have existed");
}

View File

@@ -0,0 +1,206 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using System;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement.Contracts;
using Moq;
using Newtonsoft.Json.Linq;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
public static class ObjectManagementTestUtils
{
private static ObjectManagementService _objectManagementService;
static ObjectManagementTestUtils()
{
ObjectManagementService.ConnectionServiceInstance = LiveConnectionHelper.GetLiveTestConnectionService();
_objectManagementService = new ObjectManagementService();
}
internal static ObjectManagementService Service
{
get
{
return _objectManagementService;
}
}
public static string TestCredentialName = "Current User";
internal static string GetCurrentUserIdentity()
{
return string.Format(@"{0}\{1}", Environment.UserDomainName, Environment.UserName);
}
internal static string GetLoginURN(string name)
{
return string.Format("Server/Login[@Name='{0}']", name);
}
internal static string GetUserURN(string database, string name)
{
return string.Format("Server/Database[@Name='{0}']/User[@Name='{1}']", database, name);
}
internal static string GetCredentialURN(string name)
{
return string.Format("Server/Credential[@Name = '{0}']", name);
}
internal static LoginInfo GetTestLoginInfo()
{
return new LoginInfo()
{
Name = "TestLoginName_" + new Random().NextInt64(10000000, 90000000).ToString(),
AuthenticationType = LoginAuthenticationType.Sql,
WindowsGrantAccess = true,
MustChangePassword = false,
IsEnabled = false,
IsLockedOut = false,
EnforcePasswordPolicy = false,
EnforcePasswordExpiration = false,
Password = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER",
OldPassword = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER",
DefaultLanguage = "English - us_english",
DefaultDatabase = "master"
};
}
internal static UserInfo GetTestUserInfo(DatabaseUserType userType, string userName = null, string loginName = null)
{
return new UserInfo()
{
Type = userType,
AuthenticationType = ServerAuthenticationType.Sql,
Name = userName ?? "TestUserName_" + new Random().NextInt64(10000000, 90000000).ToString(),
LoginName = loginName,
Password = "placeholder" + new Random().NextInt64(10000000, 90000000).ToString() + "!*PLACEHOLDER",
DefaultSchema = "dbo",
OwnedSchemas = new string[] { "" }
};
}
internal static CredentialInfo GetTestCredentialInfo()
{
return new CredentialInfo()
{
Identity = GetCurrentUserIdentity(),
Name = TestCredentialName
};
}
internal static InitializeViewRequestParams GetInitializeViewRequestParams(string connectionUri, string database, bool isNewObject, SqlObjectType objectType, string parentUrn, string objectUrn)
{
return new InitializeViewRequestParams()
{
ConnectionUri = connectionUri,
Database = database,
IsNewObject = isNewObject,
ObjectType = objectType,
ContextId = Guid.NewGuid().ToString(),
ParentUrn = parentUrn,
ObjectUrn = objectUrn
};
}
internal static async Task SaveObject(InitializeViewRequestParams parameters, SqlObject obj)
{
// Initialize the view
var initViewRequestContext = new Mock<RequestContext<SqlObjectViewInfo>>();
initViewRequestContext.Setup(x => x.SendResult(It.IsAny<SqlObjectViewInfo>()))
.Returns(Task.FromResult<SqlObjectViewInfo>(null));
await Service.HandleInitializeViewRequest(parameters, initViewRequestContext.Object);
// Save the object
var saveObjectRequestContext = new Mock<RequestContext<SaveObjectRequestResponse>>();
saveObjectRequestContext.Setup(x => x.SendResult(It.IsAny<SaveObjectRequestResponse>()))
.Returns(Task.FromResult<SaveObjectRequestResponse>(new SaveObjectRequestResponse()));
await Service.HandleSaveObjectRequest(new SaveObjectRequestParams { ContextId = parameters.ContextId, Object = JToken.FromObject(obj) }, saveObjectRequestContext.Object);
// Dispose the view
var disposeViewRequestContext = new Mock<RequestContext<DisposeViewRequestResponse>>();
disposeViewRequestContext.Setup(x => x.SendResult(It.IsAny<DisposeViewRequestResponse>()))
.Returns(Task.FromResult<DisposeViewRequestResponse>(new DisposeViewRequestResponse()));
await Service.HandleDisposeViewRequest(new DisposeViewRequestParams { ContextId = parameters.ContextId }, disposeViewRequestContext.Object);
}
internal static async Task ScriptObject(InitializeViewRequestParams parameters, SqlObject obj)
{
// Initialize the view
var initViewRequestContext = new Mock<RequestContext<SqlObjectViewInfo>>();
initViewRequestContext.Setup(x => x.SendResult(It.IsAny<SqlObjectViewInfo>()))
.Returns(Task.FromResult<SqlObjectViewInfo>(null));
await Service.HandleInitializeViewRequest(parameters, initViewRequestContext.Object);
// Script the object
var scriptObjectRequestContext = new Mock<RequestContext<string>>();
scriptObjectRequestContext.Setup(x => x.SendResult(It.IsAny<string>()))
.Returns(Task.FromResult<string>(""));
await Service.HandleScriptObjectRequest(new ScriptObjectRequestParams { ContextId = parameters.ContextId, Object = JToken.FromObject(obj) }, scriptObjectRequestContext.Object);
// Dispose the view
var disposeViewRequestContext = new Mock<RequestContext<DisposeViewRequestResponse>>();
disposeViewRequestContext.Setup(x => x.SendResult(It.IsAny<DisposeViewRequestResponse>()))
.Returns(Task.FromResult<DisposeViewRequestResponse>(new DisposeViewRequestResponse()));
await Service.HandleDisposeViewRequest(new DisposeViewRequestParams { ContextId = parameters.ContextId }, disposeViewRequestContext.Object);
}
internal static async Task DropObject(string connectionUri, string objectUrn)
{
var dropParams = new DropRequestParams
{
ConnectionUri = connectionUri,
ObjectUrn = objectUrn
};
var dropRequestContext = new Mock<RequestContext<DropRequestResponse>>();
dropRequestContext.Setup(x => x.SendResult(It.IsAny<DropRequestResponse>()))
.Returns(Task.FromResult(new DropRequestResponse()));
await Service.HandleDropRequest(dropParams, dropRequestContext.Object);
}
internal static async Task<LoginInfo> CreateTestLogin(string connectionUri)
{
var testLogin = GetTestLoginInfo();
var parametersForCreation = GetInitializeViewRequestParams(connectionUri, "master", true, SqlObjectType.ServerLevelLogin, "", "");
await SaveObject(parametersForCreation, testLogin);
return testLogin;
}
internal static async Task<UserInfo> CreateTestUser(string connectionUri, DatabaseUserType userType,
string userName = null,
string loginName = null,
string databaseName = "master",
bool scriptUser = false)
{
var testUser = GetTestUserInfo(userType, userName, loginName);
var parametersForCreation = GetInitializeViewRequestParams(connectionUri, databaseName, true, SqlObjectType.User, "", "");
await SaveObject(parametersForCreation, testUser);
return testUser;
}
internal static async Task<CredentialInfo> SetupCredential(string connectionUri)
{
var credential = GetTestCredentialInfo();
var parametersForCreation = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionUri, "master", true, SqlObjectType.Credential, "", "");
await DropObject(connectionUri, GetCredentialURN(credential.Name));
await ObjectManagementTestUtils.SaveObject(parametersForCreation, credential);
return credential;
}
internal static async Task CleanupCredential(string connectionUri, CredentialInfo credential)
{
await DropObject(connectionUri, GetCredentialURN(credential.Name));
}
}
}

View File

@@ -0,0 +1,97 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Threading.Tasks;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
using Microsoft.SqlTools.ServiceLayer.ObjectManagement;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using NUnit.Framework;
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.ObjectManagement
{
/// <summary>
/// Tests for the User management component
/// </summary>
public class UserTests
{
/// <summary>
/// Test the basic Create User method handler
/// </summary>
[Test]
public async Task TestHandleSaveUserWithLoginRequest()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var connectionUri = connectionResult.ConnectionInfo.OwnerUri;
var login = await ObjectManagementTestUtils.CreateTestLogin(connectionUri);
var user = await ObjectManagementTestUtils.CreateTestUser(connectionUri, DatabaseUserType.WithLogin, null, login.Name);
var userUrn = ObjectManagementTestUtils.GetUserURN(connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName, user.Name);
var parameters = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionUri, "master", false, SqlObjectType.User, "", userUrn);
await ObjectManagementTestUtils.SaveObject(parameters, user);
await ObjectManagementTestUtils.DropObject(connectionUri, userUrn);
await ObjectManagementTestUtils.DropObject(connectionUri, ObjectManagementTestUtils.GetLoginURN(login.Name));
}
}
/// <summary>
/// Test the basic Create User method handler
/// </summary>
// [Test] - Windows-only
public async Task TestHandleCreateUserWithWindowsGroup()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var connectionUri = connectionResult.ConnectionInfo.OwnerUri;
var user = await ObjectManagementTestUtils.CreateTestUser(connectionUri, DatabaseUserType.WithWindowsGroupLogin, $"{Environment.MachineName}\\Administrator");
await ObjectManagementTestUtils.DropObject(connectionUri, ObjectManagementTestUtils.GetUserURN(connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName, user.Name));
}
}
/// <summary>
/// Test the basic Create User method handler
/// </summary>
// [Test] - needs contained DB
public async Task TestHandleCreateUserWithContainedSqlPassword()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
string databaseName = "CRM";
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync(databaseName, queryTempFile.FilePath);
var connectionUri = connectionResult.ConnectionInfo.OwnerUri;
var user = await ObjectManagementTestUtils.CreateTestUser(connectionUri, DatabaseUserType.Contained,
userName: null,
loginName: null,
databaseName: connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName);
await ObjectManagementTestUtils.DropObject(connectionResult.ConnectionInfo.OwnerUri, ObjectManagementTestUtils.GetUserURN(connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName, user.Name));
}
}
/// <summary>
/// Test the basic Create User method handler
/// </summary>
[Test]
public async Task TestScriptUserWithLogin()
{
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
{
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
var connectionUri = connectionResult.ConnectionInfo.OwnerUri;
var login = await ObjectManagementTestUtils.CreateTestLogin(connectionUri);
var user = await ObjectManagementTestUtils.CreateTestUser(connectionUri, DatabaseUserType.WithLogin, null, login.Name);
var userUrn = ObjectManagementTestUtils.GetUserURN(connectionResult.ConnectionInfo.ConnectionDetails.DatabaseName, user.Name);
var parameters = ObjectManagementTestUtils.GetInitializeViewRequestParams(connectionUri, "master", false, SqlObjectType.User, "", userUrn);
await ObjectManagementTestUtils.ScriptObject(parameters, user);
await ObjectManagementTestUtils.DropObject(connectionUri, userUrn);
await ObjectManagementTestUtils.DropObject(connectionUri, ObjectManagementTestUtils.GetLoginURN(login.Name));
}
}
}
}