mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-16 18:47:57 -05:00
Added endpoint for fetching all notebook jobs (#834)
* Added endpoint for fetching all notebook jobs * Refractored NotebookJobInfo to AgentNotebookInfo to make it more consistent with the rest of the codebase * Added Notebook History endpoint in contracts. * Added Create, Update, Delete notebook endpoints. Also added separate fetch template, materialized notebook endpoints. This will make the Notebook Request and Notebook History responses lighter. * AgentNotebookInfo is now derived from AgentJobInfo * added fetch noteook history endpoint * Added fetching materialized notebook endpoint * Added code for cleaning up the directory * Added create notebook api * Added Update and delete notebook job * Fixed notebook history API * Added last run info to the script and template folder * Added execute database feature for notebook Jobs * SQL commands are now using sqlparameters to prevent any injection attacks * Changed rundate and runtime to string to preserve leading zeros * integration test for agentnotebooks api * Made some changes mentioned in PR * Refactored the code, removed enpoint logic from the notebook handler and wrote test cases * changes select statements, fixed a bug in the test job cleanup and fixed other stuff mentioned in the PR. * added notebook_error column in notebook history select statement * Added get template notebook endpoint
This commit is contained in:
@@ -13,7 +13,6 @@ using Microsoft.SqlServer.Management.Common;
|
||||
using Microsoft.SqlServer.Management.Smo;
|
||||
using Microsoft.SqlServer.Management.Smo.Agent;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Admin;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.Hosting;
|
||||
@@ -122,6 +121,22 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
this.ServiceHost.SetRequestHandler(UpdateAgentScheduleRequest.Type, HandleUpdateAgentScheduleRequest);
|
||||
this.ServiceHost.SetRequestHandler(DeleteAgentScheduleRequest.Type, HandleDeleteAgentScheduleRequest);
|
||||
|
||||
// Notebook request handlers
|
||||
this.ServiceHost.SetRequestHandler(AgentNotebooksRequest.Type, HandleAgentNotebooksRequest);
|
||||
this.ServiceHost.SetRequestHandler(AgentNotebookHistoryRequest.Type, HandleAgentNotebookHistoryRequest);
|
||||
this.ServiceHost.SetRequestHandler(AgentNotebookMaterializedRequest.Type, HandleAgentNotebookMaterializedRequest);
|
||||
this.ServiceHost.SetRequestHandler(AgentNotebookTemplateRequest.Type, HandleAgentNotebookTemplateRequest);
|
||||
this.ServiceHost.SetRequestHandler(CreateAgentNotebookRequest.Type, HandleCreateAgentNotebookRequest);
|
||||
this.ServiceHost.SetRequestHandler(DeleteAgentNotebookRequest.Type, HandleDeleteAgentNotebooksRequest);
|
||||
this.ServiceHost.SetRequestHandler(UpdateAgentNotebookRequest.Type, HandleUpdateAgentNotebookRequest);
|
||||
|
||||
|
||||
serviceHost.RegisterShutdownTask(async (shutdownParams, shutdownRequestContext) =>
|
||||
{
|
||||
DeleteAgentNotebooksTempFiles();
|
||||
await Task.FromResult(0);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
#region "Jobs Handlers"
|
||||
@@ -191,7 +206,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
SqlConnectionInfo sqlConnInfo = tuple.Item1;
|
||||
DataTable dt = tuple.Item2;
|
||||
ServerConnection connection = tuple.Item3;
|
||||
|
||||
|
||||
// Send Steps, Alerts and Schedules with job history in background
|
||||
// Add steps to the job if any
|
||||
JobStepCollection steps = jobs[parameters.JobName].JobSteps;
|
||||
@@ -271,7 +286,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
var serverConnection = ConnectionService.OpenServerConnection(connInfo);
|
||||
var jobHelper = new JobHelper(serverConnection);
|
||||
jobHelper.JobName = parameters.JobName;
|
||||
switch(parameters.Action)
|
||||
switch (parameters.Action)
|
||||
{
|
||||
case "run":
|
||||
jobHelper.Start();
|
||||
@@ -475,7 +490,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
IsEnabled = alert.IsEnabled,
|
||||
JobId = alert.JobID.ToString(),
|
||||
JobName = alert.JobName,
|
||||
LastOccurrenceDate =alert.LastOccurrenceDate.ToString(),
|
||||
LastOccurrenceDate = alert.LastOccurrenceDate.ToString(),
|
||||
LastResponseDate = alert.LastResponseDate.ToString(),
|
||||
MessageId = alert.MessageID,
|
||||
NotificationMessage = alert.NotificationMessage,
|
||||
@@ -912,12 +927,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
}
|
||||
|
||||
// Execute step actions if they exist
|
||||
if (jobInfo.JobSteps != null && jobInfo.JobSteps.Length > 0)
|
||||
if (configAction != ConfigAction.Drop && jobInfo.JobSteps != null && jobInfo.JobSteps.Length > 0)
|
||||
{
|
||||
foreach (AgentJobStepInfo step in jobInfo.JobSteps)
|
||||
{
|
||||
{
|
||||
configAction = ConfigAction.Create;
|
||||
foreach(JobStep jobStep in dataContainer.Server.JobServer.Jobs[originalJobName].JobSteps)
|
||||
foreach (JobStep jobStep in dataContainer.Server.JobServer.Jobs[originalJobName].JobSteps)
|
||||
{
|
||||
// any changes made to step other than name or ordering
|
||||
if ((step.StepName == jobStep.Name && step.Id == jobStep.ID) ||
|
||||
@@ -928,7 +943,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
configAction = ConfigAction.Update;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
await ConfigureAgentJobStep(ownerUri, step, configAction, runType, jobData, dataContainer);
|
||||
}
|
||||
@@ -1018,9 +1033,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
||||
dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jobData == null)
|
||||
{
|
||||
// If the alert is being created inside a job
|
||||
@@ -1162,7 +1177,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
|
||||
STParameters param = new STParameters(dataContainer.Document);
|
||||
string originalName = jobInfo != null && !string.Equals(jobName, jobInfo.Name) ? jobName : string.Empty;
|
||||
param.SetParam("job", configAction == ConfigAction.Update ? jobName : string.Empty);
|
||||
param.SetParam("job", configAction == ConfigAction.Update ? jobName : string.Empty);
|
||||
param.SetParam("jobid", string.Empty);
|
||||
|
||||
jobData = new JobData(dataContainer, jobInfo, configAction);
|
||||
@@ -1211,6 +1226,272 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
return new Tuple<SqlConnectionInfo, DataTable, ServerConnection>(sqlConnInfo, dt, serverConnection);
|
||||
}
|
||||
|
||||
internal async Task HandleAgentNotebooksRequest(AgentNotebooksParams parameters, RequestContext<AgentNotebooksResult> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new AgentNotebooksResult();
|
||||
try
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(
|
||||
parameters.OwnerUri,
|
||||
out connInfo);
|
||||
result.Success = true;
|
||||
result.Notebooks = AgentNotebookHelper.GetAgentNotebooks(connInfo).Result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleAgentNotebookHistoryRequest(
|
||||
AgentNotebookHistoryParams parameters, RequestContext<AgentNotebookHistoryResult> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new AgentNotebookHistoryResult();
|
||||
try
|
||||
{
|
||||
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(
|
||||
parameters.OwnerUri,
|
||||
out connInfo);
|
||||
|
||||
result = GetAgentNotebookHistories(
|
||||
connInfo,
|
||||
parameters.JobId,
|
||||
parameters.JobName,
|
||||
parameters.TargetDatabase
|
||||
);
|
||||
|
||||
result.Success = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleAgentNotebookMaterializedRequest(AgentNotebookMaterializedParams parameters, RequestContext<AgentNotebookMaterializedResult> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new AgentNotebookMaterializedResult();
|
||||
try
|
||||
{
|
||||
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(
|
||||
parameters.OwnerUri,
|
||||
out connInfo);
|
||||
result.NotebookMaterialized = AgentNotebookHelper.GetMaterializedNotebook(connInfo, parameters.NotebookMaterializedId, parameters.TargetDatabase).Result;
|
||||
result.Success = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleAgentNotebookTemplateRequest(AgentNotebookTemplateParams parameters, RequestContext<AgentNotebookTemplateResult> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new AgentNotebookTemplateResult();
|
||||
try
|
||||
{
|
||||
|
||||
ConnectionInfo connInfo;
|
||||
ConnectionServiceInstance.TryFindConnection(
|
||||
parameters.OwnerUri,
|
||||
out connInfo);
|
||||
result.NotebookTemplate = AgentNotebookHelper.GetTemplateNotebook(connInfo, parameters.JobId, parameters.TargetDatabase).Result;
|
||||
result.Success = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleCreateAgentNotebookRequest(CreateAgentNotebookParams parameters, RequestContext<CreateAgentNotebookResult> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new CreateAgentNotebookResult();
|
||||
try
|
||||
{
|
||||
// storing result
|
||||
result.Success = true;
|
||||
await AgentNotebookHelper.CreateNotebook(
|
||||
this,
|
||||
parameters.OwnerUri,
|
||||
parameters.Notebook,
|
||||
parameters.TemplateFilePath,
|
||||
ManagementUtils.asRunType(parameters.TaskExecutionMode)
|
||||
);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleDeleteAgentNotebooksRequest(DeleteAgentNotebookParams parameters, RequestContext<ResultStatus> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new ResultStatus();
|
||||
try
|
||||
{
|
||||
// Calling delete notebook helper function
|
||||
await AgentNotebookHelper.DeleteNotebook(
|
||||
this,
|
||||
parameters.OwnerUri,
|
||||
parameters.Notebook,
|
||||
ManagementUtils.asRunType(parameters.TaskExecutionMode)
|
||||
);
|
||||
result.Success = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
internal async Task HandleUpdateAgentNotebookRequest(UpdateAgentNotebookParams parameters, RequestContext<UpdateAgentNotebookResult> requestContext)
|
||||
{
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var result = new UpdateAgentNotebookResult();
|
||||
try
|
||||
{
|
||||
// Calling update helper function
|
||||
await AgentNotebookHelper.UpdateNotebook(
|
||||
this,
|
||||
parameters.OwnerUri,
|
||||
parameters.OriginalNotebookName,
|
||||
parameters.Notebook,
|
||||
parameters.TemplateFilePath,
|
||||
ManagementUtils.asRunType(parameters.TaskExecutionMode));
|
||||
result.Success = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result.Success = false;
|
||||
result.ErrorMessage = e.ToString();
|
||||
|
||||
}
|
||||
await requestContext.SendResult(result);
|
||||
});
|
||||
}
|
||||
|
||||
public AgentNotebookHistoryResult GetAgentNotebookHistories(
|
||||
ConnectionInfo connInfo,
|
||||
string jobId,
|
||||
string jobName,
|
||||
string targetDatabase
|
||||
)
|
||||
{
|
||||
AgentNotebookHistoryResult result = new AgentNotebookHistoryResult();
|
||||
// fetching Job information
|
||||
CDataContainer dataContainer = CDataContainer.CreateDataContainer(connInfo, databaseExists: true);
|
||||
var jobServer = dataContainer.Server.JobServer;
|
||||
var jobs = jobServer.Jobs;
|
||||
Tuple<SqlConnectionInfo, DataTable, ServerConnection> tuple = CreateSqlConnection(connInfo, jobId);
|
||||
SqlConnectionInfo sqlConnInfo = tuple.Item1;
|
||||
DataTable dt = tuple.Item2;
|
||||
|
||||
// add steps to the job if any
|
||||
JobStepCollection steps = jobs[jobName].JobSteps;
|
||||
var jobSteps = new List<AgentJobStepInfo>();
|
||||
foreach (JobStep step in steps)
|
||||
{
|
||||
jobSteps.Add(AgentUtilities.ConvertToAgentJobStepInfo(step, jobId, jobName));
|
||||
}
|
||||
result.Steps = jobSteps.ToArray();
|
||||
|
||||
// add schedules to the job if any
|
||||
JobScheduleCollection schedules = jobs[jobName].JobSchedules;
|
||||
var jobSchedules = new List<AgentScheduleInfo>();
|
||||
foreach (JobSchedule schedule in schedules)
|
||||
{
|
||||
jobSchedules.Add(AgentUtilities.ConvertToAgentScheduleInfo(schedule));
|
||||
}
|
||||
result.Schedules = jobSchedules.ToArray();
|
||||
|
||||
// add histories
|
||||
int count = dt.Rows.Count;
|
||||
List<AgentNotebookHistoryInfo> notebookHistories = new List<AgentNotebookHistoryInfo>();
|
||||
if (count > 0)
|
||||
{
|
||||
var job = dt.Rows[0];
|
||||
Guid tempjobId = (Guid)job[AgentUtilities.UrnJobId];
|
||||
int runStatus = Convert.ToInt32(job[AgentUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture);
|
||||
var t = new LogSourceJobHistory(jobName, sqlConnInfo, null, runStatus, tempjobId, null);
|
||||
var tlog = t as ILogSource;
|
||||
tlog.Initialize();
|
||||
var logEntries = t.LogEntries;
|
||||
var jobHistories = AgentUtilities.ConvertToAgentNotebookHistoryInfo(logEntries, job, steps);
|
||||
// fetching notebook part of histories
|
||||
Dictionary<string, DataRow> notebookHistoriesDict = new Dictionary<string, DataRow>();
|
||||
DataTable materializedNotebookTable = AgentNotebookHelper.GetAgentNotebookHistories(connInfo, jobId, targetDatabase).Result;
|
||||
foreach (DataRow materializedNotebookRow in materializedNotebookTable.Rows)
|
||||
{
|
||||
string materializedRunDateTime = materializedNotebookRow["run_date"].ToString() + materializedNotebookRow["run_time"].ToString();
|
||||
notebookHistoriesDict.Add(materializedRunDateTime, materializedNotebookRow);
|
||||
}
|
||||
|
||||
// adding notebook information to job histories
|
||||
foreach (var jobHistory in jobHistories)
|
||||
{
|
||||
string jobRuntime = jobHistory.RunDate.ToString("yyyyMMddHHmmss");
|
||||
AgentNotebookHistoryInfo notebookHistory = jobHistory;
|
||||
if (notebookHistoriesDict.ContainsKey(jobRuntime))
|
||||
{
|
||||
notebookHistory.MaterializedNotebookId = (int)notebookHistoriesDict[jobRuntime]["materialized_id"];
|
||||
notebookHistory.MaterializedNotebookErrorInfo = notebookHistoriesDict[jobRuntime]["notebook_error"] as string;
|
||||
}
|
||||
notebookHistories.Add(notebookHistory);
|
||||
}
|
||||
result.Histories = notebookHistories.ToArray();
|
||||
tlog.CloseReader();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion // "Helpers"
|
||||
|
||||
internal void DeleteAgentNotebooksTempFiles()
|
||||
{
|
||||
if (FileUtilities.SafeDirectoryExists(FileUtilities.AgentNotebookTempFolder))
|
||||
{
|
||||
FileUtilities.SafeDirectoryDelete(FileUtilities.AgentNotebookTempFolder, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user