From 29fb715ad5691acd186813e9d27a877a264d833a Mon Sep 17 00:00:00 2001 From: Karl Burtram Date: Mon, 18 Jun 2018 10:18:23 -0700 Subject: [PATCH] Add initial schedule request handlers (#638) --- .../Agent/AgentService.cs | 99 +++++- .../Agent/Contracts/AgentProxyRequest.cs | 16 +- .../Agent/Contracts/AgentScheduleInfo.cs | 68 ++++ .../Agent/Contracts/AgentScheduleRequest.cs | 124 +++++++ .../Agent/Jobs/JobSchedulesActions.cs | 317 ++++-------------- .../Agent/Jobs/JobSchedulesData.cs | 7 - .../Agent/Jobs/ScheduleData.cs | 21 +- .../Agent/AgentProxyTests.cs | 4 +- .../Agent/AgentScheduleTests.cs | 95 ++++++ .../Agent/AgentTestUtils.cs | 83 ++++- 10 files changed, 555 insertions(+), 279 deletions(-) create mode 100644 src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleInfo.cs create mode 100644 src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleRequest.cs create mode 100644 test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentScheduleTests.cs diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs index 81193c47..1a81ace4 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/AgentService.cs @@ -114,6 +114,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.ServiceHost.SetRequestHandler(CreateAgentProxyRequest.Type, HandleCreateAgentProxyRequest); this.ServiceHost.SetRequestHandler(UpdateAgentProxyRequest.Type, HandleUpdateAgentProxyRequest); this.ServiceHost.SetRequestHandler(DeleteAgentProxyRequest.Type, HandleDeleteAgentProxyRequest); + + // Schedule request handlers + this.ServiceHost.SetRequestHandler(AgentSchedulesRequest.Type, HandleAgentSchedulesRequest); + this.ServiceHost.SetRequestHandler(CreateAgentScheduleRequest.Type, HandleCreateAgentScheduleRequest); + this.ServiceHost.SetRequestHandler(UpdateAgentScheduleRequest.Type, HandleUpdateAgentScheduleRequest); + this.ServiceHost.SetRequestHandler(DeleteAgentScheduleRequest.Type, HandleDeleteAgentScheduleRequest); } #region "Jobs Handlers" @@ -558,6 +564,61 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent #endregion // "Proxy Handlers" + #region "Schedule Handlers" + + internal async Task HandleAgentSchedulesRequest(AgentSchedulesParams parameters, RequestContext requestContext) + { + await requestContext.SendResult(null); + } + + internal async Task HandleCreateAgentScheduleRequest(CreateAgentScheduleParams parameters, RequestContext requestContext) + { + var result = await ConfigureAgentSchedule( + parameters.OwnerUri, + parameters.Schedule, + ConfigAction.Create, + RunType.RunNow); + + await requestContext.SendResult(new AgentScheduleResult() + { + Success = result.Item1, + ErrorMessage = result.Item2, + Schedule = parameters.Schedule + }); + } + + internal async Task HandleUpdateAgentScheduleRequest(UpdateAgentScheduleParams parameters, RequestContext requestContext) + { + var result = await ConfigureAgentSchedule( + parameters.OwnerUri, + parameters.Schedule, + ConfigAction.Update, + RunType.RunNow); + + await requestContext.SendResult(new AgentScheduleResult() + { + Success = result.Item1, + ErrorMessage = result.Item2, + Schedule = parameters.Schedule + }); + } + + internal async Task HandleDeleteAgentScheduleRequest(DeleteAgentScheduleParams parameters, RequestContext requestContext) + { + var result = await ConfigureAgentSchedule( + parameters.OwnerUri, + parameters.Schedule, + ConfigAction.Drop, + RunType.RunNow); + + await requestContext.SendResult(new ResultStatus() + { + Success = result.Item1, + ErrorMessage = result.Item2 + }); + } + + #endregion // "Schedule Handlers" #region "Helpers" @@ -720,7 +781,43 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent return new Tuple(false, ex.ToString()); } }); - } + } + + internal async Task> ConfigureAgentSchedule( + string ownerUri, + AgentScheduleInfo schedule, + ConfigAction configAction, + RunType runType) + { + return await Task.Run(() => + { + try + { + JobData jobData; + CDataContainer dataContainer; + CreateJobData(ownerUri, schedule.JobName, out dataContainer, out jobData); + + const string UrnFormatStr = "Server[@Name='{0}']/JobServer[@Name='{0}']/Job[@Name='{1}']/Schedule[@Name='{2}']"; + string serverName = dataContainer.Server.Name.ToUpper(); + string scheduleUrn = string.Format(UrnFormatStr, serverName, jobData.Job.Name, schedule.Name); + + STParameters param = new STParameters(dataContainer.Document); + param.SetParam("urn", scheduleUrn); + + using (JobSchedulesActions actions = new JobSchedulesActions(dataContainer, jobData, schedule, configAction)) + { + var executionHandler = new ExecutonHandler(actions); + executionHandler.RunNow(runType, this); + } + + return new Tuple(true, string.Empty); + } + catch (Exception ex) + { + return new Tuple(false, ex.ToString()); + } + }); + } private void CreateJobData( string ownerUri, diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs index 77dbc915..47371a8a 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentProxyRequest.cs @@ -29,14 +29,6 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts public AgentProxyInfo[] Proxies { get; set; } } - /// - /// SQL Agent Proxy result - /// - public class AgentProxyResult : ResultStatus - { - public AgentProxyInfo Proxy { get; set; } - } - /// /// SQL Agent Proxy Accounts request type /// @@ -50,6 +42,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts RequestType.Create("agent/proxies"); } + /// + /// SQL Agent Proxy result + /// + public class AgentProxyResult : ResultStatus + { + public AgentProxyInfo Proxy { get; set; } + } + /// /// SQL Agent create Proxy Account params /// diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleInfo.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleInfo.cs new file mode 100644 index 00000000..a487e282 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleInfo.cs @@ -0,0 +1,68 @@ +// +// 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 Microsoft.SqlTools.ServiceLayer.Agent; + +namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts +{ + [Flags] + public enum FrequencyTypes + { + Unknown = 0, + OneTime = 1, + Daily = 4, + Weekly = 8, + Monthly = 16, + MonthlyRelative = 32, + AutoStart = 64, + OnIdle = 128 + } + + [Flags] + public enum FrequencySubDayTypes + { + Unknown = 0, + Once = 1, + Second = 2, + Minute = 4, + Hour = 8 + } + + [Flags] + public enum FrequencyRelativeIntervals + { + First = 1, + Second = 2, + Third = 4, + Fourth = 8, + Last = 16 + } + + /// + /// a class for storing various properties of agent schedules + /// + public class AgentScheduleInfo + { + public int ID { get; set; } + public string Name { get; set; } + public string JobName { get; set; } + public bool IsEnabled { get; set; } + public FrequencyTypes FrequencyTypes { get; set; } + public FrequencySubDayTypes FrequencySubDayTypes { get; set; } + public int FrequencySubDayInterval { get; set; } + public FrequencyRelativeIntervals FrequencyRelativeIntervals { get; set; } + public int FrequencyRecurrenceFactor { get; set; } + public int FrequencyInterval { get; set; } + public DateTime DateCreated { get; set; } + public TimeSpan ActiveStartTimeOfDay { get; set; } + public DateTime ActiveStartDate { get; set; } + public TimeSpan ActiveEndTimeOfDay { get; set; } + public int JobCount { get; set; } + public DateTime ActiveEndDate { get; set; } + public Guid ScheduleUid { get; set; } + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleRequest.cs new file mode 100644 index 00000000..ec1c0660 --- /dev/null +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Contracts/AgentScheduleRequest.cs @@ -0,0 +1,124 @@ +// +// 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.Utility; +using Microsoft.SqlTools.Utility; + +namespace Microsoft.SqlTools.ServiceLayer.Agent.Contracts +{ + /// + /// SQL Agent Schedules parameters + /// + public class AgentSchedulesParams : GeneralRequestDetails + { + public string OwnerUri { get; set; } + } + + /// + /// SQL Agent Schedules result + /// + public class AgentSchedulesResult + { + public bool Success { get; set; } + + public string ErrorMessage { get; set; } + + public AgentScheduleInfo[] Schedules { get; set; } + } + + /// + /// SQL Agent Schedules request type + /// + public class AgentSchedulesRequest + { + /// + /// Request definition + /// + public static readonly + RequestType Type = + RequestType.Create("agent/schedules"); + } + + /// + /// SQL Agent Schedule result + /// + public class AgentScheduleResult : ResultStatus + { + public AgentScheduleInfo Schedule { get; set; } + } + + + /// + /// SQL Agent create Schedules params + /// + public class CreateAgentScheduleParams : GeneralRequestDetails + { + public string OwnerUri { get; set; } + + public AgentScheduleInfo Schedule { get; set; } + } + + /// + /// SQL Agent create Schedule request type + /// + public class CreateAgentScheduleRequest + { + /// + /// Request definition + /// + public static readonly + RequestType Type = + RequestType.Create("agent/createschedule"); + } + + /// + /// SQL Agent update Schedule params + /// + public class UpdateAgentScheduleParams : GeneralRequestDetails + { + public string OwnerUri { get; set; } + + public string OriginalScheduleName { get; set; } + + public AgentScheduleInfo Schedule { get; set; } + } + + /// + /// SQL Agent update Schedule request type + /// + public class UpdateAgentScheduleRequest + { + /// + /// Request definition + /// + public static readonly + RequestType Type = + RequestType.Create("agent/updateschedule"); + } + + /// + /// SQL Agent delete Schedule params + /// + public class DeleteAgentScheduleParams : GeneralRequestDetails + { + public string OwnerUri { get; set; } + + public AgentScheduleInfo Schedule { get; set; } + } + + /// + /// SQL Agent delete Schedule request type + /// + public class DeleteAgentScheduleRequest + { + /// + /// Request definition + /// + public static readonly + RequestType Type = + RequestType.Create("agent/deleteschedule"); + } +} diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesActions.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesActions.cs index dfd36e38..84d33094 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesActions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesActions.cs @@ -4,27 +4,61 @@ // using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text; +using Microsoft.SqlServer.Management.Smo.Agent; +using Microsoft.SqlTools.ServiceLayer.Agent.Contracts; using Microsoft.SqlTools.ServiceLayer.Management; namespace Microsoft.SqlTools.ServiceLayer.Agent { /// - /// Summary description for JobSchedules. + /// Summary description for JobSchedulesActions. /// internal class JobSchedulesActions : ManagementActionBase { private bool sharedSchedulesSupported = false; private JobData data; + private JobScheduleData scheduleData = null; + private AgentScheduleInfo scheduleInfo; + private ConfigAction configAction; - public JobSchedulesActions(CDataContainer dataContainer, JobData data) + public JobSchedulesActions(CDataContainer dataContainer, JobData data, AgentScheduleInfo scheduleInfo, ConfigAction configAction) { this.DataContainer = dataContainer; this.data = data; - + this.configAction = configAction; + this.scheduleInfo = scheduleInfo; this.sharedSchedulesSupported = this.DataContainer.Server.Information.Version.Major >= 9; + + if (configAction == ConfigAction.Create) + { + this.scheduleData = new JobScheduleData(this.data.Job); + this.scheduleData.SetJobSchedule(new JobSchedule()); + } + else + { + // get the JobScheduleData from the urn + string urn = null; + STParameters parameters = new STParameters(); + parameters.SetDocument(this.DataContainer.Document); + parameters.GetParam("urn", ref urn); + + JobSchedule jobStep = this.data.Job.Parent.Parent.GetSmoObject(urn) as JobSchedule; + if (jobStep != null) + { + this.scheduleData = new JobScheduleData(jobStep); + } + + if (configAction == ConfigAction.Update && this.scheduleData == null) + { + throw new Exception("Schedule urn parameter cannot be null"); + } + } + + // copy properties from AgentScheduelInfo + if (this.scheduleData != null) + { + this.scheduleData.Name = scheduleInfo.Name; + } } /// @@ -38,263 +72,36 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent base.Dispose(disposing); } - #region ui stuff - private void InitializeData() - { - if (this.data == null) - { - return; - } - // load the grid - //PopulateGrid(this.data.JobSchedules); - } - // private void PopulateGrid(JobSchedulesData schedules) - // { - // // add non-shared schedules - // for (int i = 0; i < schedules.Schedules.Count; i++) - // { - // JobScheduleData schedule = schedules.Schedules[i] as JobScheduleData; - // if (schedule != null) - // { - // // add rows to the grid - // GridCellCollection row = new GridCellCollection(); - // GridCell cell; - - // // ID - // cell = new GridCell(ConvertIdToDisplayName(schedule.ID)); - // row.Add(cell); - // // Name - // cell = new GridCell(schedule.Name); - // row.Add(cell); - // // Enabled - // cell = new GridCell(schedule.Enabled ? JobSR.Yes : JobSR.No); - // row.Add(cell); - // // Description - // cell = new GridCell(schedule.Description); - // row.Add(cell); - - // // Hyperlink 'Jobs in Schedule' - // if (this.sharedSchedulesSupported) - // { - // // don't add a hyperlink if the schedule has not yet been created - // cell = new GridCell(schedule.Created ? JobSR.ViewJobsInScheduleHyperlink : String.Empty); - // row.Add(cell); - // } - - // this.scheduleList.AddRow(row); - // } - // } - // } /// - /// Convert an id into a user friendly name. Converts new id to "new" + /// called by PreProcessExecution to enable derived classes to take over execution /// - /// - /// - private string ConvertIdToDisplayName(int id) + /// + /// + /// + /// true if regular execution should take place, false if everything, + /// has been done by this function + /// + protected override bool DoPreProcessExecution(RunType runType, out ExecutionMode executionResult) { - string rv; - // convert -1 into New - if (id < 0) + base.DoPreProcessExecution(runType, out executionResult); + if (this.scheduleData == null) { - rv = "JobSR.New"; + return false; + } + + if (this.configAction == ConfigAction.Drop) + { + var jobSchedule = this.scheduleData.SourceSchedule; + if (jobSchedule != null) + { + jobSchedule.DropIfExists(); + } } else { - rv = Convert.ToString(id, System.Globalization.CultureInfo.CurrentCulture); + this.scheduleData.ApplyChanges(); } - return rv; - } - - #endregion - - #region ui event handlers - - - // private void addSchedule_Click(object sender, System.EventArgs e) - // { - // System.Diagnostics.Debug.Assert(this.DataContainer.Server.Information.Version.Major >= 9, "Shared Schedules supported only for Yukon - this button should be disabled if target server is Shiloh/Sphinx"); - - // StringBuilder excludedSchedules = this.BuildListOfScheduleId(this.data.JobSchedules.Schedules); - // StringBuilder removedSchedules = this.BuildListOfScheduleId(this.data.JobSchedules.RemovedSchedules); - - // STParameters param = new STParameters(); - - // param.SetDocument(this.DataContainer.Document); - - // param.SetParam("excludedschedules", excludedSchedules.ToString()); - // if (this.data.Mode == JobData.DialogMode.Properties) - // { - // param.SetParam("removedschedules", removedSchedules.ToString()); - // param.SetParam("joburn", this.data.Urn); - // } - - // ManageSchedulesForm formPickUpSharedSchedule = new ManageSchedulesForm - // ( - // this.DataContainer, - // ((this.data != null) && (this.data.Name != null)) ? this.data.Name : String.Empty, - // this.ServiceProvider - // ); - // using (formPickUpSharedSchedule) - // { - // DialogResult dr = formPickUpSharedSchedule.ShowDialog(); - - // // cleanup the datacontainer - // param.SetParam("excludedschedules", String.Empty); - // if (this.data.Mode == JobData.DialogMode.Properties) - // { - // param.SetParam("removedschedules", String.Empty); - // param.SetParam("joburn", String.Empty); - // } - - // if (dr == DialogResult.OK) - // { - // JobSchedule schedule = formPickUpSharedSchedule.SelectedSchedule; - // System.Diagnostics.Debug.Assert(schedule != null); - // System.Diagnostics.Debug.Assert(schedule.Name != null); - - // bool scheduleAlreadyAdded = false; - // foreach (object o in this.data.JobSchedules.Schedules) - // { - // JobScheduleData jsd = o as JobScheduleData; - - // System.Diagnostics.Debug.Assert(jsd != null, "non JobScheduleData found in this.data.Schedules"); - // if ((jsd != null) && (jsd.ID == schedule.ID)) - // { - // scheduleAlreadyAdded = true; - // break; - // } - // } - - // if (scheduleAlreadyAdded == false) - // { - // JobScheduleData scheduleData = new JobScheduleData(schedule); - // this.data.JobSchedules.AddSchedule(scheduleData); - - // this.scheduleList.DeleteAllRows(); - // PopulateGrid(this.data.JobSchedules); - // UpdateControlStatus(); - // } - // } - // } - // } - - private void editSchedule_Click(object sender, System.EventArgs e) - { - // EditSelectedSchedule(); - } - - // private void deleteSchedule_Click(object sender, System.EventArgs e) - // { - // // check that a row is selected first - // STrace.Assert(this.scheduleList.SelectedCells.Count > 0, "there are no selected rows"); - // if (this.scheduleList.SelectedCells.Count == 0) - // { - // return; - // } - // int row = (int)this.scheduleList.SelectedCells[0].Y; - - // // check that this is a valid row - // STrace.Assert(row <= this.data.JobSchedules.Schedules.Count, "selected row does not exist in data structures"); - // if (row > this.data.JobSchedules.Schedules.Count) - // { - // return; - // } - - // JobScheduleData data = this.data.JobSchedules.Schedules[row] as JobScheduleData; - // if (data != null) - // { - // this.data.JobSchedules.DeleteSchedule(data); // if is non-shared it will be marked for deletion here - - // this.scheduleList.DeleteAllRows(); - // PopulateGrid(this.data.JobSchedules); - // } - // UpdateControlStatus(); - // } - - // private void EditSelectedSchedule() - // { - // // check that a row is selected first - // STrace.Assert(this.scheduleList.SelectedCells.Count > 0, "there are no selected rows"); - // if (this.scheduleList.SelectedCells.Count == 0) - // { - // return; - // } - // int row = (int)this.scheduleList.SelectedCells[0].Y; - - // // check that this is a valid row - // STrace.Assert(row <= this.data.JobSchedules.Schedules.Count, "selected row does not exist in data structures"); - // if (row > this.data.JobSchedules.Schedules.Count) - // { - // return; - // } - - // JobScheduleData data = this.data.JobSchedules.Schedules[row] as JobScheduleData; - // if (data != null) - // { - // try - // { - // using (ScheduleDialog jobSchedule = new ScheduleDialog(data, - // this.data.JobSchedules.Schedules, - // this.DataContainer, - // this.ServiceProvider)) - // { - // jobSchedule.Text = JobSR.EditSchedule(data.Name); - // jobSchedule.SetSite(this.ServiceProvider); - - // try - // { - // if (DialogResult.OK == jobSchedule.ShowDialog()) - // { - // this.scheduleList.DeleteAllRows(); - // PopulateGrid(this.data.JobSchedules); - // UpdateControlStatus(); - // } - // } - // catch (ApplicationException error) - // { - // DisplayExceptionMessage(error); - // jobSchedule.DialogResult = DialogResult.None; - // } - - // } - // } - // catch (ApplicationException error) - // { - // DisplayExceptionMessage(error); - // } - // } - // } - - // } - #endregion - - /// - /// enumerates schedules list and returns back as s string in format 1,2,3 - /// - /// - /// - private StringBuilder BuildListOfScheduleId(List schedules) - { - if (schedules == null) - { - throw new ArgumentNullException("schedules"); - } - StringBuilder scheduleIdList = new StringBuilder(); - foreach (JobScheduleData schedule in schedules) - { - if (schedule != null) - { - if (scheduleIdList.Length > 0) - { - scheduleIdList.AppendFormat(CultureInfo.InvariantCulture, ", {0}", schedule.ID); - } - else - { - scheduleIdList.Append(schedule.ID); - } - } - } - return scheduleIdList; + return false; } } } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesData.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesData.cs index b07b4e40..9aa8976f 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/JobSchedulesData.cs @@ -305,10 +305,3 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent } } } - - - - - - - diff --git a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/ScheduleData.cs b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/ScheduleData.cs index 815ef299..f992ec63 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/ScheduleData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Agent/Jobs/ScheduleData.cs @@ -801,6 +801,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.id = value; } } + public string Name { get @@ -812,6 +813,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.currentName = value; } } + public bool Enabled { get @@ -823,6 +825,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.enabled = value; } } + public bool AllowEnableDisable { get @@ -830,6 +833,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent return this.allowEnableDisable; } } + public string Description { get @@ -837,6 +841,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent return this.ToString(); } } + public bool Created { get @@ -848,6 +853,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.alreadyCreated = value; } } + public DateTime ActiveStartDate { get @@ -859,6 +865,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.startDate = value; } } + public TimeSpan ActiveStartTime { get @@ -870,6 +877,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.startTime = value; } } + public DateTime ActiveEndDate { get @@ -883,6 +891,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent : value; } } + public TimeSpan ActiveEndTime { get @@ -896,6 +905,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent : value; } } + public bool HasEndDate { get @@ -903,6 +913,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent return this.endDate < JobScheduleData.MaxAgentDateValue; } } + public bool HasEndTime { get @@ -910,6 +921,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent return this.endTime < JobScheduleData.MaxAgentTimeValue; } } + public FrequencyTypes FrequencyTypes { get @@ -921,6 +933,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.frequencyType = value; } } + public int FrequencyInterval { get @@ -932,6 +945,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.frequencyInterval = value; } } + public int FrequencyRecurranceFactor { get @@ -943,6 +957,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.frequencyRecurranceFactor = value; } } + public FrequencyRelativeIntervals FrequencyRelativeIntervals { get @@ -954,6 +969,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.frequencyRelativeInterval = value; } } + public int FrequencySubDayInterval { get @@ -965,6 +981,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.frequencySubDayInterval = value; } } + public FrequencySubDayTypes FrequencySubDayTypes { get @@ -976,6 +993,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent this.frequencySubDayTypes = value; } } + public JobSchedule SourceSchedule { get @@ -983,6 +1001,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent return this.source; } } + public bool IsReadOnly { get { return isReadOnly; } @@ -1328,7 +1347,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Agent private void SetDefaults() { this.alreadyCreated = false; - currentName = originalName = String.Empty; + currentName = originalName = string.Empty; this.enabled = true; this.frequencyType = FrequencyTypes.Weekly; //SQL2K default value diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentProxyTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentProxyTests.cs index aac2dc54..b058a288 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentProxyTests.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentProxyTests.cs @@ -53,7 +53,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent { using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) { - // cleanup + // setup var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath); var credential = await SecurityTestUtils.SetupCredential(connectionResult); var service = new AgentService(); @@ -80,7 +80,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent { using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) { - // cleanup + // setup var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath); var credential = await SecurityTestUtils.SetupCredential(connectionResult); var service = new AgentService(); diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentScheduleTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentScheduleTests.cs new file mode 100644 index 00000000..74eb220e --- /dev/null +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentScheduleTests.cs @@ -0,0 +1,95 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System.Threading.Tasks; +using Microsoft.SqlTools.Hosting.Protocol; +using Microsoft.SqlTools.ServiceLayer.Agent; +using Microsoft.SqlTools.ServiceLayer.Agent.Contracts; +using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility; +using Microsoft.SqlTools.ServiceLayer.Test.Common; +using Microsoft.SqlTools.ServiceLayer.Utility; +using Moq; +using Xunit; +using static Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility.LiveConnectionHelper; + +namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent +{ + public class AgentScheduleTests + { + /// + /// TestHandleCreateAgentScheduleRequest + /// + [Fact] + public async Task TestHandleCreateAgentScheduleRequest() + { + using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) + { + // setup + var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath); + var service = new AgentService(); + var job = await AgentTestUtils.SetupJob(connectionResult); + var schedule = AgentTestUtils.GetTestScheduleInfo(); + await AgentTestUtils.DeleteAgentSchedule(service, connectionResult, schedule); + + // test + await AgentTestUtils.CreateAgentSchedule(service, connectionResult, schedule); + + // cleanup + await AgentTestUtils.DeleteAgentSchedule(service, connectionResult, schedule); + await AgentTestUtils.CleanupJob(connectionResult, job); + } + } + + /// + /// TestHandleUpdateAgentScheduleRequest + /// + [Fact] + public async Task TestHandleUpdateAgentScheduleRequest() + { + using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) + { + // setup + var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath); + var service = new AgentService(); + var job = await AgentTestUtils.SetupJob(connectionResult); + var schedule = AgentTestUtils.GetTestScheduleInfo(); + await AgentTestUtils.DeleteAgentSchedule(service, connectionResult, schedule); + await AgentTestUtils.CreateAgentSchedule(service, connectionResult, schedule); + + // test + schedule.IsEnabled = !schedule.IsEnabled; + await AgentTestUtils.UpdateAgentSchedule(service, connectionResult, schedule.Name, schedule); + + // cleanup + await AgentTestUtils.DeleteAgentSchedule(service, connectionResult, schedule); + await AgentTestUtils.CleanupJob(connectionResult, job); + } + } + + /// + /// TestHandleDeleteAgentScheduleRequest + /// + [Fact] + public async Task TestHandleDeleteAgentScheduleRequest() + { + using (SelfCleaningTempFile queryTempFile = new SelfCleaningTempFile()) + { + // setup + var connectionResult = await LiveConnectionHelper.InitLiveConnectionInfoAsync("master", queryTempFile.FilePath); + var service = new AgentService(); + var job = await AgentTestUtils.SetupJob(connectionResult); + var schedule = AgentTestUtils.GetTestScheduleInfo(); + await AgentTestUtils.DeleteAgentSchedule(service, connectionResult, schedule); + await AgentTestUtils.CreateAgentSchedule(service, connectionResult, schedule); + + // test + await AgentTestUtils.DeleteAgentSchedule(service, connectionResult, schedule); + + // cleanup + await AgentTestUtils.CleanupJob(connectionResult, job); + } + } + } +} diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentTestUtils.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentTestUtils.cs index a1c796f3..0a6835eb 100644 --- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentTestUtils.cs +++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Agent/AgentTestUtils.cs @@ -18,6 +18,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent { public static class AgentTestUtils { + public const string TestJobName = "Test Job"; + internal static AgentJobStepInfo GetTestJobStepInfo( TestConnectionResult connectionResult, AgentJobInfo job, @@ -40,7 +42,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent { return new AgentJobInfo() { - Name = "Test Job", + Name = TestJobName, Owner = "sa", Description = "Test job description", CurrentExecutionStatus = 1, @@ -79,7 +81,17 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent Description = "Test proxy description", IsEnabled = true }; - } + } + + internal static AgentScheduleInfo GetTestScheduleInfo() + { + return new AgentScheduleInfo() + { + Name = "Test Schedule", + JobName = TestJobName, + IsEnabled = true + }; + } internal static async Task CreateAgentJob( AgentService service, @@ -211,7 +223,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent Operator = operatorInfo }, context.Object); context.VerifyAll(); - } + } internal static async Task CreateAgentProxy( AgentService service, @@ -241,7 +253,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent Proxy = proxy }, context.Object); context.VerifyAll(); - } + } internal static async Task DeleteAgentProxy( AgentService service, @@ -255,7 +267,51 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent Proxy = proxy }, context.Object); context.VerifyAll(); - } + } + + internal static async Task CreateAgentSchedule( + AgentService service, + TestConnectionResult connectionResult, + AgentScheduleInfo schedule) + { + var context = new Mock>(); + await service.HandleCreateAgentScheduleRequest(new CreateAgentScheduleParams + { + OwnerUri = connectionResult.ConnectionInfo.OwnerUri, + Schedule = schedule + }, context.Object); + context.VerifyAll(); + } + + internal static async Task UpdateAgentSchedule( + AgentService service, + TestConnectionResult connectionResult, + string originalScheduleName, + AgentScheduleInfo schedule) + { + var context = new Mock>(); + await service.HandleUpdateAgentScheduleRequest(new UpdateAgentScheduleParams() + { + OwnerUri = connectionResult.ConnectionInfo.OwnerUri, + OriginalScheduleName = originalScheduleName, + Schedule = schedule + }, context.Object); + context.VerifyAll(); + } + + internal static async Task DeleteAgentSchedule( + AgentService service, + TestConnectionResult connectionResult, + AgentScheduleInfo schedule) + { + var context = new Mock>(); + await service.HandleDeleteAgentScheduleRequest(new DeleteAgentScheduleParams() + { + OwnerUri = connectionResult.ConnectionInfo.OwnerUri, + Schedule = schedule + }, context.Object); + context.VerifyAll(); + } internal static async Task GetAgentAlerts(string connectionUri) { @@ -274,5 +330,22 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.Agent await service.HandleAgentAlertsRequest(requestParams, requestContext.Object); return agentAlerts; } + + public static async Task SetupJob(TestConnectionResult connectionResult) + { + var service = new AgentService(); + var job = GetTestJobInfo(); + await DeleteAgentJob(service, connectionResult, job); + await CreateAgentJob(service, connectionResult, job); + return job; + } + + public static async Task CleanupJob( + TestConnectionResult connectionResult, + AgentJobInfo job) + { + var service = new AgentService(); + await DeleteAgentJob(service, connectionResult, job); + } } }