Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
Alan Ren 2c783a4011 use file path instead of content in the request (#1562)
* use file path instead of content in the request

* remove unused using statement
2022-06-30 15:19:56 -07:00

405 lines
16 KiB
C#

//
// 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.Threading.Tasks;
using Microsoft.SqlServer.Dac;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using DacTableDesigner = Microsoft.Data.Tools.Sql.DesignServices.TableDesigner.TableDesigner;
namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
/// <summary>
/// Main class for DacFx service
/// </summary>
class DacFxService
{
private static ConnectionService connectionService = null;
private SqlTaskManager sqlTaskManagerInstance = null;
private static readonly Lazy<DacFxService> instance = new Lazy<DacFxService>(() => new DacFxService());
private readonly Lazy<ConcurrentDictionary<string, DacFxOperation>> operations =
new Lazy<ConcurrentDictionary<string, DacFxOperation>>(() => new ConcurrentDictionary<string, DacFxOperation>());
/// <summary>
/// Gets the singleton instance object
/// </summary>
public static DacFxService Instance
{
get { return instance.Value; }
}
/// <summary>
/// Initializes the service instance
/// </summary>
/// <param name="serviceHost"></param>
public void InitializeService(ServiceHost serviceHost)
{
serviceHost.SetRequestHandler(ExportRequest.Type, this.HandleExportRequest);
serviceHost.SetRequestHandler(ImportRequest.Type, this.HandleImportRequest);
serviceHost.SetRequestHandler(ExtractRequest.Type, this.HandleExtractRequest);
serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest);
serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest);
serviceHost.SetRequestHandler(GenerateDeployPlanRequest.Type, this.HandleGenerateDeployPlanRequest);
serviceHost.SetRequestHandler(GetOptionsFromProfileRequest.Type, this.HandleGetOptionsFromProfileRequest);
serviceHost.SetRequestHandler(ValidateStreamingJobRequest.Type, this.HandleValidateStreamingJobRequest);
serviceHost.SetRequestHandler(GetDefaultPublishOptionsRequest.Type, this.HandleGetDefaultPublishOptionsRequest);
serviceHost.SetRequestHandler(ParseTSqlScriptRequest.Type, this.HandleParseTSqlScriptRequest);
}
/// <summary>
/// The collection of active operations
/// </summary>
internal ConcurrentDictionary<string, DacFxOperation> ActiveOperations => operations.Value;
/// <summary>
/// Handles request to export a bacpac
/// </summary>
/// <returns></returns>
public async Task HandleExportRequest(ExportParams parameters, RequestContext<DacFxResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
ExportOperation operation = new ExportOperation(parameters, connInfo);
ExecuteOperation(operation, parameters, SR.ExportBacpacTaskName, requestContext);
}
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to import a bacpac
/// </summary>
/// <returns></returns>
public async Task HandleImportRequest(ImportParams parameters, RequestContext<DacFxResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
ImportOperation operation = new ImportOperation(parameters, connInfo);
ExecuteOperation(operation, parameters, SR.ImportBacpacTaskName, requestContext);
}
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to extract a dacpac
/// </summary>
/// <returns></returns>
public async Task HandleExtractRequest(ExtractParams parameters, RequestContext<DacFxResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
// Set connection details database name to ensure the connection string gets created correctly for DW(extract doesn't work if connection is to master)
connInfo.ConnectionDetails.DatabaseName = parameters.DatabaseName;
ExtractOperation operation = new ExtractOperation(parameters, connInfo);
string taskName = parameters.ExtractTarget == DacExtractTarget.DacPac ? SR.ExtractDacpacTaskName : SR.ProjectExtractTaskName;
ExecuteOperation(operation, parameters, taskName, requestContext);
}
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to deploy a dacpac
/// </summary>
/// <returns></returns>
public async Task HandleDeployRequest(DeployParams parameters, RequestContext<DacFxResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
DeployOperation operation = new DeployOperation(parameters, connInfo);
ExecuteOperation(operation, parameters, SR.DeployDacpacTaskName, requestContext);
}
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to generate deploy script
/// </summary>
/// <returns></returns>
public async Task HandleGenerateDeployScriptRequest(GenerateDeployScriptParams parameters, RequestContext<DacFxResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
GenerateDeployScriptOperation operation = new GenerateDeployScriptOperation(parameters, connInfo);
SqlTask sqlTask = null;
TaskMetadata metadata = new TaskMetadata();
metadata.TaskOperation = operation;
metadata.TaskExecutionMode = parameters.TaskExecutionMode;
metadata.ServerName = connInfo.ConnectionDetails.ServerName;
metadata.DatabaseName = parameters.DatabaseName;
metadata.Name = SR.GenerateScriptTaskName;
sqlTask = SqlTaskManagerInstance.CreateAndRun<SqlTask>(metadata);
await requestContext.SendResult(new DacFxResult()
{
OperationId = operation.OperationId,
Success = true,
ErrorMessage = string.Empty
});
}
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to generate deploy plan
/// </summary>
/// <returns></returns>
public async Task HandleGenerateDeployPlanRequest(GenerateDeployPlanParams parameters, RequestContext<GenerateDeployPlanRequestResult> requestContext)
{
try
{
ConnectionInfo connInfo;
ConnectionServiceInstance.TryFindConnection(
parameters.OwnerUri,
out connInfo);
if (connInfo != null)
{
GenerateDeployPlanOperation operation = new GenerateDeployPlanOperation(parameters, connInfo);
operation.Execute(parameters.TaskExecutionMode);
await requestContext.SendResult(new GenerateDeployPlanRequestResult()
{
OperationId = operation.OperationId,
Success = true,
ErrorMessage = string.Empty,
Report = operation.DeployReport
});
}
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to get the options from a publish profile
/// </summary>
/// <returns></returns>
public async Task HandleGetOptionsFromProfileRequest(GetOptionsFromProfileParams parameters, RequestContext<DacFxOptionsResult> requestContext)
{
try
{
DeploymentOptions options = null;
if (parameters.ProfilePath != null)
{
DacProfile profile = DacProfile.Load(parameters.ProfilePath);
if (profile.DeployOptions != null)
{
options = DeploymentOptions.GetDefaultPublishOptions();
await options.InitializeFromProfile(profile.DeployOptions, parameters.ProfilePath);
}
}
await requestContext.SendResult(new DacFxOptionsResult()
{
DeploymentOptions = options,
Success = true,
ErrorMessage = string.Empty,
});
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to validate an ASA streaming job
/// </summary>
/// <returns></returns>
public async Task HandleValidateStreamingJobRequest(ValidateStreamingJobParams parameters, RequestContext<ValidateStreamingJobResult> requestContext)
{
try
{
ValidateStreamingJobOperation operation = new ValidateStreamingJobOperation(parameters);
ValidateStreamingJobResult result = operation.ValidateQuery();
await requestContext.SendResult(result);
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
/// <summary>
/// Handles request to create default publish options for DacFx
/// </summary>
/// <returns></returns>
public async Task HandleGetDefaultPublishOptionsRequest(GetDefaultPublishOptionsParams parameters, RequestContext<DacFxOptionsResult> requestContext)
{
try
{
// this does not need to be an async operation since this only creates and returns the default object
DeploymentOptions options = DeploymentOptions.GetDefaultPublishOptions();
await requestContext.SendResult(new DacFxOptionsResult()
{
DeploymentOptions = options,
Success = true,
ErrorMessage = null
});
}
catch (Exception e)
{
await requestContext.SendResult(new DacFxOptionsResult()
{
DeploymentOptions = null,
Success = false,
ErrorMessage = e.Message
});
}
}
public async Task HandleParseTSqlScriptRequest(ParseTSqlScriptRequestParams requestParams, RequestContext<ParseTSqlScriptResult> requestContext)
{
try
{
var script = System.IO.File.ReadAllText(requestParams.FilePath);
await requestContext.SendResult(new ParseTSqlScriptResult()
{
ContainsCreateTableStatement = DacTableDesigner.ScriptContainsCreateTableStatements(script, requestParams.DatabaseSchemaProvider)
});
}
catch (Exception e)
{
await requestContext.SendError(e);
}
}
private void ExecuteOperation(DacFxOperation operation, DacFxParams parameters, string taskName, RequestContext<DacFxResult> requestContext)
{
Task.Run(async () =>
{
try
{
// show file location for export and extract operations
string targetLocation = (operation is ExportOperation || operation is ExtractOperation) ? parameters.PackageFilePath : null;
TaskMetadata metadata = TaskMetadata.Create(parameters, taskName, operation, ConnectionServiceInstance, targetLocation);
// put appropriate database name since connection passed was to master
metadata.DatabaseName = parameters.DatabaseName;
SqlTask sqlTask = SqlTaskManagerInstance.CreateTask<SqlTask>(metadata);
await sqlTask.RunAsync();
await requestContext.SendResult(new DacFxResult()
{
OperationId = operation.OperationId,
Success = sqlTask.TaskStatus == SqlTaskStatus.Succeeded,
ErrorMessage = string.Empty
});
}
catch (Exception e)
{
await requestContext.SendResult(new DacFxResult()
{
OperationId = operation.OperationId,
Success = false,
ErrorMessage = e.Message
});
}
});
}
private SqlTaskManager SqlTaskManagerInstance
{
get
{
if (sqlTaskManagerInstance == null)
{
sqlTaskManagerInstance = SqlTaskManager.Instance;
}
return sqlTaskManagerInstance;
}
set
{
sqlTaskManagerInstance = value;
}
}
/// <summary>
/// Internal for testing purposes only
/// </summary>
internal static ConnectionService ConnectionServiceInstance
{
get
{
if (connectionService == null)
{
connectionService = ConnectionService.Instance;
}
return connectionService;
}
set
{
connectionService = value;
}
}
/// <summary>
/// For testing purpose only
/// </summary>
/// <param name="operation"></param>
internal void PerformOperation(DacFxOperation operation, TaskExecutionMode taskExecutionMode)
{
operation.Execute(taskExecutionMode);
}
}
}