From a89c4f6237d5712199568211be352a5326cc1f74 Mon Sep 17 00:00:00 2001 From: Mike Cunneen Date: Wed, 9 Aug 2023 14:41:02 +0800 Subject: [PATCH] Display human representation of cron patterns using "cronstrue" library. --- lib/controllers/agendash.js | 32 ++++++++++++++++++++++++++++---- package.json | 1 + public/app/js/joblist.js | 2 ++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/controllers/agendash.js b/lib/controllers/agendash.js index ef770e8a..a7b77fd9 100644 --- a/lib/controllers/agendash.js +++ b/lib/controllers/agendash.js @@ -1,7 +1,22 @@ "use strict"; const semver = require("semver"); const { ObjectId } = require("mongodb"); // We rely on the Agenda's "mongodb", thus our package.json lists "*" as required version +const cronstrue = require("cronstrue"); // gets a human representation of the cron interval +/** + * Get a human representation of the cron interval + * @param {string} repeatInterval - a cron interval e.g. "0 0 * * *" + * @returns {string} human representation of the cron interval e.g. "Every day at 00:00" + */ +const getHumanRepeatInterval = (repeatInterval) => { + let result = repeatInterval; + // if the repeat interval doesn't have at least 5 spaces, it's not a cron expression, in which case just return the original value + const isRepeatIntervalCronExpr = !!(repeatInterval.split(" ").length > 4); + if (isRepeatIntervalCronExpr) { + result = cronstrue.toString(repeatInterval, { verbose: true, throwExceptionOnParseError: false }); + } + return result; +} module.exports = function (agenda, options) { options = options || {}; @@ -34,7 +49,7 @@ module.exports = function (agenda, options) { }); // Options = {query = '', property = '', isObjectId = false, limit, skip} - const getJobs = function (job, state, options) { + const getJobs = async (job, state, options) => { const preMatch = {}; if (job) { preMatch.name = job; @@ -56,7 +71,7 @@ module.exports = function (agenda, options) { } const collection = agenda._collection.collection || agenda._collection; - return collection + const jobsQueryResult = await collection .aggregate([ { $match: preMatch }, { @@ -118,8 +133,17 @@ module.exports = function (agenda, options) { filtered: [{ $skip: options.skip }, { $limit: options.limit }], }, }, - ]) - .toArray(); + ]).toArray(); + + // add a human representation of the cron interval for each job + jobsQueryResult?.[0]?.filtered?.forEach((job) => { + const repeatIntervalHuman = getHumanRepeatInterval(job.job.repeatInterval); + if (repeatIntervalHuman !== job.job.repeatInterval) { // only add the human representation if it's different from the cron expression + job.job.repeatIntervalHuman = repeatIntervalHuman; + } + }); + + return jobsQueryResult; }; const getOverview = async () => { diff --git a/package.json b/package.json index 5565860d..bf0abc42 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "agenda": "^4.2.1", "body-parser": "^1.19.2", "commander": "^2.9.0", + "cronstrue": "^2.28.0", "express": "^4.17.3", "mongodb": "*", "semver": "^7.3.4" diff --git a/public/app/js/joblist.js b/public/app/js/joblist.js index 0e27f8d4..772f50df 100644 --- a/public/app/js/joblist.js +++ b/public/app/js/joblist.js @@ -142,6 +142,7 @@ const jobList = Vue.component("job-list", { {{job.job.repeatInterval}} + {{job.job.repeatIntervalHuman}} Scheduled Completed Queued @@ -183,6 +184,7 @@ const jobList = Vue.component("job-list", {
{{job.job.repeatInterval}} + {{job.job.repeatIntervalHuman}} Scheduled Completed Queued