@adaptabletools/adaptable
Version:
Powerful AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
226 lines (225 loc) • 7.14 kB
JavaScript
import StringExtensions from '../../Extensions/StringExtensions';
import { getNextCronOccurrence, isCronExpressionValid as isCronExpressionValidInternal, } from './CronExpression';
const WEEKDAY_TO_CRON = {
Sunday: 0,
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
Saturday: 6,
};
const CRON_WEEKDAY_NAMES = [
'Sunday',
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
];
export function buildCronExpression(preset, hour, minute, options) {
const m = clampMinute(minute);
const h = clampHour(hour);
switch (preset) {
case 'daily':
return `${m} ${h} * * *`;
case 'weekdays':
return `${m} ${h} * * 1-5`;
case 'selectedDays': {
const days = options?.days?.length ? options.days : ['Monday'];
const dow = days.map((d) => WEEKDAY_TO_CRON[d]).sort((a, b) => a - b).join(',');
return `${m} ${h} * * ${dow}`;
}
case 'monthly': {
const dom = options?.dayOfMonth ?? 1;
return `${m} ${h} ${dom} * *`;
}
case 'custom':
return options?.customExpression?.trim() || `${m} ${h} * * *`;
default:
return `${m} ${h} * * *`;
}
}
export function buildRunAtIso(date, hour, minute) {
const d = new Date(date);
d.setHours(clampHour(hour), clampMinute(minute), 0, 0);
return d.toISOString();
}
export function parseTimeFromSchedule(schedule) {
if (schedule.IsOneOff && schedule.RunAt) {
const d = new Date(schedule.RunAt);
return { hour: d.getHours(), minute: d.getMinutes() };
}
const parts = schedule.CronExpression?.trim().split(/\s+/) ?? [];
if (parts.length >= 2) {
const minute = parseInt(parts[0], 10);
const hour = parseInt(parts[1], 10);
if (!Number.isNaN(minute) && !Number.isNaN(hour)) {
return { hour, minute };
}
}
return { hour: 9, minute: 0 };
}
export function inferPresetFromCron(cronExpression) {
const cron = cronExpression?.trim();
if (!cron) {
return { preset: 'weekdays', days: [], dayOfMonth: 1 };
}
const parts = cron.split(/\s+/);
if (parts.length < 5) {
return { preset: 'custom', days: [], dayOfMonth: 1 };
}
const [, , dom, month, dow] = parts;
if (dom !== '*' || month !== '*') {
if (dom !== '*' && month === '*') {
const dayOfMonth = parseInt(dom, 10);
return {
preset: 'monthly',
days: [],
dayOfMonth: Number.isNaN(dayOfMonth) ? 1 : dayOfMonth,
};
}
return { preset: 'custom', days: [], dayOfMonth: 1 };
}
if (dow === '*') {
return { preset: 'daily', days: [], dayOfMonth: 1 };
}
if (dow === '1-5') {
return { preset: 'weekdays', days: [], dayOfMonth: 1 };
}
const dayNumbers = dow.split(',').map((n) => parseInt(n.trim(), 10));
if (dayNumbers.every((n) => !Number.isNaN(n) && n >= 0 && n <= 6)) {
const days = dayNumbers.map((n) => CRON_WEEKDAY_NAMES[n]).filter(Boolean);
return { preset: 'selectedDays', days, dayOfMonth: 1 };
}
return { preset: 'custom', days: [], dayOfMonth: 1 };
}
export function parseOneOffDateFromRunAt(runAt) {
if (!runAt) {
return '';
}
const d = new Date(runAt);
if (Number.isNaN(d.getTime())) {
return '';
}
const y = d.getFullYear();
const mo = `${d.getMonth() + 1}`.padStart(2, '0');
const day = `${d.getDate()}`.padStart(2, '0');
return `${y}-${mo}-${day}`;
}
export function getNextRunDateFromSchedule(schedule) {
if (!schedule) {
return null;
}
try {
if (schedule.IsOneOff) {
if (!schedule.RunAt) {
return null;
}
const date = new Date(schedule.RunAt);
return date > new Date() ? date : null;
}
if (!schedule.CronExpression?.trim()) {
return null;
}
const now = new Date();
const next = getNextCronOccurrence(schedule.CronExpression.trim(), now);
return next && next > now ? next : null;
}
catch {
return null;
}
}
export function isScheduleValid(schedule) {
if (!schedule) {
return 'Schedule is not specified';
}
if (schedule.IsOneOff) {
if (!schedule.RunAt) {
return 'A date and time is required for a one-off schedule';
}
const date = new Date(schedule.RunAt);
if (Number.isNaN(date.getTime())) {
return 'One-off date is not valid';
}
return true;
}
if (!schedule.CronExpression?.trim()) {
return 'A recurring schedule is required';
}
const { preset, days } = inferPresetFromCron(schedule.CronExpression);
if (preset === 'selectedDays' && days.length === 0) {
return 'Select at least one day';
}
if (!isCronExpressionValidInternal(schedule.CronExpression.trim())) {
return 'Cron expression is not valid';
}
return true;
}
export function getScheduleDescription(schedule) {
if (!schedule) {
return '[No Schedule]';
}
if (schedule.IsOneOff) {
if (!schedule.RunAt) {
return 'One-off (not set)';
}
const d = new Date(schedule.RunAt);
return `Once on ${d.toLocaleDateString()} @ ${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
}
const { preset, days, dayOfMonth } = inferPresetFromCron(schedule.CronExpression);
const { hour, minute } = parseTimeFromSchedule(schedule);
const time = `@ ${pad2(hour)}:${pad2(minute)}`;
switch (preset) {
case 'daily':
return `Every Day ${time}`;
case 'weekdays':
return `Weekdays ${time}`;
case 'selectedDays': {
const names = days.map((d) => getShortDay(d)).join(', ');
return `${names || 'Selected Days'} ${time}`;
}
case 'monthly':
return `Monthly on day ${dayOfMonth} ${time}`;
case 'custom':
return schedule.CronExpression?.trim() || 'Custom Cron';
default:
return `${schedule.CronExpression ?? 'Custom'} ${time}`;
}
}
function getShortDay(day) {
switch (day) {
case 'Sunday':
return 'Sun';
case 'Monday':
return 'Mon';
case 'Tuesday':
return 'Tues';
case 'Wednesday':
return 'Weds';
case 'Thursday':
return 'Thurs';
case 'Friday':
return 'Fri';
case 'Saturday':
return 'Sat';
default:
return day;
}
}
function pad2(n) {
return `${n}`.padStart(2, '0');
}
function clampHour(h) {
return Math.min(23, Math.max(0, h));
}
function clampMinute(m) {
return Math.min(59, Math.max(0, m));
}
export function isValidCronExpression(expression) {
if (StringExtensions.IsNullOrEmpty(expression)) {
return false;
}
return isCronExpressionValidInternal(expression.trim());
}