2019-06-23 03:35:23 -07:00
import { ITriggerFunctions } from 'n8n-core' ;
import {
INodeType ,
INodeTypeDescription ,
ITriggerResponse ,
} from 'n8n-workflow' ;
import { CronJob } from 'cron' ;
interface TriggerTime {
mode : string ;
hour : number ;
minute : number ;
dayOfMonth : number ;
weekeday : number ;
[ key : string ] : string | number ;
}
export class Cron implements INodeType {
description : INodeTypeDescription = {
displayName : 'Cron' ,
name : 'cron' ,
icon : 'fa:calendar' ,
2022-04-11 06:12:13 -07:00
group : [ 'trigger' , 'schedule' ] ,
2019-06-23 03:35:23 -07:00
version : 1 ,
description : 'Triggers the workflow at a specific time' ,
:sparkles: Improve Waiting Webhook call state in WF Canvas (#2430)
* N8N-2586 Improve Waiting Webhook call state in WF Canvas
* N8N-2586 Added watcher for showing Webhook's Node Tooltip on execution
* N8N-2586 Show helping tooltip for trigger node if wokrflow is running, it is a trigger node, if it is only one trigger node in WF
* N8N-2586 Rework/Move logic to computed property, Created getter for ActveTriggerNodesInWokrflow, Add style to trigger node's tooltip, remove comments
* N8N-2586 Added EventTriggerDescription prop in INodeTypeDescription Interface, Updated Logic for TriggerNode Tooltip based on the new prop
* N8N-2586 Add new use cases/watcher to show Trigger Nodes Tooltip / If has issues, if paused, if wokrlfow is running, Refactor Getter
* N8N-2586 Added z-index to tooltip, Added new Scenario for Tooltip if it is Draged&Droped on the WF
* N8N-2586 Refactor computed property for draged nodes
* N8N-2586 Fixed Conflicts
* N8N-2586 Fixed Tooltip
* N8N-2586 Dont show tooltip on core trigger nodes that execute automatically
* N8N-2586 Fixed Webhook tooltip when adding/deleting canvas during WF execution
* N8N-2586 Updated Logic, Simplify the code
* N8N-2586 Simplify Code
* N8N-2586 Added check for nodetype
* update dragging to use local state
* N8N-2586 Added eventTriggerDescription to Interval Node
* add comment, use new getter
* update to always check
Co-authored-by: Mutasem <mutdmour@gmail.com>
2021-11-25 14:33:41 -08:00
eventTriggerDescription : '' ,
2022-01-21 09:00:00 -08:00
activationMessage : 'Your cron trigger will now trigger executions on the schedule you have defined.' ,
2019-06-23 03:35:23 -07:00
defaults : {
name : 'Cron' ,
color : '#00FF00' ,
} ,
2022-04-22 09:29:51 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
2019-06-23 03:35:23 -07:00
inputs : [ ] ,
outputs : [ 'main' ] ,
properties : [
2022-06-20 12:39:24 -07:00
{
displayName : 'This workflow will run on the schedule you define here once you <a data-key="activate">activate</a> it.<br><br>For testing, you can also trigger it manually: by going back to the canvas and clicking ‘ execute workflow’ ' ,
name : 'notice' ,
type : 'notice' ,
default : '' ,
} ,
2019-06-23 03:35:23 -07:00
{
displayName : 'Trigger Times' ,
name : 'triggerTimes' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
multipleValueButtonText : 'Add Time' ,
} ,
default : { } ,
description : 'Triggers for the workflow' ,
placeholder : 'Add Cron Time' ,
options : [
{
name : 'item' ,
displayName : 'Item' ,
values : [
{
displayName : 'Mode' ,
name : 'mode' ,
type : 'options' ,
2022-06-03 10:23:49 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
2019-06-23 03:35:23 -07:00
options : [
2019-11-12 22:46:40 -08:00
{
name : 'Every Minute' ,
2019-12-31 15:41:47 -08:00
value : 'everyMinute' ,
2019-11-12 22:46:40 -08:00
} ,
2019-07-10 23:00:57 -07:00
{
name : 'Every Hour' ,
2019-12-31 15:41:47 -08:00
value : 'everyHour' ,
2019-07-10 23:00:57 -07:00
} ,
2019-06-23 03:35:23 -07:00
{
name : 'Every Day' ,
2019-12-31 15:41:47 -08:00
value : 'everyDay' ,
2019-06-23 03:35:23 -07:00
} ,
{
name : 'Every Week' ,
2019-12-31 15:41:47 -08:00
value : 'everyWeek' ,
2019-06-23 03:35:23 -07:00
} ,
{
name : 'Every Month' ,
2019-12-31 15:41:47 -08:00
value : 'everyMonth' ,
} ,
{
name : 'Every X' ,
value : 'everyX' ,
2019-06-23 03:35:23 -07:00
} ,
2019-07-10 23:00:57 -07:00
{
name : 'Custom' ,
2019-12-31 15:41:47 -08:00
value : 'custom' ,
2019-07-10 23:00:57 -07:00
} ,
2019-06-23 03:35:23 -07:00
] ,
default : 'everyDay' ,
2022-05-06 14:01:25 -07:00
description : 'How often to trigger' ,
2019-06-23 03:35:23 -07:00
} ,
{
displayName : 'Hour' ,
name : 'hour' ,
type : 'number' ,
typeOptions : {
minValue : 0 ,
maxValue : 23 ,
} ,
2019-07-10 23:00:57 -07:00
displayOptions : {
hide : {
mode : [
'custom' ,
'everyHour' ,
2019-12-31 15:41:47 -08:00
'everyMinute' ,
'everyX' ,
2019-07-10 23:00:57 -07:00
] ,
} ,
} ,
2019-06-23 03:35:23 -07:00
default : 14 ,
2022-05-06 14:01:25 -07:00
description : 'The hour of the day to trigger (24h format)' ,
2019-06-23 03:35:23 -07:00
} ,
{
displayName : 'Minute' ,
name : 'minute' ,
type : 'number' ,
typeOptions : {
minValue : 0 ,
maxValue : 59 ,
} ,
2019-07-10 23:00:57 -07:00
displayOptions : {
hide : {
mode : [
'custom' ,
2019-12-31 15:41:47 -08:00
'everyMinute' ,
'everyX' ,
2019-07-10 23:00:57 -07:00
] ,
} ,
} ,
2019-06-23 03:35:23 -07:00
default : 0 ,
2022-05-06 14:01:25 -07:00
description : 'The minute of the day to trigger' ,
2019-06-23 03:35:23 -07:00
} ,
{
displayName : 'Day of Month' ,
name : 'dayOfMonth' ,
type : 'number' ,
displayOptions : {
show : {
mode : [
'everyMonth' ,
] ,
} ,
} ,
typeOptions : {
minValue : 1 ,
maxValue : 31 ,
} ,
default : 1 ,
2022-05-06 14:01:25 -07:00
description : 'The day of the month to trigger' ,
2019-06-23 03:35:23 -07:00
} ,
{
displayName : 'Weekday' ,
name : 'weekday' ,
type : 'options' ,
displayOptions : {
show : {
mode : [
'everyWeek' ,
] ,
} ,
} ,
2022-06-03 10:23:49 -07:00
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
2019-06-23 03:35:23 -07:00
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' ,
2022-05-06 14:01:25 -07:00
description : 'The weekday to trigger' ,
2019-06-23 03:35:23 -07:00
} ,
2019-07-10 23:00:57 -07:00
{
displayName : 'Cron Expression' ,
name : 'cronExpression' ,
type : 'string' ,
displayOptions : {
show : {
mode : [
'custom' ,
] ,
} ,
} ,
default : '* * * * * *' ,
2022-04-22 09:29:51 -07:00
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>.' ,
2019-07-10 23:00:57 -07:00
} ,
2019-12-31 15:41:47 -08:00
{
displayName : 'Value' ,
name : 'value' ,
type : 'number' ,
typeOptions : {
minValue : 0 ,
maxValue : 1000 ,
} ,
displayOptions : {
show : {
mode : [
'everyX' ,
] ,
} ,
} ,
default : 2 ,
2022-05-06 14:01:25 -07:00
description : 'All how many X minutes/hours it should trigger' ,
2019-12-31 15:41:47 -08:00
} ,
{
displayName : 'Unit' ,
name : 'unit' ,
type : 'options' ,
displayOptions : {
show : {
mode : [
'everyX' ,
] ,
} ,
} ,
options : [
{
name : 'Minutes' ,
2020-10-22 06:46:03 -07:00
value : 'minutes' ,
2019-12-31 15:41:47 -08:00
} ,
{
name : 'Hours' ,
2020-10-22 06:46:03 -07:00
value : 'hours' ,
2019-12-31 15:41:47 -08:00
} ,
] ,
default : 'hours' ,
2022-05-06 14:01:25 -07:00
description : 'If it should trigger all X minutes or hours' ,
2019-12-31 15:41:47 -08:00
} ,
2020-10-22 06:46:03 -07:00
] ,
2019-06-23 03:35:23 -07:00
} ,
] ,
2020-10-22 06:46:03 -07:00
} ,
] ,
2019-06-23 03:35:23 -07:00
} ;
async trigger ( this : ITriggerFunctions ) : Promise < ITriggerResponse > {
const triggerTimes = this . getNodeParameter ( 'triggerTimes' ) as unknown as {
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 = [ ] ;
2019-07-10 23:00:57 -07:00
if ( item . mode === 'custom' ) {
cronTimes . push ( item . cronExpression as string ) ;
continue ;
}
2019-11-12 06:09:42 -08:00
if ( item . mode === 'everyMinute' ) {
2019-11-12 22:46:40 -08:00
cronTimes . push ( ` ${ Math . floor ( Math . random ( ) * 60 ) . toString ( ) } * * * * * ` ) ;
2019-11-12 06:09:42 -08:00
continue ;
}
2019-12-31 15:41:47 -08:00
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 ;
}
2019-07-10 23:00:57 -07:00
2019-06-23 03:35:23 -07:00
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 ( ' ' ) ) ;
}
}
// The trigger function to execute when the cron-time got reached
// or when manually triggered
const executeTrigger = ( ) = > {
this . emit ( [ this . helpers . returnJsonArray ( [ { } ] ) ] ) ;
} ;
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 ) ) ;
}
// Stop the cron-jobs
async function closeFunction() {
for ( const cronJob of cronJobs ) {
cronJob . stop ( ) ;
}
}
async function manualTriggerFunction() {
executeTrigger ( ) ;
}
return {
closeFunction ,
manualTriggerFunction ,
} ;
}
}