mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,37 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
};
|
||||
}
|
||||
|
||||
public static AgentNotebookInfo ConvertToAgentNotebookInfo(JobProperties job)
|
||||
{
|
||||
return new AgentNotebookInfo(){
|
||||
Name = job.Name,
|
||||
Description = job.Description,
|
||||
CurrentExecutionStatus = (Contracts.JobExecutionStatus) job.CurrentExecutionStatus,
|
||||
LastRunOutcome = (Contracts.CompletionResult) job.LastRunOutcome,
|
||||
CurrentExecutionStep = job.CurrentExecutionStep,
|
||||
Enabled = job.Enabled,
|
||||
HasTarget = job.HasTarget,
|
||||
HasSchedule = job.HasSchedule,
|
||||
HasStep = job.HasStep,
|
||||
Runnable = job.Runnable,
|
||||
Category = job.Category,
|
||||
CategoryId = job.CategoryID,
|
||||
CategoryType = job.CategoryType,
|
||||
LastRun = job.LastRun != null ? job.LastRun.ToString() : string.Empty,
|
||||
NextRun = job.NextRun != null ? job.NextRun.ToString() : string.Empty,
|
||||
JobId = job.JobID != null ? job.JobID.ToString() : null,
|
||||
OperatorToEmail = job.OperatorToEmail,
|
||||
OperatorToPage = job.OperatorToPage,
|
||||
StartStepId = job.StartStepID,
|
||||
EmailLevel = job.EmailLevel,
|
||||
PageLevel = job.PageLevel,
|
||||
EventLogLevel = job.EventLogLevel,
|
||||
DeleteLevel = job.DeleteLevel,
|
||||
Owner = job.Owner
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
internal static AgentJobStep ConvertToAgentJobStep(JobStep step, LogSourceJobHistory.LogEntryJobHistory logEntry, string jobId)
|
||||
{
|
||||
AgentJobStepInfo stepInfo = new AgentJobStepInfo();
|
||||
@@ -120,6 +151,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
stepInfo.ProxyName = step.ProxyName;
|
||||
return stepInfo;
|
||||
}
|
||||
|
||||
|
||||
internal static AgentScheduleInfo ConvertToAgentScheduleInfo(JobSchedule schedule)
|
||||
{
|
||||
@@ -221,5 +253,47 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
}
|
||||
return jobs;
|
||||
}
|
||||
|
||||
public static List<AgentNotebookHistoryInfo> ConvertToAgentNotebookHistoryInfo(List<ILogEntry> logEntries, DataRow jobRow, JobStepCollection steps)
|
||||
{
|
||||
List<AgentNotebookHistoryInfo> jobs = new List<AgentNotebookHistoryInfo>();
|
||||
// get all the values for a job history
|
||||
foreach (ILogEntry entry in logEntries)
|
||||
{
|
||||
// Make a new AgentJobHistoryInfo object
|
||||
var jobHistoryInfo = new AgentNotebookHistoryInfo();
|
||||
jobHistoryInfo.InstanceId = Convert.ToInt32(jobRow[UrnInstanceID], System.Globalization.CultureInfo.InvariantCulture);
|
||||
jobHistoryInfo.JobId = (Guid) jobRow[UrnJobId];
|
||||
var logEntry = entry as LogSourceJobHistory.LogEntryJobHistory;
|
||||
jobHistoryInfo.RunStatus = entry.Severity == SeverityClass.Error ? 0 : 1;
|
||||
jobHistoryInfo.SqlMessageId = logEntry.SqlMessageID;
|
||||
jobHistoryInfo.Message = logEntry.Message;
|
||||
jobHistoryInfo.StepId = logEntry.StepID;
|
||||
jobHistoryInfo.StepName = logEntry.StepName;
|
||||
jobHistoryInfo.SqlSeverity = logEntry.SqlSeverity;
|
||||
jobHistoryInfo.JobName = logEntry.JobName;
|
||||
jobHistoryInfo.RunDate = entry.PointInTime;
|
||||
jobHistoryInfo.RunDuration = logEntry.Duration;
|
||||
jobHistoryInfo.OperatorEmailed = logEntry.OperatorEmailed;
|
||||
jobHistoryInfo.OperatorNetsent = logEntry.OperatorNetsent;
|
||||
jobHistoryInfo.OperatorPaged = logEntry.OperatorPaged;
|
||||
jobHistoryInfo.RetriesAttempted = logEntry.RetriesAttempted;
|
||||
jobHistoryInfo.Server = logEntry.Server;
|
||||
|
||||
// Add steps to the job if any
|
||||
var jobSteps = new List<AgentJobStep>();
|
||||
foreach (LogSourceJobHistory.LogEntryJobHistory subEntry in entry.SubEntries)
|
||||
{
|
||||
if (steps.Contains(subEntry.StepName))
|
||||
{
|
||||
var jobId = jobRow[UrnJobId].ToString();
|
||||
jobSteps.Add(AgentUtilities.ConvertToAgentJobStep(steps.ItemById(Convert.ToInt32(subEntry.StepID)), logEntry, jobId));
|
||||
}
|
||||
}
|
||||
jobHistoryInfo.Steps = jobSteps.ToArray();
|
||||
jobs.Add(jobHistoryInfo);
|
||||
}
|
||||
return jobs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,4 +58,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
|
||||
public CompletionResult runStatus;
|
||||
public AgentJobStepInfo stepDetails;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// a class for storing various properties of a agent notebook history
|
||||
/// </summary>
|
||||
public class AgentNotebookHistoryInfo : AgentJobHistoryInfo
|
||||
{
|
||||
public int MaterializedNotebookId { get; set; }
|
||||
public int MaterializedNotebookErrorFlag { get; set; }
|
||||
public string MaterializedNotebookErrorInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,4 +61,16 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
|
||||
public AgentScheduleInfo[] JobSchedules { get; set; }
|
||||
public AgentAlertInfo[] Alerts { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// a class for storing variour properties of notebook Jobs
|
||||
/// </summary>
|
||||
public class AgentNotebookInfo : AgentJobInfo
|
||||
{
|
||||
public string TemplateId { get; set; }
|
||||
public string TargetDatabase { get; set; }
|
||||
public string LastRunNotebookError { get; set; }
|
||||
public string ExecuteDatabase { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// 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;
|
||||
using Microsoft.SqlTools.ServiceLayer.TaskServices;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// SQL Agent Notebooks activity parameters
|
||||
/// </summary>
|
||||
public class AgentNotebooksParams : GeneralRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook activity result
|
||||
/// </summary>
|
||||
public class AgentNotebooksResult : ResultStatus
|
||||
{
|
||||
public AgentNotebookInfo[] Notebooks { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook request type
|
||||
/// </summary>
|
||||
public class AgentNotebooksRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Request definition
|
||||
/// </summary>
|
||||
public static readonly
|
||||
RequestType<AgentNotebooksParams, AgentNotebooksResult> Type =
|
||||
RequestType<AgentNotebooksParams, AgentNotebooksResult>.Create("agent/notebooks");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook history parameters
|
||||
/// </summary>
|
||||
public class AgentNotebookHistoryParams : GeneralRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
public string JobId { get; set; }
|
||||
public string TargetDatabase { get; set; }
|
||||
public string JobName { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook history results
|
||||
/// </summary>
|
||||
public class AgentNotebookHistoryResult : ResultStatus
|
||||
{
|
||||
public AgentNotebookHistoryInfo[] Histories { get; set; }
|
||||
public AgentJobStepInfo[] Steps { get; set; }
|
||||
|
||||
public AgentScheduleInfo[] Schedules { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook history request type
|
||||
/// <summary>
|
||||
public class AgentNotebookHistoryRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Request definition
|
||||
/// </summary>
|
||||
public static readonly
|
||||
RequestType<AgentNotebookHistoryParams, AgentNotebookHistoryResult> Type =
|
||||
RequestType<AgentNotebookHistoryParams, AgentNotebookHistoryResult>.Create("agent/notebookhistory");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent create Notebook params
|
||||
/// </summary>
|
||||
public class CreateAgentNotebookParams : TaskRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
public AgentNotebookInfo Notebook { get; set; }
|
||||
public string TemplateFilePath { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent create Notebook result
|
||||
/// </summary>
|
||||
public class CreateAgentNotebookResult : ResultStatus
|
||||
{
|
||||
public AgentNotebookInfo Job { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent create Notebook request type
|
||||
/// </summary>
|
||||
public class CreateAgentNotebookRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<CreateAgentNotebookParams, CreateAgentNotebookResult> Type =
|
||||
RequestType<CreateAgentNotebookParams, CreateAgentNotebookResult>.Create("agent/createnotebook");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent update Notebook params
|
||||
/// </summary>
|
||||
public class UpdateAgentNotebookParams : TaskRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
public string OriginalNotebookName { get; set; }
|
||||
public AgentNotebookInfo Notebook { get; set; }
|
||||
public string TemplateFilePath { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent update Notebook result
|
||||
/// </summary>
|
||||
public class UpdateAgentNotebookResult : ResultStatus
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent update Notebook request type
|
||||
/// </summary>
|
||||
public class UpdateAgentNotebookRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Request definition
|
||||
/// </summary>
|
||||
public static readonly
|
||||
RequestType<UpdateAgentNotebookParams, UpdateAgentNotebookResult> Type =
|
||||
RequestType<UpdateAgentNotebookParams, UpdateAgentNotebookResult>.Create("agent/updatenotebook");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent delete Notebook params
|
||||
/// </summary>
|
||||
public class DeleteAgentNotebookParams : TaskRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
public AgentNotebookInfo Notebook { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent delete Notebook request type
|
||||
/// </summary>
|
||||
public class DeleteAgentNotebookRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Request definition
|
||||
/// </summary>
|
||||
public static readonly
|
||||
RequestType<DeleteAgentNotebookParams, ResultStatus> Type =
|
||||
RequestType<DeleteAgentNotebookParams, ResultStatus>.Create("agent/deletenotebook");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook materialized params
|
||||
/// </summary>
|
||||
public class AgentNotebookMaterializedParams : TaskRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
public string TargetDatabase { get; set; }
|
||||
public int NotebookMaterializedId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook materialized result
|
||||
/// </summary>
|
||||
public class AgentNotebookMaterializedResult : ResultStatus
|
||||
{
|
||||
public string NotebookMaterialized { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook materialized request type
|
||||
/// </summary>
|
||||
public class AgentNotebookMaterializedRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<AgentNotebookMaterializedParams, AgentNotebookMaterializedResult> Type =
|
||||
RequestType<AgentNotebookMaterializedParams, AgentNotebookMaterializedResult>.Create("agent/notebookmaterialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook templates params
|
||||
/// </summary>
|
||||
public class AgentNotebookTemplateParams : TaskRequestDetails
|
||||
{
|
||||
public string OwnerUri { get; set; }
|
||||
public string JobId { get; set; }
|
||||
public string TargetDatabase { get; set; }
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook templates results
|
||||
/// </summary>
|
||||
public class AgentNotebookTemplateResult : ResultStatus
|
||||
{
|
||||
public string NotebookTemplate { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SQL Agent Notebook templates request type
|
||||
/// </summary>
|
||||
public class AgentNotebookTemplateRequest
|
||||
{
|
||||
public static readonly
|
||||
RequestType<AgentNotebookTemplateParams, AgentNotebookTemplateResult> Type =
|
||||
RequestType<AgentNotebookTemplateParams, AgentNotebookTemplateResult>.Create("agent/notebooktemplate");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
//
|
||||
// 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.Data;
|
||||
using System.IO;
|
||||
using System.Data.SqlClient;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlServer.Management.Smo.Agent;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.Agent
|
||||
{
|
||||
internal class AgentNotebookHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// executes sql queries required by other agent notebook helper functions
|
||||
/// </summary>
|
||||
/// <param name="connInfo">connectionInfo generated from OwnerUri</param>
|
||||
/// <param name="sqlQuery">actual sql query to be executed</param>
|
||||
/// <param name="queryParameters">sql parameters required by the query</param>
|
||||
/// <param name="targetDatabase">the database on which the query will be executed</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<DataSet> ExecuteSqlQueries(
|
||||
ConnectionInfo connInfo,
|
||||
string sqlQuery,
|
||||
List<SqlParameter> queryParameters = null,
|
||||
string targetDatabase = null)
|
||||
{
|
||||
DataSet result = new DataSet();
|
||||
string originalConnectionDatabase = connInfo.ConnectionDetails.DatabaseName;
|
||||
if (!string.IsNullOrWhiteSpace(targetDatabase))
|
||||
{
|
||||
connInfo.ConnectionDetails.DatabaseName = targetDatabase;
|
||||
}
|
||||
using (SqlConnection connection = new SqlConnection(ConnectionService.BuildConnectionString(connInfo.ConnectionDetails)))
|
||||
{
|
||||
await connection.OpenAsync();
|
||||
using (SqlCommand sqlQueryCommand = new SqlCommand(sqlQuery, connection))
|
||||
{
|
||||
if (queryParameters != null)
|
||||
{
|
||||
sqlQueryCommand.Parameters.AddRange(queryParameters.ToArray());
|
||||
}
|
||||
SqlDataAdapter sqlCommandAdapter = new SqlDataAdapter(sqlQueryCommand);
|
||||
sqlCommandAdapter.Fill(result);
|
||||
}
|
||||
}
|
||||
connInfo.ConnectionDetails.DatabaseName = originalConnectionDatabase;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// a function which fetches notebooks jobs accessible to the user
|
||||
/// </summary>
|
||||
/// <param name="connInfo">connectionInfo generated from OwnerUri</param>
|
||||
/// <returns>array of agent notebooks</returns>
|
||||
public static async Task<AgentNotebookInfo[]> GetAgentNotebooks(ConnectionInfo connInfo)
|
||||
{
|
||||
AgentNotebookInfo[] result;
|
||||
// Fetching all agent Jobs accessible to the user
|
||||
var serverConnection = ConnectionService.OpenServerConnection(connInfo);
|
||||
var fetcher = new JobFetcher(serverConnection);
|
||||
var filter = new JobActivityFilter();
|
||||
var jobs = fetcher.FetchJobs(filter);
|
||||
|
||||
|
||||
Dictionary<Guid, JobProperties> allJobsHashTable = new Dictionary<Guid, JobProperties>();
|
||||
if (jobs != null)
|
||||
{
|
||||
foreach (var job in jobs.Values)
|
||||
{
|
||||
allJobsHashTable.Add(job.JobID, job);
|
||||
}
|
||||
}
|
||||
// Fetching notebooks across all databases accessible by the user
|
||||
string getJobIdsFromDatabaseQueryString =
|
||||
@"
|
||||
DECLARE @script AS VARCHAR(MAX)
|
||||
SET @script =
|
||||
'
|
||||
USE [?];
|
||||
IF EXISTS
|
||||
(
|
||||
SELECT * FROM INFORMATION_SCHEMA.TABLES
|
||||
WHERE
|
||||
TABLE_SCHEMA = N''notebooks''
|
||||
AND
|
||||
TABLE_NAME = N''nb_template''
|
||||
)
|
||||
BEGIN
|
||||
SELECT
|
||||
[notebooks].[nb_template].job_id,
|
||||
[notebooks].[nb_template].template_id,
|
||||
[notebooks].[nb_template].last_run_notebook_error,
|
||||
[notebooks].[nb_template].execute_database,
|
||||
DB_NAME() AS db_name
|
||||
FROM [?].notebooks.nb_template
|
||||
INNER JOIN
|
||||
msdb.dbo.sysjobs
|
||||
ON
|
||||
[?].notebooks.nb_template.job_id = msdb.dbo.sysjobs.job_id
|
||||
END
|
||||
'
|
||||
EXEC sp_MSforeachdb @script";
|
||||
var agentNotebooks = new List<AgentNotebookInfo>();
|
||||
DataSet jobIdsDataSet = await ExecuteSqlQueries(connInfo, getJobIdsFromDatabaseQueryString);
|
||||
foreach (DataTable templateTable in jobIdsDataSet.Tables)
|
||||
{
|
||||
foreach (DataRow templateRow in templateTable.Rows)
|
||||
{
|
||||
AgentNotebookInfo notebookJob =
|
||||
AgentUtilities.ConvertToAgentNotebookInfo(allJobsHashTable[(Guid)templateRow["job_id"]]);
|
||||
notebookJob.TemplateId = templateRow["template_id"] as string;
|
||||
notebookJob.TargetDatabase = templateRow["db_name"] as string;
|
||||
notebookJob.LastRunNotebookError = templateRow["last_run_notebook_error"] as string;
|
||||
notebookJob.ExecuteDatabase = templateRow["execute_database"] as string;
|
||||
agentNotebooks.Add(notebookJob);
|
||||
}
|
||||
}
|
||||
result = agentNotebooks.ToArray();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static async Task<AgentNotebookInfo> CreateNotebook(
|
||||
AgentService agentServiceInstance,
|
||||
string ownerUri,
|
||||
AgentNotebookInfo notebook,
|
||||
string templatePath,
|
||||
RunType runType)
|
||||
{
|
||||
if (!File.Exists(templatePath))
|
||||
{
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
AgentNotebookInfo result;
|
||||
ConnectionInfo connInfo;
|
||||
agentServiceInstance.ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
||||
|
||||
// creating notebook job step
|
||||
notebook.JobSteps = CreateNotebookPowerShellStep(notebook.Name, notebook.TargetDatabase);
|
||||
|
||||
// creating sql agent job
|
||||
var jobCreationResult = await agentServiceInstance.ConfigureAgentJob(
|
||||
ownerUri,
|
||||
notebook.Name,
|
||||
notebook,
|
||||
ConfigAction.Create,
|
||||
runType);
|
||||
|
||||
if (jobCreationResult.Item1 == false)
|
||||
{
|
||||
throw new Exception(jobCreationResult.Item2);
|
||||
}
|
||||
|
||||
// creating notebook metadata for the job
|
||||
string jobId =
|
||||
await SetUpNotebookAndGetJobId(
|
||||
connInfo,
|
||||
notebook.Name,
|
||||
notebook.TargetDatabase,
|
||||
templatePath,
|
||||
notebook.ExecuteDatabase);
|
||||
notebook.JobId = jobId;
|
||||
result = notebook;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static async Task DeleteNotebook(
|
||||
AgentService agentServiceInstance,
|
||||
string ownerUri,
|
||||
AgentNotebookInfo notebook,
|
||||
RunType runType)
|
||||
{
|
||||
ConnectionInfo connInfo;
|
||||
agentServiceInstance.ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
||||
|
||||
// deleting job from sql agent
|
||||
var deleteJobResult = await agentServiceInstance.ConfigureAgentJob(
|
||||
ownerUri,
|
||||
notebook.Name,
|
||||
notebook,
|
||||
ConfigAction.Drop,
|
||||
runType);
|
||||
|
||||
if(!deleteJobResult.Item1)
|
||||
{
|
||||
throw new Exception(deleteJobResult.Item2);
|
||||
}
|
||||
// deleting notebook metadata from target database
|
||||
await DeleteNotebookMetadata(
|
||||
connInfo,
|
||||
notebook.JobId,
|
||||
notebook.TargetDatabase);
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal static async Task UpdateNotebook(
|
||||
AgentService agentServiceInstance,
|
||||
string ownerUri,
|
||||
string originalNotebookName,
|
||||
AgentNotebookInfo notebook,
|
||||
string templatePath,
|
||||
RunType runType)
|
||||
{
|
||||
|
||||
ConnectionInfo connInfo;
|
||||
agentServiceInstance.ConnectionServiceInstance.TryFindConnection(ownerUri, out connInfo);
|
||||
|
||||
if (!string.IsNullOrEmpty(templatePath) && !File.Exists(templatePath))
|
||||
{
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
// updating notebook agent job
|
||||
var updateJobResult =
|
||||
await agentServiceInstance.ConfigureAgentJob(
|
||||
ownerUri,
|
||||
originalNotebookName,
|
||||
notebook,
|
||||
ConfigAction.Update,
|
||||
runType);
|
||||
|
||||
if(!updateJobResult.Item1)
|
||||
{
|
||||
throw new Exception(updateJobResult.Item2);
|
||||
}
|
||||
|
||||
// update notebook metadata
|
||||
UpdateNotebookInfo(
|
||||
connInfo,
|
||||
notebook.JobId,
|
||||
templatePath,
|
||||
notebook.ExecuteDatabase,
|
||||
notebook.TargetDatabase);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// fetches all notebook histories for a particular notebook job
|
||||
/// </summary>
|
||||
/// <param name="connInfo">connectionInfo generated from OwnerUri</param>
|
||||
/// <param name="JobId">unique ID of the sql agent notebook job</param>
|
||||
/// <param name="targetDatabase">database used to store notebook metadata</param>
|
||||
/// <returns>array of notebook history info</returns>
|
||||
public static async Task<DataTable> GetAgentNotebookHistories(
|
||||
ConnectionInfo connInfo,
|
||||
string JobId,
|
||||
string targetDatabase)
|
||||
{
|
||||
DataTable result;
|
||||
string getNotebookHistoryQueryString =
|
||||
@"
|
||||
SELECT
|
||||
materialized_id,
|
||||
run_time,
|
||||
run_date,
|
||||
notebook_error
|
||||
FROM
|
||||
notebooks.nb_materialized
|
||||
WHERE JOB_ID = @jobId";
|
||||
List<SqlParameter> getNotebookHistoryQueryParams = new List<SqlParameter>();
|
||||
getNotebookHistoryQueryParams.Add(new SqlParameter("jobId", JobId));
|
||||
DataSet notebookHistoriesDataSet =
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
getNotebookHistoryQueryString,
|
||||
getNotebookHistoryQueryParams,
|
||||
targetDatabase);
|
||||
result = notebookHistoriesDataSet.Tables[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="connInfo"></param>
|
||||
/// <param name="materializedId"></param>
|
||||
/// <param name="targetDatabase"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetMaterializedNotebook(
|
||||
ConnectionInfo connInfo,
|
||||
int materializedId,
|
||||
string targetDatabase)
|
||||
{
|
||||
string materializedNotebookQueryString =
|
||||
@"
|
||||
SELECT
|
||||
notebook
|
||||
FROM
|
||||
notebooks.nb_materialized
|
||||
WHERE
|
||||
materialized_id = @notebookMaterializedID";
|
||||
List<SqlParameter> materializedNotebookQueryParams = new List<SqlParameter>();
|
||||
materializedNotebookQueryParams.Add(new SqlParameter("notebookMaterializedID", materializedId));
|
||||
DataSet materializedNotebookDataSet =
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
materializedNotebookQueryString,
|
||||
materializedNotebookQueryParams,
|
||||
targetDatabase);
|
||||
DataTable materializedNotebookTable = materializedNotebookDataSet.Tables[0];
|
||||
DataRow materializedNotebookRows = materializedNotebookTable.Rows[0];
|
||||
return materializedNotebookRows["notebook"] as string;
|
||||
}
|
||||
|
||||
public static async Task<string> GetTemplateNotebook(
|
||||
ConnectionInfo connInfo,
|
||||
string jobId,
|
||||
string targetDatabase)
|
||||
{
|
||||
string templateNotebookQueryString =
|
||||
@"
|
||||
SELECT
|
||||
notebook
|
||||
FROM
|
||||
notebooks.nb_template
|
||||
WHERE
|
||||
job_id = @jobId";
|
||||
List<SqlParameter> templateNotebookQueryParams = new List<SqlParameter>();
|
||||
templateNotebookQueryParams.Add(new SqlParameter("jobId", jobId));
|
||||
DataSet templateNotebookDataSet =
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
templateNotebookQueryString,
|
||||
templateNotebookQueryParams,
|
||||
targetDatabase);
|
||||
DataTable templateNotebookTable = templateNotebookDataSet.Tables[0];
|
||||
DataRow templateNotebookRows = templateNotebookTable.Rows[0];
|
||||
return templateNotebookRows["notebook"] as string;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="notebookName"></param>
|
||||
/// <param name="storageDatabase"></param>
|
||||
/// <returns></returns>
|
||||
public static AgentJobStepInfo[] CreateNotebookPowerShellStep(
|
||||
string notebookName,
|
||||
string storageDatabase)
|
||||
{
|
||||
AgentJobStepInfo[] result;
|
||||
var assembly = Assembly.GetAssembly(typeof(AgentService));
|
||||
string execNotebookScript;
|
||||
string notebookScriptResourcePath = "Microsoft.SqlTools.ServiceLayer.Agent.NotebookResources.NotebookJobScript.ps1";
|
||||
using (Stream scriptStream = assembly.GetManifestResourceStream(notebookScriptResourcePath))
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(scriptStream))
|
||||
{
|
||||
execNotebookScript =
|
||||
"$TargetDatabase = \"" +
|
||||
storageDatabase +
|
||||
"\"" +
|
||||
Environment.NewLine +
|
||||
reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
result = new AgentJobStepInfo[1];
|
||||
result[0] = new AgentJobStepInfo()
|
||||
{
|
||||
AppendLogToTable = false,
|
||||
AppendToLogFile = false,
|
||||
AppendToStepHist = false,
|
||||
Command = execNotebookScript,
|
||||
CommandExecutionSuccessCode = 0,
|
||||
DatabaseName = "",
|
||||
DatabaseUserName = null,
|
||||
FailStepId = 0,
|
||||
FailureAction = StepCompletionAction.QuitWithFailure,
|
||||
Id = 1,
|
||||
JobId = null,
|
||||
JobName = notebookName,
|
||||
OutputFileName = null,
|
||||
ProxyName = null,
|
||||
RetryAttempts = 0,
|
||||
RetryInterval = 0,
|
||||
Script = execNotebookScript,
|
||||
ScriptName = null,
|
||||
Server = "",
|
||||
StepName = "Exec-Notebook",
|
||||
SubSystem = AgentSubSystem.PowerShell,
|
||||
SuccessAction = StepCompletionAction.QuitWithSuccess,
|
||||
SuccessStepId = 0,
|
||||
WriteLogToTable = false,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<string> SetUpNotebookAndGetJobId(
|
||||
ConnectionInfo connInfo,
|
||||
string notebookName,
|
||||
string targetDatabase,
|
||||
string templatePath,
|
||||
string executionDatabase)
|
||||
{
|
||||
string jobId;
|
||||
string notebookDatabaseSetupQueryString =
|
||||
@"
|
||||
IF NOT EXISTS (
|
||||
SELECT schema_name
|
||||
FROM information_schema.schemata
|
||||
WHERE schema_name = 'notebooks' )
|
||||
BEGIN
|
||||
EXEC sp_executesql N'CREATE SCHEMA notebooks'
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.objects
|
||||
WHERE object_id = OBJECT_ID(N'[notebooks].[nb_template]') AND TYPE IN (N'U'))
|
||||
BEGIN
|
||||
CREATE TABLE [notebooks].[nb_template](
|
||||
template_id INT PRIMARY KEY IDENTITY(1,1),
|
||||
job_id UNIQUEIDENTIFIER NOT NULL,
|
||||
notebook NVARCHAR(MAX),
|
||||
execute_database NVARCHAR(MAX),
|
||||
last_run_notebook_error NVARCHAR(MAX)
|
||||
)
|
||||
END
|
||||
|
||||
IF NOT EXISTS (SELECT * FROM sys.objects
|
||||
WHERE object_id = OBJECT_ID(N'[notebooks].[nb_materialized]') AND TYPE IN (N'U'))
|
||||
BEGIN
|
||||
CREATE TABLE [notebooks].[nb_materialized](
|
||||
materialized_id INT PRIMARY KEY IDENTITY(1,1),
|
||||
job_id UNIQUEIDENTIFIER NOT NULL,
|
||||
run_time VARCHAR(100),
|
||||
run_date VARCHAR(100),
|
||||
notebook NVARCHAR(MAX),
|
||||
notebook_error NVARCHAR(MAX)
|
||||
)
|
||||
END
|
||||
USE [msdb];
|
||||
SELECT
|
||||
job_id
|
||||
FROM
|
||||
msdb.dbo.sysjobs
|
||||
WHERE
|
||||
name= @jobName;
|
||||
";
|
||||
List<SqlParameter> notebookDatabaseSetupQueryParams = new List<SqlParameter>();
|
||||
notebookDatabaseSetupQueryParams.Add(new SqlParameter("jobName", notebookName));
|
||||
DataSet jobIdDataSet =
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
notebookDatabaseSetupQueryString,
|
||||
notebookDatabaseSetupQueryParams,
|
||||
targetDatabase);
|
||||
DataTable jobIdDataTable = jobIdDataSet.Tables[0];
|
||||
DataRow jobIdDataRow = jobIdDataTable.Rows[0];
|
||||
jobId = ((Guid)jobIdDataRow["job_id"]).ToString();
|
||||
StoreNotebookTemplate(
|
||||
connInfo,
|
||||
jobId,
|
||||
templatePath,
|
||||
targetDatabase,
|
||||
executionDatabase);
|
||||
return jobId;
|
||||
}
|
||||
|
||||
static async void StoreNotebookTemplate(
|
||||
ConnectionInfo connInfo,
|
||||
string jobId,
|
||||
string templatePath,
|
||||
string targetDatabase,
|
||||
string executionDatabase)
|
||||
{
|
||||
string templateFileContents = File.ReadAllText(templatePath);
|
||||
string insertTemplateJsonQuery =
|
||||
@"
|
||||
INSERT
|
||||
INTO
|
||||
notebooks.nb_template(
|
||||
job_id,
|
||||
notebook,
|
||||
last_run_notebook_error,
|
||||
execute_database)
|
||||
VALUES
|
||||
(@jobId, @templateFileContents, N'', @executeDatabase)
|
||||
";
|
||||
List<SqlParameter> insertTemplateJsonQueryParams = new List<SqlParameter>();
|
||||
insertTemplateJsonQueryParams.Add(new SqlParameter("jobId", jobId));
|
||||
insertTemplateJsonQueryParams.Add(new SqlParameter("templateFileContents", templateFileContents));
|
||||
insertTemplateJsonQueryParams.Add(new SqlParameter("executeDatabase", executionDatabase));
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
insertTemplateJsonQuery,
|
||||
insertTemplateJsonQueryParams,
|
||||
targetDatabase);
|
||||
}
|
||||
|
||||
public static async Task DeleteNotebookMetadata(ConnectionInfo connInfo, string jobId, string targetDatabase)
|
||||
{
|
||||
string deleteNotebookRowQuery =
|
||||
@"
|
||||
DELETE FROM notebooks.nb_template
|
||||
WHERE
|
||||
job_id = @jobId;
|
||||
DELETE FROM notebooks.nb_materialized
|
||||
WHERE
|
||||
job_id = @jobId;
|
||||
IF NOT EXISTS (SELECT * FROM notebooks.nb_template)
|
||||
BEGIN
|
||||
DROP TABLE notebooks.nb_template;
|
||||
DROP TABLE notebooks.nb_materialized;
|
||||
DROP SCHEMA notebooks;
|
||||
END
|
||||
";
|
||||
List<SqlParameter> deleteNotebookRowQueryParams = new List<SqlParameter>();
|
||||
deleteNotebookRowQueryParams.Add(new SqlParameter("jobId", jobId));
|
||||
await ExecuteSqlQueries(connInfo, deleteNotebookRowQuery, deleteNotebookRowQueryParams, targetDatabase);
|
||||
}
|
||||
|
||||
public static async void UpdateNotebookInfo(
|
||||
ConnectionInfo connInfo,
|
||||
string jobId,
|
||||
string templatePath,
|
||||
string executionDatabase,
|
||||
string targetDatabase)
|
||||
{
|
||||
if (templatePath != null)
|
||||
{
|
||||
string templateFileContents = File.ReadAllText(templatePath);
|
||||
string insertTemplateJsonQuery =
|
||||
@"
|
||||
UPDATE notebooks.nb_template
|
||||
SET
|
||||
notebook = @templateFileContents
|
||||
WHERE
|
||||
job_id = @jobId
|
||||
";
|
||||
List<SqlParameter> insertTemplateJsonQueryParams = new List<SqlParameter>();
|
||||
insertTemplateJsonQueryParams.Add(new SqlParameter("templateFileContents", templateFileContents));
|
||||
insertTemplateJsonQueryParams.Add(new SqlParameter("jobId", jobId));
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
insertTemplateJsonQuery,
|
||||
insertTemplateJsonQueryParams,
|
||||
targetDatabase);
|
||||
}
|
||||
string updateExecuteDatabaseQuery =
|
||||
@"
|
||||
UPDATE notebooks.nb_template
|
||||
SET
|
||||
execute_database = @executeDatabase
|
||||
WHERE
|
||||
job_id = @jobId
|
||||
";
|
||||
List<SqlParameter> updateExecuteDatabaseQueryParams = new List<SqlParameter>();
|
||||
updateExecuteDatabaseQueryParams.Add(new SqlParameter("executeDatabase", executionDatabase));
|
||||
updateExecuteDatabaseQueryParams.Add(new SqlParameter("jobId", jobId));
|
||||
await ExecuteSqlQueries(
|
||||
connInfo,
|
||||
updateExecuteDatabaseQuery,
|
||||
updateExecuteDatabaseQueryParams,
|
||||
targetDatabase);
|
||||
}
|
||||
|
||||
public static async Task<string> GetTemplateFile(
|
||||
ConnectionInfo connInfo,
|
||||
string job_id,
|
||||
string targetDatabase,
|
||||
string templateFileContents)
|
||||
{
|
||||
String getNotebookTemplateQuery =
|
||||
@"
|
||||
SELECT notebook
|
||||
from
|
||||
notebooks.nb_template
|
||||
where
|
||||
job_id = @jobId;
|
||||
";
|
||||
List<SqlParameter> getNotebookTemplateQueryParams = new List<SqlParameter>();
|
||||
getNotebookTemplateQueryParams.Add(new SqlParameter("job_id", getNotebookTemplateQueryParams));
|
||||
DataSet templateDataSet = await AgentNotebookHelper.ExecuteSqlQueries(
|
||||
connInfo,
|
||||
getNotebookTemplateQuery,
|
||||
getNotebookTemplateQueryParams,
|
||||
targetDatabase);
|
||||
|
||||
DataTable templateDataTable = templateDataSet.Tables[0];
|
||||
DataRow templateDataRow = templateDataTable.Rows[0];
|
||||
return templateDataRow["notebook"] as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
$JobId = "$(ESCAPE_SQUOTE(JOBID))"
|
||||
$StartTime = "$(ESCAPE_SQUOTE(STRTTM))"
|
||||
$StartDate = "$(ESCAPE_SQUOTE(STRTDT))"
|
||||
$JSONTable = "select * from notebooks.nb_template where job_id = $JobId"
|
||||
$sqlResult = Invoke-Sqlcmd -Query $JSONTable -Database $TargetDatabase -MaxCharLength 2147483647
|
||||
$FirstNotebookError = ""
|
||||
function ParseTableToNotebookOutput {
|
||||
param (
|
||||
[System.Data.DataTable]
|
||||
$DataTable,
|
||||
|
||||
[int]
|
||||
$CellExecutionCount
|
||||
)
|
||||
$TableHTMLText = "<table>"
|
||||
$TableSchemaFeilds = @()
|
||||
$TableHTMLText += "<tr>"
|
||||
foreach ($ColumnName in $DataTable.Columns) {
|
||||
$TableSchemaFeilds += @(@{name = $ColumnName.toString() })
|
||||
$TableHTMLText += "<th>" + $ColumnName.toString() + "</th>"
|
||||
}
|
||||
$TableHTMLText += "</tr>"
|
||||
$TableSchema = @{ }
|
||||
$TableSchema["fields"] = $TableSchemaFeilds
|
||||
|
||||
$TableDataRows = @()
|
||||
foreach ($Row in $DataTable) {
|
||||
$TableDataRow = [ordered]@{ }
|
||||
$TableHTMLText += "<tr>"
|
||||
$i = 0
|
||||
foreach ($Cell in $Row.ItemArray) {
|
||||
$TableDataRow[$i.ToString()] = $Cell.toString()
|
||||
$TableHTMLText += "<td>" + $Cell.toString() + "</td>"
|
||||
$i++
|
||||
}
|
||||
$TableHTMLText += "</tr>"
|
||||
$TableDataRows += $TableDataRow
|
||||
}
|
||||
$TableDataResource = @{ }
|
||||
$TableDataResource["schema"] = $TableSchema
|
||||
$TableDataResource["data"] = $TableDataRows
|
||||
$TableData = @{ }
|
||||
$TableData["application/vnd.dataresource+json"] = $TableDataResource
|
||||
$TableData["text/html"] = $TableHTMLText
|
||||
$TableOutput = @{ }
|
||||
$TableOutput["output_type"] = "execute_result"
|
||||
$TableOutput["data"] = $TableData
|
||||
$TableOutput["metadata"] = @{ }
|
||||
$TableOutput["execution_count"] = $CellExecutionCount
|
||||
return $TableOutput
|
||||
}
|
||||
|
||||
function ParseQueryErrorToNotebookOutput {
|
||||
param (
|
||||
$QueryError
|
||||
)
|
||||
$ErrorString = "Msg " + $QueryError.Exception.InnerException.Number +
|
||||
", Level " + $QueryError.Exception.InnerException.Class +
|
||||
", State " + $QueryError.Exception.InnerException.State +
|
||||
", Line " + $QueryError.Exception.InnerException.LineNumber +
|
||||
"`r`n" + $QueryError.Exception.Message
|
||||
|
||||
$ErrorOutput = @{ }
|
||||
$ErrorOutput["output_type"] = "error"
|
||||
$ErrorOutput["traceback"] = @()
|
||||
$ErrorOutput["evalue"] = $ErrorString
|
||||
return $ErrorOutput
|
||||
}
|
||||
|
||||
function ParseStringToNotebookOutput {
|
||||
param (
|
||||
[System.String]
|
||||
$InputString
|
||||
)
|
||||
$StringOutputData = @{ }
|
||||
$StringOutputData["text/html"] = $InputString
|
||||
$StringOutput = @{ }
|
||||
$StringOutput["output_type"] = "display_data"
|
||||
$StringOutput["data"] = $StringOutputData
|
||||
$StringOutput["metadata"] = @{ }
|
||||
return $StringOutput
|
||||
}
|
||||
|
||||
$TemplateNotebook = $sqlResult.notebook
|
||||
$executeDatabase = $sqlResult.execute_database
|
||||
try {
|
||||
$TemplateNotebookJsonObject = ConvertFrom-Json -InputObject $TemplateNotebook
|
||||
}
|
||||
catch {
|
||||
Throw $_.Exception
|
||||
}
|
||||
|
||||
$DatabaseQueryHashTable = @{ }
|
||||
$DatabaseQueryHashTable["Verbose"] = $true
|
||||
$DatabaseQueryHashTable["ErrorVariable"] = "SqlQueryError"
|
||||
$DatabaseQueryHashTable["OutputAs"] = "DataTables"
|
||||
$DatabaseQueryHashTable["Database"] = $executeDatabase
|
||||
$CellExcecutionCount = 1
|
||||
|
||||
foreach ($NotebookCell in $TemplateNotebookJsonObject.cells) {
|
||||
$NotebookCellOutputs = @()
|
||||
if ($NotebookCell.cell_type -eq "markdown" -or $NotebookCell.cell_type -eq "raw" -or $NotebookCell.source -eq "") {
|
||||
continue;
|
||||
}
|
||||
switch($NotebookCell.source.getType()){
|
||||
System.Object[] {
|
||||
$DatabaseQueryHashTable["Query"] = ($NotebookCell.source -join "`r`n" | Out-String)
|
||||
}
|
||||
String {
|
||||
$DatabaseQueryHashTable["Query"] = $NotebookCell.source
|
||||
}
|
||||
}
|
||||
$SqlQueryExecutionTime = Measure-Command { $SqlQueryResult = @(Invoke-Sqlcmd @DatabaseQueryHashTable 4>&1) }
|
||||
$NotebookCell.execution_count = $CellExcecutionCount++
|
||||
$NotebookCellTableOutputs = @()
|
||||
if ($SqlQueryResult) {
|
||||
foreach ($SQLQueryResultElement in $SqlQueryResult) {
|
||||
switch ($SQLQueryResultElement.getType()) {
|
||||
System.Management.Automation.VerboseRecord {
|
||||
$NotebookCellOutputs += ParseStringToNotebookOutput($SQLQueryResultElement.Message)
|
||||
}
|
||||
System.Data.DataTable {
|
||||
$NotebookCellTableOutputs += ParseTableToNotebookOutput $SQLQueryResultElement $CellExcecutionCount
|
||||
}
|
||||
Default { }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($SqlQueryError) {
|
||||
if(!$FirstNotebookError){
|
||||
$FirstNotebookError = $SqlQueryError.Exception.Message.Replace("'", "''")
|
||||
}
|
||||
$NotebookCellOutputs += ParseQueryErrorToNotebookOutput($SqlQueryError)
|
||||
}
|
||||
if ($SqlQueryExecutionTime) {
|
||||
$NotebookCellExcutionTimeString = "Total execution time: " + $SqlQueryExecutionTime.ToString("hh\:mm\:ss\.fff")
|
||||
$NotebookCellOutputs += ParseStringToNotebookOutput($NotebookCellExcutionTimeString)
|
||||
}
|
||||
$NotebookCellOutputs += $NotebookCellTableOutputs
|
||||
$NotebookCell.outputs = $NotebookCellOutputs
|
||||
}
|
||||
|
||||
$result = ($TemplateNotebookJsonObject | ConvertTo-Json -Depth 100)
|
||||
Write-Output $result
|
||||
$result = $result.Replace("'","''")
|
||||
$InsertQuery = "INSERT INTO notebooks.nb_materialized (job_id, run_time, run_date, notebook, notebook_error) VALUES ($JobID, '$StartTime', '$StartDate','$result','$FirstNotebookError')"
|
||||
$SqlResult = Invoke-Sqlcmd -Query $InsertQuery -Database $TargetDatabase
|
||||
$InsertQuery = "UPDATE notebooks.nb_template SET last_run_notebook_error = '$FirstNotebookError' where job_id = $JobID"
|
||||
$SqlResult = Invoke-Sqlcmd -Query $InsertQuery -Database $TargetDatabase
|
||||
@@ -39,4 +39,7 @@
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include=".\Agent\NotebookResources\NotebookJobScript.ps1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Utility
|
||||
internal static class FileUtilities
|
||||
{
|
||||
internal static string PeekDefinitionTempFolder = Path.GetTempPath() + "mssql_definition";
|
||||
internal static string AgentNotebookTempFolder = Path.GetTempPath() + "mssql_notebooks";
|
||||
internal static bool PeekDefinitionTempFolderCreated = false;
|
||||
|
||||
internal static string GetPeekDefinitionTempFolder()
|
||||
|
||||
@@ -92,7 +92,7 @@ gulp.task('ext:nuget-restore', function() {
|
||||
|
||||
|
||||
gulp.task('ext:code-coverage', function(done) {
|
||||
cproc.execFile('cmd.exe', [ '/c', 'codecoverage.bat' ], {maxBuffer: 1024 * 500}, function(err, stdout) {
|
||||
cproc.execFile('cmd.exe', [ '/c', 'codecoverage.bat' ], {maxBuffer: 1024 * 1000}, function(err, stdout) {
|
||||
if (err) {
|
||||
throw new gutil.PluginError('ext:code-coverage', err);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Test.Common;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
{
|
||||
public class AgentNotebookTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Test case for fetch notebook jobs Request Handler
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task TestHandleAgentNotebooksRequest()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
var fetchNotebooksContext = new Mock<RequestContext<AgentNotebooksResult>>();
|
||||
|
||||
fetchNotebooksContext.Setup(x => x.SendResult(It.IsAny<AgentNotebooksResult>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleAgentNotebooksRequest(new AgentNotebooksParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri
|
||||
}, fetchNotebooksContext.Object);
|
||||
|
||||
fetchNotebooksContext.Verify(x => x.SendResult(It.Is<AgentNotebooksResult>(p => p.Success == true)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the create job helper function
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestAgentNotebookCreateHelper()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
AgentNotebookInfo notebook = AgentTestUtils.GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
Assert.Equal(false, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
notebook = AgentTestUtils.SetupNotebookJob(connectionResult).Result;
|
||||
Assert.Equal(true, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
await AgentTestUtils.CleanupNotebookJob(connectionResult, notebook);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the create job request handler with an invalid file path
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestHandleCreateAgentNotebookRequestWithInvalidTemplatePath()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
AgentNotebookInfo notebook = AgentTestUtils.GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
var createNotebookContext = new Mock<RequestContext<CreateAgentNotebookResult>>();
|
||||
createNotebookContext.Setup(x => x.SendResult(It.IsAny<CreateAgentNotebookResult>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleCreateAgentNotebookRequest(new CreateAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook,
|
||||
TemplateFilePath = "garbargepath"
|
||||
}, createNotebookContext.Object);
|
||||
|
||||
createNotebookContext.Verify(x => x.SendResult(It.Is<CreateAgentNotebookResult>(p => p.Success == false)));
|
||||
Assert.Equal(false, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// creating a job with duplicate name
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestDuplicateJobCreation()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
AgentNotebookInfo notebook = AgentTestUtils.GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
var createNotebookContext = new Mock<RequestContext<CreateAgentNotebookResult>>();
|
||||
createNotebookContext.Setup(x => x.SendResult(It.IsAny<CreateAgentNotebookResult>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleCreateAgentNotebookRequest(new CreateAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook,
|
||||
TemplateFilePath = AgentTestUtils.CreateTemplateNotebookFile()
|
||||
}, createNotebookContext.Object);
|
||||
|
||||
createNotebookContext.Verify(x => x.SendResult(It.Is<CreateAgentNotebookResult>(p => p.Success == true)));
|
||||
await service.HandleCreateAgentNotebookRequest(new CreateAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook,
|
||||
TemplateFilePath = AgentTestUtils.CreateTemplateNotebookFile()
|
||||
}, createNotebookContext.Object);
|
||||
createNotebookContext.Verify(x => x.SendResult(It.Is<CreateAgentNotebookResult>(p => p.Success == false)));
|
||||
await AgentTestUtils.CleanupNotebookJob(connectionResult, notebook);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the create notebook job handler
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestCreateAgentNotebookHandler()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
AgentNotebookInfo notebook = AgentTestUtils.GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
var createNotebookContext = new Mock<RequestContext<CreateAgentNotebookResult>>();
|
||||
createNotebookContext.Setup(x => x.SendResult(It.IsAny<CreateAgentNotebookResult>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleCreateAgentNotebookRequest(new CreateAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook,
|
||||
TemplateFilePath = AgentTestUtils.CreateTemplateNotebookFile()
|
||||
}, createNotebookContext.Object);
|
||||
createNotebookContext.Verify(x => x.SendResult(It.Is<CreateAgentNotebookResult>(p => p.Success == true)));
|
||||
Assert.Equal(true, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
var createdNotebook = AgentTestUtils.GetNotebook(connectionResult, notebook.Name);
|
||||
await AgentTestUtils.CleanupNotebookJob(connectionResult, createdNotebook);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the delete notebook job handler
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestDeleteAgentNotebookHandler()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
//creating a notebook job
|
||||
AgentNotebookInfo notebook = AgentTestUtils.SetupNotebookJob(connectionResult).Result;
|
||||
//verifying it's getting created
|
||||
Assert.Equal(true, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
//deleting the notebook job
|
||||
var deleteNotebookContext = new Mock<RequestContext<ResultStatus>>();
|
||||
deleteNotebookContext.Setup(x => x.SendResult(It.IsAny<ResultStatus>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleDeleteAgentNotebooksRequest(new DeleteAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook
|
||||
}, deleteNotebookContext.Object);
|
||||
deleteNotebookContext.Verify(x => x.SendResult(It.Is<ResultStatus>(p => p.Success == true)));
|
||||
//verifying if the job is deleted
|
||||
Assert.Equal(false, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// deleting a existing notebook job
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestDeleteNonExistentJob()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
//getting a test notebook object
|
||||
var notebook = AgentTestUtils.GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
|
||||
var deleteNotebookContext = new Mock<RequestContext<ResultStatus>>();
|
||||
deleteNotebookContext.Setup(x => x.SendResult(It.IsAny<ResultStatus>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleDeleteAgentNotebooksRequest(new DeleteAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook
|
||||
}, deleteNotebookContext.Object);
|
||||
//endpoint should error out
|
||||
deleteNotebookContext.Verify(x => x.SendResult(It.Is<ResultStatus>(p => p.Success == false)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// updating a non existing notebook job
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestUpdateNonExistentJob()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
//getting a test notebook object
|
||||
AgentNotebookInfo notebook = AgentTestUtils.GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
|
||||
var updateNotebookContext = new Mock<RequestContext<UpdateAgentNotebookResult>>();
|
||||
updateNotebookContext.Setup(x => x.SendResult(It.IsAny<UpdateAgentNotebookResult>())).Returns(Task.FromResult(new object()));
|
||||
await service.HandleUpdateAgentNotebookRequest(new UpdateAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook,
|
||||
TemplateFilePath = AgentTestUtils.CreateTemplateNotebookFile()
|
||||
}, updateNotebookContext.Object);
|
||||
// enpoint should error out
|
||||
updateNotebookContext.Verify(x => x.SendResult(It.Is<UpdateAgentNotebookResult>(p => p.Success == false)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// update notebook handler with garbage path
|
||||
/// </summary>
|
||||
[Fact]
|
||||
internal async Task TestUpdateWithGarbagePath()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
|
||||
//seting up a temp notebook job
|
||||
var notebook = AgentTestUtils.SetupNotebookJob(connectionResult).Result;
|
||||
//verifying that the notebook is created
|
||||
Assert.Equal(true, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
|
||||
var updateNotebookContext = new Mock<RequestContext<UpdateAgentNotebookResult>>();
|
||||
updateNotebookContext.Setup(x => x.SendResult(It.IsAny<UpdateAgentNotebookResult>())).Returns(Task.FromResult(new object()));
|
||||
//calling the endpoint with a garbage path
|
||||
await service.HandleUpdateAgentNotebookRequest(new UpdateAgentNotebookParams()
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Notebook = notebook,
|
||||
TemplateFilePath = "garbargepath"
|
||||
}, updateNotebookContext.Object);
|
||||
//the enpoint should return false
|
||||
updateNotebookContext.Verify(x => x.SendResult(It.Is<UpdateAgentNotebookResult>(p => p.Success == false)));
|
||||
|
||||
//cleaning up the job
|
||||
await AgentTestUtils.CleanupNotebookJob(connectionResult, notebook);
|
||||
Assert.Equal(false, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
internal async Task TestDeletingUpdatedJob()
|
||||
{
|
||||
using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile())
|
||||
{
|
||||
var service = new AgentService();
|
||||
var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath);
|
||||
|
||||
//seting up a temp notebook job
|
||||
var notebook = AgentTestUtils.SetupNotebookJob(connectionResult).Result;
|
||||
//verifying that the notebook is created
|
||||
Assert.Equal(true, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
|
||||
var originalName = notebook.Name;
|
||||
//Changing the notebookName
|
||||
notebook.Name = "myTestNotebookJob" + Guid.NewGuid().ToString();
|
||||
|
||||
Assert.Equal(false, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
|
||||
await AgentNotebookHelper.UpdateNotebook(
|
||||
service,
|
||||
connectionResult.ConnectionInfo.OwnerUri,
|
||||
originalName,
|
||||
notebook,
|
||||
null,
|
||||
ManagementUtils.asRunType(0)
|
||||
);
|
||||
|
||||
Assert.Equal(true, AgentTestUtils.VerifyNotebook(connectionResult, notebook));
|
||||
|
||||
//cleaning up the job
|
||||
await AgentTestUtils.CleanupNotebookJob(connectionResult, notebook);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,14 @@
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent;
|
||||
using Microsoft.SqlTools.ServiceLayer.Agent.Contracts;
|
||||
using Microsoft.SqlTools.ServiceLayer.Connection;
|
||||
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Security;
|
||||
using Microsoft.SqlTools.ServiceLayer.Management;
|
||||
using Microsoft.SqlTools.ServiceLayer.Utility;
|
||||
using Moq;
|
||||
using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper;
|
||||
@@ -22,7 +24,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
|
||||
internal static AgentJobStepInfo GetTestJobStepInfo(
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobInfo job,
|
||||
AgentJobInfo job,
|
||||
string stepName = "Test Job Step1")
|
||||
{
|
||||
return new AgentJobStepInfo()
|
||||
@@ -57,7 +59,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
CategoryType = 1,
|
||||
LastRun = "today",
|
||||
NextRun = "tomorrow",
|
||||
JobId = Guid.NewGuid().ToString()
|
||||
JobId = Guid.NewGuid().ToString(),
|
||||
Owner = "sa"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -74,12 +77,12 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
internal static AgentProxyInfo GetTestProxyInfo()
|
||||
{
|
||||
return new AgentProxyInfo()
|
||||
{
|
||||
{
|
||||
AccountName = "Test Proxy",
|
||||
CredentialName = SecurityTestUtils.TestCredentialName,
|
||||
Description = "Test proxy description",
|
||||
IsEnabled = true
|
||||
};
|
||||
IsEnabled = true
|
||||
};
|
||||
}
|
||||
|
||||
internal static AgentScheduleInfo GetTestScheduleInfo()
|
||||
@@ -93,11 +96,11 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task CreateAgentJob(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobInfo job)
|
||||
{
|
||||
var context = new Mock<RequestContext<CreateAgentJobResult>>();
|
||||
var context = new Mock<RequestContext<CreateAgentJobResult>>();
|
||||
await service.HandleCreateAgentJobRequest(new CreateAgentJobParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
@@ -107,12 +110,12 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task UpdateAgentJob(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobInfo job)
|
||||
{
|
||||
job.Description = "Update job description";
|
||||
var context = new Mock<RequestContext<UpdateAgentJobResult>>();
|
||||
var context = new Mock<RequestContext<UpdateAgentJobResult>>();
|
||||
await service.HandleUpdateAgentJobRequest(new UpdateAgentJobParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
@@ -122,12 +125,12 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task DeleteAgentJob(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobInfo job,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobInfo job,
|
||||
bool verify = true)
|
||||
{
|
||||
var context = new Mock<RequestContext<ResultStatus>>();
|
||||
var context = new Mock<RequestContext<ResultStatus>>();
|
||||
await service.HandleDeleteAgentJobRequest(new DeleteAgentJobParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
@@ -141,11 +144,11 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task CreateAgentJobStep(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobStepInfo stepInfo)
|
||||
{
|
||||
var context = new Mock<RequestContext<CreateAgentJobStepResult>>();
|
||||
var context = new Mock<RequestContext<CreateAgentJobStepResult>>();
|
||||
await service.HandleCreateAgentJobStepRequest(new CreateAgentJobStepParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
@@ -153,13 +156,13 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}, context.Object);
|
||||
context.VerifyAll();
|
||||
}
|
||||
|
||||
|
||||
internal static async Task UpdateAgentJobStep(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobStepInfo stepInfo)
|
||||
{
|
||||
var context = new Mock<RequestContext<UpdateAgentJobStepResult>>();
|
||||
var context = new Mock<RequestContext<UpdateAgentJobStepResult>>();
|
||||
await service.HandleUpdateAgentJobStepRequest(new UpdateAgentJobStepParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
@@ -169,11 +172,11 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task DeleteAgentJobStep(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentJobStepInfo stepInfo)
|
||||
{
|
||||
var context = new Mock<RequestContext<ResultStatus>>();
|
||||
var context = new Mock<RequestContext<ResultStatus>>();
|
||||
await service.HandleDeleteAgentJobStepRequest(new DeleteAgentJobStepParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
@@ -183,7 +186,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task CreateAgentOperator(
|
||||
AgentService service,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentOperatorInfo operatorInfo)
|
||||
{
|
||||
@@ -197,7 +200,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task UpdateAgentOperator(
|
||||
AgentService service,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentOperatorInfo operatorInfo)
|
||||
{
|
||||
@@ -211,7 +214,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task DeleteAgentOperator(
|
||||
AgentService service,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentOperatorInfo operatorInfo)
|
||||
{
|
||||
@@ -225,7 +228,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task CreateAgentProxy(
|
||||
AgentService service,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentProxyInfo proxy)
|
||||
{
|
||||
@@ -239,7 +242,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task UpdateAgentProxy(
|
||||
AgentService service,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
string originalProxyName,
|
||||
AgentProxyInfo proxy)
|
||||
@@ -255,8 +258,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task DeleteAgentProxy(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentProxyInfo proxy)
|
||||
{
|
||||
var context = new Mock<RequestContext<ResultStatus>>();
|
||||
@@ -269,7 +272,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task CreateAgentSchedule(
|
||||
AgentService service,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentScheduleInfo schedule)
|
||||
{
|
||||
@@ -277,16 +280,16 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
await service.HandleCreateAgentScheduleRequest(new CreateAgentScheduleParams
|
||||
{
|
||||
OwnerUri = connectionResult.ConnectionInfo.OwnerUri,
|
||||
Schedule = schedule
|
||||
Schedule = schedule
|
||||
}, context.Object);
|
||||
context.VerifyAll();
|
||||
}
|
||||
|
||||
internal static async Task UpdateAgentSchedule(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
string originalScheduleName,
|
||||
AgentScheduleInfo schedule)
|
||||
internal static async Task UpdateAgentSchedule(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
string originalScheduleName,
|
||||
AgentScheduleInfo schedule)
|
||||
{
|
||||
var context = new Mock<RequestContext<AgentScheduleResult>>();
|
||||
await service.HandleUpdateAgentScheduleRequest(new UpdateAgentScheduleParams()
|
||||
@@ -299,8 +302,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
}
|
||||
|
||||
internal static async Task DeleteAgentSchedule(
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentService service,
|
||||
TestConnectionResult connectionResult,
|
||||
AgentScheduleInfo schedule)
|
||||
{
|
||||
var context = new Mock<RequestContext<ResultStatus>>();
|
||||
@@ -346,5 +349,116 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent
|
||||
var service = new AgentService();
|
||||
await DeleteAgentJob(service, connectionResult, job);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static async Task<AgentNotebookInfo> SetupNotebookJob(
|
||||
TestConnectionResult connectionResult,
|
||||
AgentNotebookInfo notebook = null)
|
||||
{
|
||||
var service = new AgentService();
|
||||
if (notebook == null)
|
||||
{
|
||||
notebook = GetTestNotebookInfo("myTestNotebookJob" + Guid.NewGuid().ToString(), "master");
|
||||
}
|
||||
string tempNotebookPath = CreateTemplateNotebookFile();
|
||||
|
||||
await AgentNotebookHelper.CreateNotebook(
|
||||
service,
|
||||
connectionResult.ConnectionInfo.OwnerUri,
|
||||
notebook,
|
||||
tempNotebookPath,
|
||||
ManagementUtils.asRunType(0)
|
||||
);
|
||||
|
||||
var createdNotebook = GetNotebook(connectionResult, notebook.Name);
|
||||
File.Delete(tempNotebookPath);
|
||||
return createdNotebook;
|
||||
}
|
||||
|
||||
public static async Task CleanupNotebookJob(TestConnectionResult connectionResult, AgentNotebookInfo notebook)
|
||||
{
|
||||
var service = new AgentService();
|
||||
await AgentNotebookHelper.DeleteNotebook(
|
||||
service,
|
||||
connectionResult.ConnectionInfo.OwnerUri,
|
||||
notebook,
|
||||
ManagementUtils.asRunType(0)
|
||||
);
|
||||
}
|
||||
|
||||
public static AgentNotebookInfo GetNotebook(TestConnectionResult connectionResult, string name){
|
||||
var notebookList = AgentNotebookHelper.GetAgentNotebooks(connectionResult.ConnectionInfo).Result;
|
||||
foreach(AgentNotebookInfo n in notebookList)
|
||||
{
|
||||
if(n.Name == name)
|
||||
{
|
||||
return n;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static bool VerifyNotebook(TestConnectionResult connectionResult, AgentNotebookInfo notebook)
|
||||
{
|
||||
var notebookList = AgentNotebookHelper.GetAgentNotebooks(connectionResult.ConnectionInfo).Result;
|
||||
foreach (AgentNotebookInfo n in notebookList)
|
||||
{
|
||||
if (NotebookObjectEquals(notebook, n))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool NotebookObjectEquals(AgentNotebookInfo expectedNotebook, AgentNotebookInfo actualNotebook)
|
||||
{
|
||||
return (
|
||||
expectedNotebook.Name == actualNotebook.Name
|
||||
&&
|
||||
expectedNotebook.Description == actualNotebook.Description
|
||||
);
|
||||
}
|
||||
|
||||
internal static string CreateTemplateNotebookFile()
|
||||
{
|
||||
Assembly assembly = Assembly.GetAssembly(typeof(AgentNotebookTests));
|
||||
Stream scriptStream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".Agent.NotebookResources.TestNotebook.ipynb");
|
||||
StreamReader reader = new StreamReader(scriptStream);
|
||||
string testNotebookString = reader.ReadToEnd();
|
||||
string tempNotebookPath = System.IO.Path.GetTempFileName().Replace(".tmp", ".ipynb");
|
||||
File.WriteAllText(tempNotebookPath, testNotebookString);
|
||||
return tempNotebookPath;
|
||||
}
|
||||
|
||||
internal static AgentNotebookInfo GetTestNotebookInfo(string TestJobName, string TargetDatabase)
|
||||
{
|
||||
return new AgentNotebookInfo()
|
||||
{
|
||||
Name = TestJobName,
|
||||
Description = "Test job description",
|
||||
CurrentExecutionStatus = JobExecutionStatus.Executing,
|
||||
LastRunOutcome = CompletionResult.InProgress,
|
||||
CurrentExecutionStep = "Step 1",
|
||||
Enabled = false,
|
||||
HasTarget = false,
|
||||
HasSchedule = false,
|
||||
HasStep = false,
|
||||
Runnable = true,
|
||||
Category = "Cateory 1",
|
||||
CategoryId = 1,
|
||||
CategoryType = 1,
|
||||
LastRun = "today",
|
||||
NextRun = "tomorrow",
|
||||
JobId = new Guid().ToString(),
|
||||
TargetDatabase = TargetDatabase,
|
||||
Owner = "sa",
|
||||
ExecuteDatabase = TargetDatabase,
|
||||
JobSchedules = new AgentScheduleInfo[0],
|
||||
Alerts = new AgentAlertInfo[0]
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"name": "SQL",
|
||||
"display_name": "SQL",
|
||||
"language": "sql"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "sql",
|
||||
"version": ""
|
||||
}
|
||||
},
|
||||
"nbformat_minor": 2,
|
||||
"nbformat": 4,
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": "select * from sys.all_objects",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"execution_count": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -44,4 +44,10 @@
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Integration' ">
|
||||
<DefineConstants>$(DefineConstants);WINDOWS_ONLY_BUILD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove=".\Agent\NotebookResources\TestNotebook.ipynb" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include=".\Agent\NotebookResources\TestNotebook.ipynb" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user