Skip to content

Commit

Permalink
[N8N-4339] Unify all Cron-specific code. Improve typing, and add tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy committed Aug 16, 2022
1 parent e3f0ee6 commit bf75c88
Show file tree
Hide file tree
Showing 9 changed files with 346 additions and 756 deletions.
9 changes: 0 additions & 9 deletions packages/cli/test/integration/shared/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,6 @@ export type PostgresSchemaSection = {
[K in 'host' | 'port' | 'schema' | 'user' | 'password']: { env: string };
};

export interface TriggerTime {
mode: string;
hour: number;
minute: number;
dayOfMonth: number;
weekeday: number;
[key: string]: string | number;
}

export type InstalledPackagePayload = {
packageName: string;
installedVersion: string;
Expand Down
252 changes: 5 additions & 247 deletions packages/cli/test/integration/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { URL } from 'url';
import bodyParser from 'body-parser';
import { set } from 'lodash';
import { CronJob } from 'cron';
import { BinaryDataManager, UserSettings } from 'n8n-core';
import { BinaryDataManager, TriggerTime, triggerToCronExpression, UserSettings } from 'n8n-core';
import {
ICredentialType,
IDataObject,
Expand All @@ -20,6 +20,7 @@ import {
ITriggerFunctions,
ITriggerResponse,
LoggerProxy,
NodeHelpers,
} from 'n8n-workflow';

import config from '../../../config';
Expand Down Expand Up @@ -56,7 +57,6 @@ import type {
InstalledNodePayload,
InstalledPackagePayload,
PostgresSchemaSection,
TriggerTime,
} from './types';
import type { N8nApp } from '../../../src/UserManagement/Interfaces';
import { workflowsController } from '../../../src/api/workflows.api';
Expand Down Expand Up @@ -281,192 +281,7 @@ export async function initNodeTypes() {
default: {},
description: 'Triggers for the workflow',
placeholder: 'Add Cron Time',
options: [
{
name: 'item',
displayName: 'Item',
values: [
{
displayName: 'Mode',
name: 'mode',
type: 'options',
options: [
{
name: 'Every Minute',
value: 'everyMinute',
},
{
name: 'Every Hour',
value: 'everyHour',
},
{
name: 'Every Day',
value: 'everyDay',
},
{
name: 'Every Week',
value: 'everyWeek',
},
{
name: 'Every Month',
value: 'everyMonth',
},
{
name: 'Every X',
value: 'everyX',
},
{
name: 'Custom',
value: 'custom',
},
],
default: 'everyDay',
description: 'How often to trigger.',
},
{
displayName: 'Hour',
name: 'hour',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 23,
},
displayOptions: {
hide: {
mode: ['custom', 'everyHour', 'everyMinute', 'everyX'],
},
},
default: 14,
description: 'The hour of the day to trigger (24h format).',
},
{
displayName: 'Minute',
name: 'minute',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 59,
},
displayOptions: {
hide: {
mode: ['custom', 'everyMinute', 'everyX'],
},
},
default: 0,
description: 'The minute of the day to trigger.',
},
{
displayName: 'Day of Month',
name: 'dayOfMonth',
type: 'number',
displayOptions: {
show: {
mode: ['everyMonth'],
},
},
typeOptions: {
minValue: 1,
maxValue: 31,
},
default: 1,
description: 'The day of the month to trigger.',
},
{
displayName: 'Weekday',
name: 'weekday',
type: 'options',
displayOptions: {
show: {
mode: ['everyWeek'],
},
},
options: [
{
name: 'Monday',
value: '1',
},
{
name: 'Tuesday',
value: '2',
},
{
name: 'Wednesday',
value: '3',
},
{
name: 'Thursday',
value: '4',
},
{
name: 'Friday',
value: '5',
},
{
name: 'Saturday',
value: '6',
},
{
name: 'Sunday',
value: '0',
},
],
default: '1',
description: 'The weekday to trigger.',
},
{
displayName: 'Cron Expression',
name: 'cronExpression',
type: 'string',
displayOptions: {
show: {
mode: ['custom'],
},
},
default: '* * * * * *',
description:
'Use custom cron expression. Values and ranges as follows:<ul><li>Seconds: 0-59</li><li>Minutes: 0 - 59</li><li>Hours: 0 - 23</li><li>Day of Month: 1 - 31</li><li>Months: 0 - 11 (Jan - Dec)</li><li>Day of Week: 0 - 6 (Sun - Sat)</li></ul>.',
},
{
displayName: 'Value',
name: 'value',
type: 'number',
typeOptions: {
minValue: 0,
maxValue: 1000,
},
displayOptions: {
show: {
mode: ['everyX'],
},
},
default: 2,
description: 'All how many X minutes/hours it should trigger.',
},
{
displayName: 'Unit',
name: 'unit',
type: 'options',
displayOptions: {
show: {
mode: ['everyX'],
},
},
options: [
{
name: 'Minutes',
value: 'minutes',
},
{
name: 'Hours',
value: 'hours',
},
],
default: 'hours',
description: 'If it should trigger all X minutes or hours.',
},
],
},
],
options: NodeHelpers.cronNodeOptions,
},
],
},
Expand All @@ -475,61 +290,8 @@ export async function initNodeTypes() {
item: TriggerTime[];
};

