Skip to content

Commit

Permalink
enhance: support L to mean last day of the month in cron
Browse files Browse the repository at this point in the history
Update the cron parsing package we use so that we could support "last
day of the month" syntax.

Also, fix a small display issue with the last day of the month in the
admin UI.

Signed-off-by: Donnie Adams <[email protected]>
  • Loading branch information
thedadams committed Jan 13, 2025
1 parent b343734 commit d3c9fd8
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 30 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ replace (
)

require (
github.com/adhocore/gronx v1.19.5
github.com/adrg/xdg v0.5.3
github.com/docker/docker v27.4.1+incompatible
github.com/dustin/go-humanize v1.0.1
Expand All @@ -30,7 +31,6 @@ require (
github.com/obot-platform/obot/logger v0.0.0-20241217130503-4004a5c69f32
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
github.com/pterm/pterm v0.12.79
github.com/robfig/cron/v3 v3.0.1
github.com/rs/cors v1.11.1
github.com/spf13/cobra v1.8.1
golang.org/x/crypto v0.31.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0k
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
github.com/adhocore/gronx v1.19.5 h1:cwIG4nT1v9DvadxtHBe6MzE+FZ1JDvAUC45U2fl4eSQ=
github.com/adhocore/gronx v1.19.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
Expand Down Expand Up @@ -585,8 +587,6 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
Expand Down
11 changes: 5 additions & 6 deletions pkg/api/handlers/cronjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"net/http"
"time"

"github.com/adhocore/gronx"
"github.com/obot-platform/obot/apiclient/types"
"github.com/obot-platform/obot/pkg/alias"
"github.com/obot-platform/obot/pkg/api"
"github.com/obot-platform/obot/pkg/controller/handlers/cronjob"
v1 "github.com/obot-platform/obot/pkg/storage/apis/obot.obot.ai/v1"
"github.com/obot-platform/obot/pkg/system"
"github.com/robfig/cron/v3"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -134,9 +134,8 @@ func (a *CronJobHandler) Execute(req api.Context) error {

func convertCronJob(cronJob v1.CronJob) types.CronJob {
var nextRunAt *time.Time
if sched, err := cron.ParseStandard(cronjob.GetSchedule(cronJob)); err == nil {
nextRunAt = new(time.Time)
*nextRunAt = sched.Next(time.Now())
if next, err := gronx.NextTick(cronjob.GetSchedule(cronJob), true); err == nil {
nextRunAt = &next
}

return types.CronJob{
Expand All @@ -153,8 +152,8 @@ func parseAndValidateCronManifest(req api.Context) (*types.CronJobManifest, erro
if err := req.Read(&manifest); err != nil {
return nil, err
}
if _, err := cron.ParseStandard(manifest.Schedule); err != nil {
return nil, apierrors.NewBadRequest(fmt.Sprintf("invalid schedule %s: %v", manifest.Schedule, err))
if !gronx.IsValid(manifest.Schedule) {
return nil, apierrors.NewBadRequest(fmt.Sprintf("invalid schedule %s", manifest.Schedule))
}

var workflow v1.Workflow
Expand Down
10 changes: 7 additions & 3 deletions pkg/controller/handlers/cronjob/cronjob.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import (
"fmt"
"time"

"github.com/adhocore/gronx"
"github.com/obot-platform/nah/pkg/router"
"github.com/obot-platform/obot/apiclient/types"
"github.com/obot-platform/obot/pkg/alias"
v1 "github.com/obot-platform/obot/pkg/storage/apis/obot.obot.ai/v1"
"github.com/obot-platform/obot/pkg/system"
"github.com/robfig/cron/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -31,6 +31,10 @@ func GetSchedule(cronJob v1.CronJob) string {
case "weekly":
return fmt.Sprintf("%d %d * * %d", cronJob.Spec.TaskSchedule.Minute, cronJob.Spec.TaskSchedule.Hour, cronJob.Spec.TaskSchedule.Weekday)
case "monthly":
if cronJob.Spec.TaskSchedule.Day == -1 {
// The day being -1 means the last day of the month. The cron parsing package we use uses `L` for this.
return fmt.Sprintf("%d %d L * *", cronJob.Spec.TaskSchedule.Minute, cronJob.Spec.TaskSchedule.Hour)
}
return fmt.Sprintf("%d %d %d * *", cronJob.Spec.TaskSchedule.Minute, cronJob.Spec.TaskSchedule.Hour, cronJob.Spec.TaskSchedule.Day)
}
}
Expand All @@ -44,12 +48,12 @@ func (h *Handler) Run(req router.Request, resp router.Response) error {
lastRun = &cj.CreationTimestamp
}

sched, err := cron.ParseStandard(GetSchedule(*cj))
next, err := gronx.NextTickAfter(GetSchedule(*cj), lastRun.Time, true)
if err != nil {
return fmt.Errorf("failed to parse schedule: %w", err)
}

if until := time.Until(sched.Next(lastRun.Time)); until > 0 {
if until := time.Until(next); until > 0 {
resp.RetryAfter(until)
return nil
}
Expand Down
15 changes: 11 additions & 4 deletions pkg/controller/handlers/knowledgesource/reschedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import (
"fmt"
"time"

"github.com/adhocore/gronx"
"github.com/obot-platform/nah/pkg/router"
"github.com/obot-platform/obot/apiclient/types"
v1 "github.com/obot-platform/obot/pkg/storage/apis/obot.obot.ai/v1"
"github.com/robfig/cron/v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand All @@ -29,13 +29,20 @@ func (k *Handler) Reschedule(req router.Request, _ router.Response) error {
}

if source.Status.NextSyncTime.IsZero() {
schedule, err := cron.ParseStandard(source.Spec.Manifest.SyncSchedule)
if !gronx.IsValid(source.Spec.Manifest.SyncSchedule) {
source.Status.Error = fmt.Sprintf("invalid sync schedule: %s", source.Spec.Manifest.SyncSchedule)
source.Status.SyncState = types.KnowledgeSourceStateError
return nil
}

tick, err := gronx.NextTickAfter(source.Spec.Manifest.SyncSchedule, source.Status.LastSyncStartTime.Time, true)
if err != nil {
source.Status.Error = fmt.Sprintf("invalid sync schedule: %s", err)
source.Status.Error = fmt.Sprintf("failed to calculate next sync time: %v", err)
source.Status.SyncState = types.KnowledgeSourceStateError
return nil
}
source.Status.NextSyncTime = metav1.NewTime(schedule.Next(source.Status.LastSyncStartTime.Time))

source.Status.NextSyncTime = metav1.NewTime(tick)
} else if source.Status.NextSyncTime.Time.Before(time.Now()) {
source.Status.NextSyncTime = metav1.Time{}
source.Status.SyncState = types.KnowledgeSourceStatePending
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function getCronFrequency(
hourly: /^(0|\*\/\d+) \* \* \* \*$/, // ex. "0 * * * *" or "*/15 * * * *"
daily: /^0 \d+ \* \* \*$/, // ex. "0 6 * * *"
weekly: /^0 \d+ \* \* \d$/, // ex. "0 0 * * 3"
monthly: /^0 \d+ \d+ \* \*$/, // "0 0 15 * *"
monthly: /^0 \d+ [\d+L] \* \*$/, // "0 0 15 * *" or "0 0 L * *"
};

for (const [frequency, pattern] of Object.entries(patterns)) {
Expand Down
26 changes: 13 additions & 13 deletions ui/admin/app/lib/model/oauthApps/providers/zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ const schema = z.object({
});

const scopes = [
"user:read:user",
"meeting:read:summary",
"meeting:read:invitation",
"meeting:read:list_templates",
"meeting:read:meeting",
"meeting:read:list_upcoming_meetings",
"meeting:delete:meeting",
"meeting:update:meeting",
"meeting:write:meeting",
"meeting:read:list_meetings",
"meeting:read:past_meeting",
"cloud_recording:read:list_recording_files",
"cloud_recording:read:list_user_recordings",
"user:read:user",
"meeting:read:summary",
"meeting:read:invitation",
"meeting:read:list_templates",
"meeting:read:meeting",
"meeting:read:list_upcoming_meetings",
"meeting:delete:meeting",
"meeting:update:meeting",
"meeting:write:meeting",
"meeting:read:list_meetings",
"meeting:read:past_meeting",
"cloud_recording:read:list_recording_files",
"cloud_recording:read:list_user_recordings",
];

const steps: OAuthFormStep<z.infer<typeof schema>>[] = [
Expand Down

0 comments on commit d3c9fd8

Please sign in to comment.