Skip to content

Commit

Permalink
fix: improve Go schema extraction errors (#927)
Browse files Browse the repository at this point in the history
Previously:

```
failed to build module: failed to extract module schema: ftl/foo:
/Users/alec/dev/example/backend/modules/foo/foo.go:14:2: could not import
ftl/bar (invalid package name: "")
```

Now:

```
failed to build module: failed to extract module schema:
/Users/alec/dev/example/backend/modules/foo/foo.go:2:1:
/Users/alec/dev/example/backend/modules/foo/foo.go:88:1:
/Users/alec/dev/example/backend/modules/foo/foo.go:88:138:
/Users/alec/dev/example/backend/modules/foo/foo.go:106:2:
/Users/alec/dev/example/backend/modules/foo/foo.go:106:23: call first
argument must be a function but is an unresolved reference to
bar.BarRequest
```

Fixes #924

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
alecthomas and github-actions[bot] authored Feb 12, 2024
1 parent 47f7569 commit 76ed3b5
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/workflow-roadmap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
types:
- assigned
- labeled
- closed
- deleted
workflow_dispatch:
# We need to separately trigger when one of the other workflows completes
# because GHA won't trigger another workflow based only on changes from
Expand Down
40 changes: 40 additions & 0 deletions examples/go/httpingress/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,43 @@ module ftl/httpingress
go 1.21.6

replace github.com/TBD54566975/ftl => ../../..

require github.com/TBD54566975/ftl v0.0.0-00010101000000-000000000000

require (
connectrpc.com/connect v1.14.0 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.0 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
github.com/alecthomas/types v0.10.1 // indirect
github.com/alessio/shellescape v1.4.2 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.5.3 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/oklog/ulid/v2 v2.1.0 // indirect
github.com/swaggest/jsonschema-go v0.3.64 // indirect
github.com/swaggest/refl v1.3.0 // indirect
github.com/zalando/go-keyring v0.2.3 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
golang.design/x/reflect v0.0.0-20220504060917-02c43be63f3b // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
)
143 changes: 143 additions & 0 deletions examples/go/httpingress/go.sum

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

38 changes: 19 additions & 19 deletions examples/go/httpingress/httpingress.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"

"ftl/builtin"

"github.com/TBD54566975/ftl/go-runtime/sdk"
)

