From bcd9772ec9fd05005f05b818bc775ad6bc209307 Mon Sep 17 00:00:00 2001 From: gak Date: Wed, 12 Jun 2024 15:10:11 +1000 Subject: [PATCH] fix: cron was not being parsed at all! (#1750) Added integration tests to ensure these are being parsed correctly. --- .../cronjobs/testdata/go/cron/cron.go | 10 ++++++ backend/schema/metadatacronjob.go | 2 +- backend/schema/schema_test.go | 36 +++++++++++++++++++ go-runtime/compile/parser.go | 3 +- go-runtime/compile/schema.go | 2 +- go-runtime/compile/schema_test.go | 5 +-- .../compile/testdata/failing/failing.go | 5 +++ internal/cron/pattern.go | 8 ++++- 8 files changed, 65 insertions(+), 6 deletions(-) diff --git a/backend/controller/cronjobs/testdata/go/cron/cron.go b/backend/controller/cronjobs/testdata/go/cron/cron.go index 9b8376fb12..05005239d7 100644 --- a/backend/controller/cronjobs/testdata/go/cron/cron.go +++ b/backend/controller/cronjobs/testdata/go/cron/cron.go @@ -19,3 +19,13 @@ type EchoResponse struct { func Cron(ctx context.Context) error { return os.WriteFile(os.Getenv("DEST_FILE"), []byte("Hello, world!"), 0644) } + +//ftl:cron 5m +func FiveMinutes(ctx context.Context) error { + return nil +} + +//ftl:cron Sat +func Saturday(ctx context.Context) error { + return nil +} diff --git a/backend/schema/metadatacronjob.go b/backend/schema/metadatacronjob.go index 243793c526..f8d6451891 100644 --- a/backend/schema/metadatacronjob.go +++ b/backend/schema/metadatacronjob.go @@ -11,7 +11,7 @@ import ( type MetadataCronJob struct { Pos Position `parser:"" protobuf:"1,optional"` - Cron string `parser:"'+' 'cron' Whitespace @((' ' | Number | '-' | '/' | '*' | ',')+)" protobuf:"2"` + Cron string `parser:"'+' 'cron' Whitespace @( (Number ('s' | 'm' | 'h')) | ('Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' | 'Sat' | 'Sun') | (' ' | Number | '-' | '/' | '*' | ',')+ )" protobuf:"2"` } var _ Metadata = (*MetadataCronJob)(nil) diff --git a/backend/schema/schema_test.go b/backend/schema/schema_test.go index c227946b2d..345aad39b9 100644 --- a/backend/schema/schema_test.go +++ b/backend/schema/schema_test.go @@ -53,8 +53,14 @@ module todo { export verb destroy(builtin.HttpRequest) builtin.HttpResponse +ingress http GET /todo/destroy/{name} + verb mondays(Unit) Unit + +cron Mon + verb scheduled(Unit) Unit +cron */10 * * 1-10,11-31 * * * + + verb twiceADay(Unit) Unit + +cron 12h } module foo { @@ -192,6 +198,14 @@ Module Unit Unit MetadataCronJob + Verb + Unit + Unit + MetadataCronJob + Verb + Unit + Unit + MetadataCronJob ` actual := &strings.Builder{} i := 0 @@ -719,6 +733,10 @@ module todo { +ingress http GET /todo/destroy/{name} verb scheduled(Unit) Unit +cron */10 * * 1-10,11-31 * * * + verb twiceADay(Unit) Unit + +cron 12h + verb mondays(Unit) Unit + +cron Mon } ` actual, err := ParseModuleString("", input) @@ -845,6 +863,24 @@ var testSchema = MustValidate(&Schema{ }, }, }, + &Verb{Name: "twiceADay", + Request: &Unit{Unit: true}, + Response: &Unit{Unit: true}, + Metadata: []Metadata{ + &MetadataCronJob{ + Cron: "12h", + }, + }, + }, + &Verb{Name: "mondays", + Request: &Unit{Unit: true}, + Response: &Unit{Unit: true}, + Metadata: []Metadata{ + &MetadataCronJob{ + Cron: "Mon", + }, + }, + }, }, }, { diff --git a/go-runtime/compile/parser.go b/go-runtime/compile/parser.go index 292195ff53..8707583237 100644 --- a/go-runtime/compile/parser.go +++ b/go-runtime/compile/parser.go @@ -14,6 +14,7 @@ import ( "github.com/alecthomas/participle/v2" "github.com/TBD54566975/ftl/backend/schema" + "github.com/TBD54566975/ftl/internal/cron" ) // This file contains a parser for Go FTL directives. @@ -152,7 +153,7 @@ func (d *directiveIngress) String() string { type directiveCronJob struct { Pos schema.Position - Cron string `parser:"'cron' Whitespace @((' ' | Number | '-' | '/' | '*' | ',')+)"` + Cron cron.Pattern `parser:"'cron' @@"` } func (*directiveCronJob) directive() {} diff --git a/go-runtime/compile/schema.go b/go-runtime/compile/schema.go index 3d73f895ea..d38ac8cd23 100644 --- a/go-runtime/compile/schema.go +++ b/go-runtime/compile/schema.go @@ -1201,7 +1201,7 @@ func visitFuncDecl(pctx *parseContext, node *ast.FuncDecl) (verb *schema.Verb) { isExported = false metadata = append(metadata, &schema.MetadataCronJob{ Pos: dir.Pos, - Cron: dir.Cron, + Cron: dir.Cron.String(), }) case *directiveRetry: metadata = append(metadata, &schema.MetadataRetry{ diff --git a/go-runtime/compile/schema_test.go b/go-runtime/compile/schema_test.go index 28985bf7a1..acd1b45ab9 100644 --- a/go-runtime/compile/schema_test.go +++ b/go-runtime/compile/schema_test.go @@ -546,8 +546,9 @@ func TestErrorReporting(t *testing.T) { `145:1-35: type can not be a variant of more than 1 type enums (TypeEnum1, TypeEnum2)`, `151:27-27: enum discriminator "TypeEnum3" cannot contain exported methods`, `154:1-35: enum discriminator "NoMethodsTypeEnum" must define at least one method`, - `168:2-62: can not publish directly to topics in other modules`, - `169:9-26: can not call verbs in other modules directly: use ftl.Call(…) instead`, + `166:3-3: unexpected token "d"`, + `173:2-62: can not publish directly to topics in other modules`, + `174:9-26: can not call verbs in other modules directly: use ftl.Call(…) instead`, } assert.Equal(t, expected, actual) } diff --git a/go-runtime/compile/testdata/failing/failing.go b/go-runtime/compile/testdata/failing/failing.go index a58aca5b14..16a9d8a80f 100644 --- a/go-runtime/compile/testdata/failing/failing.go +++ b/go-runtime/compile/testdata/failing/failing.go @@ -163,6 +163,11 @@ func BadCron(ctx context.Context) error { return nil } +//ftl:cron 4d +func DaysNotPossible(ctx context.Context) error { + return nil +} + //ftl:verb func BadPublish(ctx context.Context) error { ps.PublicBroadcast.Publish(ctx, ps.PayinEvent{Name: "Test"}) diff --git a/internal/cron/pattern.go b/internal/cron/pattern.go index e8fed3e5dd..ae834e691b 100644 --- a/internal/cron/pattern.go +++ b/internal/cron/pattern.go @@ -38,12 +38,18 @@ var ( ) type Pattern struct { - Duration *string `parser:"@(Number (?! Whitespace) Ident)+"` + Duration *string `parser:"@(Number ('s' | 'm' | 'h'))"` DayOfWeek *DayOfWeek `parser:"| @('Mon' | 'Tue' | 'Wed' | 'Thu' | 'Fri' | 'Sat' | 'Sun')"` Components []Component `parser:"| @@*"` } func (p Pattern) String() string { + if p.Duration != nil { + return *p.Duration + } + if p.DayOfWeek != nil { + return string(*p.DayOfWeek) + } return strings.Join(slices.Map(p.Components, func(component Component) string { return component.String() }), " ")