From 9678519e3ebdfb68fbac150449492001224b801a Mon Sep 17 00:00:00 2001 From: James Harris Date: Tue, 16 Jul 2024 11:59:10 +1000 Subject: [PATCH] Ignore disabled handlers that are unconfigured. --- aggregate.go | 15 +++++++++------ aggregateconfigurer.go | 11 ++++++++++- application.go | 10 +++++++--- application_test.go | 10 ++++++++++ applicationconfigurer.go | 26 +++++++++++++++++--------- entityconfigurer.go | 13 ++++++++++--- handlerconfigurer.go | 10 ++++++---- integration.go | 12 +++++++----- integrationconfigurer.go | 10 +++++++++- process.go | 13 +++++++------ processconfigurer.go | 11 ++++++++++- projection.go | 17 ++++++++--------- projectionconfigurer.go | 19 +++++++++++++++---- 13 files changed, 125 insertions(+), 52 deletions(-) diff --git a/aggregate.go b/aggregate.go index 2c4105d7..a84aebf0 100644 --- a/aggregate.go +++ b/aggregate.go @@ -4,7 +4,6 @@ import ( "context" "reflect" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" ) @@ -28,6 +27,14 @@ type RichAggregate interface { // It panics if the handler is configured incorrectly. Use Recover() to convert // configuration related panic values to errors. func FromAggregate(h dogma.AggregateMessageHandler) RichAggregate { + cfg, c := fromAggregate(h) + c.mustValidate() + return cfg +} + +func fromAggregate( + h dogma.AggregateMessageHandler, +) (*aggregate, *aggregateConfigurer) { cfg := &aggregate{ handler: handler{ entity: entity{ @@ -48,11 +55,7 @@ func FromAggregate(h dogma.AggregateMessageHandler) RichAggregate { h.Configure(c) - c.validate() - c.mustConsume(message.CommandRole) - c.mustProduce(message.EventRole) - - return cfg + return cfg, c } // aggregate is an implementation of RichAggregate. diff --git a/aggregateconfigurer.go b/aggregateconfigurer.go index bced2288..1f4379a2 100644 --- a/aggregateconfigurer.go +++ b/aggregateconfigurer.go @@ -1,6 +1,9 @@ package configkit -import "github.com/dogmatiq/dogma" +import ( + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" +) type aggregateConfigurer struct { handlerConfigurer @@ -11,3 +14,9 @@ func (c *aggregateConfigurer) Routes(routes ...dogma.AggregateRoute) { c.route(r) } } + +func (c *aggregateConfigurer) mustValidate() { + c.handlerConfigurer.mustValidate() + c.mustConsume(message.CommandRole) + c.mustProduce(message.EventRole) +} diff --git a/application.go b/application.go index 32981cc4..2d4d1ee6 100644 --- a/application.go +++ b/application.go @@ -36,6 +36,12 @@ type RichApplication interface { // It panics if the application is configured incorrectly. Use Recover() to // convert configuration related panic values to errors. func FromApplication(a dogma.Application) RichApplication { + cfg, c := fromApplication(a) + c.mustValidate() + return cfg +} + +func fromApplication(a dogma.Application) (*application, *applicationConfigurer) { cfg := &application{ entity: entity{ rt: reflect.TypeOf(a), @@ -52,9 +58,7 @@ func FromApplication(a dogma.Application) RichApplication { a.Configure(c) - c.validate() - - return cfg + return cfg, c } // IsApplicationEqual compares two applications for equality. diff --git a/application_test.go b/application_test.go index d289ae21..64dea1a8 100644 --- a/application_test.go +++ b/application_test.go @@ -68,6 +68,15 @@ var _ = Describe("func FromApplication()", func() { }, } + disabled := &fixtures.ProjectionMessageHandler{ + ConfigureFunc: func(c dogma.ProjectionConfigurer) { + // Verify that disabled handlers with no identity / route + // configuration are excluded from the application + // configuration. + c.Disable() + }, + } + app = &fixtures.Application{ ConfigureFunc: func(c dogma.ApplicationConfigurer) { c.Identity("", appKey) @@ -75,6 +84,7 @@ var _ = Describe("func FromApplication()", func() { c.RegisterProcess(process) c.RegisterIntegration(integration) c.RegisterProjection(projection) + c.RegisterProjection(disabled) }, } }) diff --git a/applicationconfigurer.go b/applicationconfigurer.go index 3839e19e..19fcac11 100644 --- a/applicationconfigurer.go +++ b/applicationconfigurer.go @@ -27,27 +27,35 @@ func (c *applicationConfigurer) Identity(name, key string) { } func (c *applicationConfigurer) RegisterAggregate(h dogma.AggregateMessageHandler, _ ...dogma.RegisterAggregateOption) { - cfg := FromAggregate(h) - c.register(cfg) + c.registerIfConfigured(fromAggregate(h)) } func (c *applicationConfigurer) RegisterProcess(h dogma.ProcessMessageHandler, _ ...dogma.RegisterProcessOption) { - cfg := FromProcess(h) - c.register(cfg) + c.registerIfConfigured(fromProcess(h)) } func (c *applicationConfigurer) RegisterIntegration(h dogma.IntegrationMessageHandler, _ ...dogma.RegisterIntegrationOption) { - cfg := FromIntegration(h) - c.register(cfg) + c.registerIfConfigured(fromIntegration(h)) } func (c *applicationConfigurer) RegisterProjection(h dogma.ProjectionMessageHandler, _ ...dogma.RegisterProjectionOption) { - cfg := FromProjection(h) - c.register(cfg) + c.registerIfConfigured(fromProjection(h)) } // register adds a handler configuration to the application. -func (c *applicationConfigurer) register(h RichHandler) { +func (c *applicationConfigurer) registerIfConfigured( + h RichHandler, + hc interface { + isConfigured() bool + mustValidate() + }, +) { + if h.IsDisabled() && !hc.isConfigured() { + return + } + + hc.mustValidate() + c.guardAgainstConflictingIdentities(h) c.guardAgainstConflictingRoles(h) c.guardAgainstConflictingRoutes(h) diff --git a/entityconfigurer.go b/entityconfigurer.go index d06c330c..e997b416 100644 --- a/entityconfigurer.go +++ b/entityconfigurer.go @@ -12,11 +12,14 @@ import "github.com/dogmatiq/configkit/internal/validation" // - [dogma.ProjectionConfigurer] type entityConfigurer struct { // entity is the target entity to populate with the configuration values. - entity *entity + entity *entity + configured bool } // Identity sets the entity's identity. func (c *entityConfigurer) Identity(n string, k string) { + c.configured = true + if !c.entity.ident.IsZero() { validation.Panicf( "%s is configured with multiple identities (%s and %s/%s), Identity() must be called exactly once within Configure()", @@ -39,8 +42,12 @@ func (c *entityConfigurer) Identity(n string, k string) { } } -// validate panics if the configuration is invalid. -func (c *entityConfigurer) validate() { +func (c *entityConfigurer) isConfigured() bool { + return c.configured +} + +// mustValidate panics if the configuration is invalid. +func (c *entityConfigurer) mustValidate() { if c.entity.ident.IsZero() { validation.Panicf( "%s is configured without an identity, Identity() must be called exactly once within Configure()", diff --git a/handlerconfigurer.go b/handlerconfigurer.go index 044b68d5..0ca5cb0b 100644 --- a/handlerconfigurer.go +++ b/handlerconfigurer.go @@ -22,7 +22,13 @@ type handlerConfigurer struct { handler *handler } +func (c *handlerConfigurer) Disable(...dogma.DisableOption) { + c.handler.isDisabled = true +} + func (c *handlerConfigurer) route(r dogma.Route) { + c.configured = true + switch r := r.(type) { case dogma.HandlesCommandRoute: c.consumes(r.Type, message.CommandRole, "HandlesCommand") @@ -156,7 +162,3 @@ func (c *handlerConfigurer) mustProduce(r message.Role) { route, ) } - -func (c *handlerConfigurer) Disable(...dogma.DisableOption) { - c.handler.isDisabled = true -} diff --git a/integration.go b/integration.go index 7fe85858..e184de3b 100644 --- a/integration.go +++ b/integration.go @@ -4,7 +4,6 @@ import ( "context" "reflect" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" ) @@ -28,6 +27,12 @@ type RichIntegration interface { // It panics if the handler is configured incorrectly. Use Recover() to convert // configuration related panic values to errors. func FromIntegration(h dogma.IntegrationMessageHandler) RichIntegration { + cfg, c := fromIntegration(h) + c.mustValidate() + return cfg +} + +func fromIntegration(h dogma.IntegrationMessageHandler) (*integration, *integrationConfigurer) { cfg := &integration{ handler: handler{ entity: entity{ @@ -48,10 +53,7 @@ func FromIntegration(h dogma.IntegrationMessageHandler) RichIntegration { h.Configure(c) - c.validate() - c.mustConsume(message.CommandRole) - - return cfg + return cfg, c } // integration is an implementation of RichIntegration. diff --git a/integrationconfigurer.go b/integrationconfigurer.go index a5d66f47..42e02532 100644 --- a/integrationconfigurer.go +++ b/integrationconfigurer.go @@ -1,6 +1,9 @@ package configkit -import "github.com/dogmatiq/dogma" +import ( + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" +) type integrationConfigurer struct { handlerConfigurer @@ -11,3 +14,8 @@ func (c *integrationConfigurer) Routes(routes ...dogma.IntegrationRoute) { c.route(r) } } + +func (c *integrationConfigurer) mustValidate() { + c.handlerConfigurer.mustValidate() + c.mustConsume(message.CommandRole) +} diff --git a/process.go b/process.go index fd33f1f2..17d0a12e 100644 --- a/process.go +++ b/process.go @@ -4,7 +4,6 @@ import ( "context" "reflect" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" ) @@ -28,6 +27,12 @@ type RichProcess interface { // It panics if the handler is configured incorrectly. Use Recover() to convert // configuration related panic values to errors. func FromProcess(h dogma.ProcessMessageHandler) RichProcess { + cfg, c := fromProcess(h) + c.mustValidate() + return cfg +} + +func fromProcess(h dogma.ProcessMessageHandler) (*process, *processConfigurer) { cfg := &process{ handler: handler{ entity: entity{ @@ -48,11 +53,7 @@ func FromProcess(h dogma.ProcessMessageHandler) RichProcess { h.Configure(c) - c.validate() - c.mustConsume(message.EventRole) - c.mustProduce(message.CommandRole) - - return cfg + return cfg, c } // process is an implementation of RichProcess. diff --git a/processconfigurer.go b/processconfigurer.go index 40302e27..f4c41264 100644 --- a/processconfigurer.go +++ b/processconfigurer.go @@ -1,6 +1,9 @@ package configkit -import "github.com/dogmatiq/dogma" +import ( + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" +) type processConfigurer struct { handlerConfigurer @@ -11,3 +14,9 @@ func (c *processConfigurer) Routes(routes ...dogma.ProcessRoute) { c.route(r) } } + +func (c *processConfigurer) mustValidate() { + c.handlerConfigurer.mustValidate() + c.mustConsume(message.EventRole) + c.mustProduce(message.CommandRole) +} diff --git a/projection.go b/projection.go index 87c4d8f7..9acc3a54 100644 --- a/projection.go +++ b/projection.go @@ -4,7 +4,6 @@ import ( "context" "reflect" - "github.com/dogmatiq/configkit/message" "github.com/dogmatiq/dogma" ) @@ -31,6 +30,12 @@ type RichProjection interface { // It panics if the handler is configured incorrectly. Use Recover() to convert // configuration related panic values to errors. func FromProjection(h dogma.ProjectionMessageHandler) RichProjection { + cfg, c := fromProjection(h) + c.mustValidate() + return cfg +} + +func fromProjection(h dogma.ProjectionMessageHandler) (*projection, *projectionConfigurer) { cfg := &projection{ handler: handler{ entity: entity{ @@ -48,18 +53,12 @@ func FromProjection(h dogma.ProjectionMessageHandler) RichProjection { }, handler: &cfg.handler, }, + projection: cfg, } h.Configure(c) - c.validate() - c.mustConsume(message.EventRole) - - if c.deliveryPolicy != nil { - cfg.deliveryPolicy = c.deliveryPolicy - } - - return cfg + return cfg, c } // projection is an implementation of RichProjection. diff --git a/projectionconfigurer.go b/projectionconfigurer.go index a943db28..fd7ed81a 100644 --- a/projectionconfigurer.go +++ b/projectionconfigurer.go @@ -1,10 +1,14 @@ package configkit -import "github.com/dogmatiq/dogma" +import ( + "github.com/dogmatiq/configkit/internal/validation" + "github.com/dogmatiq/configkit/message" + "github.com/dogmatiq/dogma" +) type projectionConfigurer struct { handlerConfigurer - deliveryPolicy dogma.ProjectionDeliveryPolicy + projection *projection } func (c *projectionConfigurer) Routes(routes ...dogma.ProjectionRoute) { @@ -14,9 +18,16 @@ func (c *projectionConfigurer) Routes(routes ...dogma.ProjectionRoute) { } func (c *projectionConfigurer) DeliveryPolicy(p dogma.ProjectionDeliveryPolicy) { + c.configured = true + if p == nil { - panic("delivery policy must not be nil") + validation.Panicf("delivery policy must not be nil") } - c.deliveryPolicy = p + c.projection.deliveryPolicy = p +} + +func (c *projectionConfigurer) mustValidate() { + c.handlerConfigurer.mustValidate() + c.mustConsume(message.EventRole) }