Skip to content

Commit

Permalink
feat: add retry metadata to fsm decl
Browse files Browse the repository at this point in the history
  • Loading branch information
matt2e committed May 24, 2024
1 parent dc09486 commit 5588502
Show file tree
Hide file tree
Showing 7 changed files with 510 additions and 443 deletions.
841 changes: 427 additions & 414 deletions backend/protos/xyz/block/ftl/v1/schema/schema.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions backend/protos/xyz/block/ftl/v1/schema/schema.proto
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ message FSM {
string name = 3;
repeated Ref start = 4;
repeated FSMTransition transitions = 5;
repeated Metadata metadata = 6;
}

message FSMTransition {
Expand Down
14 changes: 13 additions & 1 deletion backend/schema/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type FSM struct {
Name string `parser:"'fsm' @Ident '{'" protobuf:"3"`
Start []*Ref `parser:"('start' @@)*" protobuf:"4"` // Start states.
Transitions []*FSMTransition `parser:"('transition' @@)* '}'" protobuf:"5"`
Metadata []Metadata `parser:"@@*" protobuf:"6"`
}

func FSMFromProto(pb *schemapb.FSM) *FSM {
Expand All @@ -25,6 +26,7 @@ func FSMFromProto(pb *schemapb.FSM) *FSM {
Name: pb.Name,
Start: slices.Map(pb.Start, RefFromProto),
Transitions: slices.Map(pb.Transitions, FSMTransitionFromProto),
Metadata: metadataListToSchema(pb.Metadata),
}
}

Expand All @@ -39,7 +41,13 @@ func (f *FSM) schemaSymbol() {}

func (f *FSM) String() string {
w := &strings.Builder{}
fmt.Fprintf(w, "fsm %s {\n", f.Name)
if len(f.Metadata) == 0 {
fmt.Fprintf(w, "fsm %s {\n", f.Name)
} else {
fmt.Fprintf(w, "fsm %s\n", f.Name)
fmt.Fprint(w, indent(encodeMetadata(f.Metadata)))
fmt.Fprintf(w, "\n{\n")
}
for _, s := range f.Start {
fmt.Fprintf(w, " start %s\n", s)
}
Expand All @@ -60,6 +68,7 @@ func (f *FSM) ToProto() protoreflect.ProtoMessage {
Transitions: slices.Map(f.Transitions, func(t *FSMTransition) *schemapb.FSMTransition {
return t.ToProto().(*schemapb.FSMTransition) //nolint: forcetypeassert
}),
Metadata: metadataListToProto(f.Metadata),
}
}

Expand All @@ -71,6 +80,9 @@ func (f *FSM) schemaChildren() []Node {
for _, t := range f.Transitions {
out = append(out, t)
}
for _, m := range f.Metadata {
out = append(out, m)
}
return out
}

Expand Down
6 changes: 6 additions & 0 deletions frontend/src/protos/xyz/block/ftl/v1/schema/schema_pb.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

85 changes: 58 additions & 27 deletions go-runtime/compile/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ func ExtractModuleSchema(dir string, sch *schema.Schema) (optional.Option[ParseR
case *ast.GenDecl:
visitGenDecl(pctx, node)

case *ast.CommentGroup:
visitComments(pctx, node)

default:
}
return next()
Expand Down Expand Up @@ -243,7 +246,7 @@ func extractTypeDeclsForNode(pctx *parseContext, node *ast.GenDecl) {
case *types.Basic:
enum := &schema.Enum{
Pos: goPosToSchemaPos(node.Pos()),
Comments: visitComments(node.Doc),
Comments: parseComments(node.Doc),
Name: strcase.ToUpperCamel(t.Name.Name),
Type: nil, //TODO: explain
Export: dir.IsExported(),
Expand All @@ -270,7 +273,7 @@ func extractTypeDeclsForNode(pctx *parseContext, node *ast.GenDecl) {

enum := &schema.Enum{
Pos: goPosToSchemaPos(node.Pos()),
Comments: visitComments(node.Doc),
Comments: parseComments(node.Doc),
Name: strcase.ToUpperCamel(t.Name.Name),
Export: dir.IsExported(),
}
Expand All @@ -286,7 +289,7 @@ func extractTypeDeclsForNode(pctx *parseContext, node *ast.GenDecl) {
case *directiveTypeAlias:
alias := &schema.TypeAlias{
Pos: goPosToSchemaPos(node.Pos()),
Comments: visitComments(node.Doc),
Comments: parseComments(node.Doc),
Name: strcase.ToUpperCamel(t.Name.Name),
Export: dir.IsExported(),
Type: nil, //TODO: explain
Expand Down Expand Up @@ -404,8 +407,9 @@ func parseFSMDecl(pctx *parseContext, node *ast.CallExpr) {
}

fsm := &schema.FSM{
Pos: goPosToSchemaPos(node.Pos()),
Name: name,
Pos: goPosToSchemaPos(node.Pos()),
Name: name,
Metadata: []schema.Metadata{},
}
pctx.module.Decls = append(pctx.module.Decls, fsm)

Expand All @@ -422,6 +426,25 @@ func parseFSMDecl(pctx *parseContext, node *ast.CallExpr) {
}
parseFSMTransition(pctx, call, fn, fsm)
}

if commentGroup, ok := pctx.commentNodeByLine[fset.Position(node.Pos()).Line-1]; ok {
directives, err := parseDirectives(node, fset, commentGroup)
if err != nil {
pctx.errors.add(err)
}
for _, dir := range directives {
if dir, ok := dir.(*directiveRetry); ok {
fsm.Metadata = append(fsm.Metadata, &schema.MetadataRetry{
Pos: dir.Pos,
Count: dir.Count,
MinBackoff: dir.MinBackoff,
MaxBackoff: dir.MaxBackoff,
})
} else {
pctx.errors.add(errorf(node, "unexpected directive %T", dir))
}
}
}
}

// Parse a Start or Transition call in an FSM declaration and add it to the FSM.
Expand Down Expand Up @@ -565,7 +588,7 @@ func visitFile(pctx *parseContext, node *ast.File) {
if node.Doc == nil {
return
}
pctx.module.Comments = visitComments(node.Doc)
pctx.module.Comments = parseComments(node.Doc)
}

func isType[T types.Type](t types.Type) bool {
Expand Down Expand Up @@ -753,7 +776,7 @@ func maybeVisitTypeEnumVariant(pctx *parseContext, node *ast.GenDecl, directives

enumVariant := &schema.EnumVariant{
Pos: goPosToSchemaPos(node.Pos()),
Comments: visitComments(node.Doc),
Comments: parseComments(node.Doc),
Name: strcase.ToUpperCamel(t.Name.Name),
}

Expand Down Expand Up @@ -918,7 +941,7 @@ func visitValueSpec(pctx *parseContext, node *ast.ValueSpec) {
if value, ok := visitConst(pctx, c).Get(); ok {
variant := &schema.EnumVariant{
Pos: goPosToSchemaPos(c.Pos()),
Comments: visitComments(node.Doc),
Comments: parseComments(node.Doc),
Name: strcase.ToUpperCamel(c.Id()),
Value: value,
}
Expand Down Expand Up @@ -1056,7 +1079,7 @@ func visitFuncDecl(pctx *parseContext, node *ast.FuncDecl) (verb *schema.Verb) {
}
verb = &schema.Verb{
Pos: goPosToSchemaPos(node.Pos()),
Comments: visitComments(node.Doc),
Comments: parseComments(node.Doc),
Export: isExported,
Name: strcase.ToLowerCamel(node.Name.Name),
Request: reqV,
Expand All @@ -1068,7 +1091,13 @@ func visitFuncDecl(pctx *parseContext, node *ast.FuncDecl) (verb *schema.Verb) {
return verb
}

func visitComments(doc *ast.CommentGroup) []string {
func visitComments(pctx *parseContext, node *ast.CommentGroup) {
for line := fset.Position(node.Pos()).Line; line <= fset.Position(node.End()).Line; line++ {
pctx.commentNodeByLine[line] = node
}
}

func parseComments(doc *ast.CommentGroup) []string {
comments := []string{}
if doc := doc.Text(); doc != "" {
comments = strings.Split(strings.TrimSpace(doc), "\n")
Expand Down Expand Up @@ -1160,11 +1189,11 @@ func visitStruct(pctx *parseContext, pos token.Pos, tnode types.Type, isExported
switch path := path[i].(type) {
case *ast.TypeSpec:
if path.Doc != nil {
out.Comments = visitComments(path.Doc)
out.Comments = parseComments(path.Doc)
}
case *ast.GenDecl:
if path.Doc != nil {
out.Comments = visitComments(path.Doc)
out.Comments = parseComments(path.Doc)
}
}
}
Expand Down Expand Up @@ -1458,25 +1487,27 @@ func deref[T types.Object](pkg *packages.Package, node ast.Expr) (string, T) {
}

type parseContext struct {
pkg *packages.Package
pkgs []*packages.Package
module *schema.Module
nativeNames NativeNames
enumInterfaces enumInterfaces
activeVerb *schema.Verb
errors errorSet
schema *schema.Schema
pkg *packages.Package
pkgs []*packages.Package
module *schema.Module
nativeNames NativeNames
enumInterfaces enumInterfaces
activeVerb *schema.Verb
commentNodeByLine map[int]*ast.CommentGroup
errors errorSet
schema *schema.Schema
}

func newParseContext(pkg *packages.Package, pkgs []*packages.Package, module *schema.Module, sch *schema.Schema) *parseContext {
return &parseContext{
pkg: pkg,
pkgs: pkgs,
module: module,
nativeNames: NativeNames{},
enumInterfaces: enumInterfaces{},
errors: errorSet{},
schema: sch,
pkg: pkg,
pkgs: pkgs,
module: module,
nativeNames: NativeNames{},
enumInterfaces: enumInterfaces{},
commentNodeByLine: map[int]*ast.CommentGroup{},
errors: errorSet{},
schema: sch,
}
}

Expand Down
4 changes: 3 additions & 1 deletion go-runtime/compile/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,9 @@ func TestExtractModuleSchemaFSM(t *testing.T) {
assert.Equal(t, r.MustGet().Errors, nil, "expected no schema errors")
actual := schema.Normalise(r.MustGet().Module)
expected := `module fsm {
fsm payment {
fsm payment
+retry 10 5s 10m
{
start fsm.created
start fsm.paid
transition fsm.created to fsm.paid
Expand Down
2 changes: 2 additions & 0 deletions go-runtime/compile/testdata/fsm/fsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
)

// The payment FSM.
//
//ftl:retry 10 5s 10m
var paymentFSM = ftl.FSM("payment",
ftl.Start(Created),
ftl.Start(Paid),
Expand Down

0 comments on commit 5588502

Please sign in to comment.