diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs index b2e3dfbd..0f6c545b 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs @@ -106,7 +106,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent var fetcher = new JobFetcher(serverConnection); var filter = new JobActivityFilter(); this.jobs = fetcher.FetchJobs(filter); - var agentJobs = new List(); if (this.jobs != null) { @@ -118,8 +117,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent } result.Succeeded = true; result.Jobs = agentJobs.ToArray(); + sqlConnection.Close(); } - await requestContext.SendResult(result); } catch (Exception e) @@ -142,47 +141,28 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent out connInfo); if (connInfo != null) { - Tuple tuple = CreateSqlConnection(connInfo, parameters.JobId); + Tuple tuple = CreateSqlConnection(connInfo, parameters.JobId); SqlConnectionInfo sqlConnInfo = tuple.Item1; DataTable dt = tuple.Item2; + ServerConnection connection = tuple.Item3; int count = dt.Rows.Count; - var agentJobs = new List(); - - var agentStepMap = new Dictionary>(); - for (int i = 0; i < count; ++i) + List jobHistories = new List(); + if (count > 0) { - var job = dt.Rows[i]; - if (JobUtilities.IsStep(job, sqlConnInfo)) - { - var agentJobStep = JobUtilities.ConvertToAgentJobStep(job, sqlConnInfo); - if (agentStepMap.ContainsKey(agentJobStep.RunDate)) - { - agentStepMap[agentJobStep.RunDate].Add(agentJobStep); - } - else - { - var agentJobSteps = new List(); - agentJobSteps.Add(agentJobStep); - agentStepMap[agentJobStep.RunDate] = agentJobSteps; - } - } - else - { - var agentJobHistoryInfo = JobUtilities.ConvertToAgentJobHistoryInfo(job, sqlConnInfo); - agentJobs.Add(agentJobHistoryInfo); - } + var job = dt.Rows[0]; + string jobName = Convert.ToString(job[JobUtilities.UrnJobName], System.Globalization.CultureInfo.InvariantCulture); + Guid jobId = (Guid) job[JobUtilities.UrnJobId]; + int runStatus = Convert.ToInt32(job[JobUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture); + var t = new LogSourceJobHistory(jobName, sqlConnInfo, null, runStatus, jobId, null); + var tlog = t as ILogSource; + tlog.Initialize(); + var logEntries = t.LogEntries; + jobHistories = JobUtilities.ConvertToAgentJobHistoryInfo(logEntries, job); + tlog.CloseReader(); } + result.Jobs = jobHistories.ToArray(); result.Succeeded = true; - foreach (AgentJobHistoryInfo agentJobHistoryInfo in agentJobs) - { - if (agentStepMap.ContainsKey(agentJobHistoryInfo.RunDate)) - { - var agentStepList = agentStepMap[agentJobHistoryInfo.RunDate].ToList(); - agentStepList.Sort(delegate (AgentJobStep s1, AgentJobStep s2) { return s1.StepId.CompareTo(s2.StepId); }); - agentJobHistoryInfo.Steps = agentStepList.ToArray(); - } - } - result.Jobs = agentJobs.ToArray(); + connection.Disconnect(); await requestContext.SendResult(result); } } @@ -242,7 +222,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent } } - private Tuple CreateSqlConnection(ConnectionInfo connInfo, String jobId) + private Tuple CreateSqlConnection(ConnectionInfo connInfo, String jobId) { var sqlConnection = ConnectionService.OpenSqlConnection(connInfo); var serverConnection = new ServerConnection(sqlConnection); @@ -251,7 +231,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent filter.JobID = new Guid(jobId); var dt = server.JobServer.EnumJobHistory(filter); var sqlConnInfo = new SqlConnectionInfo(serverConnection, SqlServer.Management.Common.ConnectionType.SqlConnection); - return new Tuple(sqlConnInfo, dt); + return new Tuple(sqlConnInfo, dt, serverConnection); } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentJobHistoryInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentJobHistoryInfo.cs index 03934fad..549ca9a6 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentJobHistoryInfo.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentJobHistoryInfo.cs @@ -18,27 +18,27 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts public class AgentJobHistoryInfo { public int InstanceId { get; set; } - public int SqlMessageId { get; set; } + public string SqlMessageId { get; set; } public string Message { get; set; } - public int StepId { get; set; } + public string StepId { get; set; } public string StepName { get; set; } - public int SqlSeverity { get; set; } + public string SqlSeverity { get; set; } public Guid JobId { get; set; } public string JobName { get; set; } public int RunStatus { get; set; } public DateTime RunDate { get; set; } - public int RunDuration { get; set; } + public string RunDuration { get; set; } public string OperatorEmailed { get; set; } public string OperatorNetsent { get; set; } public string OperatorPaged { get; set; } - public int RetriesAttempted { get; set; } + public string RetriesAttempted { get; set; } public string Server { get; set; } public AgentJobStep[] Steps { get; set; } } public class AgentJobStep { - public int StepId { get; set; } + public string StepId { get; set; } public string StepName { get; set; } public string Message { get; set; } public DateTime RunDate { get; set; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobHistoryItem.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/JobHistoryItem.cs index a4368bff..09041862 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobHistoryItem.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/JobHistoryItem.cs @@ -38,33 +38,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent Error } - /// - /// command that can be executed by a log viewer (ILogViewer) - /// - internal enum LogViewerCommand - { - Load = 0, - Refresh, - Export, - Columns, - Filter, - Search, - Delete, - Help, - Close, - Cancel - } - - /// - /// command options - /// - internal enum LogViewerCommandOption - { - None = 0, - Hide, - Show - } - public interface ILogEntry { string OriginalSourceTypeName {get;} @@ -79,15 +52,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent internal class LogSourceJobHistory : ILogSource, IDisposable //, ITypedColumns, ILogCommandTarget { - internal const string JobDialogParameterTemplate = ""; - internal const string JobStepDialogParameterTemplate = "true"; - - internal enum DialogType - { - JobDialog = 0, - JobStepDialog = 1 - } - #region Variables private string m_jobName = null; //assigning invalid jobCategoryId @@ -105,8 +69,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent private TypedColumnCollection columnTypes; private IServiceProvider serviceProvider = null; - // ILogCommandHandler m_customCommandHandler = null; - private static string historyTableDeclaration = "declare @tmp_sp_help_jobhistory table"; private static string historyTableDeclaration80 = "create table #tmp_sp_help_jobhistory"; private static string historyTableName = "@tmp_sp_help_jobhistory"; @@ -173,6 +135,14 @@ ORDER BY [InstanceID] ASC"; return m_jobName; } } + + public List LogEntries + { + get + { + return m_logEntries; + } + } #endregion public void Dispose() @@ -206,11 +176,8 @@ ORDER BY [InstanceID] ASC"; } } - - #region Constructor - public LogSourceJobHistory(string jobName, SqlConnectionInfo sqlCi, object customCommandHandler, int jobCategoryId, Guid JobId, IServiceProvider serviceProvider) { m_logName = jobName; @@ -429,27 +396,8 @@ ORDER BY [InstanceID] ASC"; /// internal class LogEntryJobHistory : ILogEntry { - #region Constants - internal const string cUrnRunDate = "RunDate"; - internal const string cUrnRunStatus = "RunStatus"; - internal const string cUrnRunDuration = "RunDuration"; - internal const string cUrnJobName = "JobName"; - internal const string cUrnStepID = "StepID"; - internal const string cUrnStepName = "StepName"; - internal const string cUrnMessage = "Message"; - internal const string cUrnSqlSeverity = "SqlSeverity"; - internal const string cUrnSqlMessageID = "SqlMessageID"; - internal const string cUrnOperatorEmailed = "OperatorEmailed"; - internal const string cUrnOperatorNetsent = "OperatorNetsent"; - internal const string cUrnOperatorPaged = "OperatorPaged"; - internal const string cUrnRetriesAttempted = "RetriesAttempted"; - internal const string cUrnServerName = "Server"; - internal const string cUrnServerTime = "CurrentDate"; - internal const string cUrnInstanceID = "InstanceID"; // internally used by Agent to mark order in which steps were executed - #endregion - #region Variables string m_originalSourceName = null; DateTime m_pointInTime = DateTime.MinValue; @@ -526,10 +474,10 @@ ORDER BY [InstanceID] ASC"; { m_originalSourceName = sourceName; - m_pointInTime = Convert.ToDateTime(dr[cUrnRunDate], System.Globalization.CultureInfo.InvariantCulture); - m_serverName = Convert.ToString(dr[cUrnServerName], System.Globalization.CultureInfo.InvariantCulture); - m_fieldJobName = Convert.ToString(dr[cUrnJobName], System.Globalization.CultureInfo.InvariantCulture); - switch ((Microsoft.SqlServer.Management.Smo.Agent.CompletionResult)Convert.ToInt32(dr[cUrnRunStatus], System.Globalization.CultureInfo.InvariantCulture)) + m_pointInTime = Convert.ToDateTime(dr[JobUtilities.UrnRunDate], System.Globalization.CultureInfo.InvariantCulture); + m_serverName = Convert.ToString(dr[JobUtilities.UrnServer], System.Globalization.CultureInfo.InvariantCulture); + m_fieldJobName = Convert.ToString(dr[JobUtilities.UrnJobName], System.Globalization.CultureInfo.InvariantCulture); + switch ((Microsoft.SqlServer.Management.Smo.Agent.CompletionResult)Convert.ToInt32(dr[JobUtilities.UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture)) { case CompletionResult.Cancelled: m_severity = SeverityClass.Cancelled; @@ -576,7 +524,7 @@ ORDER BY [InstanceID] ASC"; // if stepId is zero then dont show stepID and step name in log viewer // Valid step Ids starts from index 1 - int currentStepId = (int)dr[cUrnStepID]; + int currentStepId = (int)dr[JobUtilities.UrnStepID]; if (currentStepId == 0) { m_fieldStepID = String.Empty; @@ -586,18 +534,18 @@ ORDER BY [InstanceID] ASC"; else { m_fieldStepID = Convert.ToString(currentStepId, System.Globalization.CultureInfo.CurrentCulture); - m_fieldStepName = Convert.ToString(dr[cUrnStepName], System.Globalization.CultureInfo.CurrentCulture); + m_fieldStepName = Convert.ToString(dr[JobUtilities.UrnStepName], System.Globalization.CultureInfo.CurrentCulture); } - m_fieldMessage = Convert.ToString(dr[cUrnMessage], System.Globalization.CultureInfo.CurrentCulture); - m_fieldSqlSeverity = Convert.ToString(dr[cUrnSqlSeverity], System.Globalization.CultureInfo.CurrentCulture); - m_fieldSqlMessageID = Convert.ToString(dr[cUrnSqlMessageID], System.Globalization.CultureInfo.CurrentCulture); - m_fieldOperatorEmailed = Convert.ToString(dr[cUrnOperatorEmailed], System.Globalization.CultureInfo.CurrentCulture); - m_fieldOperatorNetsent = Convert.ToString(dr[cUrnOperatorNetsent], System.Globalization.CultureInfo.CurrentCulture); - m_fieldOperatorPaged = Convert.ToString(dr[cUrnOperatorPaged], System.Globalization.CultureInfo.CurrentCulture); - m_fieldRetriesAttempted = Convert.ToString(dr[cUrnRetriesAttempted], System.Globalization.CultureInfo.CurrentCulture); + m_fieldMessage = Convert.ToString(dr[JobUtilities.UrnMessage], System.Globalization.CultureInfo.CurrentCulture); + m_fieldSqlSeverity = Convert.ToString(dr[JobUtilities.UrnSqlSeverity], System.Globalization.CultureInfo.CurrentCulture); + m_fieldSqlMessageID = Convert.ToString(dr[JobUtilities.UrnSqlMessageID], System.Globalization.CultureInfo.CurrentCulture); + m_fieldOperatorEmailed = Convert.ToString(dr[JobUtilities.UrnOperatorEmailed], System.Globalization.CultureInfo.CurrentCulture); + m_fieldOperatorNetsent = Convert.ToString(dr[JobUtilities.UrnOperatorNetsent], System.Globalization.CultureInfo.CurrentCulture); + m_fieldOperatorPaged = Convert.ToString(dr[JobUtilities.UrnOperatorPaged], System.Globalization.CultureInfo.CurrentCulture); + m_fieldRetriesAttempted = Convert.ToString(dr[JobUtilities.UrnRetriesAttempted], System.Globalization.CultureInfo.CurrentCulture); - Int64 hhmmss = Convert.ToInt64(dr[cUrnRunDuration], System.Globalization.CultureInfo.InvariantCulture); // HHMMSS + Int64 hhmmss = Convert.ToInt64(dr[JobUtilities.UrnRunDuration], System.Globalization.CultureInfo.InvariantCulture); // HHMMSS int hh = Convert.ToInt32(hhmmss / 10000, System.Globalization.CultureInfo.InvariantCulture); int mm = Convert.ToInt32((hhmmss / 100) % 100, System.Globalization.CultureInfo.InvariantCulture); int ss = Convert.ToInt32(hhmmss % 100, System.Globalization.CultureInfo.InvariantCulture); @@ -630,7 +578,7 @@ ORDER BY [InstanceID] ASC"; { DataRow dr = dt.Rows[i]; - object o = dr[cUrnStepID]; + object o = dr[JobUtilities.UrnStepID]; try { @@ -668,8 +616,8 @@ ORDER BY [InstanceID] ASC"; { m_originalSourceName = sourceName; - m_pointInTime = Convert.ToDateTime(dr[cUrnRunDate], System.Globalization.CultureInfo.InvariantCulture); - m_fieldJobName = Convert.ToString(dr[cUrnJobName], System.Globalization.CultureInfo.InvariantCulture); + m_pointInTime = Convert.ToDateTime(dr[JobUtilities.UrnRunDate], System.Globalization.CultureInfo.InvariantCulture); + m_fieldJobName = Convert.ToString(dr[JobUtilities.UrnJobName], System.Globalization.CultureInfo.InvariantCulture); m_severity = SeverityClass.InProgress; @@ -684,7 +632,7 @@ ORDER BY [InstanceID] ASC"; m_fieldRetriesAttempted = null; m_serverName = null; - m_fieldDuration = Convert.ToString(Convert.ToDateTime(dr[cUrnServerTime]) - Convert.ToDateTime(dr[cUrnRunDate], System.Globalization.CultureInfo.InvariantCulture), System.Globalization.CultureInfo.InvariantCulture); + m_fieldDuration = Convert.ToString(Convert.ToDateTime(dr[JobUtilities.UrnServerTime]) - Convert.ToDateTime(dr[JobUtilities.UrnRunDate], System.Globalization.CultureInfo.InvariantCulture), System.Globalization.CultureInfo.InvariantCulture); } catch (InvalidCastException) { @@ -745,6 +693,69 @@ ORDER BY [InstanceID] ASC"; { get { return m_subEntries; } } + + /* Public Properties */ + + internal string Duration + { + get { return m_fieldDuration; } + } + + internal string JobName + { + get { return m_fieldJobName; } + } + + internal string Message + { + get { return m_fieldMessage; } + } + + internal string StepID + { + get { return m_fieldStepID; } + } + + internal string OperatorEmailed + { + get { return m_fieldOperatorEmailed; } + } + + internal string OperatorNetsent + { + get { return m_fieldOperatorNetsent; } + } + + internal string OperatorPaged + { + get { return m_fieldOperatorPaged; } + } + + internal string StepName + { + get { return m_fieldStepName; } + } + + internal string RetriesAttempted + { + get { return m_fieldRetriesAttempted; } + } + + internal string SqlMessageID + { + get { return m_fieldSqlMessageID; } + } + + internal string SqlSeverity + { + get { return m_fieldSqlSeverity; } + } + + internal string Server + { + get { return m_serverName; } + } + #endregion } #endregion diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobUtilities.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/JobUtilities.cs index c91b79b7..bd6c0d5c 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/JobUtilities.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/JobUtilities.cs @@ -33,6 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent public const string UrnOperatorPaged = "OperatorPaged"; public const string UrnRetriesAttempted = "RetriesAttempted"; public const string UrnServer = "Server"; + internal const string UrnServerTime = "CurrentDate"; public static AgentJobInfo ConvertToAgentJobInfo(JobProperties job) { @@ -56,64 +57,13 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent }; } - public static bool IsStep(DataRow row, SqlConnectionInfo sqlConnectionInfo) + public static AgentJobStep ConvertToAgentJobStep(ILogEntry logEntry, DataRow jobRow) { - int stepId = Convert.ToInt32(row[UrnStepID], System.Globalization.CultureInfo.InvariantCulture); - return stepId != 0; - } - - public static AgentJobHistoryInfo ConvertToAgentJobHistoryInfo(DataRow jobRow, SqlConnectionInfo sqlConnInfo) - { - // get all the values for a job history - int instanceId = Convert.ToInt32(jobRow[UrnInstanceID], System.Globalization.CultureInfo.InvariantCulture); - int sqlMessageId = Convert.ToInt32(jobRow[UrnSqlMessageID], System.Globalization.CultureInfo.InvariantCulture); - string message = Convert.ToString(jobRow[UrnMessage], System.Globalization.CultureInfo.InvariantCulture); - int stepId = Convert.ToInt32(jobRow[UrnStepID], System.Globalization.CultureInfo.InvariantCulture); - string stepName = Convert.ToString(jobRow[UrnStepName], System.Globalization.CultureInfo.InvariantCulture); - int sqlSeverity = Convert.ToInt32(jobRow[UrnSqlSeverity], System.Globalization.CultureInfo.InvariantCulture); - Guid jobId = (Guid) jobRow[UrnJobId]; - string jobName = Convert.ToString(jobRow[UrnJobName], System.Globalization.CultureInfo.InvariantCulture); - int runStatus = Convert.ToInt32(jobRow[UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture); - DateTime runDate = Convert.ToDateTime(jobRow[UrnRunDate], System.Globalization.CultureInfo.InvariantCulture); - int runDuration = Convert.ToInt32(jobRow[UrnRunDuration], System.Globalization.CultureInfo.InvariantCulture); - string operatorEmailed = Convert.ToString(jobRow[UrnOperatorEmailed], System.Globalization.CultureInfo.InvariantCulture); - string operatorNetsent = Convert.ToString(jobRow[UrnOperatorNetsent], System.Globalization.CultureInfo.InvariantCulture); - string operatorPaged = Convert.ToString(jobRow[UrnOperatorPaged], System.Globalization.CultureInfo.InvariantCulture); - int retriesAttempted = Convert.ToInt32(jobRow[UrnRetriesAttempted], System.Globalization.CultureInfo.InvariantCulture); - string server = Convert.ToString(jobRow[UrnServer], System.Globalization.CultureInfo.InvariantCulture); - - // initialize logger - var t = new LogSourceJobHistory(jobName, sqlConnInfo, null, runStatus, jobId, null); - var tlog = t as ILogSource; - tlog.Initialize(); - - // return new job history object as a result - var jobHistoryInfo = new AgentJobHistoryInfo(); - jobHistoryInfo.InstanceId = instanceId; - jobHistoryInfo.SqlMessageId = sqlMessageId; - jobHistoryInfo.Message = message; - jobHistoryInfo.StepId = stepId; - jobHistoryInfo.StepName = stepName; - jobHistoryInfo.SqlSeverity = sqlSeverity; - jobHistoryInfo.JobId = jobId; - jobHistoryInfo.JobName = jobName; - jobHistoryInfo.RunStatus = runStatus; - jobHistoryInfo.RunDate = runDate; - jobHistoryInfo.RunDuration = runDuration; - jobHistoryInfo.OperatorEmailed = operatorEmailed; - jobHistoryInfo.OperatorNetsent = operatorNetsent; - jobHistoryInfo.OperatorPaged = operatorPaged; - jobHistoryInfo.RetriesAttempted = retriesAttempted; - jobHistoryInfo.Server = server; - return jobHistoryInfo; - } - - public static AgentJobStep ConvertToAgentJobStep(DataRow jobRow, SqlConnectionInfo sqlConnInfo) - { - int stepId = Convert.ToInt32(jobRow[UrnStepID], System.Globalization.CultureInfo.InvariantCulture); - string stepName = Convert.ToString(jobRow[UrnStepName], System.Globalization.CultureInfo.InvariantCulture); - string message = Convert.ToString(jobRow[UrnMessage], System.Globalization.CultureInfo.InvariantCulture); - DateTime runDate = Convert.ToDateTime(jobRow[UrnRunDate], System.Globalization.CultureInfo.InvariantCulture); + var entry = logEntry as LogSourceJobHistory.LogEntryJobHistory; + string stepId = entry.StepID; + string stepName = entry.StepName; + string message = entry.Message; + DateTime runDate = logEntry.PointInTime; int runStatus = Convert.ToInt32(jobRow[UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture); AgentJobStep step = new AgentJobStep(); step.StepId = stepId; @@ -123,5 +73,46 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent step.RunStatus = runStatus; return step; } + + public static List ConvertToAgentJobHistoryInfo(List logEntries, DataRow jobRow) + { + List jobs = new List(); + // get all the values for a job history + foreach (ILogEntry entry in logEntries) + { + // Make a new AgentJobHistoryInfo object + var jobHistoryInfo = new AgentJobHistoryInfo(); + jobHistoryInfo.InstanceId = Convert.ToInt32(jobRow[UrnInstanceID], System.Globalization.CultureInfo.InvariantCulture); + jobHistoryInfo.RunStatus = Convert.ToInt32(jobRow[UrnRunStatus], System.Globalization.CultureInfo.InvariantCulture); + jobHistoryInfo.JobId = (Guid) jobRow[UrnJobId]; + var logEntry = entry as LogSourceJobHistory.LogEntryJobHistory; + 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(); + if (entry.CanLoadSubEntries) + { + foreach (ILogEntry step in entry.SubEntries) + { + jobSteps.Add(JobUtilities.ConvertToAgentJobStep(step, jobRow)); + } + } + jobHistoryInfo.Steps = jobSteps.ToArray(); + jobs.Add(jobHistoryInfo); + } + return jobs; + } } } \ No newline at end of file