diff options
| -rw-r--r-- | app/controllers/sparkles/controller_test.go | 5 | ||||
| -rw-r--r-- | app/db/in_memory_repository.go | 14 | ||||
| -rw-r--r-- | app/db/in_memory_repository_test.go | 25 | ||||
| -rw-r--r-- | app/db/publishing_repository.go | 36 | ||||
| -rw-r--r-- | app/db/publishing_repository_test.go | 35 | ||||
| -rw-r--r-- | app/init.go | 7 |
6 files changed, 87 insertions, 35 deletions
diff --git a/app/controllers/sparkles/controller_test.go b/app/controllers/sparkles/controller_test.go index 5c37a11..71e1d96 100644 --- a/app/controllers/sparkles/controller_test.go +++ b/app/controllers/sparkles/controller_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xlgmokha/x/pkg/event" "github.com/xlgmokha/x/pkg/serde" "github.com/xlgmokha/x/pkg/test" "github.com/xlgmokha/x/pkg/x" @@ -37,7 +36,7 @@ func (f *FailingResponseWriter) Write([]byte) (int, error) { func TestSparkles(t *testing.T) { t.Run("GET /sparkles", func(t *testing.T) { sparkle := x.New[*domain.Sparkle](domain.WithText("@tanuki for helping me")) - store := db.NewRepository[*domain.Sparkle](event.New[*domain.Sparkle]()) + store := db.NewRepository[*domain.Sparkle]() store.Save(t.Context(), sparkle) mux := http.NewServeMux() @@ -66,7 +65,7 @@ func TestSparkles(t *testing.T) { t.Run("POST /sparkles", func(t *testing.T) { t.Run("when a user is authenticated", func(t *testing.T) { currentUser := x.New[*domain.User](domain.WithID[*domain.User](domain.ID("1"))) - repository := db.NewRepository[*domain.Sparkle](event.New[*domain.Sparkle]()) + repository := db.NewRepository[*domain.Sparkle]() t.Run("when the user is authorized", func(t *testing.T) { mux := http.NewServeMux() diff --git a/app/db/in_memory_repository.go b/app/db/in_memory_repository.go index 1177662..2aa1fed 100644 --- a/app/db/in_memory_repository.go +++ b/app/db/in_memory_repository.go @@ -5,23 +5,20 @@ import ( "sort" "sync" - "github.com/xlgmokha/x/pkg/event" "github.com/xlgmokha/x/pkg/x" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls" ) type inMemoryRepository[T domain.Entity] struct { - aggregator *event.TypedAggregator[T] - items []T - mu sync.RWMutex + items []T + mu sync.RWMutex } -func NewRepository[T domain.Entity](aggregator *event.TypedAggregator[T]) domain.Repository[T] { +func NewRepository[T domain.Entity]() domain.Repository[T] { return &inMemoryRepository[T]{ - aggregator: aggregator, - items: []T{}, - mu: sync.RWMutex{}, + items: []T{}, + mu: sync.RWMutex{}, } } @@ -53,6 +50,5 @@ func (r *inMemoryRepository[T]) Save(ctx context.Context, item T) error { sort.Slice(r.items, func(i, j int) bool { return r.items[i].GetID() > r.items[j].GetID() }) - r.aggregator.Publish("after.create", item) return nil } diff --git a/app/db/in_memory_repository_test.go b/app/db/in_memory_repository_test.go index 5bb220e..b89f16a 100644 --- a/app/db/in_memory_repository_test.go +++ b/app/db/in_memory_repository_test.go @@ -7,14 +7,12 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xlgmokha/x/pkg/event" "github.com/xlgmokha/x/pkg/x" "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" ) func TestInMemoryRepository(t *testing.T) { - aggregator := event.New[*domain.Sparkle]() - storage := NewRepository[*domain.Sparkle](aggregator) + storage := NewRepository[*domain.Sparkle]() t.Run("Save", func(t *testing.T) { t.Run("an invalid Sparkle", func(t *testing.T) { @@ -33,25 +31,8 @@ func TestInMemoryRepository(t *testing.T) { assert.Equal(t, "because", sparkles[0].Reason) }) - t.Run("publishes an event", func(t *testing.T) { - called := false - var payload *domain.Sparkle - - aggregator.SubscribeTo("after.create", func(item *domain.Sparkle) { - called = true - payload = item - }) - - sparkle := &domain.Sparkle{Sparklee: "@tanuki", Reason: "because"} - require.NoError(t, storage.Save(t.Context(), sparkle)) - - require.True(t, called) - require.NotNil(t, payload) - assert.Equal(t, sparkle, payload) - }) - t.Run("prevents race conditions", func(t *testing.T) { - repository := NewRepository[*domain.Sparkle](aggregator) + repository := NewRepository[*domain.Sparkle]() ctx := context.Background() numGoroutines := 100 @@ -112,7 +93,7 @@ func TestInMemoryRepository(t *testing.T) { }) t.Run("All", func(t *testing.T) { - repository := NewRepository[*domain.Sparkle](aggregator) + repository := NewRepository[*domain.Sparkle]() require.NoError(t, repository.Save(t.Context(), &domain.Sparkle{ Sparklee: "@tanuki", Reason: "because", diff --git a/app/db/publishing_repository.go b/app/db/publishing_repository.go new file mode 100644 index 0000000..6be8cf8 --- /dev/null +++ b/app/db/publishing_repository.go @@ -0,0 +1,36 @@ +package db + +import ( + "context" + + "github.com/xlgmokha/x/pkg/event" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" +) + +type PublishingRepository[T domain.Entity] struct { + aggregator *event.TypedAggregator[T] + repository domain.Repository[T] +} + +func NewPublishingRepository[T domain.Entity](aggregator *event.TypedAggregator[T], repository domain.Repository[T]) domain.Repository[T] { + return &PublishingRepository[T]{ + aggregator: aggregator, + repository: repository, + } +} + +func (r *PublishingRepository[T]) All(ctx context.Context) []T { + return r.repository.All(ctx) +} + +func (r *PublishingRepository[T]) Find(ctx context.Context, id domain.ID) T { + return r.repository.Find(ctx, id) +} + +func (r *PublishingRepository[T]) Save(ctx context.Context, item T) error { + err := r.repository.Save(ctx, item) + if err == nil { + r.aggregator.Publish("after.create", item) + } + return err +} diff --git a/app/db/publishing_repository_test.go b/app/db/publishing_repository_test.go new file mode 100644 index 0000000..7bbc999 --- /dev/null +++ b/app/db/publishing_repository_test.go @@ -0,0 +1,35 @@ +package db + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/xlgmokha/x/pkg/event" + "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/domain" +) + +func TestPublishingRepository(t *testing.T) { + aggregator := event.New[*domain.Sparkle]() + repository := NewRepository[*domain.Sparkle]() + storage := NewPublishingRepository[*domain.Sparkle](aggregator, repository) + + t.Run("Save", func(t *testing.T) { + t.Run("publishes an event", func(t *testing.T) { + called := false + var payload *domain.Sparkle + + aggregator.SubscribeTo("after.create", func(item *domain.Sparkle) { + called = true + payload = item + }) + + sparkle := &domain.Sparkle{Sparklee: "@tanuki", Reason: "because"} + require.NoError(t, storage.Save(t.Context(), sparkle)) + + require.True(t, called) + require.NotNil(t, payload) + assert.Equal(t, sparkle, payload) + }) + }) +} diff --git a/app/init.go b/app/init.go index ea67e48..f0d40b2 100644 --- a/app/init.go +++ b/app/init.go @@ -47,7 +47,12 @@ func init() { )) }) ioc.RegisterSingleton[domain.Repository[*domain.Sparkle]](c, func() domain.Repository[*domain.Sparkle] { - return db.NewRepository[*domain.Sparkle](ioc.MustResolve[*event.TypedAggregator[*domain.Sparkle]](c)) + aggregator := ioc.MustResolve[*event.TypedAggregator[*domain.Sparkle]](c) + repo := db.NewPublishingRepository[*domain.Sparkle](aggregator, db.NewRepository[*domain.Sparkle]()) + return db.NewSecureRepository[*domain.Sparkle]( + ioc.MustResolve[*authzed.Client](c), + repo, + ) }) ioc.RegisterSingleton[*http.ServeMux](c, func() *http.ServeMux { return http.NewServeMux() |
