mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-25 17:24:17 -05:00
Add getObjects from model request (#1634)
Co-authored-by: Alex Ma <alma1@microsoft.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// 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.DacFx.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Parameters to get objects from SQL model
|
||||
/// </summary>
|
||||
public class GetObjectsFromTSqlModelParams
|
||||
{
|
||||
/// <summary>
|
||||
/// URI of the project file this model is for
|
||||
/// </summary>
|
||||
public string ProjectUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Object types to query
|
||||
/// </summary>
|
||||
public string[] ObjectTypes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the get objects sql model request
|
||||
/// </summary>
|
||||
class GetObjectsFromTSqlModelRequest
|
||||
{
|
||||
public static readonly RequestType<GetObjectsFromTSqlModelParams, TSqlObjectInfo[]> Type =
|
||||
RequestType<GetObjectsFromTSqlModelParams, TSqlObjectInfo[]>.Create("dacFx/getObjectsFromTSqlModel");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// 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.DacFx.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes TSqlObject information
|
||||
/// </summary>
|
||||
public class TSqlObjectInfo
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string ObjectType { get; set; }
|
||||
|
||||
public TSqlObjectRelationship[] ReferencedObjects { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes objects referenced and their relationship type
|
||||
/// </summary>
|
||||
public class TSqlObjectRelationship
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public string ObjectType { get; set; }
|
||||
|
||||
public string RelationshipType { get; set; }
|
||||
|
||||
public string FromObjectName { get; set; }
|
||||
|
||||
public string FromObjectType { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,6 +57,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
||||
serviceHost.SetRequestHandler(GetDefaultPublishOptionsRequest.Type, this.HandleGetDefaultPublishOptionsRequest);
|
||||
serviceHost.SetRequestHandler(ParseTSqlScriptRequest.Type, this.HandleParseTSqlScriptRequest);
|
||||
serviceHost.SetRequestHandler(GenerateTSqlModelRequest.Type, this.HandleGenerateTSqlModelRequest);
|
||||
serviceHost.SetRequestHandler(GetObjectsFromTSqlModelRequest.Type, this.HandleGetObjectsFromTSqlModelRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -286,6 +287,28 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles request to get objects from sql model
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task HandleGetObjectsFromTSqlModelRequest(GetObjectsFromTSqlModelParams requestParams, RequestContext<TSqlObjectInfo[]> requestContext)
|
||||
{
|
||||
TSqlObjectInfo[] objectInfos = { };
|
||||
var model = projectModels.Value[requestParams.ProjectUri];
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
await requestContext.SendError(new Exception(SR.SqlProjectModelNotFound(requestParams.ProjectUri)));
|
||||
}
|
||||
else
|
||||
{
|
||||
GetObjectsFromTSqlModelOperation operation = new GetObjectsFromTSqlModelOperation(requestParams, model);
|
||||
objectInfos = operation.GetObjectsFromTSqlModel();
|
||||
await requestContext.SendResult(objectInfos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void ExecuteOperation(DacFxOperation operation, DacFxParams parameters, string taskName, RequestContext<DacFxResult> requestContext)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// 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.Linq;
|
||||
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.SqlServer.Dac.Model;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.DacFx
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to represent request to get objects from model
|
||||
/// </summary>
|
||||
public class GetObjectsFromTSqlModelOperation
|
||||
{
|
||||
private TSqlModel Model;
|
||||
public GetObjectsFromTSqlModelParams Parameters { get; }
|
||||
|
||||
public GetObjectsFromTSqlModelOperation(GetObjectsFromTSqlModelParams parameters, TSqlModel model)
|
||||
{
|
||||
Validate.IsNotNull("parameters", parameters);
|
||||
Validate.IsNotNull("model", model);
|
||||
this.Parameters = parameters;
|
||||
this.Model = model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get user defined top level type objects from model
|
||||
/// </summary>
|
||||
public TSqlObjectInfo[] GetObjectsFromTSqlModel()
|
||||
{
|
||||
try
|
||||
{
|
||||
var filters = Parameters.ObjectTypes.Select(t => MapType(t)).ToArray();
|
||||
var objects = Model.GetObjects(DacQueryScopes.UserDefined, filters).ToList();
|
||||
|
||||
return objects.Select(o => new TSqlObjectInfo
|
||||
{
|
||||
Name = o.Name.ToString(),
|
||||
ObjectType = o.ObjectType.Name,
|
||||
ReferencedObjects = o.GetReferencedRelationshipInstances().ToList().Select(r => new TSqlObjectRelationship
|
||||
{
|
||||
Name = r.ObjectName.ToString(),
|
||||
ObjectType = r.Object.ObjectType.Name,
|
||||
RelationshipType = r.Relationship.Type.ToString(),
|
||||
FromObjectName = r.FromObject.Name.ToString(),
|
||||
FromObjectType = r.FromObject.ObjectType.Name
|
||||
}).ToArray()
|
||||
}).ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(new Exception(SR.GetUserDefinedObjectsFromModelFailed, ex));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Class to represent the type of objects to query within the sql model
|
||||
/// </summary>
|
||||
public static ModelTypeClass MapType(string type)
|
||||
{
|
||||
switch (type.ToLower())
|
||||
{
|
||||
case "table":
|
||||
return ModelSchema.Table;
|
||||
case "view":
|
||||
return ModelSchema.View;
|
||||
default:
|
||||
throw new ArgumentException(SR.UnsupportedModelType(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9429,6 +9429,14 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetUserDefinedObjectsFromModelFailed
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.GetUserDefinedObjectsFromModelFailed);
|
||||
}
|
||||
}
|
||||
|
||||
public static string ConnectionServiceListDbErrorNotConnected(string uri)
|
||||
{
|
||||
return Keys.GetString(Keys.ConnectionServiceListDbErrorNotConnected, uri);
|
||||
@@ -9814,6 +9822,16 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
return Keys.GetString(Keys.ComputedColumnNeedToBePersistedInForeignKeyRuleDescription, columnName, foreignKeyName);
|
||||
}
|
||||
|
||||
public static string SqlProjectModelNotFound(string projectUri)
|
||||
{
|
||||
return Keys.GetString(Keys.SqlProjectModelNotFound, projectUri);
|
||||
}
|
||||
|
||||
public static string UnsupportedModelType(string type)
|
||||
{
|
||||
return Keys.GetString(Keys.UnsupportedModelType, type);
|
||||
}
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
@@ -13578,6 +13596,15 @@ namespace Microsoft.SqlTools.ServiceLayer
|
||||
public const string ComputedColumnNeedToBePersistedInForeignKeyRuleDescription = "ComputedColumnNeedToBePersistedInForeignKeyRuleDescription";
|
||||
|
||||
|
||||
public const string SqlProjectModelNotFound = "SqlProjectModelNotFound";
|
||||
|
||||
|
||||
public const string UnsupportedModelType = "UnsupportedModelType";
|
||||
|
||||
|
||||
public const string GetUserDefinedObjectsFromModelFailed = "GetUserDefinedObjectsFromModelFailed";
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
|
||||
@@ -5206,4 +5206,18 @@ The Query Processor estimates that implementing the following index could improv
|
||||
<comment>.
|
||||
Parameters: 0 - columnName (string), 1 - foreignKeyName (string) </comment>
|
||||
</data>
|
||||
<data name="SqlProjectModelNotFound" xml:space="preserve">
|
||||
<value>Could not find SQL model from project: {0}.</value>
|
||||
<comment>.
|
||||
Parameters: 0 - projectUri (string) </comment>
|
||||
</data>
|
||||
<data name="UnsupportedModelType" xml:space="preserve">
|
||||
<value>Unsupported model type: {0}.</value>
|
||||
<comment>.
|
||||
Parameters: 0 - type (string) </comment>
|
||||
</data>
|
||||
<data name="GetUserDefinedObjectsFromModelFailed" xml:space="preserve">
|
||||
<value>Failed to get user defined objects from model.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -2381,4 +2381,11 @@ ClusteredIndexCannotHaveFilterPredicateRuleDescription = Filter predicate is not
|
||||
ColumnCanOnlyAppearOnceInIndexIncludedColumnsRuleDescription(string columnName, string indexName, int rowNumber) = Column with name '{0}' has already been included to the index '{1}'. Row number: {2}.
|
||||
ColumnCannotDuplicateWitIndexKeyColumnsRuleDescription(string columnName, string indexName, int rowNumber) = Included column with name '{0}' has already been part of the index '{1}' and it cannot be included. Row number: {2}.
|
||||
ComputedColumnNeedToBePersistedAndNotNullInPrimaryKeyRuleDescription(string columnName) = The computed column with name '{0}' has to be persisted and not nullable to be part of a primary key.
|
||||
ComputedColumnNeedToBePersistedInForeignKeyRuleDescription(string columnName, string foreignKeyName) = The computed column with name '{0}' has to be persisted to be part of the foreign key '{1}'.
|
||||
ComputedColumnNeedToBePersistedInForeignKeyRuleDescription(string columnName, string foreignKeyName) = The computed column with name '{0}' has to be persisted to be part of the foreign key '{1}'.
|
||||
|
||||
############################################################################
|
||||
# TSql Model
|
||||
|
||||
SqlProjectModelNotFound(string projectUri) = Could not find SQL model from project: {0}.
|
||||
UnsupportedModelType(string type) = Unsupported model type: {0}.
|
||||
GetUserDefinedObjectsFromModelFailed = Failed to get user defined objects from model.
|
||||
@@ -6331,6 +6331,23 @@ The Query Processor estimates that implementing the following index could improv
|
||||
<target state="new">Dropped Ledger Views</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SqlProjectModelNotFound">
|
||||
<source>Could not find SQL model from project: {0}.</source>
|
||||
<target state="new">Could not find SQL model from project: {0}.</target>
|
||||
<note>.
|
||||
Parameters: 0 - projectUri (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="UnsupportedModelType">
|
||||
<source>Unsupported model type: {0}.</source>
|
||||
<target state="new">Unsupported model type: {0}.</target>
|
||||
<note>.
|
||||
Parameters: 0 - type (string) </note>
|
||||
</trans-unit>
|
||||
<trans-unit id="GetUserDefinedObjectsFromModelFailed">
|
||||
<source>Failed to get user defined objects from model.</source>
|
||||
<target state="new">Failed to get user defined objects from model.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ComputedColumnNeedToBePersistedAndNotNullInPrimaryKeyRuleDescription">
|
||||
<source>The computed column with name '{0}' has to be persisted and not nullable to be part of a primary key.</source>
|
||||
<target state="new">The computed column with name '{0}' has to be persisted and not nullable to be part of a primary key.</target>
|
||||
|
||||
@@ -1057,4 +1057,57 @@ public class TSqlModelRequestTests
|
||||
await service.HandleGenerateTSqlModelRequest(generateTSqlScriptParams, requestContext.Object);
|
||||
Assert.That(service.projectModels.Value, Contains.Key(generateTSqlScriptParams.ProjectUri), "Model was not stored under project uri");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify the get objects TSql Model handle
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task VerifyGetObjectsFromTSqlModelHandle()
|
||||
{
|
||||
string sqlTable1DefinitionPath = Path.Join(TSqlModelTestFolder, "table1.sql");
|
||||
string sqlTable2DefinitionPath = Path.Join(TSqlModelTestFolder, "table2.sql");
|
||||
string view1DefinitionPath = Path.Join(TSqlModelTestFolder, "view1.sql");
|
||||
const string table1 = @"CREATE TABLE [dbo].[table1]
|
||||
(
|
||||
[ID] INT NOT NULL PRIMARY KEY,
|
||||
)";
|
||||
const string table2 = @"CREATE TABLE [dbo].[table2]
|
||||
(
|
||||
[ID] INT NOT NULL PRIMARY KEY,
|
||||
)";
|
||||
const string view1 = "CREATE VIEW [dbo].[view1] AS SELECT dbo.table1.* FROM dbo.table1";
|
||||
// create sql file
|
||||
File.WriteAllText(sqlTable1DefinitionPath, table1);
|
||||
File.WriteAllText(sqlTable2DefinitionPath, table2);
|
||||
File.WriteAllText(view1DefinitionPath, view1);
|
||||
|
||||
var generateTSqlScriptParams = new GenerateTSqlModelParams
|
||||
{
|
||||
ProjectUri = Path.Join(TSqlModelTestFolder, "test.sqlproj"),
|
||||
ModelTargetVersion = "Sql160",
|
||||
FilePaths = new[] { sqlTable1DefinitionPath, sqlTable2DefinitionPath }
|
||||
};
|
||||
|
||||
GenerateTSqlModelOperation op = new GenerateTSqlModelOperation(generateTSqlScriptParams);
|
||||
var model = op.GenerateTSqlModel();
|
||||
|
||||
service.projectModels.Value.TryAdd(generateTSqlScriptParams.ProjectUri, model);
|
||||
|
||||
var getObjectsParams = new GetObjectsFromTSqlModelParams
|
||||
{
|
||||
ProjectUri = Path.Join(TSqlModelTestFolder, "test.sqlproj"),
|
||||
ObjectTypes = new[] { "Table" }
|
||||
};
|
||||
|
||||
var requestContext = new Mock<RequestContext<TSqlObjectInfo[]>>();
|
||||
var actualResponse = new List<TSqlObjectInfo>();
|
||||
requestContext.Setup(x => x.SendResult(It.IsAny<TSqlObjectInfo[]>()))
|
||||
.Callback<TSqlObjectInfo[]>(actual => actualResponse = actual.ToList())
|
||||
.Returns(Task.CompletedTask);
|
||||
await service.HandleGetObjectsFromTSqlModelRequest(getObjectsParams, requestContext.Object);
|
||||
|
||||
Assert.IsNotNull(actualResponse);
|
||||
Assert.AreEqual(actualResponse.Count, 2);
|
||||
CollectionAssert.AreEquivalent(actualResponse.Select(o => o.Name), new[] { "[dbo].[table1]", "[dbo].[table2]" }, "Table names do not match");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user