diff --git a/awx/ui/src/components/Schedule/shared/ScheduleForm.js b/awx/ui/src/components/Schedule/shared/ScheduleForm.js index c29a927868dd..a0d171123101 100644 --- a/awx/ui/src/components/Schedule/shared/ScheduleForm.js +++ b/awx/ui/src/components/Schedule/shared/ScheduleForm.js @@ -20,6 +20,7 @@ import ScheduleFormFields from './ScheduleFormFields'; import UnsupportedScheduleForm from './UnsupportedScheduleForm'; import parseRuleObj, { UnsupportedRRuleError } from './parseRuleObj'; import buildRuleObj from './buildRuleObj'; +import buildRuleSet from './buildRuleSet'; const NUM_DAYS_PER_FREQUENCY = { week: 7, @@ -411,6 +412,26 @@ function ScheduleForm({ } }); + if (values.exceptionFrequency.length > 0) { + let rangeToCheck = 1; + values.frequency.forEach((freq) => { + if (NUM_DAYS_PER_FREQUENCY[freq] > rangeToCheck) { + rangeToCheck = NUM_DAYS_PER_FREQUENCY[freq]; + } + }); + const ruleSet = buildRuleSet(values, true); + const startDate = DateTime.fromISO(values.startDate); + const endDate = startDate.plus({ days: rangeToCheck }); + const instances = ruleSet.between( + startDate.toJSDate(), + endDate.toJSDate(), + true + ); + if (instances.length === 0) { + errors.exceptionFrequency = t`This schedule has no occurrences with the selected exceptions.`; + } + } + return errors; }; diff --git a/awx/ui/src/components/Schedule/shared/buildRuleObj.js b/awx/ui/src/components/Schedule/shared/buildRuleObj.js index 3fcba27240b2..c631ed959d18 100644 --- a/awx/ui/src/components/Schedule/shared/buildRuleObj.js +++ b/awx/ui/src/components/Schedule/shared/buildRuleObj.js @@ -36,11 +36,19 @@ function pad(num) { return num < 10 ? `0${num}` : num; } -export default function buildRuleObj(values) { +export default function buildRuleObj(values, includeStart) { const ruleObj = { interval: values.interval, }; + if (includeStart) { + ruleObj.dtstart = buildDateTime( + values.startDate, + values.startTime, + values.timezone + ); + } + switch (values.frequency) { case 'none': ruleObj.count = 1; @@ -91,16 +99,11 @@ export default function buildRuleObj(values) { ruleObj.count = values.occurrences; break; case 'onDate': { - const [endHour, endMinute] = parseTime(values.endTime); - const localEndDate = DateTime.fromISO(`${values.endDate}T000000`, { - zone: values.timezone, - }); - const localEndTime = localEndDate.set({ - hour: endHour, - minute: endMinute, - second: 0, - }); - ruleObj.until = localEndTime.toJSDate(); + ruleObj.until = buildDateTime( + values.endDate, + values.endTime, + values.timezone + ); break; } default: @@ -110,3 +113,16 @@ export default function buildRuleObj(values) { return ruleObj; } + +function buildDateTime(dateString, timeString, timezone) { + const localDate = DateTime.fromISO(`${dateString}T000000`, { + zone: timezone, + }); + const [hour, minute] = parseTime(timeString); + const localTime = localDate.set({ + hour, + minute, + second: 0, + }); + return localTime.toJSDate(); +} diff --git a/awx/ui/src/components/Schedule/shared/buildRuleSet.js b/awx/ui/src/components/Schedule/shared/buildRuleSet.js index 0f8182bc01b6..0304b368874d 100644 --- a/awx/ui/src/components/Schedule/shared/buildRuleSet.js +++ b/awx/ui/src/components/Schedule/shared/buildRuleSet.js @@ -4,7 +4,7 @@ import buildRuleObj, { buildDtStartObj } from './buildRuleObj'; window.RRuleSet = RRuleSet; const frequencies = ['minute', 'hour', 'day', 'week', 'month', 'year']; -export default function buildRuleSet(values) { +export default function buildRuleSet(values, includeStart) { const set = new RRuleSet(); const startRule = buildDtStartObj({ @@ -15,13 +15,16 @@ export default function buildRuleSet(values) { set.rrule(startRule); if (values.frequency.length === 0) { - const rule = buildRuleObj({ - startDate: values.startDate, - startTime: values.startTime, - timezone: values.timezone, - frequency: 'none', - interval: 1, - }); + const rule = buildRuleObj( + { + startDate: values.startDate, + startTime: values.startTime, + timezone: values.timezone, + frequency: 'none', + interval: 1, + }, + includeStart + ); set.rrule(new RRule(rule)); } @@ -29,13 +32,16 @@ export default function buildRuleSet(values) { if (!values.frequency.includes(frequency)) { return; } - const rule = buildRuleObj({ - startDate: values.startDate, - startTime: values.startTime, - timezone: values.timezone, - frequency, - ...values.frequencyOptions[frequency], - }); + const rule = buildRuleObj( + { + startDate: values.startDate, + startTime: values.startTime, + timezone: values.timezone, + frequency, + ...values.frequencyOptions[frequency], + }, + includeStart + ); set.rrule(new RRule(rule)); }); @@ -43,13 +49,16 @@ export default function buildRuleSet(values) { if (!values.exceptionFrequency?.includes(frequency)) { return; } - const rule = buildRuleObj({ - startDate: values.startDate, - startTime: values.startTime, - timezone: values.timezone, - frequency, - ...values.exceptionOptions[frequency], - }); + const rule = buildRuleObj( + { + startDate: values.startDate, + startTime: values.startTime, + timezone: values.timezone, + frequency, + ...values.exceptionOptions[frequency], + }, + includeStart + ); set.exrule(new RRule(rule)); });