// Define the order the cron-time-parameter appear
const parameterOrder = [
'second', // 0 - 59
'minute', // 0 - 59
'hour', // 0 - 23
'dayOfMonth', // 1 - 31
'month', // 0 - 11(Jan - Dec)
'weekday', // 0 - 6(Sun - Sat)
];

// Get all the trigger times
const cronTimes: string[] = [];
let cronTime: string[];
let parameterName: string;
if (triggerTimes.item !== undefined) {
for (const item of triggerTimes.item) {
cronTime = [];
if (item.mode === 'custom') {
cronTimes.push(item.cronExpression as string);
continue;
}
if (item.mode === 'everyMinute') {
cronTimes.push(`${Math.floor(Math.random() * 60).toString()} * * * * *`);
continue;
}
if (item.mode === 'everyX') {
if (item.unit === 'minutes') {
cronTimes.push(
`${Math.floor(Math.random() * 60).toString()} */${item.value} * * * *`,
);
} else if (item.unit === 'hours') {
cronTimes.push(
`${Math.floor(Math.random() * 60).toString()} 0 */${item.value} * * *`,
);
}
continue;
}

for (parameterName of parameterOrder) {
if (item[parameterName] !== undefined) {
// Value is set so use it
cronTime.push(item[parameterName] as string);
} else if (parameterName === 'second') {
// For seconds we use by default a random one to make sure to
// balance the load a little bit over time
cronTime.push(Math.floor(Math.random() * 60).toString());
} else {
// For all others set "any"
cronTime.push('*');
}
}

cronTimes.push(cronTime.join(' '));
}
}
const cronTimes = (triggerTimes.item || []).map(triggerToCronExpression);

// The trigger function to execute when the cron-time got reached
// or when manually triggered
Expand All @@ -540,10 +302,7 @@ export async function initNodeTypes() {
const timezone = this.getTimezone();

// Start the cron-jobs
const cronJobs: CronJob[] = [];
for (const cronTime of cronTimes) {
cronJobs.push(new CronJob(cronTime, executeTrigger, undefined, true, timezone));
}
const cronJobs = cronTimes.map(cronTime => new CronJob(cronTime, executeTrigger, undefined, true, timezone));

// Stop the cron-jobs
async function closeFunction() {
Expand Down Expand Up @@ -929,4 +688,3 @@ export const emptyPackage = () => {

return Promise.resolve(installedPackage);
};

56 changes: 4 additions & 52 deletions packages/core/src/ActiveWorkflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import {
WorkflowExecuteMode,
} from 'n8n-workflow';

import { TriggerTime, triggerToCronExpression } from './Cron';
// eslint-disable-next-line import/no-cycle
import { ITriggerTime, IWorkflowData } from '.';
import { IWorkflowData } from '.';

export class ActiveWorkflows {
private workflowData: {
Expand Down Expand Up @@ -156,60 +157,11 @@ export class ActiveWorkflows {
const pollFunctions = getPollFunctions(workflow, node, additionalData, mode, activation);

const pollTimes = pollFunctions.getNodeParameter('pollTimes') as unknown as {
item: ITriggerTime[];
item: TriggerTime[];
};

// Define the order the cron-time-parameter appear
const parameterOrder = [
'second', // 0 - 59
'minute', // 0 - 59
'hour', // 0 - 23
'dayOfMonth', // 1 - 31
'month', // 0 - 11(Jan - Dec)
'weekday', // 0 - 6(Sun - Sat)
];

// Get all the trigger times
const cronTimes: string[] = [];
let cronTime: string[];
let parameterName: string;
if (pollTimes.item !== undefined) {
for (const item of pollTimes.item) {
cronTime = [];
if (item.mode === 'custom') {
cronTimes.push((item.cronExpression as string).trim());
continue;
}
if (item.mode === 'everyMinute') {
cronTimes.push(`${Math.floor(Math.random() * 60).toString()} * * * * *`);
continue;
}
if (item.mode === 'everyX') {
if (item.unit === 'minutes') {
cronTimes.push(`${Math.floor(Math.random() * 60).toString()} */${item.value} * * * *`);
} else if (item.unit === 'hours') {
cronTimes.push(`${Math.floor(Math.random() * 60).toString()} 0 */${item.value} * * *`);
}
continue;
}

for (parameterName of parameterOrder) {
if (item[parameterName] !== undefined) {
// Value is set so use it
cronTime.push(item[parameterName] as string);
} else if (parameterName === 'second') {
// For seconds we use by default a random one to make sure to
// balance the load a little bit over time
cronTime.push(Math.floor(Math.random() * 60).toString());
} else {
// For all others set "any"
cronTime.push('*');
}
}

cronTimes.push(cronTime.join(' '));
}
}
const cronTimes = (pollTimes.item || []).map(triggerToCronExpression);

// The trigger function to execute when the cron-time got reached
const executeTrigger = async () => {
Expand Down
Loading

0 comments on commit bf75c88

Please sign in to comment.