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:
Aasim Khan
2019-08-22 12:37:19 -07:00
committed by GitHub
parent 4f928133e1
commit 487235b1e9
15 changed files with 1825 additions and 55 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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");
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -39,4 +39,7 @@
<EmbeddedResource Include="Localization\sr.resx" />
<None Include="Localization\sr.strings" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include=".\Agent\NotebookResources\NotebookJobScript.ps1" />
</ItemGroup>
</Project>

View File

@@ -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()

View File

@@ -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);
}

View File

@@ -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);
}
}
}
}

View File

@@ -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]
};
}
}
}

View File

@@ -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
}
]
}

View File

@@ -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>