Adding some basic cron validation to publishing, currently the error is not explained if it is hit - still need some frontend for this, but this now means that an error is provided to users when attempting to publish, and we can re-use this validation in the automation UI. Need to have both backend and frontend validation as invalid CRONs will already exist, backend makes sure these are error'd on.
This commit is contained in:
parent
7aa1176a36
commit
8bf79c5f10
|
@ -101,7 +101,13 @@
|
|||
} catch (error) {
|
||||
console.error(error)
|
||||
analytics.captureException(error)
|
||||
notifications.error("Error publishing app")
|
||||
const baseMsg = "Error publishing app"
|
||||
const message = error.message
|
||||
if (message) {
|
||||
notifications.error(`${baseMsg} - ${message}`)
|
||||
} else {
|
||||
notifications.error(baseMsg)
|
||||
}
|
||||
}
|
||||
publishing = false
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
} from "@budibase/types"
|
||||
import sdk from "../sdk"
|
||||
import { automationsEnabled } from "../features"
|
||||
import { helpers } from "@budibase/shared-core"
|
||||
import tracer from "dd-trace"
|
||||
|
||||
const REBOOT_CRON = "@reboot"
|
||||
|
@ -198,6 +199,13 @@ export async function enableCronTrigger(appId: any, automation: Automation) {
|
|||
!isRebootTrigger(automation) &&
|
||||
trigger?.inputs.cron
|
||||
) {
|
||||
const cronExp = trigger.inputs.cron
|
||||
const validation = helpers.cron.validate(cronExp)
|
||||
if (!validation.valid) {
|
||||
throw new Error(
|
||||
`Invalid automation CRON "${cronExp}" - ${validation.err.join(", ")}`
|
||||
)
|
||||
}
|
||||
// make a job id rather than letting Bull decide, makes it easier to handle on way out
|
||||
const jobId = `${appId}_cron_${newid()}`
|
||||
const job: any = await automationQueue.add(
|
||||
|
@ -205,7 +213,7 @@ export async function enableCronTrigger(appId: any, automation: Automation) {
|
|||
automation,
|
||||
event: { appId, timestamp: Date.now() },
|
||||
},
|
||||
{ repeat: { cron: trigger.inputs.cron }, jobId }
|
||||
{ repeat: { cron: cronExp }, jobId }
|
||||
)
|
||||
// Assign cron job ID from bull so we can remove it later if the cron trigger is removed
|
||||
trigger.cronJobId = job.id
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
"check:types": "tsc -p tsconfig.json --noEmit --paths null"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/types": "0.0.0"
|
||||
"@budibase/types": "0.0.0",
|
||||
"cron-validate": "^1.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"rimraf": "3.0.2",
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
import cronValidate from "cron-validate"
|
||||
|
||||
const INPUT_CRON_START = "(Input cron: "
|
||||
const ERROR_SWAPS = {
|
||||
"smaller than lower limit": "less than",
|
||||
"bigger than upper limit": "greater than",
|
||||
daysOfMonth: "'days of the month'",
|
||||
daysOfWeek: "'days of the week'",
|
||||
years: "'years'",
|
||||
months: "'months'",
|
||||
hours: "'hours'",
|
||||
minutes: "'minutes'",
|
||||
seconds: "'seconds'",
|
||||
}
|
||||
|
||||
function improveErrors(errors: string[]): string[] {
|
||||
const finalErrors: string[] = []
|
||||
|
||||
for (let error of errors) {
|
||||
if (error.includes(INPUT_CRON_START)) {
|
||||
error = error.split(INPUT_CRON_START)[0].trim()
|
||||
}
|
||||
for (let [key, value] of Object.entries(ERROR_SWAPS)) {
|
||||
if (error.includes(key)) {
|
||||
error = error.replace(new RegExp(key, "g"), value)
|
||||
}
|
||||
}
|
||||
finalErrors.push(error)
|
||||
}
|
||||
return finalErrors
|
||||
}
|
||||
|
||||
export function validate(
|
||||
cronExpression: string
|
||||
): { valid: false; err: string[] } | { valid: true } {
|
||||
const result = cronValidate(cronExpression, {
|
||||
preset: "npm-node-cron",
|
||||
override: {
|
||||
useSeconds: false,
|
||||
},
|
||||
})
|
||||
if (!result.isValid()) {
|
||||
return { valid: false, err: improveErrors(result.getError()) }
|
||||
} else {
|
||||
return { valid: true }
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
export * from "./helpers"
|
||||
export * from "./integrations"
|
||||
export * as cron from "./cron"
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { expect, describe, it } from "vitest"
|
||||
import { cron } from "../helpers"
|
||||
|
||||
describe("check valid and invalid crons", () => {
|
||||
it("invalid - 0 0 0 11 *", () => {
|
||||
expect(cron.validate("0 0 0 11 *")).toStrictEqual({
|
||||
valid: false,
|
||||
err: [expect.stringContaining("less than '1'")],
|
||||
})
|
||||
})
|
||||
|
||||
it("invalid - 5 4 32 1 1", () => {
|
||||
expect(cron.validate("5 4 32 1 1")).toStrictEqual({
|
||||
valid: false,
|
||||
err: [expect.stringContaining("greater than '31'")],
|
||||
})
|
||||
})
|
||||
|
||||
it("valid - * * * * *", () => {
|
||||
expect(cron.validate("* * * * *")).toStrictEqual({ valid: true })
|
||||
})
|
||||
})
|
36
yarn.lock
36
yarn.lock
|
@ -1980,6 +1980,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.10.5":
|
||||
version "7.23.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
|
||||
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.21.0", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.23.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650"
|
||||
|
@ -5431,6 +5438,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.200.tgz#435b6035c7eba9cdf1e039af8212c9e9281e7149"
|
||||
integrity sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q==
|
||||
|
||||
"@types/lodash@^4.14.165":
|
||||
version "4.14.202"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
|
||||
integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
|
||||
|
||||
"@types/long@^4.0.0":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
|
||||
|
@ -8541,6 +8553,13 @@ cron-parser@^4.2.1:
|
|||
dependencies:
|
||||
luxon "^3.2.1"
|
||||
|
||||
cron-validate@^1.4.5:
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/cron-validate/-/cron-validate-1.4.5.tgz#eceb221f7558e6302e5f84c7b3a454fdf4d064c3"
|
||||
integrity sha512-nKlOJEnYKudMn/aNyNH8xxWczlfpaazfWV32Pcx/2St51r2bxWbGhZD7uwzMcRhunA/ZNL+Htm/i0792Z59UMQ==
|
||||
dependencies:
|
||||
yup "0.32.9"
|
||||
|
||||
cross-spawn@^6.0.0:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
|
@ -14440,7 +14459,7 @@ lock@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/lock/-/lock-1.1.0.tgz#53157499d1653b136ca66451071fca615703fa55"
|
||||
integrity sha512-NZQIJJL5Rb9lMJ0Yl1JoVr9GSdo4HTPsUEWsSFzB8dE8DSoiLCVavWZPi7Rnlv/o73u6I24S/XYc/NmG4l8EKA==
|
||||
|
||||
lodash-es@^4.17.21:
|
||||
lodash-es@^4.17.15, lodash-es@^4.17.21:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
|
||||
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
|
||||
|
@ -14590,7 +14609,7 @@ lodash.xor@^4.5.0:
|
|||
resolved "https://registry.yarnpkg.com/lodash.xor/-/lodash.xor-4.5.0.tgz#4d48ed7e98095b0632582ba714d3ff8ae8fb1db6"
|
||||
integrity sha512-sVN2zimthq7aZ5sPGXnSz32rZPuqcparVW50chJQe+mzTYV+IsxSsl/2gnkWWE2Of7K3myBQBqtLKOUEHJKRsQ==
|
||||
|
||||
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0:
|
||||
lodash@4.17.21, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.7.0:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
@ -22020,6 +22039,19 @@ yocto-queue@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
||||
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||
|
||||
yup@0.32.9:
|
||||
version "0.32.9"
|
||||
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.9.tgz#9367bec6b1b0e39211ecbca598702e106019d872"
|
||||
integrity sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.10.5"
|
||||
"@types/lodash" "^4.14.165"
|
||||
lodash "^4.17.20"
|
||||
lodash-es "^4.17.15"
|
||||
nanoclone "^0.2.1"
|
||||
property-expr "^2.0.4"
|
||||
toposort "^2.0.2"
|
||||
|
||||
yup@^0.32.11:
|
||||
version "0.32.11"
|
||||
resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"
|
||||
|
|
Loading…
Reference in New Issue