-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🪟🐛 client side validation of cron expressions #17887
Conversation
.string() | ||
.required("form.empty.error") | ||
.test("validCron", "form.cronExpression.error", (expression) => | ||
!expression ? false : validateCronExpression(expression) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this check (!expression
) be part of validateCronExpression to make this simpler?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's probably a good call. Yup.string.required()
unfortunately doesn't narrow the type (it's a bug), so it's still string | undefined
when it reaches the .test()
method.
@@ -29,6 +29,7 @@ import { ValuesProps } from "hooks/services/useConnectionHook"; | |||
import { useCurrentWorkspace } from "services/workspaces/WorkspacesService"; | |||
|
|||
import calculateInitialCatalog from "./calculateInitialCatalog"; | |||
import { validateCronExpression } from "./ScheduleField"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This utility is pretty generic, maybe it's best on src/utils/cron
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can move it there. I wonder if over time src/utils
might get bloated if we move all pure/generic functions there, but we can cross that bridge when we get there.
@@ -204,6 +203,11 @@ const ScheduleField: React.FC = () => { | |||
onChange={(item: DropDownOptionDataItem) => onCronChange(item, field, form, "cronTimeZone")} | |||
/> | |||
</div> | |||
{(form.errors?.scheduleData as ConnectionScheduleData)?.cron?.cronExpression && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This value could be set as a var before the return so that it's easier to use:
const cronExpression = (form.errors?.scheduleData as ConnectionScheduleData)?.cron?.cronExpression
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's kind of a nasty expression, I'll extract it to make the JSX cleaner.
airbyte-webapp/src/views/Connection/ConnectionForm/ScheduleField/validateCronExpression.ts
Outdated
Show resolved
Hide resolved
airbyte-webapp/src/views/Connection/ConnectionForm/ScheduleField/ScheduleField.tsx
Show resolved
Hide resolved
const cronValidationError = useMemo(() => { | ||
return (errors?.scheduleData as ConnectionScheduleData)?.cron?.cronExpression; | ||
}, [errors]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to get rid of the typecasting here (which imho looks a bit weird, given that TS thinks scheduleData
is string
), we could directly pull the error of that field (and get rid of the whole useMemo
here):
const [, { error: cronValidationError }] = useField("scheduleData.cron.cronExpression");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, that is cleaner, I'll use that. It still is not super type safe (useField()
will accept any string without complaining) but it's not as ugly 😅
I think to get proper typing we would need to change the yup schema away from overloading scheduleData
with the ambiguous .mixed().when(...)
, but I didn't want to blow up the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have tested locally. Works for required and invalid cron expressions. Code LGTM, I left one suggestion to get rid of a typecast.
What
Closes https://github.com/airbytehq/airbyte-cloud/issues/2772
Adds frontend validation of cron expressions
How
I tested various cron parsing libraries, but unfortunately did not find any that work perfectly with the quartz syntax.
Instead of parsing the cron expression, I'm relying on a pretty loose regex for the frontend validation. This will give the user quick feedback if, for example, the cron is too short or has invalid characters. I have included tests for all examples on the quartz docs as well as every single cron expression one of our cloud customers is currently using.
It is possible that an invalid cron expression makes it through the frontend validation. In that case, the backend will return an error which we display to the user.