Skip to content

Commit fd93a26

Browse files
authored
feat: add cron support (#3269)
1 parent 9812839 commit fd93a26

File tree

2 files changed

+163
-15
lines changed

2 files changed

+163
-15
lines changed

src/flows/pipes/Schedule/view.tsx

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,116 @@ import {isFlagEnabled} from 'src/shared/utils/featureFlag'
2626

2727
import './style.scss'
2828

29+
const validCron = (text: string): boolean => {
30+
const isNumber = (str: string, min: number, max: number) => {
31+
if (str.search(/[^\d-,\/*]/) !== -1) {
32+
return false
33+
}
34+
35+
return str.split(',').every(item => {
36+
if (item.trim().endsWith('/')) {
37+
return false
38+
}
39+
const splits = item.split('/')
40+
41+
if (splits.length > 2) {
42+
return false
43+
}
44+
45+
const [left, right] = splits
46+
const sides = left.split('-')
47+
48+
if (
49+
right !== undefined &&
50+
(!/^\d+$/.test(right) || parseInt(right) <= 0)
51+
) {
52+
return false
53+
}
54+
55+
if (sides.length > 2) {
56+
return false
57+
}
58+
59+
if (sides.length === 1) {
60+
return (
61+
sides[0] === '*' ||
62+
(/^\d+$/.test(sides[0]) &&
63+
min <= parseInt(sides[0]) &&
64+
parseInt(sides[0]) <= max)
65+
)
66+
}
67+
68+
if (!/^\d+$/.test(sides[0]) || !/^\d+$/.test(sides[1])) {
69+
return false
70+
}
71+
72+
const small = parseInt(sides[0])
73+
const big = parseInt(sides[1])
74+
75+
return (
76+
!isNaN(small) &&
77+
!isNaN(big) &&
78+
small <= big &&
79+
min <= small &&
80+
small <= max &&
81+
min <= big &&
82+
big <= max
83+
)
84+
})
85+
}
86+
87+
const mapMonth = (str: string): string =>
88+
str.toLowerCase().replace(
89+
/[a-z]{3}/g,
90+
_str =>
91+
({
92+
jan: '1',
93+
feb: '2',
94+
mar: '3',
95+
apr: '4',
96+
may: '5',
97+
jun: '6',
98+
jul: '7',
99+
aug: '8',
100+
sep: '9',
101+
oct: '10',
102+
nov: '11',
103+
dec: '12',
104+
}[_str] || _str)
105+
)
106+
const mapDay = (str: string): string =>
107+
str.toLowerCase().replace(
108+
/[a-z]{3}/g,
109+
_str =>
110+
({
111+
sun: '0',
112+
mon: '1',
113+
tue: '2',
114+
wed: '3',
115+
thu: '4',
116+
fri: '5',
117+
sat: '6',
118+
}[_str] || _str)
119+
)
120+
const split = text.trim().split(/\s+/)
121+
if (split.length < 5 || split.length > 6) {
122+
return false
123+
}
124+
125+
if (split.length === 5) {
126+
split.unshift('*')
127+
}
128+
129+
return [
130+
isNumber(split[0], 0, 59),
131+
isNumber(split[1], 0, 59),
132+
isNumber(split[2], 0, 23),
133+
isNumber(split[3], 1, 31),
134+
isNumber(mapMonth(split[4]), 1, 12),
135+
isNumber(mapDay(split[5]), 0, 7),
136+
].reduce((acc, curr) => acc && curr, true)
137+
}
138+
29139
const Schedule: FC<PipeProp> = ({Context}) => {
30140
const {id, data, update} = useContext(PipeContext)
31141
const {simplify, getPanelQueries} = useContext(FlowQueryContext)
@@ -37,7 +147,8 @@ const Schedule: FC<PipeProp> = ({Context}) => {
37147
intervalError = 'Required'
38148
} else if (
39149
data.interval !==
40-
data.interval.match(/(?:(\d+(y|mo|s|m|w|h){1}))/g)?.join('')
150+
data.interval.match(/(?:(\d+(y|mo|s|m|w|h){1}))/g)?.join('') &&
151+
!validCron(data.interval)
41152
) {
42153
intervalError = 'Invalid Time'
43154
}
@@ -98,7 +209,11 @@ const Schedule: FC<PipeProp> = ({Context}) => {
98209
}
99210

100211
if (data.interval && !intervalError) {
101-
params.every = data.interval
212+
if (validCron(data.interval)) {
213+
params.cron = `"${data.interval}"`
214+
} else {
215+
params.every = data.interval
216+
}
102217
}
103218

104219
if (data.offset && !offsetError) {
@@ -221,6 +336,23 @@ const Schedule: FC<PipeProp> = ({Context}) => {
221336
<FlexBox.Child grow={1} shrink={1} style={{alignSelf: 'start'}}>
222337
<Form.Element
223338
label="Every"
339+
helpText={
340+
((
341+
<>
342+
Supports{' '}
343+
<a
344+
href="https://docs.influxdata.com/flux/v0.x/data-types/basic/duration/#duration-syntax"
345+
target="_blank"
346+
>
347+
flux durations
348+
</a>{' '}
349+
and{' '}
350+
<a href="https://crontab.guru" target="_blank">
351+
cron intervals
352+
</a>
353+
</>
354+
) as unknown) as string
355+
}
224356
required={true}
225357
errorMessage={intervalError}
226358
>
@@ -241,6 +373,19 @@ const Schedule: FC<PipeProp> = ({Context}) => {
241373
<FlexBox.Child grow={1} shrink={1} style={{alignSelf: 'start'}}>
242374
<Form.Element
243375
label="Offset"
376+
helpText={
377+
((
378+
<>
379+
Supports{' '}
380+
<a
381+
href="https://docs.influxdata.com/flux/v0.x/data-types/basic/duration/#duration-syntax"
382+
target="_blank"
383+
>
384+
flux durations
385+
</a>
386+
</>
387+
) as unknown) as string
388+
}
244389
required={false}
245390
errorMessage={offsetError}
246391
>

src/flows/templates/types/task.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,15 @@ export default register =>
1515
refresh: AUTOREFRESH_DEFAULT,
1616
pipes: [
1717
{
18-
activeQuery: 0,
19-
queries: [
18+
buckets: [],
19+
tags: [
2020
{
21-
text: '',
22-
editMode: 'advanced',
23-
builderConfig: {
24-
buckets: [],
25-
tags: [],
26-
functions: [],
27-
},
21+
key: '_measurement',
22+
values: [],
23+
aggregateFunctionType: 'filter',
2824
},
2925
],
30-
type: 'rawFluxEditor',
26+
type: 'queryBuilder',
3127
title: 'Query to Run',
3228
visible: true,
3329
},
@@ -38,7 +34,7 @@ export default register =>
3834
},
3935
{
4036
type: 'schedule',
41-
title: 'Schedule',
37+
title: 'Schedule as a Task',
4238
visible: true,
4339
},
4440
],
@@ -104,10 +100,17 @@ export default register =>
104100
},
105101
{
106102
type: 'schedule',
107-
title: 'Schedule',
103+
title: 'Schedule as a Task',
108104
visible: true,
109-
interval: taskParams.every,
105+
interval:
106+
taskParams.every ||
107+
taskParams.cron.replace(/(^")|("$)/g, ''),
110108
offset: taskParams.offset,
109+
task: {
110+
id,
111+
name: resp.name,
112+
flux: resp.flux,
113+
},
111114
},
112115
],
113116
},

0 commit comments

Comments
 (0)