type GetRequest struct {
Expand All @@ -26,13 +28,13 @@ type GetResponse struct {
//
//ftl:verb
//ftl:ingress http GET /http/users/{userID}/posts
func Get(ctx context.Context, req builtin.HttpRequest[GetRequest]) (builtin.HttpResponse[GetResponse], error) {
return builtin.HttpResponse[GetResponse]{
func Get(ctx context.Context, req builtin.HttpRequest[GetRequest]) (builtin.HttpResponse[GetResponse, sdk.Unit], error) {
return builtin.HttpResponse[GetResponse, sdk.Unit]{
Headers: map[string][]string{"Get": {"Header from FTL"}},
Body: GetResponse{
Body: sdk.Some(GetResponse{
Message: fmt.Sprintf("Got userId %s and postId %s", req.Body.UserID, req.Body.PostID),
Nested: Nested{GoodStuff: "Nested Good Stuff"},
},
}),
}, nil
}

Expand All @@ -49,11 +51,11 @@ type PostResponse struct {
//
//ftl:verb
//ftl:ingress http POST /http/users
func Post(ctx context.Context, req builtin.HttpRequest[PostRequest]) (builtin.HttpResponse[PostResponse], error) {
return builtin.HttpResponse[PostResponse]{
func Post(ctx context.Context, req builtin.HttpRequest[PostRequest]) (builtin.HttpResponse[PostResponse, sdk.Unit], error) {
return builtin.HttpResponse[PostResponse, sdk.Unit]{
Status: 201,
Headers: map[string][]string{"Post": {"Header from FTL"}},
Body: PostResponse{Success: true},
Body: sdk.Some(PostResponse{Success: true}),
}, nil
}

Expand All @@ -68,10 +70,9 @@ type PutResponse struct{}
//
//ftl:verb
//ftl:ingress http PUT /http/users/{userID}
func Put(ctx context.Context, req builtin.HttpRequest[PutRequest]) (builtin.HttpResponse[PutResponse], error) {
return builtin.HttpResponse[PutResponse]{
func Put(ctx context.Context, req builtin.HttpRequest[PutRequest]) (builtin.HttpResponse[PutResponse, sdk.Unit], error) {
return builtin.HttpResponse[PutResponse, sdk.Unit]{
Headers: map[string][]string{"Put": {"Header from FTL"}},
Body: PutResponse{},
}, nil
}

Expand All @@ -85,31 +86,30 @@ type DeleteResponse struct{}
//
//ftl:verb
//ftl:ingress http DELETE /http/users/{userID}
func Delete(ctx context.Context, req builtin.HttpRequest[DeleteRequest]) (builtin.HttpResponse[DeleteResponse], error) {
return builtin.HttpResponse[DeleteResponse]{
func Delete(ctx context.Context, req builtin.HttpRequest[DeleteRequest]) (builtin.HttpResponse[DeleteResponse, sdk.Unit], error) {
return builtin.HttpResponse[DeleteResponse, sdk.Unit]{
Headers: map[string][]string{"Put": {"Header from FTL"}},
Body: DeleteResponse{},
}, nil
}

type HtmlRequest struct{}

//ftl:verb
//ftl:ingress http GET /http/html
func Html(ctx context.Context, req builtin.HttpRequest[HtmlRequest]) (builtin.HttpResponse[string], error) {
return builtin.HttpResponse[string]{
func Html(ctx context.Context, req builtin.HttpRequest[HtmlRequest]) (builtin.HttpResponse[string, sdk.Unit], error) {
return builtin.HttpResponse[string, sdk.Unit]{
Headers: map[string][]string{"Content-Type": {"text/html; charset=utf-8"}},
Body: "<html><body><h1>HTML Page From FTL 🚀!</h1></body></html>",
Body: sdk.Some("<html><body><h1>HTML Page From FTL 🚀!</h1></body></html>"),
}, nil
}

// Example: curl -X POST http://localhost:8892/ingress/http/bytes -d 'Your data here'
//
//ftl:verb
//ftl:ingress http POST /http/bytes
func Bytes(ctx context.Context, req builtin.HttpRequest[[]byte]) (builtin.HttpResponse[[]byte], error) {
return builtin.HttpResponse[[]byte]{
func Bytes(ctx context.Context, req builtin.HttpRequest[[]byte]) (builtin.HttpResponse[[]byte, sdk.Unit], error) {
return builtin.HttpResponse[[]byte, sdk.Unit]{
Headers: map[string][]string{"Content-Type": {"application/octet-stream"}},
Body: req.Body,
Body: sdk.Some(req.Body),
}, nil
}
13 changes: 11 additions & 2 deletions go-runtime/compile/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ func ExtractModuleSchema(dir string) (NativeNames, *schema.Module, error) {
}
nativeNames := NativeNames{}
module := &schema.Module{}
merr := []error{}
for _, pkg := range pkgs {
if len(pkg.Errors) > 0 {
return nil, nil, fmt.Errorf("%s: %w", pkg.PkgPath, pkg.Errors[0])
for _, perr := range pkg.Errors {
merr = append(merr, fmt.Errorf("%s: %w", pkg.PkgPath, perr))
}
}
pctx := &parseContext{pkg: pkg, pkgs: pkgs, module: module, nativeNames: NativeNames{}}
for _, file := range pkg.Syntax {
Expand Down Expand Up @@ -102,6 +105,9 @@ func ExtractModuleSchema(dir string) (NativeNames, *schema.Module, error) {
nativeNames[decl] = nativeName
}
}
if len(merr) > 0 {
return nil, nil, errors.Join(merr...)
}
if module.Name == "" {
return nil, nil, fmt.Errorf("//ftl:module directive is required")
}
Expand All @@ -121,7 +127,10 @@ func visitCallExpr(pctx *parseContext, verb *schema.Verb, node *ast.CallExpr) er
}
_, verbFn := deref[*types.Func](pctx.pkg, node.Args[1])
if verbFn == nil {
return fmt.Errorf("call first argument must be a function but is %s", node.Args[1])
if sel, ok := node.Args[1].(*ast.SelectorExpr); ok {
return fmt.Errorf("call first argument must be a function but is an unresolved reference to %s.%s", sel.X, sel.Sel)
}
return fmt.Errorf("call first argument must be a function but is %T", node.Args[1])
}
moduleName := verbFn.Pkg().Name()
if moduleName == pctx.pkg.Name {
Expand Down

0 comments on commit 76ed3b5

Please sign in to comment.