Skip to content

Commit

Permalink
feat: inject DB verb resources (#2985)
Browse files Browse the repository at this point in the history
you can declare a DB by defining a type that implements
`PostgresDatabaseConfig` (the only supported interface implementing
`DatabaseConfig`, at present):

```
type MyConfig struct {}

// name provides the DB name
func (MyConfig) Name() string { return "testdb" }

// tags the type as a DB decl
func (MyConfig) db()

// tags the DB type as `postgres`
func (MyConfig  pg()
```

to avoid the verbosity of implementing `db()` and `pg()`, you can
instead embed `ftl.DefaultPostgresDatabaseConfig`.
this will implement these "tag" methods but not any required methods,
e.g. `Name()` which must be explicitly provided in the user code.

```
type MyConfig struct {
   ftl.DefaultPostgresDatabaseConfig
}

// name provides the DB name
func (MyConfig) Name() string { return "testdb" }
```

you can then use the DB you've configured by injecting a handle around
its config into a verb signature:
```
//ftl:verb
func Query(ctx context.Context, in Request, db ftl.DatabaseHandle[MyConfig]) (Response, error) {
  db.Get(ctx).Exec(...)
  // ...
}
```

eventually we will extend `DatabaseConfig` with additional methods, e.g.
RAM, disk, max connections, etc.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
worstell and github-actions[bot] authored Oct 31, 2024
1 parent 8fbf480 commit be8d02f
Show file tree
Hide file tree
Showing 113 changed files with 977 additions and 365 deletions.
1 change: 0 additions & 1 deletion backend/controller/admin/testdata/go/dischema/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/admin/testdata/go/dischema/go.sum

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

1 change: 0 additions & 1 deletion backend/controller/console/testdata/go/console/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/console/testdata/go/console/go.sum

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

1 change: 0 additions & 1 deletion backend/controller/cronjobs/testdata/go/cron/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/cronjobs/testdata/go/cron/go.sum

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

27 changes: 27 additions & 0 deletions backend/controller/cronjobs/testdata/go/cron/types.ftl.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/encryption/testdata/go/encryption/go.sum

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

1 change: 0 additions & 1 deletion backend/controller/ingress/testdata/go/httpingress/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/ingress/testdata/go/httpingress/go.sum

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

1 change: 0 additions & 1 deletion backend/controller/pubsub/testdata/go/publisher/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/pubsub/testdata/go/publisher/go.sum

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

1 change: 0 additions & 1 deletion backend/controller/pubsub/testdata/go/slow/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/pubsub/testdata/go/slow/go.sum

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

1 change: 0 additions & 1 deletion backend/controller/pubsub/testdata/go/subscriber/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
github.com/alecthomas/types v0.16.0 // indirect
Expand Down
2 changes: 0 additions & 2 deletions backend/controller/pubsub/testdata/go/subscriber/go.sum

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

12 changes: 8 additions & 4 deletions backend/controller/sql/testdata/go/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import (
"github.com/TBD54566975/ftl/go-runtime/ftl" // Import the FTL SDK.
)

var db = ftl.PostgresDatabase("testdb")
type MyDbConfig struct {
ftl.DefaultPostgresDatabaseConfig
}

func (MyDbConfig) Name() string { return "testdb" }

type InsertRequest struct {
Data string
Expand All @@ -15,16 +19,16 @@ type InsertRequest struct {
type InsertResponse struct{}

//ftl:verb
func Insert(ctx context.Context, req InsertRequest) (InsertResponse, error) {
err := persistRequest(ctx, req)
func Insert(ctx context.Context, req InsertRequest, db ftl.DatabaseHandle[MyDbConfig]) (InsertResponse, error) {
err := persistRequest(ctx, req, db)
if err != nil {
return InsertResponse{}, err
}

return InsertResponse{}, nil
}

func persistRequest(ctx context.Context, req InsertRequest) error {
func persistRequest(ctx context.Context, req InsertRequest, db ftl.DatabaseHandle[MyDbConfig]) error {
_, err := db.Get(ctx).Exec(`CREATE TABLE IF NOT EXISTS requests
(
data TEXT,
Expand Down
22 changes: 16 additions & 6 deletions backend/controller/sql/testdata/go/database/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,26 @@ import (

func TestDatabase(t *testing.T) {
ctx := ftltest.Context(
ftltest.WithCallsAllowedWithinModule(),
ftltest.WithProjectFile("ftl-project.toml"),
ftltest.WithDatabase(db),
ftltest.WithDatabase[MyDbConfig](),
)

Insert(ctx, InsertRequest{Data: "unit test 1"})
_, err := ftltest.Call[InsertClient, InsertRequest, InsertResponse](ctx, InsertRequest{Data: "unit test 1"})
assert.NoError(t, err)
list, err := getAll(ctx)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
assert.Equal(t, "unit test 1", list[0])

ctx = ftltest.Context(
ftltest.WithCallsAllowedWithinModule(),
ftltest.WithProjectFile("ftl-project.toml"),
ftltest.WithDatabase(db),
ftltest.WithDatabase[MyDbConfig](),
)

Insert(ctx, InsertRequest{Data: "unit test 2"})
_, err = ftltest.Call[InsertClient, InsertRequest, InsertResponse](ctx, InsertRequest{Data: "unit test 2"})
assert.NoError(t, err)
list, err = getAll(ctx)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
Expand All @@ -35,18 +39,24 @@ func TestDatabase(t *testing.T) {

func TestOptionOrdering(t *testing.T) {
ctx := ftltest.Context(
ftltest.WithDatabase(db), // <--- consumes DSNs
ftltest.WithCallsAllowedWithinModule(),
ftltest.WithDatabase[MyDbConfig](), // <--- consumes DSNs
ftltest.WithProjectFile("ftl-project.toml"), // <--- provides DSNs
)

Insert(ctx, InsertRequest{Data: "unit test 1"})
_, err := ftltest.Call[InsertClient, InsertRequest, InsertResponse](ctx, InsertRequest{Data: "unit test 1"})
assert.NoError(t, err)
list, err := getAll(ctx)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
assert.Equal(t, "unit test 1", list[0])
}

func getAll(ctx context.Context) ([]string, error) {
db, err := ftltest.GetDatabaseHandle[MyDbConfig]()
if err != nil {
return nil, err
}
rows, err := db.Get(ctx).Query("SELECT data FROM requests ORDER BY created_at;")
if err != nil {
return nil, err
Expand Down
20 changes: 20 additions & 0 deletions backend/controller/sql/testdata/go/database/types.ftl.go

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

8 changes: 6 additions & 2 deletions backend/provisioner/testdata/go/echo/echo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ import (
"github.com/TBD54566975/ftl/go-runtime/ftl"
)

var db = ftl.PostgresDatabase("echodb")
type EchoDBConfig struct {
ftl.DefaultPostgresDatabaseConfig
}

func (EchoDBConfig) Name() string { return "echodb" }

// Echo returns a greeting with the current time.
//
//ftl:verb export
func Echo(ctx context.Context, req string) (string, error) {
func Echo(ctx context.Context, req string, db ftl.DatabaseHandle[EchoDBConfig]) (string, error) {
_, err := db.Get(ctx).Exec(`CREATE TABLE IF NOT EXISTS messages(
message TEXT
);`)
Expand Down
9 changes: 6 additions & 3 deletions docs/content/docs/reference/unittests.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,22 @@ ctx := ftltest.Context(
```

### Databases
By default, calling `Get(ctx)` on a database panics.

To enable database access in a test, you must first [provide a DSN via a project file](#project-files-configs-and-secrets). You can then set up a test database:
```go
ctx := ftltest.Context(
ftltest.WithDefaultProjectFile(),
ftltest.WithDatabase(db),
ftltest.WithDatabase[MyDBConfig](),
)
```
This will:
- Take the provided DSN and appends `_test` to the database name. Eg: `accounts` becomes `accounts_test`
- Wipe all tables in the database so each test run happens on a clean database

You can access the database in your test using its handle:
```go
db, err := ftltest.GetDatabaseHandle[MyDBConfig]()
db.Get(ctx).Exec(...)
```

### Maps
By default, calling `Get(ctx)` on a map handle will panic.
Expand Down
1 change: 0 additions & 1 deletion examples/go/cron/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions examples/go/cron/go.sum

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

1 change: 0 additions & 1 deletion examples/go/http/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions examples/go/http/go.sum

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

1 change: 0 additions & 1 deletion examples/go/pubsub/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions examples/go/pubsub/go.sum

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

1 change: 0 additions & 1 deletion frontend/cli/testdata/go/echo/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
2 changes: 0 additions & 2 deletions frontend/cli/testdata/go/echo/go.sum

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

1 change: 0 additions & 1 deletion frontend/cli/testdata/go/time/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
connectrpc.com/connect v1.16.2 // indirect
connectrpc.com/grpcreflect v1.2.0 // indirect
connectrpc.com/otelconnect v0.7.1 // indirect
github.com/XSAM/otelsql v0.35.0 // indirect
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
github.com/alecthomas/concurrency v0.0.2 // indirect
github.com/alecthomas/participle/v2 v2.1.1 // indirect
Expand Down
Loading

0 comments on commit be8d02f

Please sign in to comment.