Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Disable() on handler configurers. #289

Merged
merged 8 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions aggregate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"reflect"

"github.com/dogmatiq/configkit/message"
"github.com/dogmatiq/dogma"
)

Expand All @@ -28,9 +27,17 @@ 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{
entity: entity{
rt: reflect.TypeOf(h),
handler: handler{
entity: entity{
rt: reflect.TypeOf(h),
},
},
impl: h,
}
Expand All @@ -40,21 +47,18 @@ func FromAggregate(h dogma.AggregateMessageHandler) RichAggregate {
entityConfigurer: entityConfigurer{
entity: &cfg.entity,
},
handler: &cfg.handler,
},
}

h.Configure(c)

c.validate()
c.mustConsume(message.CommandRole)
c.mustProduce(message.EventRole)

return cfg
return cfg, c
}

// aggregate is an implementation of RichAggregate.
type aggregate struct {
entity
handler

impl dogma.AggregateMessageHandler
}
Expand Down
24 changes: 23 additions & 1 deletion aggregate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var _ = Describe("func FromAggregate()", func() {
When("the configuration is valid", func() {
var cfg RichAggregate

BeforeEach(func() {
JustBeforeEach(func() {
cfg = FromAggregate(handler)
})

Expand Down Expand Up @@ -124,11 +124,33 @@ var _ = Describe("func FromAggregate()", func() {
})
})

Describe("func IsDisabled()", func() {
It("returns false", func() {
Expect(cfg.IsDisabled()).To(BeFalse())
})
})

Describe("func Handler()", func() {
It("returns the underlying handler", func() {
Expect(cfg.Handler()).To(BeIdenticalTo(handler))
})
})

When("the handler is disabled", func() {
BeforeEach(func() {
configure := handler.ConfigureFunc
handler.ConfigureFunc = func(c dogma.AggregateConfigurer) {
configure(c)
c.Disable()
}
})

Describe("func IsDisabled()", func() {
It("returns true", func() {
Expect(cfg.IsDisabled()).To(BeTrue())
})
})
})
})

DescribeTable(
Expand Down
11 changes: 10 additions & 1 deletion aggregateconfigurer.go
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
}
1 change: 1 addition & 0 deletions api/blackbox_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ var _ = Context("end-to-end tests", func() {
dogma.HandlesEvent[MessageE](),
dogma.HandlesEvent[MessageJ](),
)
c.Disable()
},
})
},
Expand Down
5 changes: 4 additions & 1 deletion api/marshaling.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ func unmarshalApplication(in *configspec.Application) (configkit.Application, er

// marshalHandler marshals a handler config to its protobuf representation.
func marshalHandler(in configkit.Handler) (*configspec.Handler, error) {
out := &configspec.Handler{}
out := &configspec.Handler{
IsDisabled: in.IsDisabled(),
}

var err error
out.Identity, err = marshalIdentity(in.Identity())
Expand Down Expand Up @@ -122,6 +124,7 @@ func unmarshalHandler(in *configspec.Handler) (configkit.Handler, error) {
Produced: message.NameRoles{},
Consumed: message.NameRoles{},
},
IsDisabledValue: in.GetIsDisabled(),
}

var err error
Expand Down
10 changes: 7 additions & 3 deletions application.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,23 @@ 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("<app>", appKey)
c.RegisterAggregate(aggregate)
c.RegisterProcess(process)
c.RegisterIntegration(integration)
c.RegisterProjection(projection)
c.RegisterProjection(disabled)
},
}
})
Expand Down
26 changes: 17 additions & 9 deletions applicationconfigurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 10 additions & 3 deletions entityconfigurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()",
Expand All @@ -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()",
Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ module github.com/dogmatiq/configkit
go 1.21

require (
github.com/dogmatiq/dogma v0.13.0
github.com/dogmatiq/dogma v0.13.1
github.com/dogmatiq/iago v0.4.0
github.com/dogmatiq/interopspec v0.5.3
github.com/dogmatiq/interopspec v0.5.4
github.com/emicklei/dot v1.6.2
github.com/google/uuid v1.6.0
github.com/onsi/ginkgo v1.16.5
Expand All @@ -25,7 +25,7 @@ require (
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
google.golang.org/protobuf v1.34.1 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 6 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dogmatiq/dogma v0.13.0 h1:MKk9MHErGKD53Y+43I4fcoPZMQjX0N2DUZEc4rLp+Hk=
github.com/dogmatiq/dogma v0.13.0/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ=
github.com/dogmatiq/dogma v0.13.1 h1:b1nsqYNmICEG2egvZ43K8w04Jma+MWYL6yF5Ad12y9o=
github.com/dogmatiq/dogma v0.13.1/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ=
github.com/dogmatiq/iago v0.4.0 h1:57nZqVT34IZxtCZEW/RFif7DNUEjMXgevfr/Mmd0N8I=
github.com/dogmatiq/iago v0.4.0/go.mod h1:fishMWBtzYcjgis6d873VTv9kFm/wHYLOzOyO9ECBDc=
github.com/dogmatiq/interopspec v0.5.3 h1:AES184nLWcek8/vafykZJLmm59NlRiYiC40Tll8hEeg=
github.com/dogmatiq/interopspec v0.5.3/go.mod h1:3RdRwvDzgqLwemSieP+YAvKp3CMP0rYwZFl5tpmn6DI=
github.com/dogmatiq/interopspec v0.5.4 h1:klyGPy16zUKJartJIJOBZ4JrQ4LxFiY3wgzFTTiNlDk=
github.com/dogmatiq/interopspec v0.5.4/go.mod h1:JL0QFXBTRGH+RgQqExhEUdhtv5Vn802w43RxKQbk8Co=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
Expand Down Expand Up @@ -110,8 +110,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
16 changes: 16 additions & 0 deletions handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ type Handler interface {

// HandlerType returns the type of handler.
HandlerType() HandlerType

// IsDisabled returns true if the handler is disabled.
IsDisabled() bool
}

// RichHandler is a specialization of the Handler interface that exposes
Expand All @@ -15,6 +18,9 @@ type RichHandler interface {

// HandlerType returns the type of handler.
HandlerType() HandlerType

// IsDisabled returns true if the handler is disabled.
IsDisabled() bool
}

// IsHandlerEqual compares two handlers for equality.
Expand All @@ -36,5 +42,15 @@ func IsHandlerEqual(a, b Handler) bool {
return a.Identity() == b.Identity() &&
a.TypeName() == b.TypeName() &&
a.HandlerType() == b.HandlerType() &&
a.IsDisabled() == b.IsDisabled() &&
a.MessageNames().IsEqual(b.MessageNames())
}

type handler struct {
entity
isDisabled bool
}

func (h *handler) IsDisabled() bool {
return h.isDisabled
}
14 changes: 14 additions & 0 deletions handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ var _ = Describe("func IsHandlerEqual()", func() {
},
}),
),
Entry(
"disabled state differs",
FromAggregate(&fixtures.AggregateMessageHandler{
ConfigureFunc: func(c dogma.AggregateConfigurer) {
c.Identity("<name>", aggregateKey)
c.Routes(
dogma.HandlesCommand[fixtures.MessageA](),
dogma.HandlesCommand[fixtures.MessageB](),
dogma.RecordsEvent[fixtures.MessageE](),
)
c.Disable()
},
}),
),
Entry(
"messages differ",
FromAggregate(&fixtures.AggregateMessageHandler{
Expand Down
7 changes: 7 additions & 0 deletions handlerconfigurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ import (
// - [dogma.ProjectionConfigurer]
type handlerConfigurer struct {
entityConfigurer
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")
Expand Down
Loading