diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/AssessmentRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/AssessmentRequest.cs index c360d854..ed48a0d0 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/AssessmentRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/AssessmentRequest.cs @@ -1,129 +1,129 @@ -// -// 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; - -using Microsoft.SqlServer.Management.Assessment; - -namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts -{ - /// - /// Parameters for executing a query from a provided string - /// - public class AssessmentParams - { - /// - /// Gets or sets the owner uri to get connection from - /// - public string OwnerUri { get; set; } - - /// - /// Gets or sets the target type - /// - public SqlObjectType TargetType { get; set; } - } - - /// - /// Describes an item returned by SQL Assessment RPC methods - /// - public class AssessmentItemInfo - { - /// - /// Gets or sets assessment ruleset version. - /// - public string RulesetVersion { get; set; } - - /// - /// Gets or sets assessment ruleset name - /// - public string RulesetName { get; set; } - - /// - /// Gets or sets assessed target's type. - /// Supported values: 1 - server, 2 - database. - /// - public SqlObjectType TargetType { get; set; } - - /// - /// Gets or sets the assessed object's name. - /// - public string TargetName { get; set; } - - /// - /// Gets or sets check's ID. - /// - public string CheckId { get; set; } - - /// - /// Gets or sets tags assigned to this item. - /// - public string[] Tags { get; set; } - - /// - /// Gets or sets a display name for this item. - /// - public string DisplayName { get; set; } - - /// - /// Gets or sets a brief description of the item's purpose. - /// - public string Description { get; set; } - - /// - /// Gets or sets a containing - /// an link to a page providing detailed explanation - /// of the best practice. - /// - public string HelpLink { get; set; } - - /// - /// Gets or sets a indicating - /// severity level assigned to this items. - /// Values are: "Information", "Warning", "Critical". - /// - public string Level { get; set; } - } - - /// - /// Generic SQL Assessment Result - /// - /// - /// Result item's type derived from - /// - public class AssessmentResultData - where T : AssessmentItemInfo - { - /// - /// Gets the collection of assessment results. - /// - public List Items { get; } = new List(); - - /// - /// Gets or sets SQL Assessment API version. - /// - public string ApiVersion { get; set; } - } - - /// - /// Generic SQL Assessment Result - /// - /// - /// Result item's type derived from - /// - public class AssessmentResult : AssessmentResultData - where T : AssessmentItemInfo - { - /// - /// Gets or sets a value indicating - /// if assessment operation was successful. - /// - public bool Success { get; set; } - - /// - /// Gets or sets an status message for the operation. - /// - public string ErrorMessage { get; set; } - } -} +// +// 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; + +using Microsoft.SqlServer.Management.Assessment; + +namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts +{ + /// + /// Parameters for executing a query from a provided string + /// + public class AssessmentParams + { + /// + /// Gets or sets the owner uri to get connection from + /// + public string OwnerUri { get; set; } + + /// + /// Gets or sets the target type + /// + public SqlObjectType TargetType { get; set; } + } + + /// + /// Describes an item returned by SQL Assessment RPC methods + /// + public class AssessmentItemInfo + { + /// + /// Gets or sets assessment ruleset version. + /// + public string RulesetVersion { get; set; } + + /// + /// Gets or sets assessment ruleset name + /// + public string RulesetName { get; set; } + + /// + /// Gets or sets assessed target's type. + /// Supported values: 1 - server, 2 - database. + /// + public SqlObjectType TargetType { get; set; } + + /// + /// Gets or sets the assessed object's name. + /// + public string TargetName { get; set; } + + /// + /// Gets or sets check's ID. + /// + public string CheckId { get; set; } + + /// + /// Gets or sets tags assigned to this item. + /// + public string[] Tags { get; set; } + + /// + /// Gets or sets a display name for this item. + /// + public string DisplayName { get; set; } + + /// + /// Gets or sets a brief description of the item's purpose. + /// + public string Description { get; set; } + + /// + /// Gets or sets a containing + /// an link to a page providing detailed explanation + /// of the best practice. + /// + public string HelpLink { get; set; } + + /// + /// Gets or sets a indicating + /// severity level assigned to this items. + /// Values are: "Information", "Warning", "Critical". + /// + public string Level { get; set; } + } + + /// + /// Generic SQL Assessment Result + /// + /// + /// Result item's type derived from + /// + public class AssessmentResultData + where T : AssessmentItemInfo + { + /// + /// Gets the collection of assessment results. + /// + public List Items { get; } = new List(); + + /// + /// Gets or sets SQL Assessment API version. + /// + public string ApiVersion { get; set; } + } + + /// + /// Generic SQL Assessment Result + /// + /// + /// Result item's type derived from + /// + public class AssessmentResult : AssessmentResultData + where T : AssessmentItemInfo + { + /// + /// Gets or sets a value indicating + /// if assessment operation was successful. + /// + public bool Success { get; set; } + + /// + /// Gets or sets an status message for the operation. + /// + public string ErrorMessage { get; set; } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GenerateScriptRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GenerateScriptRequest.cs index ead2b543..0a9f666d 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GenerateScriptRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GenerateScriptRequest.cs @@ -1,56 +1,56 @@ -// -// 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; -using Microsoft.SqlTools.Hosting.Protocol.Contracts; -using Microsoft.SqlTools.ServiceLayer.TaskServices; -using Microsoft.SqlTools.ServiceLayer.Utility; - -namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts -{ - /// - /// Parameters for executing a query from a provided string - /// - public class GenerateScriptParams - { - /// - /// Gets or sets a list of assessment result items - /// to be written to a table - /// - public List Items { get; set; } - - public TaskExecutionMode TaskExecutionMode { get; set; } - - public string TargetServerName { get; set; } - - public string TargetDatabaseName { get; set; } - } - - public class GenerateScriptResult - { - /// - /// Gets or sets a value indicating - /// if assessment operation was successful - /// - public bool Success { get; set; } - - /// - /// Gets or sets an status message for the operation - /// - public string ErrorMessage { get; set; } - - /// - /// Gets or sets script text - /// - public string Script { get; set; } - } - - public class GenerateScriptRequest - { - public static readonly - RequestType Type = - RequestType.Create("assessment/generateScript"); - } -} +// +// 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; +using Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts +{ + /// + /// Parameters for executing a query from a provided string + /// + public class GenerateScriptParams + { + /// + /// Gets or sets a list of assessment result items + /// to be written to a table + /// + public List Items { get; set; } + + public TaskExecutionMode TaskExecutionMode { get; set; } + + public string TargetServerName { get; set; } + + public string TargetDatabaseName { get; set; } + } + + public class GenerateScriptResult + { + /// + /// Gets or sets a value indicating + /// if assessment operation was successful + /// + public bool Success { get; set; } + + /// + /// Gets or sets an status message for the operation + /// + public string ErrorMessage { get; set; } + + /// + /// Gets or sets script text + /// + public string Script { get; set; } + } + + public class GenerateScriptRequest + { + public static readonly + RequestType Type = + RequestType.Create("assessment/generateScript"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GetAssessmentItemsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GetAssessmentItemsRequest.cs index b194da7e..c194b7a5 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GetAssessmentItemsRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/GetAssessmentItemsRequest.cs @@ -1,33 +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.SqlAssessment.Contracts -{ - /// - /// Parameters for executing a query from a provided string - /// - public class GetAssessmentItemsParams : AssessmentParams - { - // a placeholder for future specialization - } - - /// - /// Describes a check used to assess SQL Server objects. - /// - public class CheckInfo : AssessmentItemInfo - { - // a placeholder for future specialization - } - - - public class GetAssessmentItemsRequest - { - public static readonly RequestType> Type = - RequestType>.Create( - "assessment/getAssessmentItems"); - } -} +// +// 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.SqlAssessment.Contracts +{ + /// + /// Parameters for executing a query from a provided string + /// + public class GetAssessmentItemsParams : AssessmentParams + { + // a placeholder for future specialization + } + + /// + /// Describes a check used to assess SQL Server objects. + /// + public class CheckInfo : AssessmentItemInfo + { + // a placeholder for future specialization + } + + + public class GetAssessmentItemsRequest + { + public static readonly RequestType> Type = + RequestType>.Create( + "assessment/getAssessmentItems"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/InvokeRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/InvokeRequest.cs index 6df4a01d..ede6a0ae 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/InvokeRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/Contracts/InvokeRequest.cs @@ -1,82 +1,82 @@ -// -// 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 Microsoft.SqlTools.Hosting.Protocol.Contracts; -using Microsoft.SqlTools.ServiceLayer.Utility; - -namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts -{ - /// - /// Parameters for executing a query from a provided string - /// - public class InvokeParams : AssessmentParams - { - // a placeholder for future specialization - } - - /// - /// SQL Assessment result item kind. - /// - /// - /// SQL Assessment run is a set of checks. Every check - /// may return a result item. Normally it is a note containing - /// recommendations on improving target's configuration. - /// But some checks may fail to obtain data due to access - /// restrictions or data integrity. In this case - /// the check produces an error or a warning. - /// - public enum AssessmentResultItemKind - { - /// - /// SQL Assessment item contains recommendation - /// - Note = 0, - - /// - /// SQL Assessment item contains a warning on - /// limited assessment capabilities - /// - Warning = 1, - - /// - /// SQL Assessment item contain a description of - /// error occured in the course of assessment run - /// - Error = 2 - } - - /// - /// Describes an assessment result item - /// containing a recommendation based on best practices. - /// - public class AssessmentResultItem : AssessmentItemInfo - { - /// - /// Gets or sets a message to the user - /// containing the recommendation. - /// - public string Message { get; set; } - - /// - /// Gets or sets result type: - /// 0 - real result, 1 - warning, 2 - error. - /// - public AssessmentResultItemKind Kind { get; set; } - - /// - /// Gets or sets date and time - /// when the item had been acquired. - /// - public DateTimeOffset Timestamp { get; set; } - } - - public class InvokeRequest - { - public static readonly - RequestType> Type = - RequestType>.Create("assessment/invoke"); - } -} +// +// 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 Microsoft.SqlTools.Hosting.Protocol.Contracts; +using Microsoft.SqlTools.ServiceLayer.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts +{ + /// + /// Parameters for executing a query from a provided string + /// + public class InvokeParams : AssessmentParams + { + // a placeholder for future specialization + } + + /// + /// SQL Assessment result item kind. + /// + /// + /// SQL Assessment run is a set of checks. Every check + /// may return a result item. Normally it is a note containing + /// recommendations on improving target's configuration. + /// But some checks may fail to obtain data due to access + /// restrictions or data integrity. In this case + /// the check produces an error or a warning. + /// + public enum AssessmentResultItemKind + { + /// + /// SQL Assessment item contains recommendation + /// + Note = 0, + + /// + /// SQL Assessment item contains a warning on + /// limited assessment capabilities + /// + Warning = 1, + + /// + /// SQL Assessment item contain a description of + /// error occured in the course of assessment run + /// + Error = 2 + } + + /// + /// Describes an assessment result item + /// containing a recommendation based on best practices. + /// + public class AssessmentResultItem : AssessmentItemInfo + { + /// + /// Gets or sets a message to the user + /// containing the recommendation. + /// + public string Message { get; set; } + + /// + /// Gets or sets result type: + /// 0 - real result, 1 - warning, 2 - error. + /// + public AssessmentResultItemKind Kind { get; set; } + + /// + /// Gets or sets date and time + /// when the item had been acquired. + /// + public DateTimeOffset Timestamp { get; set; } + } + + public class InvokeRequest + { + public static readonly + RequestType> Type = + RequestType>.Create("assessment/invoke"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/GenerateScriptOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/GenerateScriptOperation.cs index 65a1a0b3..4f306a1f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/GenerateScriptOperation.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/GenerateScriptOperation.cs @@ -27,8 +27,8 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment /// /// Gets the unique id associated with this instance. /// - public string OperationId { get; set; } - + public string OperationId { get; set; } + /// /// Gets the parameters containing assessment results /// to be stored in a data table. @@ -44,8 +44,8 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment /// /// Gets or sets the sql task that's executing the operation /// - public SqlTask SqlTask { get; set; } - + public SqlTask SqlTask { get; set; } + public GenerateScriptOperation(GenerateScriptParams parameters) { Validate.IsNotNull(nameof(parameters), parameters); @@ -92,24 +92,29 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment CancellationToken cancellationToken) { const string scriptPrologue = - @"IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'AssessmentResult')) -BEGIN - CREATE TABLE [dbo].[AssessmentResult]( - [CheckName] [nvarchar](max) NOT NULL, - [CheckId] [nvarchar](max) NOT NULL, - [RulesetName] [nvarchar](max) NOT NULL, - [RulesetVersion] [nvarchar](max) NOT NULL, - [Severity] [nvarchar](max) NOT NULL, - [Message] [nvarchar](max) NOT NULL, - [TargetPath] [nvarchar](max) NOT NULL, - [TargetType] [nvarchar](max) NOT NULL, - [HelpLink] [nvarchar](max) NOT NULL, - [Timestamp] [datetimeoffset](7) NOT NULL - ) -END -GO -INSERT INTO [dbo].[AssessmentResult] ([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp]) -VALUES"; + @"IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'AssessmentResult')) +BEGIN + CREATE TABLE [dbo].[AssessmentResult]( + [CheckName] [nvarchar](max), + [CheckId] [nvarchar](max), + [RulesetName] [nvarchar](max), + [RulesetVersion] [nvarchar](max), + [Severity] [nvarchar](max), + [Message] [nvarchar](max), + [TargetPath] [nvarchar](max), + [TargetType] [nvarchar](max), + [HelpLink] [nvarchar](max), + [Timestamp] [datetimeoffset](7) + ) +END +GO +INSERT INTO [dbo].[AssessmentResult] ([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp]) + SELECT rpt.[CheckName],rpt.[CheckId],rpt.[RulesetName],rpt.[RulesetVersion],rpt.[Severity],rpt.[Message],rpt.[TargetPath],rpt.[TargetType],rpt.[HelpLink],rpt.[Timestamp] + FROM (VALUES "; + + const string scriptEpilogue = + @" + ) rpt([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp])"; var sb = new StringBuilder(); if (generateScriptParams.Items != null) @@ -122,11 +127,14 @@ VALUES"; if (item.Kind == AssessmentResultItemKind.Note) { sb.Append( - $"\r\n('{CUtils.EscapeStringSQuote(item.DisplayName)}','{CUtils.EscapeStringSQuote(item.CheckId)}','{CUtils.EscapeStringSQuote(item.RulesetName)}','{item.RulesetVersion}','{item.Level}','{CUtils.EscapeStringSQuote(item.Message)}','{CUtils.EscapeStringSQuote(item.TargetName)}','{item.TargetType}','{CUtils.EscapeStringSQuote(item.HelpLink)}','{item.Timestamp:yyyy-MM-dd hh:mm:ss.fff zzz}'),"); + $@" + ('{CUtils.EscapeStringSQuote(item.DisplayName)}','{CUtils.EscapeStringSQuote(item.CheckId)}','{CUtils.EscapeStringSQuote(item.RulesetName)}','{item.RulesetVersion}','{item.Level}','{CUtils.EscapeStringSQuote(item.Message)}','{CUtils.EscapeStringSQuote(item.TargetName)}','{item.TargetType}','{CUtils.EscapeStringSQuote(item.HelpLink)}','{item.Timestamp:yyyy-MM-dd hh:mm:ss.fff zzz}'),"); } } sb.Length -= 1; + + sb.Append(scriptEpilogue); } return sb.ToString(); diff --git a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/SqlAssessmentService.cs b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/SqlAssessmentService.cs index 85ca7984..d4a028b4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/SqlAssessmentService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/SqlAssessment/SqlAssessmentService.cs @@ -1,507 +1,507 @@ -// -// 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.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; - -using Microsoft.SqlServer.Management.Assessment; -using Microsoft.SqlServer.Management.Assessment.Configuration; -using Microsoft.SqlTools.Hosting.Protocol; -using Microsoft.SqlTools.ServiceLayer.Connection; -using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; -using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; -using Microsoft.SqlTools.ServiceLayer.Hosting; -using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; -using Microsoft.SqlTools.ServiceLayer.SqlContext; -using Microsoft.SqlTools.ServiceLayer.TaskServices; -using Microsoft.SqlTools.ServiceLayer.Utility; -using Microsoft.SqlTools.ServiceLayer.Workspace; -using Microsoft.SqlTools.Utility; - -using AssessmentResultItem = Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts.AssessmentResultItem; -using ConnectionType = Microsoft.SqlTools.ServiceLayer.Connection.ConnectionType; -using InvokeParams = Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts.InvokeParams; -using InvokeRequest = Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts.InvokeRequest; - -namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment -{ - /// - /// Service for running SQL Assessment. - /// - public sealed class SqlAssessmentService : IDisposable - { - private const string ApiVersion = "1.0"; - - #region Singleton Instance Implementation - - private static readonly Lazy LazyInstance - = new Lazy(() => new SqlAssessmentService()); - - internal SqlAssessmentService( - ConnectionService connService, - WorkspaceService workspaceService) - { - ConnectionService = connService; - WorkspaceService = workspaceService; - } - - private SqlAssessmentService() - { - ConnectionService = ConnectionService.Instance; - WorkspaceService = WorkspaceService.Instance; - } - - /// - /// Singleton instance of the query execution service - /// - public static SqlAssessmentService Instance => LazyInstance.Value; - - #endregion - - #region Properties - - /// - /// Gets the used to run assessment operations. - /// - internal Engine Engine { get; } = new Engine(); - - /// - /// Gets the instance of the connection service, - /// used to get the connection info for a given owner URI. - /// - private ConnectionService ConnectionService { get; } - - private WorkspaceService WorkspaceService { get; } - - /// - /// Holds a map from the - /// to a that is being ran. - /// - private readonly Lazy> activeRequests = - new Lazy>(() => new ConcurrentDictionary()); - - /// - /// Gets a map from the - /// to a that is being ran. - /// - internal ConcurrentDictionary ActiveRequests => activeRequests.Value; - - #endregion - - /// - /// Initializes the service with the service host, - /// registers request handlers and shutdown event handler. - /// - /// The service host instance to register with - public void InitializeService(ServiceHost serviceHost) - { - // Register handlers for requests - serviceHost.SetRequestHandler(InvokeRequest.Type, HandleInvokeRequest); - serviceHost.SetRequestHandler(GetAssessmentItemsRequest.Type, HandleGetAssessmentItemsRequest); - serviceHost.SetRequestHandler(GenerateScriptRequest.Type, HandleGenerateScriptRequest); - - // Register handler for shutdown event - serviceHost.RegisterShutdownTask((shutdownParams, requestContext) => - { - Dispose(); - return Task.FromResult(0); - }); - } - - #region Request Handlers - - internal Task HandleGetAssessmentItemsRequest( - GetAssessmentItemsParams itemsParams, - RequestContext> requestContext) - { - return this.HandleAssessmentRequest(requestContext, itemsParams, this.GetAssessmentItems); - } - - internal Task HandleInvokeRequest( - InvokeParams invokeParams, - RequestContext> requestContext) - { - return this.HandleAssessmentRequest(requestContext, invokeParams, this.InvokeSqlAssessment); - } - - internal async Task HandleGenerateScriptRequest( - GenerateScriptParams parameters, - RequestContext requestContext) - { - GenerateScriptOperation operation = null; - try - { - operation = new GenerateScriptOperation(parameters); - TaskMetadata metadata = new TaskMetadata - { - TaskOperation = operation, - TaskExecutionMode = parameters.TaskExecutionMode, - ServerName = parameters.TargetServerName, - DatabaseName = parameters.TargetDatabaseName, - Name = SR.SqlAssessmentGenerateScriptTaskName - }; - - var _ = SqlTaskManager.Instance.CreateAndRun(metadata); - - await requestContext.SendResult(new ResultStatus - { - Success = true, - ErrorMessage = operation.ErrorMessage - }); - } - catch (Exception e) - { - Logger.Write(TraceEventType.Error, "SQL Assessment: failed to generate a script. Error: " + e); - await requestContext.SendResult(new ResultStatus() - { - Success = false, - ErrorMessage = operation == null ? e.Message : operation.ErrorMessage, - }); - } - } - - #endregion - - #region Helpers - - private async Task HandleAssessmentRequest( - RequestContext> requestContext, - AssessmentParams requestParams, - Func>> assessmentFunction) - where TResult : AssessmentItemInfo - { - try - { - string randomUri = Guid.NewGuid().ToString(); - - // get connection - if (!ConnectionService.TryFindConnection(requestParams.OwnerUri, out var connInfo)) - { - await requestContext.SendError(SR.SqlAssessmentQueryInvalidOwnerUri); - return; - } - - ConnectParams connectParams = new ConnectParams - { - OwnerUri = randomUri, - Connection = connInfo.ConnectionDetails, - Type = ConnectionType.Default - }; - - if(!connInfo.TryGetConnection(ConnectionType.Default, out var connection)) - { - await requestContext.SendError(SR.SqlAssessmentConnectingError); - } - - var workTask = CallAssessmentEngine( - requestParams, - connectParams, - randomUri, - assessmentFunction) - .ContinueWith(async tsk => - { - await requestContext.SendResult(tsk.Result); - }); - - ActiveRequests.TryAdd(randomUri, workTask); - } - catch (Exception ex) - { - if (ex is StackOverflowException || ex is OutOfMemoryException) - { - throw; - } - - await requestContext.SendError(ex.ToString()); - } - } - - /// - /// This function obtains a live connection, then calls - /// an assessment operation specified by - /// - /// - /// SQL Assessment result item type. - /// - /// - /// Request parameters passed from the host. - /// - /// - /// Connection parameters used to identify and access the target. - /// - /// - /// An URI identifying the request task to enable concurrent execution. - /// - /// - /// A function performing assessment operation for given target. - /// - /// - /// Returns for given target. - /// - internal async Task> CallAssessmentEngine( - AssessmentParams requestParams, - ConnectParams connectParams, - string taskUri, - Func>> assessmentFunc) - where TResult : AssessmentItemInfo - - { - var result = new AssessmentResult - { - ApiVersion = ApiVersion - }; - - await ConnectionService.Connect(connectParams); - - var connection = await ConnectionService.Instance.GetOrOpenConnection(taskUri, ConnectionType.Query); - - try - { - var serverInfo = ReliableConnectionHelper.GetServerVersion(connection); - var hostInfo = ReliableConnectionHelper.GetServerHostInfo(connection); - - var server = new SqlObjectLocator - { - Connection = connection, - EngineEdition = GetEngineEdition(serverInfo.EngineEditionId), - Name = serverInfo.ServerName, - ServerName = serverInfo.ServerName, - Type = SqlObjectType.Server, - Urn = serverInfo.ServerName, - Version = Version.Parse(serverInfo.ServerVersion), - Platform = hostInfo.Platform - }; - - switch (requestParams.TargetType) - { - case SqlObjectType.Server: - Logger.Write( - TraceEventType.Verbose, - $"SQL Assessment: running an operation on a server, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); - - result.Items.AddRange(await assessmentFunc(server)); - - Logger.Write( - TraceEventType.Verbose, - $"SQL Assessment: finished an operation on a server, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); - break; - case SqlObjectType.Database: - var db = GetDatabaseLocator(server, connection.Database); - Logger.Write( - TraceEventType.Verbose, - $"SQL Assessment: running an operation on a database, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); - - result.Items.AddRange(await assessmentFunc(db)); - - Logger.Write( - TraceEventType.Verbose, - $"SQL Assessment: finished an operation on a database, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); - break; - } - - result.Success = true; - } - finally - { - ActiveRequests.TryRemove(taskUri, out _); - ConnectionService.Disconnect(new DisconnectParams { OwnerUri = taskUri, Type = null }); - } - - return result; - } - - /// - /// Invokes SQL Assessment and formats results. - /// - /// - /// A sequence of target servers or databases to be assessed. - /// - /// - /// Returns a - /// containing assessment results. - /// - /// - /// Internal for testing - /// - internal async Task> InvokeSqlAssessment(SqlObjectLocator target) - { - var resultsList = await Engine.GetAssessmentResultsList(target); - Logger.Write(TraceEventType.Verbose, $"SQL Assessment: got {resultsList.Count} results."); - - return resultsList.Select(TranslateAssessmentResult).ToList(); - } - - /// - /// Gets the list of checks for given target servers or databases. - /// - /// - /// A sequence of target servers or databases. - /// - /// - /// Returns an - /// containing checks available for given . - /// - internal Task> GetAssessmentItems(SqlObjectLocator target) - { - var result = new List(); - - var resultsList = Engine.GetChecks(target).ToList(); - Logger.Write(TraceEventType.Verbose, $"SQL Assessment: got {resultsList.Count} items."); - - foreach (var r in resultsList) - { - var targetName = target.Type != SqlObjectType.Server - ? $"{target.ServerName}:{target.Name}" - : target.Name; - - var item = new CheckInfo() - { - CheckId = r.Id, - Description = r.Description, - DisplayName = r.DisplayName, - HelpLink = r.HelpLink, - Level = r.Level.ToString(), - TargetName = targetName, - Tags = r.Tags.ToArray(), - TargetType = target.Type, - RulesetName = Engine.Configuration.DefaultRuleset.Name, - RulesetVersion = Engine.Configuration.DefaultRuleset.Version.ToString() - }; - - result.Add(item); - } - - return Task.FromResult(result); - } - - private AssessmentResultItem TranslateAssessmentResult(IAssessmentResult r) - { - var item = new AssessmentResultItem - { - CheckId = r.Check.Id, - Description = r.Check.Description, - DisplayName = r.Check.DisplayName, - HelpLink = r.Check.HelpLink, - Level = r.Check.Level.ToString(), - Message = r.Message, - TargetName = r.TargetPath, - Tags = r.Check.Tags.ToArray(), - TargetType = r.TargetType, - RulesetVersion = Engine.Configuration.DefaultRuleset.Version.ToString(), - RulesetName = Engine.Configuration.DefaultRuleset.Name, - Timestamp = r.Timestamp - }; - - if (r is IAssessmentNote) - { - item.Kind = AssessmentResultItemKind.Note; - } - else if (r is IAssessmentWarning) - { - item.Kind = AssessmentResultItemKind.Warning; - } - else if (r is IAssessmentError) - { - item.Kind = AssessmentResultItemKind.Error; - } - - return item; - } - - /// - /// Constructs a for specified database. - /// - /// Target server locator. - /// Target database name. - /// Returns a locator for target database. - private static SqlObjectLocator GetDatabaseLocator(SqlObjectLocator server, string databaseName) - { - return new SqlObjectLocator - { - Connection = server.Connection, - EngineEdition = server.EngineEdition, - Name = databaseName, - Platform = server.Platform, - ServerName = server.Name, - Type = SqlObjectType.Database, - Urn = $"{server.Name}:{databaseName}", - Version = server.Version - }; - } - - /// - /// Converts numeric of engine edition - /// returned by SERVERPROPERTY('EngineEdition'). - /// - /// - /// A number returned by SERVERPROPERTY('EngineEdition'). - /// - /// Engine edition is not supported. - /// - /// Returns a - /// corresponding to the . - /// - private static SqlEngineEdition GetEngineEdition(int representation) - { - switch (representation) - { - case 1: return SqlEngineEdition.PersonalOrDesktopEngine; - case 2: return SqlEngineEdition.Standard; - case 3: return SqlEngineEdition.Enterprise; - case 4: return SqlEngineEdition.Express; - case 5: return SqlEngineEdition.AzureDatabase; - case 6: return SqlEngineEdition.DataWarehouse; - case 7: return SqlEngineEdition.StretchDatabase; - case 8: return SqlEngineEdition.ManagedInstance; - default: - throw new ArgumentOutOfRangeException(nameof(representation), - SR.SqlAssessmentUnsuppoertedEdition(representation)); - } - } - - #endregion - - #region IDisposable Implementation - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (disposed) - { - return; - } - - if (disposing) - { - foreach (var request in ActiveRequests) - { - request.Value.Dispose(); - } - - ActiveRequests.Clear(); - } - - disposed = true; - } - - ~SqlAssessmentService() - { - Dispose(false); - } - - #endregion - } -} +// +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; + +using Microsoft.SqlServer.Management.Assessment; +using Microsoft.SqlServer.Management.Assessment.Configuration; +using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; +using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; +using Microsoft.SqlTools.ServiceLayer.Hosting; +using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; +using Microsoft.SqlTools.ServiceLayer.SqlContext; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Microsoft.SqlTools.ServiceLayer.Utility; +using Microsoft.SqlTools.ServiceLayer.Workspace; +using Microsoft.SqlTools.Utility; + +using AssessmentResultItem = Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts.AssessmentResultItem; +using ConnectionType = Microsoft.SqlTools.ServiceLayer.Connection.ConnectionType; +using InvokeParams = Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts.InvokeParams; +using InvokeRequest = Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts.InvokeRequest; + +namespace Microsoft.SqlTools.ServiceLayer.SqlAssessment +{ + /// + /// Service for running SQL Assessment. + /// + public sealed class SqlAssessmentService : IDisposable + { + private const string ApiVersion = "1.0"; + + #region Singleton Instance Implementation + + private static readonly Lazy LazyInstance + = new Lazy(() => new SqlAssessmentService()); + + internal SqlAssessmentService( + ConnectionService connService, + WorkspaceService workspaceService) + { + ConnectionService = connService; + WorkspaceService = workspaceService; + } + + private SqlAssessmentService() + { + ConnectionService = ConnectionService.Instance; + WorkspaceService = WorkspaceService.Instance; + } + + /// + /// Singleton instance of the query execution service + /// + public static SqlAssessmentService Instance => LazyInstance.Value; + + #endregion + + #region Properties + + /// + /// Gets the used to run assessment operations. + /// + internal Engine Engine { get; } = new Engine(); + + /// + /// Gets the instance of the connection service, + /// used to get the connection info for a given owner URI. + /// + private ConnectionService ConnectionService { get; } + + private WorkspaceService WorkspaceService { get; } + + /// + /// Holds a map from the + /// to a that is being ran. + /// + private readonly Lazy> activeRequests = + new Lazy>(() => new ConcurrentDictionary()); + + /// + /// Gets a map from the + /// to a that is being ran. + /// + internal ConcurrentDictionary ActiveRequests => activeRequests.Value; + + #endregion + + /// + /// Initializes the service with the service host, + /// registers request handlers and shutdown event handler. + /// + /// The service host instance to register with + public void InitializeService(ServiceHost serviceHost) + { + // Register handlers for requests + serviceHost.SetRequestHandler(InvokeRequest.Type, HandleInvokeRequest); + serviceHost.SetRequestHandler(GetAssessmentItemsRequest.Type, HandleGetAssessmentItemsRequest); + serviceHost.SetRequestHandler(GenerateScriptRequest.Type, HandleGenerateScriptRequest); + + // Register handler for shutdown event + serviceHost.RegisterShutdownTask((shutdownParams, requestContext) => + { + Dispose(); + return Task.FromResult(0); + }); + } + + #region Request Handlers + + internal Task HandleGetAssessmentItemsRequest( + GetAssessmentItemsParams itemsParams, + RequestContext> requestContext) + { + return this.HandleAssessmentRequest(requestContext, itemsParams, this.GetAssessmentItems); + } + + internal Task HandleInvokeRequest( + InvokeParams invokeParams, + RequestContext> requestContext) + { + return this.HandleAssessmentRequest(requestContext, invokeParams, this.InvokeSqlAssessment); + } + + internal async Task HandleGenerateScriptRequest( + GenerateScriptParams parameters, + RequestContext requestContext) + { + GenerateScriptOperation operation = null; + try + { + operation = new GenerateScriptOperation(parameters); + TaskMetadata metadata = new TaskMetadata + { + TaskOperation = operation, + TaskExecutionMode = parameters.TaskExecutionMode, + ServerName = parameters.TargetServerName, + DatabaseName = parameters.TargetDatabaseName, + Name = SR.SqlAssessmentGenerateScriptTaskName + }; + + var _ = SqlTaskManager.Instance.CreateAndRun(metadata); + + await requestContext.SendResult(new ResultStatus + { + Success = true, + ErrorMessage = operation.ErrorMessage + }); + } + catch (Exception e) + { + Logger.Write(TraceEventType.Error, "SQL Assessment: failed to generate a script. Error: " + e); + await requestContext.SendResult(new ResultStatus() + { + Success = false, + ErrorMessage = operation == null ? e.Message : operation.ErrorMessage, + }); + } + } + + #endregion + + #region Helpers + + private async Task HandleAssessmentRequest( + RequestContext> requestContext, + AssessmentParams requestParams, + Func>> assessmentFunction) + where TResult : AssessmentItemInfo + { + try + { + string randomUri = Guid.NewGuid().ToString(); + + // get connection + if (!ConnectionService.TryFindConnection(requestParams.OwnerUri, out var connInfo)) + { + await requestContext.SendError(SR.SqlAssessmentQueryInvalidOwnerUri); + return; + } + + ConnectParams connectParams = new ConnectParams + { + OwnerUri = randomUri, + Connection = connInfo.ConnectionDetails, + Type = ConnectionType.Default + }; + + if(!connInfo.TryGetConnection(ConnectionType.Default, out var connection)) + { + await requestContext.SendError(SR.SqlAssessmentConnectingError); + } + + var workTask = CallAssessmentEngine( + requestParams, + connectParams, + randomUri, + assessmentFunction) + .ContinueWith(async tsk => + { + await requestContext.SendResult(tsk.Result); + }); + + ActiveRequests.TryAdd(randomUri, workTask); + } + catch (Exception ex) + { + if (ex is StackOverflowException || ex is OutOfMemoryException) + { + throw; + } + + await requestContext.SendError(ex.ToString()); + } + } + + /// + /// This function obtains a live connection, then calls + /// an assessment operation specified by + /// + /// + /// SQL Assessment result item type. + /// + /// + /// Request parameters passed from the host. + /// + /// + /// Connection parameters used to identify and access the target. + /// + /// + /// An URI identifying the request task to enable concurrent execution. + /// + /// + /// A function performing assessment operation for given target. + /// + /// + /// Returns for given target. + /// + internal async Task> CallAssessmentEngine( + AssessmentParams requestParams, + ConnectParams connectParams, + string taskUri, + Func>> assessmentFunc) + where TResult : AssessmentItemInfo + + { + var result = new AssessmentResult + { + ApiVersion = ApiVersion + }; + + await ConnectionService.Connect(connectParams); + + var connection = await ConnectionService.Instance.GetOrOpenConnection(taskUri, ConnectionType.Query); + + try + { + var serverInfo = ReliableConnectionHelper.GetServerVersion(connection); + var hostInfo = ReliableConnectionHelper.GetServerHostInfo(connection); + + var server = new SqlObjectLocator + { + Connection = connection, + EngineEdition = GetEngineEdition(serverInfo.EngineEditionId), + Name = serverInfo.ServerName, + ServerName = serverInfo.ServerName, + Type = SqlObjectType.Server, + Urn = serverInfo.ServerName, + Version = Version.Parse(serverInfo.ServerVersion), + Platform = hostInfo.Platform + }; + + switch (requestParams.TargetType) + { + case SqlObjectType.Server: + Logger.Write( + TraceEventType.Verbose, + $"SQL Assessment: running an operation on a server, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); + + result.Items.AddRange(await assessmentFunc(server)); + + Logger.Write( + TraceEventType.Verbose, + $"SQL Assessment: finished an operation on a server, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); + break; + case SqlObjectType.Database: + var db = GetDatabaseLocator(server, connection.Database); + Logger.Write( + TraceEventType.Verbose, + $"SQL Assessment: running an operation on a database, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); + + result.Items.AddRange(await assessmentFunc(db)); + + Logger.Write( + TraceEventType.Verbose, + $"SQL Assessment: finished an operation on a database, platform:{server.Platform}, edition:{server.EngineEdition.ToString()}, version:{server.Version}"); + break; + } + + result.Success = true; + } + finally + { + ActiveRequests.TryRemove(taskUri, out _); + ConnectionService.Disconnect(new DisconnectParams { OwnerUri = taskUri, Type = null }); + } + + return result; + } + + /// + /// Invokes SQL Assessment and formats results. + /// + /// + /// A sequence of target servers or databases to be assessed. + /// + /// + /// Returns a + /// containing assessment results. + /// + /// + /// Internal for testing + /// + internal async Task> InvokeSqlAssessment(SqlObjectLocator target) + { + var resultsList = await Engine.GetAssessmentResultsList(target); + Logger.Write(TraceEventType.Verbose, $"SQL Assessment: got {resultsList.Count} results."); + + return resultsList.Select(TranslateAssessmentResult).ToList(); + } + + /// + /// Gets the list of checks for given target servers or databases. + /// + /// + /// A sequence of target servers or databases. + /// + /// + /// Returns an + /// containing checks available for given . + /// + internal Task> GetAssessmentItems(SqlObjectLocator target) + { + var result = new List(); + + var resultsList = Engine.GetChecks(target).ToList(); + Logger.Write(TraceEventType.Verbose, $"SQL Assessment: got {resultsList.Count} items."); + + foreach (var r in resultsList) + { + var targetName = target.Type != SqlObjectType.Server + ? $"{target.ServerName}:{target.Name}" + : target.Name; + + var item = new CheckInfo() + { + CheckId = r.Id, + Description = r.Description, + DisplayName = r.DisplayName, + HelpLink = r.HelpLink, + Level = r.Level.ToString(), + TargetName = targetName, + Tags = r.Tags.ToArray(), + TargetType = target.Type, + RulesetName = Engine.Configuration.DefaultRuleset.Name, + RulesetVersion = Engine.Configuration.DefaultRuleset.Version.ToString() + }; + + result.Add(item); + } + + return Task.FromResult(result); + } + + private AssessmentResultItem TranslateAssessmentResult(IAssessmentResult r) + { + var item = new AssessmentResultItem + { + CheckId = r.Check.Id, + Description = r.Check.Description, + DisplayName = r.Check.DisplayName, + HelpLink = r.Check.HelpLink, + Level = r.Check.Level.ToString(), + Message = r.Message, + TargetName = r.TargetPath, + Tags = r.Check.Tags.ToArray(), + TargetType = r.TargetType, + RulesetVersion = Engine.Configuration.DefaultRuleset.Version.ToString(), + RulesetName = Engine.Configuration.DefaultRuleset.Name, + Timestamp = r.Timestamp + }; + + if (r is IAssessmentNote) + { + item.Kind = AssessmentResultItemKind.Note; + } + else if (r is IAssessmentWarning) + { + item.Kind = AssessmentResultItemKind.Warning; + } + else if (r is IAssessmentError) + { + item.Kind = AssessmentResultItemKind.Error; + } + + return item; + } + + /// + /// Constructs a for specified database. + /// + /// Target server locator. + /// Target database name. + /// Returns a locator for target database. + private static SqlObjectLocator GetDatabaseLocator(SqlObjectLocator server, string databaseName) + { + return new SqlObjectLocator + { + Connection = server.Connection, + EngineEdition = server.EngineEdition, + Name = databaseName, + Platform = server.Platform, + ServerName = server.Name, + Type = SqlObjectType.Database, + Urn = $"{server.Name}:{databaseName}", + Version = server.Version + }; + } + + /// + /// Converts numeric of engine edition + /// returned by SERVERPROPERTY('EngineEdition'). + /// + /// + /// A number returned by SERVERPROPERTY('EngineEdition'). + /// + /// Engine edition is not supported. + /// + /// Returns a + /// corresponding to the . + /// + private static SqlEngineEdition GetEngineEdition(int representation) + { + switch (representation) + { + case 1: return SqlEngineEdition.PersonalOrDesktopEngine; + case 2: return SqlEngineEdition.Standard; + case 3: return SqlEngineEdition.Enterprise; + case 4: return SqlEngineEdition.Express; + case 5: return SqlEngineEdition.AzureDatabase; + case 6: return SqlEngineEdition.DataWarehouse; + case 7: return SqlEngineEdition.StretchDatabase; + case 8: return SqlEngineEdition.ManagedInstance; + default: + throw new ArgumentOutOfRangeException(nameof(representation), + SR.SqlAssessmentUnsuppoertedEdition(representation)); + } + } + + #endregion + + #region IDisposable Implementation + + private bool disposed; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + if (disposing) + { + foreach (var request in ActiveRequests) + { + request.Value.Dispose(); + } + + ActiveRequests.Clear(); + } + + disposed = true; + } + + ~SqlAssessmentService() + { + Dispose(false); + } + + #endregion + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlAssessment/SqlAssessmentServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlAssessment/SqlAssessmentServiceTests.cs index e99ecc19..2364916e 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlAssessment/SqlAssessmentServiceTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SqlAssessment/SqlAssessmentServiceTests.cs @@ -1,225 +1,225 @@ -// -// 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.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; - -using Microsoft.SqlServer.Management.Assessment; -using Microsoft.SqlTools.ServiceLayer.Connection; -using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; -using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; -using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; -using Microsoft.SqlTools.ServiceLayer.SqlAssessment; -using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; -using Microsoft.SqlTools.ServiceLayer.Test.Common; -using NUnit.Framework; - -namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlAssessment -{ - public class SqlAssessmentServiceTests - { - private delegate Task> AssessmentMethod(SqlObjectLocator locator); - - private static readonly string[] AllowedSeverityLevels = { "Information", "Warning", "Critical" }; - - [Test] - public async Task InvokeSqlAssessmentServerTest() - { - var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master"); - - var connection = liveConnection.ConnectionInfo.AllConnections.FirstOrDefault(); - Debug.Assert(connection != null, "Live connection is always expected providing a connection"); - - var serverInfo = ReliableConnectionHelper.GetServerVersion(connection); - - var response = await CallAssessment( - nameof(SqlAssessmentService.InvokeSqlAssessment), - SqlObjectType.Server, - liveConnection); - - Assert.Multiple(() => - { - Assert.That(response.Items.Select(i => i.Message), Has.All.Not.Null.Or.Empty); - Assert.That(response.Items.Select(i => i.TargetName), Has.All.EqualTo(serverInfo.ServerName)); - foreach (var i in response.Items.Where(i => i.Kind == 0)) - { - AssertInfoPresent(i); - } - }); - } - - [Test] - public async Task GetAssessmentItemsServerTest() - { - var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master"); - - var connection = liveConnection.ConnectionInfo.AllConnections.FirstOrDefault(); - Debug.Assert(connection != null, "Live connection is always expected providing a connection"); - - var serverInfo = ReliableConnectionHelper.GetServerVersion(connection); - - var response = await CallAssessment( - nameof(SqlAssessmentService.GetAssessmentItems), - SqlObjectType.Server, - liveConnection); - - Assert.Multiple(() => - { - Assert.That(response.Items.Select(i => i.TargetName), Has.All.EqualTo(serverInfo.ServerName)); - foreach (var i in response.Items) - { - AssertInfoPresent(i); - } - }); - } - - [Test] - public async Task GetAssessmentItemsDatabaseTest() - { - const string DatabaseName = "tempdb"; - var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(DatabaseName); - var response = await CallAssessment( - nameof(SqlAssessmentService.GetAssessmentItems), - SqlObjectType.Database, - liveConnection); - - Assert.Multiple(() => - { - Assert.That(response.Items.Select(i => i.TargetName), Has.All.EndsWith(":" + DatabaseName)); - foreach (var i in response.Items) - { - AssertInfoPresent(i); - } - }); - } - - [Test] - public async Task InvokeSqlAssessmentIDatabaseTest() - { - const string DatabaseName = "tempdb"; - var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(DatabaseName); - var response = await CallAssessment( - nameof(SqlAssessmentService.InvokeSqlAssessment), - SqlObjectType.Database, - liveConnection); - - Assert.Multiple(() => - { - Assert.That(response.Items.Select(i => i.Message), Has.All.Not.Null.Or.Empty); - Assert.That(response.Items.Select(i => i.TargetName), Has.All.EndsWith(":" + DatabaseName)); - foreach (var i in response.Items.Where(i => i.Kind == 0)) - { - AssertInfoPresent(i); - } - }); - } - - private static async Task> CallAssessment( - string methodName, - SqlObjectType sqlObjectType, - LiveConnectionHelper.TestConnectionResult liveConnection) - where TResult : AssessmentItemInfo - { - var connInfo = liveConnection.ConnectionInfo; - - AssessmentResult response; - - using (var service = new SqlAssessmentService( - TestServiceProvider.Instance.ConnectionService, - TestServiceProvider.Instance.WorkspaceService)) - { - AddTestRules(service); - - string randomUri = Guid.NewGuid().ToString(); - AssessmentParams requestParams = - new AssessmentParams { OwnerUri = randomUri, TargetType = sqlObjectType }; - ConnectParams connectParams = new ConnectParams - { - OwnerUri = requestParams.OwnerUri, - Connection = connInfo.ConnectionDetails, - Type = ConnectionType.Default - }; - - var methodInfo = typeof(SqlAssessmentService).GetMethod( - methodName, - BindingFlags.Instance | BindingFlags.NonPublic); - - Assert.NotNull(methodInfo); - - var func = (AssessmentMethod)Delegate.CreateDelegate( - typeof(AssessmentMethod), - service, - methodInfo); - - response = await service.CallAssessmentEngine( - requestParams, - connectParams, - randomUri, - t => func(t)); - } - - Assert.NotNull(response); - if (response.Success) - { - Assert.That(response.Items.Select(i => i.TargetType), Has.All.EqualTo(sqlObjectType)); - Assert.That(response.Items.Select(i => i.Level), Has.All.AnyOf(AllowedSeverityLevels)); - } - - return response; - } - - private static void AddTestRules(SqlAssessmentService service) - { - const string TestRuleset = @" - { - 'name': 'Tags & Checks', - 'version': '0.3', - 'schemaVersion': '1.0', - 'rules': [ - { - 'id': 'ServerRule', - 'itemType': 'definition', - 'tags': [ 'Test' ], - 'displayName': 'Test server check', - 'description': 'This check always fails for testing purposes.', - 'message': 'This check intentionally fails', - 'target': { 'type': 'Server' } - }, - { - 'id': 'DatabaseRule', - 'itemType': 'definition', - 'tags': [ 'Test' ], - 'displayName': 'Test server check', - 'description': 'This check always fails for testing purposes.', - 'message': 'This check intentionally fails', - 'target': { 'type': 'Database' } - } - ] - } -"; - using (var reader = new StringReader(TestRuleset)) - { - service.Engine.PushRuleFactoryJson(reader); - } - } - - private void AssertInfoPresent(AssessmentItemInfo item) - { - Assert.Multiple(() => - { - Assert.That(item.CheckId, Is.Not.Null.Or.Empty); - Assert.That(item.DisplayName, Is.Not.Null.Or.Empty); - Assert.That(item.Description, Is.Not.Null.Or.Empty); - Assert.NotNull(item.Tags); - Assert.That(item.Tags, Has.All.Not.Null.Or.Empty); - }); - } - } -} +// +// 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; + +using Microsoft.SqlServer.Management.Assessment; +using Microsoft.SqlTools.ServiceLayer.Connection; +using Microsoft.SqlTools.ServiceLayer.Connection.Contracts; +using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection; +using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; +using Microsoft.SqlTools.ServiceLayer.SqlAssessment; +using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; +using Microsoft.SqlTools.ServiceLayer.Test.Common; +using NUnit.Framework; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SqlAssessment +{ + public class SqlAssessmentServiceTests + { + private delegate Task> AssessmentMethod(SqlObjectLocator locator); + + private static readonly string[] AllowedSeverityLevels = { "Information", "Warning", "Critical" }; + + [Test] + public async Task InvokeSqlAssessmentServerTest() + { + var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master"); + + var connection = liveConnection.ConnectionInfo.AllConnections.FirstOrDefault(); + Debug.Assert(connection != null, "Live connection is always expected providing a connection"); + + var serverInfo = ReliableConnectionHelper.GetServerVersion(connection); + + var response = await CallAssessment( + nameof(SqlAssessmentService.InvokeSqlAssessment), + SqlObjectType.Server, + liveConnection); + + Assert.Multiple(() => + { + Assert.That(response.Items.Select(i => i.Message), Has.All.Not.Null.Or.Empty); + Assert.That(response.Items.Select(i => i.TargetName), Has.All.EqualTo(serverInfo.ServerName)); + foreach (var i in response.Items.Where(i => i.Kind == 0)) + { + AssertInfoPresent(i); + } + }); + } + + [Test] + public async Task GetAssessmentItemsServerTest() + { + var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo("master"); + + var connection = liveConnection.ConnectionInfo.AllConnections.FirstOrDefault(); + Debug.Assert(connection != null, "Live connection is always expected providing a connection"); + + var serverInfo = ReliableConnectionHelper.GetServerVersion(connection); + + var response = await CallAssessment( + nameof(SqlAssessmentService.GetAssessmentItems), + SqlObjectType.Server, + liveConnection); + + Assert.Multiple(() => + { + Assert.That(response.Items.Select(i => i.TargetName), Has.All.EqualTo(serverInfo.ServerName)); + foreach (var i in response.Items) + { + AssertInfoPresent(i); + } + }); + } + + [Test] + public async Task GetAssessmentItemsDatabaseTest() + { + const string DatabaseName = "tempdb"; + var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(DatabaseName); + var response = await CallAssessment( + nameof(SqlAssessmentService.GetAssessmentItems), + SqlObjectType.Database, + liveConnection); + + Assert.Multiple(() => + { + Assert.That(response.Items.Select(i => i.TargetName), Has.All.EndsWith(":" + DatabaseName)); + foreach (var i in response.Items) + { + AssertInfoPresent(i); + } + }); + } + + [Test] + public async Task InvokeSqlAssessmentIDatabaseTest() + { + const string DatabaseName = "tempdb"; + var liveConnection = LiveConnectionHelper.InitLiveConnectionInfo(DatabaseName); + var response = await CallAssessment( + nameof(SqlAssessmentService.InvokeSqlAssessment), + SqlObjectType.Database, + liveConnection); + + Assert.Multiple(() => + { + Assert.That(response.Items.Select(i => i.Message), Has.All.Not.Null.Or.Empty); + Assert.That(response.Items.Select(i => i.TargetName), Has.All.EndsWith(":" + DatabaseName)); + foreach (var i in response.Items.Where(i => i.Kind == 0)) + { + AssertInfoPresent(i); + } + }); + } + + private static async Task> CallAssessment( + string methodName, + SqlObjectType sqlObjectType, + LiveConnectionHelper.TestConnectionResult liveConnection) + where TResult : AssessmentItemInfo + { + var connInfo = liveConnection.ConnectionInfo; + + AssessmentResult response; + + using (var service = new SqlAssessmentService( + TestServiceProvider.Instance.ConnectionService, + TestServiceProvider.Instance.WorkspaceService)) + { + AddTestRules(service); + + string randomUri = Guid.NewGuid().ToString(); + AssessmentParams requestParams = + new AssessmentParams { OwnerUri = randomUri, TargetType = sqlObjectType }; + ConnectParams connectParams = new ConnectParams + { + OwnerUri = requestParams.OwnerUri, + Connection = connInfo.ConnectionDetails, + Type = ConnectionType.Default + }; + + var methodInfo = typeof(SqlAssessmentService).GetMethod( + methodName, + BindingFlags.Instance | BindingFlags.NonPublic); + + Assert.NotNull(methodInfo); + + var func = (AssessmentMethod)Delegate.CreateDelegate( + typeof(AssessmentMethod), + service, + methodInfo); + + response = await service.CallAssessmentEngine( + requestParams, + connectParams, + randomUri, + t => func(t)); + } + + Assert.NotNull(response); + if (response.Success) + { + Assert.That(response.Items.Select(i => i.TargetType), Has.All.EqualTo(sqlObjectType)); + Assert.That(response.Items.Select(i => i.Level), Has.All.AnyOf(AllowedSeverityLevels)); + } + + return response; + } + + private static void AddTestRules(SqlAssessmentService service) + { + const string TestRuleset = @" + { + 'name': 'Tags & Checks', + 'version': '0.3', + 'schemaVersion': '1.0', + 'rules': [ + { + 'id': 'ServerRule', + 'itemType': 'definition', + 'tags': [ 'Test' ], + 'displayName': 'Test server check', + 'description': 'This check always fails for testing purposes.', + 'message': 'This check intentionally fails', + 'target': { 'type': 'Server' } + }, + { + 'id': 'DatabaseRule', + 'itemType': 'definition', + 'tags': [ 'Test' ], + 'displayName': 'Test server check', + 'description': 'This check always fails for testing purposes.', + 'message': 'This check intentionally fails', + 'target': { 'type': 'Database' } + } + ] + } +"; + using (var reader = new StringReader(TestRuleset)) + { + service.Engine.PushRuleFactoryJson(reader); + } + } + + private void AssertInfoPresent(AssessmentItemInfo item) + { + Assert.Multiple(() => + { + Assert.That(item.CheckId, Is.Not.Null.Or.Empty); + Assert.That(item.DisplayName, Is.Not.Null.Or.Empty); + Assert.That(item.Description, Is.Not.Null.Or.Empty); + Assert.NotNull(item.Tags); + Assert.That(item.Tags, Has.All.Not.Null.Or.Empty); + }); + } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlAssessment/GenerateScriptOperationTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlAssessment/GenerateScriptOperationTests.cs index b70c9198..14b77613 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlAssessment/GenerateScriptOperationTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/SqlAssessment/GenerateScriptOperationTests.cs @@ -1,157 +1,159 @@ -// -// 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.Threading; -using System.Threading.Tasks; -using Microsoft.SqlServer.Management.Assessment; -using Microsoft.SqlTools.ServiceLayer.SqlAssessment; -using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; -using Microsoft.SqlTools.ServiceLayer.TaskServices; -using Moq; -using NUnit.Framework; - -namespace Microsoft.SqlTools.ServiceLayer.UnitTests.SqlAssessment -{ - public class GenerateScriptOperationTests - { - private static readonly GenerateScriptParams SampleParams = new GenerateScriptParams - { - Items = new List - { - new AssessmentResultItem - { - CheckId = "C1", - Description = "Desc1", - DisplayName = "DN1", - HelpLink = "HL1", - Kind = AssessmentResultItemKind.Note, - Level = "Information", - Message = "Msg'1", - TargetName = "proj[*]_dev", - TargetType = SqlObjectType.Server, - Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.Zero), - RulesetName = "Microsoft Ruleset", - RulesetVersion = "1.3" - }, - new AssessmentResultItem - { - CheckId = "C-2", - Description = "Desc2", - DisplayName = "D N2", - HelpLink = "http://HL2", - Kind = AssessmentResultItemKind.Warning, - Level = "Warning", - Message = "Msg'1", - TargetName = "proj[*]_devW", - TargetType = SqlObjectType.Database, - Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromHours(3)), - RulesetName = "Microsoft Ruleset", - RulesetVersion = "1.3" - }, - new AssessmentResultItem - { - CheckId = "C'3", - Description = "Des'c3", - DisplayName = "D'N1", - HelpLink = "HL'1", - Kind = AssessmentResultItemKind.Error, - Level = "Critical", - Message = "Msg'1", - TargetName = "proj[*]_devE", - TargetType = SqlObjectType.Server, - Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromMinutes(-90)), - RulesetName = "Microsoft Ruleset", - RulesetVersion = "1.3" - }, - new AssessmentResultItem - { - CheckId = "C-2", - Description = "Desc2", - DisplayName = "D N2", - HelpLink = "http://HL2", - Kind = AssessmentResultItemKind.Note, - Level = "Warning", - Message = "Msg'1", - TargetName = "proj[*]_dev", - TargetType = SqlObjectType.Database, - Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromHours(3)), - RulesetName = "Microsoft Ruleset", - RulesetVersion = "1.3" - }, - new AssessmentResultItem - { - CheckId = "C'3", - Description = "Des'c3", - DisplayName = "D'N1", - HelpLink = "HL'1", - Kind = AssessmentResultItemKind.Note, - Level = "Critical", - Message = "Msg'1", - TargetName = "proj[*]_dev", - TargetType = SqlObjectType.Server, - Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromMinutes(-90)), - RulesetName = "Microsoft Ruleset", - RulesetVersion = "1.3" - } - } - }; - - private const string SampleScript = - @"IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'AssessmentResult')) -BEGIN - CREATE TABLE [dbo].[AssessmentResult]( - [CheckName] [nvarchar](max) NOT NULL, - [CheckId] [nvarchar](max) NOT NULL, - [RulesetName] [nvarchar](max) NOT NULL, - [RulesetVersion] [nvarchar](max) NOT NULL, - [Severity] [nvarchar](max) NOT NULL, - [Message] [nvarchar](max) NOT NULL, - [TargetPath] [nvarchar](max) NOT NULL, - [TargetType] [nvarchar](max) NOT NULL, - [HelpLink] [nvarchar](max) NOT NULL, - [Timestamp] [datetimeoffset](7) NOT NULL - ) -END -GO -INSERT INTO [dbo].[AssessmentResult] ([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp]) -VALUES -('DN1','C1','Microsoft Ruleset','1.3','Information','Msg''1','proj[*]_dev','Server','HL1','2001-05-25 01:42:00.000 +00:00'), -('D N2','C-2','Microsoft Ruleset','1.3','Warning','Msg''1','proj[*]_dev','Database','http://HL2','2001-05-25 01:42:00.000 +03:00'), -('D''N1','C''3','Microsoft Ruleset','1.3','Critical','Msg''1','proj[*]_dev','Server','HL''1','2001-05-25 01:42:00.000 -01:30')"; - - [Test] - public void GenerateScriptTest() - { - var scriptText = GenerateScriptOperation.GenerateScript(SampleParams, CancellationToken.None); - Assert.AreEqual(SampleScript, scriptText); - } - - [Test] - public void ExecuteTest() - { - var subject = new GenerateScriptOperation(SampleParams); - var taskMetadata = new TaskMetadata(); - using (var sqlTask = new SqlTask(taskMetadata, DummyOpFunction, DummyOpFunction)) - { - subject.SqlTask = sqlTask; - sqlTask.ScriptAdded += ValidateScriptAdded; - subject.Execute(TaskExecutionMode.Script); - } - } - - private void ValidateScriptAdded(object sender, TaskEventArgs e) - { - Assert.AreEqual(SqlTaskStatus.Succeeded, e.TaskData.Status); - Assert.AreEqual(SampleScript, e.TaskData.Script); - } - - private static Task DummyOpFunction(SqlTask _) - { - return Task.FromResult(new TaskResult() {TaskStatus = SqlTaskStatus.Succeeded}); - } - } -} +// +// 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.Threading; +using System.Threading.Tasks; +using Microsoft.SqlServer.Management.Assessment; +using Microsoft.SqlTools.ServiceLayer.SqlAssessment; +using Microsoft.SqlTools.ServiceLayer.SqlAssessment.Contracts; +using Microsoft.SqlTools.ServiceLayer.TaskServices; +using Moq; +using NUnit.Framework; + +namespace Microsoft.SqlTools.ServiceLayer.UnitTests.SqlAssessment +{ + public class GenerateScriptOperationTests + { + private static readonly GenerateScriptParams SampleParams = new GenerateScriptParams + { + Items = new List + { + new AssessmentResultItem + { + CheckId = "C1", + Description = "Desc1", + DisplayName = "DN1", + HelpLink = "HL1", + Kind = AssessmentResultItemKind.Note, + Level = "Information", + Message = "Msg'1", + TargetName = "proj[*]_dev", + TargetType = SqlObjectType.Server, + Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.Zero), + RulesetName = "Microsoft Ruleset", + RulesetVersion = "1.3" + }, + new AssessmentResultItem + { + CheckId = "C-2", + Description = "Desc2", + DisplayName = "D N2", + HelpLink = "http://HL2", + Kind = AssessmentResultItemKind.Warning, + Level = "Warning", + Message = "Msg'1", + TargetName = "proj[*]_devW", + TargetType = SqlObjectType.Database, + Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromHours(3)), + RulesetName = "Microsoft Ruleset", + RulesetVersion = "1.3" + }, + new AssessmentResultItem + { + CheckId = "C'3", + Description = "Des'c3", + DisplayName = "D'N1", + HelpLink = "HL'1", + Kind = AssessmentResultItemKind.Error, + Level = "Critical", + Message = "Msg'1", + TargetName = "proj[*]_devE", + TargetType = SqlObjectType.Server, + Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromMinutes(-90)), + RulesetName = "Microsoft Ruleset", + RulesetVersion = "1.3" + }, + new AssessmentResultItem + { + CheckId = "C-2", + Description = "Desc2", + DisplayName = "D N2", + HelpLink = "http://HL2", + Kind = AssessmentResultItemKind.Note, + Level = "Warning", + Message = "Msg'1", + TargetName = "proj[*]_dev", + TargetType = SqlObjectType.Database, + Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromHours(3)), + RulesetName = "Microsoft Ruleset", + RulesetVersion = "1.3" + }, + new AssessmentResultItem + { + CheckId = "C'3", + Description = "Des'c3", + DisplayName = "D'N1", + HelpLink = "HL'1", + Kind = AssessmentResultItemKind.Note, + Level = "Critical", + Message = "Msg'1", + TargetName = "proj[*]_dev", + TargetType = SqlObjectType.Server, + Timestamp = new DateTimeOffset(2001, 5, 25, 13, 42, 00, TimeSpan.FromMinutes(-90)), + RulesetName = "Microsoft Ruleset", + RulesetVersion = "1.3" + } + } + }; + + private const string SampleScript = + @"IF (NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'dbo' AND TABLE_NAME = 'AssessmentResult')) +BEGIN + CREATE TABLE [dbo].[AssessmentResult]( + [CheckName] [nvarchar](max), + [CheckId] [nvarchar](max), + [RulesetName] [nvarchar](max), + [RulesetVersion] [nvarchar](max), + [Severity] [nvarchar](max), + [Message] [nvarchar](max), + [TargetPath] [nvarchar](max), + [TargetType] [nvarchar](max), + [HelpLink] [nvarchar](max), + [Timestamp] [datetimeoffset](7) + ) +END +GO +INSERT INTO [dbo].[AssessmentResult] ([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp]) + SELECT rpt.[CheckName],rpt.[CheckId],rpt.[RulesetName],rpt.[RulesetVersion],rpt.[Severity],rpt.[Message],rpt.[TargetPath],rpt.[TargetType],rpt.[HelpLink],rpt.[Timestamp] + FROM (VALUES + ('DN1','C1','Microsoft Ruleset','1.3','Information','Msg''1','proj[*]_dev','Server','HL1','2001-05-25 01:42:00.000 +00:00'), + ('D N2','C-2','Microsoft Ruleset','1.3','Warning','Msg''1','proj[*]_dev','Database','http://HL2','2001-05-25 01:42:00.000 +03:00'), + ('D''N1','C''3','Microsoft Ruleset','1.3','Critical','Msg''1','proj[*]_dev','Server','HL''1','2001-05-25 01:42:00.000 -01:30') + ) rpt([CheckName],[CheckId],[RulesetName],[RulesetVersion],[Severity],[Message],[TargetPath],[TargetType],[HelpLink],[Timestamp])"; + + [Test] + public void GenerateScriptTest() + { + var scriptText = GenerateScriptOperation.GenerateScript(SampleParams, CancellationToken.None); + Assert.AreEqual(SampleScript, scriptText); + } + + [Test] + public void ExecuteTest() + { + var subject = new GenerateScriptOperation(SampleParams); + var taskMetadata = new TaskMetadata(); + using (var sqlTask = new SqlTask(taskMetadata, DummyOpFunction, DummyOpFunction)) + { + subject.SqlTask = sqlTask; + sqlTask.ScriptAdded += ValidateScriptAdded; + subject.Execute(TaskExecutionMode.Script); + } + } + + private void ValidateScriptAdded(object sender, TaskEventArgs e) + { + Assert.AreEqual(SqlTaskStatus.Succeeded, e.TaskData.Status); + Assert.AreEqual(SampleScript, e.TaskData.Script); + } + + private static Task DummyOpFunction(SqlTask _) + { + return Task.FromResult(new TaskResult() {TaskStatus = SqlTaskStatus.Succeeded}); + } + } +}