From 32327240ca1fa85cf35e2645bcbac31ceab7a5c4 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Mon, 22 Apr 2024 10:14:42 +1000 Subject: [PATCH] expose module context functionality through ftltest --- common/modulecontext/builder.go | 42 +++++++----------- go-runtime/ftl/ftltest/contextbuilder.go | 56 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 go-runtime/ftl/ftltest/contextbuilder.go diff --git a/common/modulecontext/builder.go b/common/modulecontext/builder.go index eaebb9eac6..9a54142e76 100644 --- a/common/modulecontext/builder.go +++ b/common/modulecontext/builder.go @@ -59,15 +59,15 @@ type configOrSecretItem interface { var _ configOrSecretItem = refValuePair{} var _ configOrSecretItem = configManager[cf.Configuration]{} -type ModuleContextBuilder struct { +type Builder struct { moduleName string configs []configOrSecretItem secrets []configOrSecretItem dsns []dsnEntry } -func NewBuilder(moduleName string) ModuleContextBuilder { - return ModuleContextBuilder{ +func NewBuilder(moduleName string) Builder { + return Builder{ moduleName: moduleName, configs: []configOrSecretItem{}, secrets: []configOrSecretItem{}, @@ -75,8 +75,8 @@ func NewBuilder(moduleName string) ModuleContextBuilder { } } -func NewBuilderFromProto(moduleName string, response *ftlv1.ModuleContextResponse) ModuleContextBuilder { - return ModuleContextBuilder{ +func NewBuilderFromProto(moduleName string, response *ftlv1.ModuleContextResponse) Builder { + return Builder{ moduleName: moduleName, configs: slices.Map(response.Configs, func(c *ftlv1.ModuleContextResponse_Config) configOrSecretItem { return refValuePair{ref: refFromProto(c.Ref), resolver: dataResolver{data: c.Data}} @@ -90,7 +90,7 @@ func NewBuilderFromProto(moduleName string, response *ftlv1.ModuleContextRespons } } -func (b ModuleContextBuilder) Build(ctx context.Context) (*ModuleContext, error) { +func (b Builder) Build(ctx context.Context) (*ModuleContext, error) { cm, err := newInMemoryConfigManager[cf.Configuration](ctx) if err != nil { return nil, err @@ -135,7 +135,7 @@ func newInMemoryConfigManager[R cf.Role](ctx context.Context) (*cf.Manager[R], e return manager, nil } -func buildConfigOrSecrets[R cf.Role](ctx context.Context, b ModuleContextBuilder, manager cf.Manager[R], items []configOrSecretItem) error { +func buildConfigOrSecrets[R cf.Role](ctx context.Context, b Builder, manager cf.Manager[R], items []configOrSecretItem) error { for _, item := range items { switch item := item.(type) { case refValuePair: @@ -174,7 +174,7 @@ func buildConfigOrSecrets[R cf.Role](ctx context.Context, b ModuleContextBuilder return nil } -func (b ModuleContextBuilder) AddGlobalConfig(name string, value any) ModuleContextBuilder { +func (b Builder) AddGlobalConfig(name string, value any) Builder { b.configs = append(b.configs, refValuePair{ ref: ref{ Name: name, @@ -184,14 +184,10 @@ func (b ModuleContextBuilder) AddGlobalConfig(name string, value any) ModuleCont return b } -func (b ModuleContextBuilder) AddConfig(name string, value any) ModuleContextBuilder { - return b.AddConfigForModule(name, b.moduleName, value) -} - -func (b ModuleContextBuilder) AddConfigForModule(name string, module string, value any) ModuleContextBuilder { +func (b Builder) AddConfig(name string, value any) Builder { b.configs = append(b.configs, refValuePair{ ref: ref{ - Module: optional.Some(module), + Module: optional.Some(b.moduleName), Name: name, }, resolver: valueResolver{value: value}, @@ -199,7 +195,7 @@ func (b ModuleContextBuilder) AddConfigForModule(name string, module string, val return b } -func (b ModuleContextBuilder) AddGlobalSecret(name string, value any) ModuleContextBuilder { +func (b Builder) AddGlobalSecret(name string, value any) Builder { b.secrets = append(b.secrets, refValuePair{ ref: ref{ Name: name, @@ -209,14 +205,10 @@ func (b ModuleContextBuilder) AddGlobalSecret(name string, value any) ModuleCont return b } -func (b ModuleContextBuilder) AddSecret(name string, value any) ModuleContextBuilder { - return b.AddSecretForModule(name, b.moduleName, value) -} - -func (b ModuleContextBuilder) AddSecretForModule(name string, module string, value any) ModuleContextBuilder { +func (b Builder) AddSecret(name string, value any) Builder { b.secrets = append(b.secrets, refValuePair{ ref: ref{ - Module: optional.Some(module), + Module: optional.Some(b.moduleName), Name: name, }, resolver: valueResolver{value: value}, @@ -224,7 +216,7 @@ func (b ModuleContextBuilder) AddSecretForModule(name string, module string, val return b } -func (b ModuleContextBuilder) AddDSN(name string, dbType DBType, dsn string) ModuleContextBuilder { +func (b Builder) AddDSN(name string, dbType DBType, dsn string) Builder { b.dsns = append(b.dsns, dsnEntry{ name: name, dbType: dbType, @@ -233,7 +225,7 @@ func (b ModuleContextBuilder) AddDSN(name string, dbType DBType, dsn string) Mod return b } -func (b ModuleContextBuilder) AddDSNsFromEnvarsForModule(module *schema.Module) ModuleContextBuilder { +func (b Builder) AddDSNsFromEnvarsForModule(module *schema.Module) Builder { // remove in favor of a non-envar approach once it is available for _, decl := range module.Decls { dbDecl, ok := decl.(*schema.Database) @@ -251,12 +243,12 @@ func (b ModuleContextBuilder) AddDSNsFromEnvarsForModule(module *schema.Module) return b } -func (b ModuleContextBuilder) AddConfigFromManager(cm *cf.Manager[cf.Configuration]) ModuleContextBuilder { +func (b Builder) AddConfigFromManager(cm *cf.Manager[cf.Configuration]) Builder { b.configs = append(b.configs, configManager[cf.Configuration]{manager: cm}) return b } -func (b ModuleContextBuilder) AddSecretsFromManager(sm *cf.Manager[cf.Secrets]) ModuleContextBuilder { +func (b Builder) AddSecretsFromManager(sm *cf.Manager[cf.Secrets]) Builder { b.secrets = append(b.secrets, configManager[cf.Secrets]{manager: sm}) return b } diff --git a/go-runtime/ftl/ftltest/contextbuilder.go b/go-runtime/ftl/ftltest/contextbuilder.go new file mode 100644 index 0000000000..0772b605fc --- /dev/null +++ b/go-runtime/ftl/ftltest/contextbuilder.go @@ -0,0 +1,56 @@ +package ftltest + +import ( + "context" + + "github.com/TBD54566975/ftl/common/modulecontext" +) + +type DBType int32 + +const ( + DBTypePostgres = DBType(modulecontext.DBTypePostgres) +) + +// ContextBuilder allows for building a context with configuration, secrets and DSN values set up for tests +type ContextBuilder struct { + builder modulecontext.Builder +} + +func NewContextBuilder(moduleName string) *ContextBuilder { + return &ContextBuilder{ + builder: modulecontext.NewBuilder(moduleName), + } +} + +func (b ContextBuilder) AddGlobalConfig(name string, value any) ContextBuilder { + return ContextBuilder{builder: b.builder.AddGlobalConfig(name, value)} +} + +func (b ContextBuilder) AddConfig(name string, value any) ContextBuilder { + return ContextBuilder{builder: b.builder.AddConfig(name, value)} +} + +func (b ContextBuilder) AddGlobalSecret(name string, value any) ContextBuilder { + return ContextBuilder{builder: b.builder.AddGlobalSecret(name, value)} +} + +func (b ContextBuilder) AddSecret(name string, value any) ContextBuilder { + return ContextBuilder{builder: b.builder.AddSecret(name, value)} +} + +func (b ContextBuilder) AddDSN(name string, dbType DBType, dsn string) ContextBuilder { + return ContextBuilder{builder: b.builder.AddDSN(name, modulecontext.DBType(dbType), dsn)} +} + +func (b ContextBuilder) Build() (context.Context, error) { + return b.BuildWithContext(Context()) +} + +func (b ContextBuilder) BuildWithContext(ctx context.Context) (context.Context, error) { + moduleCtx, err := b.builder.Build(ctx) + if err != nil { + return nil, err + } + return moduleCtx.ApplyToContext(ctx), nil +}