diff --git a/pkg/clients/middleware/caching_middleware_test.go b/pkg/clients/middleware/caching_middleware_test.go index 047000373bc34a3db0f6bfc1fa3403b2929c1975..b842e1183a5425a4b3aec8d59b0720e5627767ed 100644 --- a/pkg/clients/middleware/caching_middleware_test.go +++ b/pkg/clients/middleware/caching_middleware_test.go @@ -24,7 +24,7 @@ func TestClientsCache(t *testing.T) { ttl = 20 * time.Millisecond ) - ErrNotFound := errors.NotFound(errors.New("not found")) + errNotFound := errors.NotFound(errors.New("not found")) ctx := context.Background() @@ -200,9 +200,9 @@ func TestClientsCache(t *testing.T) { err = svc.Delete(ctx, spaceID, cltID) require.NoError(t, err) - cs.On("Get", mock.Anything, spaceID, cltID).Return(nil, ErrNotFound).Once() - cs.On("GetBy", mock.Anything, spaceID, &clients.GetByParams{OAuthClientID: clientID}).Return(nil, ErrNotFound).Once() - cs.On("List", mock.Anything, spaceID).Return(nil, ErrNotFound).Once() + cs.On("Get", mock.Anything, spaceID, cltID).Return(nil, errNotFound).Once() + cs.On("GetBy", mock.Anything, spaceID, &clients.GetByParams{OAuthClientID: clientID}).Return(nil, errNotFound).Once() + cs.On("List", mock.Anything, spaceID).Return(nil, errNotFound).Once() _, err = svc.Get(ctx, spaceID, cltID) require.Error(t, err) @@ -238,7 +238,7 @@ func TestClientsCache(t *testing.T) { err = svc.Delete(ctx, spaceID, cltID) require.NoError(t, err) - cs.On("List", mock.Anything, spaceID).Return(nil, ErrNotFound).Once() + cs.On("List", mock.Anything, spaceID).Return(nil, errNotFound).Once() _, err = svc.List(ctx, spaceID) require.Error(t, err) diff --git a/pkg/collaborators/middleware/caching_middleware_test.go b/pkg/collaborators/middleware/caching_middleware_test.go index 090e03890daa1b3e71b04308d6136b3d46336614..da1d6e842f542e1c664f988f38c4797ea31c38f1 100644 --- a/pkg/collaborators/middleware/caching_middleware_test.go +++ b/pkg/collaborators/middleware/caching_middleware_test.go @@ -24,7 +24,7 @@ func TestCollaboratorsCache(t *testing.T) { ttl = 20 * time.Millisecond ) - ErrNotFound := errors.NotFound(errors.New("not found")) + errNotFound := errors.NotFound(errors.New("not found")) ctx := context.Background() @@ -108,9 +108,9 @@ func TestCollaboratorsCache(t *testing.T) { cs.On("Remove", mock.Anything, spaceID, userID).Return(nil).Once() - cs.On("Get", mock.Anything, spaceID, userID).Return("", ErrNotFound).Once() - cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, ErrNotFound).Once() - cs.On("ListSpaces", mock.Anything, userID).Return(nil, ErrNotFound).Once() + cs.On("Get", mock.Anything, spaceID, userID).Return("", errNotFound).Once() + cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, errNotFound).Once() + cs.On("ListSpaces", mock.Anything, userID).Return(nil, errNotFound).Once() err = svc.Remove(ctx, spaceID, userID) @@ -162,9 +162,9 @@ func TestCollaboratorsCache(t *testing.T) { cs.On("Remove", mock.Anything, spaceID, userID).Return(nil).Once() - cs.On("Get", mock.Anything, spaceID, userID).Return("", ErrNotFound).Once() - cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, ErrNotFound).Once() - cs.On("ListSpaces", mock.Anything, userID).Return(nil, ErrNotFound).Once() + cs.On("Get", mock.Anything, spaceID, userID).Return("", errNotFound).Once() + cs.On("ListCollaborators", mock.Anything, spaceID).Return(nil, errNotFound).Once() + cs.On("ListSpaces", mock.Anything, userID).Return(nil, errNotFound).Once() err = svc.Remove(ctx, spaceID, userID) diff --git a/pkg/collections/middleware/caching_middleware_test.go b/pkg/collections/middleware/caching_middleware_test.go index 3a5f88e647b83f99662a4ee8e1701230954afe30..967a75bce7fa7d933918edc9f05600f75a6c0ff5 100644 --- a/pkg/collections/middleware/caching_middleware_test.go +++ b/pkg/collections/middleware/caching_middleware_test.go @@ -28,7 +28,7 @@ func TestCollections_Cache(t *testing.T) { ttl = 20 * time.Millisecond ) - ErrNotFound := errors.NotFound(errors.New("not found")) + errNotFound := errors.NotFound(errors.New("not found")) ctx := context.Background() @@ -354,7 +354,7 @@ func TestCollections_Cache(t *testing.T) { require.NoError(t, err) //env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once() - col.On("Get", mock.Anything, spaceID, envID, colID).Return(nil, ErrNotFound).Once() + col.On("Get", mock.Anything, spaceID, envID, colID).Return(nil, errNotFound).Once() col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{}, nil).Once() _, err = svc.Get(ctx, spaceID, envID, colID) @@ -365,7 +365,7 @@ func TestCollections_Cache(t *testing.T) { require.NoError(t, err) assert.Len(t, vl4, 0, "Ожидает что Ñлементы были удалены из кÑша.") - col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(nil, ErrNotFound).Once() + col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(nil, errNotFound).Once() _, err = svc.Get(ctx, spaceID, envAlias, colID) require.Error(t, err) diff --git a/pkg/environments/middleware/caching_middleware.go b/pkg/environments/middleware/caching_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..18b594bc3a193d962d63ef3d5eae6217408d61bd --- /dev/null +++ b/pkg/environments/middleware/caching_middleware.go @@ -0,0 +1,167 @@ +package service + +import ( + "context" + "strings" + + "git.perx.ru/perxis/perxis-go/pkg/cache" + service "git.perx.ru/perxis/perxis-go/pkg/environments" +) + +func makeKey(ss ...string) string { + return strings.Join(ss, "-") +} + +func CachingMiddleware(cache *cache.Cache) Middleware { + return func(next service.Environments) service.Environments { + return &cachingMiddleware{ + cache: cache, + next: next, + } + } +} + +type cachingMiddleware struct { + cache *cache.Cache + next service.Environments +} + +func (m cachingMiddleware) Create(ctx context.Context, env *service.Environment) (environment *service.Environment, err error) { + + environment, err = m.next.Create(ctx, env) + if err == nil { + m.cache.Remove(environment.SpaceID) + } + return environment, err +} + +func (m cachingMiddleware) Get(ctx context.Context, spaceId string, envId string) (environment *service.Environment, err error) { + + value, e := m.cache.Get(makeKey(spaceId, envId)) + if e == nil { + return value.(*service.Environment), err + } + environment, err = m.next.Get(ctx, spaceId, envId) + if err == nil { + m.cache.Set(makeKey(spaceId, environment.ID), environment) + for _, a := range environment.Aliases { + m.cache.Set(makeKey(spaceId, a), environment) + } + } + return environment, err +} + +func (m cachingMiddleware) List(ctx context.Context, spaceId string) (environments []*service.Environment, err error) { + + value, e := m.cache.Get(spaceId) + if e == nil { + return value.([]*service.Environment), err + } + environments, err = m.next.List(ctx, spaceId) + if err == nil { + m.cache.Set(spaceId, environments) + } + return environments, err +} + +func (m cachingMiddleware) Update(ctx context.Context, env *service.Environment) (err error) { + + err = m.next.Update(ctx, env) + if err == nil { + value, e := m.cache.Get(makeKey(env.SpaceID, env.ID)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + m.cache.Remove(env.SpaceID) + } + return err +} + +func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) { + + err = m.next.Delete(ctx, spaceId, envId) + if err == nil { + value, e := m.cache.Get(makeKey(spaceId, envId)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + m.cache.Remove(spaceId) + } + return err +} + +func (m cachingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + + err = m.next.SetAlias(ctx, spaceId, envId, alias) + if err == nil { + value, e := m.cache.Get(makeKey(spaceId, alias)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + + value, e = m.cache.Get(makeKey(spaceId, envId)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + m.cache.Remove(spaceId) + } + return err +} + +func (m cachingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + + err = m.next.RemoveAlias(ctx, spaceId, envId, alias) + if err == nil { + m.cache.Remove(spaceId) + value, e := m.cache.Get(makeKey(spaceId, alias)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + + value, e = m.cache.Get(makeKey(spaceId, envId)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + } + return err +} + +func (m cachingMiddleware) Migrate(ctx context.Context, spaceId, envId string, options ...*service.MigrateOptions) (err error) { + err = m.next.Migrate(ctx, spaceId, envId, options...) + + // значение из кÑша удалить вне завиÑимоÑти от Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¸, поÑкольку ÑоÑтоÑние Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³Ð»Ð¾ изменитьÑÑ + value, e := m.cache.Get(makeKey(spaceId, envId)) + if e == nil { + env := value.(*service.Environment) + m.cache.Remove(makeKey(env.SpaceID, env.ID)) + for _, a := range env.Aliases { + m.cache.Remove(makeKey(env.SpaceID, a)) + } + } + m.cache.Remove(spaceId) + return err +} diff --git a/pkg/environments/middleware/caching_middleware_test.go b/pkg/environments/middleware/caching_middleware_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f5bfa7a9b5d207ed0c00d60cd126950a6da5aba0 --- /dev/null +++ b/pkg/environments/middleware/caching_middleware_test.go @@ -0,0 +1,387 @@ +package service + +import ( + "context" + "testing" + "time" + + "git.perx.ru/perxis/perxis-go/pkg/cache" + "git.perx.ru/perxis/perxis-go/pkg/environments" + mocksenvironments "git.perx.ru/perxis/perxis-go/pkg/environments/mocks" + "git.perx.ru/perxis/perxis-go/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestEnvironmentsCache(t *testing.T) { + + const ( + envID = "envID" + spaceID = "spaceID" + envAlias = "envAlias" + size = 5 + ttl = 20 * time.Millisecond + ) + + errNotFound := errors.NotFound(errors.New("not found")) + + ctx := context.Background() + + t.Run("Get from cache", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once() + + v1, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша при повторном запроÑе по ID.") + + v3, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Same(t, v3, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кеша, при запроÑе того же объекта по alias окружениÑ.") + + envs.AssertExpectations(t) + }) + + t.Run("Get from cache(by Alias)", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envAlias).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once() + + v1, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по alias.") + + v3, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v3, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кеша, при запроÑе того же объекта по ID окружениÑ.") + + envs.AssertExpectations(t) + }) + + t.Run("List from cache", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment"}}, nil).Once() + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + envs.AssertExpectations(t) + }) + + t.Run("Invalidate cache", func(t *testing.T) { + t.Run("After SetAlias", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}}, nil).Once() + envs.On("SetAlias", mock.Anything, spaceID, envID, envAlias).Return(nil).Once() + + v1, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша.") + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + err = svc.SetAlias(ctx, spaceID, envID, envAlias) + require.NoError(t, err) + + envs.On("Get", mock.Anything, spaceID, envAlias).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}}, nil).Once() + + v4, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Contains(t, v4.Aliases, envAlias, "Ожидает что Ñлемент будет запрошен из ÑервиÑа по Alias.") + + v5, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v4, v5, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по ID.") + + vl3, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кÑша и запрошены из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After RemoveAlias", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2", envAlias}}}, nil).Once() + envs.On("RemoveAlias", mock.Anything, spaceID, envID, envAlias).Return(nil).Once() + + v1, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по ID.") + + v3, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Same(t, v2, v3, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по Alias.") + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + err = svc.RemoveAlias(ctx, spaceID, envID, envAlias) + require.NoError(t, err) + + envs.On("Get", mock.Anything, spaceID, envAlias).Return(nil, errNotFound).Once() + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{"envID2"}}}, nil).Once() + + _, err = svc.Get(ctx, spaceID, envAlias) + require.Error(t, err) + assert.EqualError(t, err, "not found", "Ожидает что Ñлемент был удален из кеша и ÑÐµÑ€Ð²Ð¸Ñ Ð²ÐµÑ€Ð½ÑƒÐ» ошибку на Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð¾ неÑущеÑтвующему Alias.") + + v4, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.NotSame(t, v3, v4, "Ожидает что Ñлемент был удален из кеша и получен из ÑервиÑа по ID.") + + vl3, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кÑша и запрошены из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After Update", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}}, nil).Once() + envs.On("Update", mock.Anything, mock.Anything).Return(nil).Once() + + v1, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша.") + + v3, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Same(t, v2, v3, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по Alias.") + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + err = svc.Update(ctx, &environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}) + require.NoError(t, err) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}}, nil).Once() + + _, err = svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v4, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.NotSame(t, v2, v4, "Ожидает что Ñлемент был удален из кÑша и будет запрошен заново из ÑервиÑа.") + + v5, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Same(t, v4, v5, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по Alias поÑле Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð±ÑŠÐµÐºÑ‚Ð° и Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾ ID.") + + vl3, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кÑша и запрошены из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After Update(List)", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}}, nil).Once() + envs.On("Update", mock.Anything, mock.Anything).Return(nil).Once() + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + err = svc.Update(ctx, &environments.Environment{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}) + require.NoError(t, err) + + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "EnvironmentUPD", Aliases: []string{envAlias}}}, nil).Once() + + vl3, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.NotSame(t, vl2[0], vl3[0], "Ожидает что объекты будут удалены из кÑша и запрошены из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After Delete", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}, nil).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment", Aliases: []string{envAlias}}}, nil).Once() + envs.On("Delete", mock.Anything, spaceID, envID).Return(nil).Once() + + v1, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша.") + + v3, err := svc.Get(ctx, spaceID, envAlias) + require.NoError(t, err) + assert.Same(t, v2, v3, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша по Alias.") + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + err = svc.Delete(ctx, spaceID, envID) + require.NoError(t, err) + + envs.On("Get", mock.Anything, spaceID, envID).Return(nil, errNotFound).Once() + envs.On("Get", mock.Anything, spaceID, envAlias).Return(nil, errNotFound).Once() + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{}, nil).Once() + + _, err = svc.Get(ctx, spaceID, envID) + require.Error(t, err) + assert.EqualError(t, err, "not found", "Ожидает что Ñлемент был удален из кÑша по ID и получена ошибка от ÑервиÑа.") + + _, err = svc.Get(ctx, spaceID, envAlias) + require.Error(t, err) + assert.EqualError(t, err, "not found", "Ожидает что Ñлемент был удален из кÑша по Alias и получена ошибка от ÑервиÑа.") + + vl3, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Len(t, vl3, 0, "Ожидает что объекты будут удалены из кÑша и запрошены из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After Create", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment"}}, nil).Once() + + vl1, err := svc.List(ctx, spaceID) + require.NoError(t, err) + + vl2, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Same(t, vl1[0], vl2[0], "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объектов из кÑша.") + + envs.On("Create", mock.Anything, mock.Anything).Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once() + _, err = svc.Create(ctx, &environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}) + require.NoError(t, err) + + envs.On("List", mock.Anything, spaceID).Return([]*environments.Environment{{ID: envID, SpaceID: spaceID, Description: "Environment"}, {ID: "envID2", SpaceID: spaceID, Description: "Environment2"}}, nil).Once() + + vl3, err := svc.List(ctx, spaceID) + require.NoError(t, err) + assert.Len(t, vl3, 2, "Ожидает что объекты были удалены из кÑша и запрошены заново из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After size exceeded", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(1, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, "envID2").Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once() + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment"}, nil).Once() + envs.On("Get", mock.Anything, spaceID, "envID2").Return(&environments.Environment{ID: "envID2", SpaceID: spaceID, Description: "Environment2"}, nil).Once() + + v1, err := svc.Get(ctx, spaceID, "envID2") + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, "envID2") + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша.") + + _, err = svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v5, err := svc.Get(ctx, spaceID, "envID2") + require.NoError(t, err) + assert.NotSame(t, v2, v5, "Ожидает что объект был удален из кÑша и будет запрошен заново из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + + t.Run("After TTL expired", func(t *testing.T) { + envs := &mocksenvironments.Environments{} + + svc := CachingMiddleware(cache.NewCache(size, ttl))(envs) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment"}, nil).Once() + v1, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + + v2, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.Same(t, v1, v2, "ОжидаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ðµ объекта из кÑша.") + + time.Sleep(2 * ttl) + + envs.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Description: "Environment"}, nil).Once() + v3, err := svc.Get(ctx, spaceID, envID) + require.NoError(t, err) + assert.NotSame(t, v2, v3, "Ожидает что объект был удален из кÑша и будет запрошен заново из ÑервиÑа.") + + envs.AssertExpectations(t) + }) + }) +} diff --git a/pkg/environments/middleware/error_logging_middleware.go b/pkg/environments/middleware/error_logging_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..b365f36d591257fe74ad49f8350755b8f6da9815 --- /dev/null +++ b/pkg/environments/middleware/error_logging_middleware.go @@ -0,0 +1,110 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../assets/templates/middleware/error_log +// gowrap: http://github.com/hexdigest/gowrap + +package service + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/error_log -o error_logging_middleware.go -l "" + +import ( + "context" + + "git.perx.ru/perxis/perxis-go/pkg/environments" + "go.uber.org/zap" +) + +// errorLoggingMiddleware implements environments.Environments that is instrumented with logging +type errorLoggingMiddleware struct { + logger *zap.Logger + next environments.Environments +} + +// ErrorLoggingMiddleware instruments an implementation of the environments.Environments with simple logging +func ErrorLoggingMiddleware(logger *zap.Logger) Middleware { + return func(next environments.Environments) environments.Environments { + return &errorLoggingMiddleware{ + next: next, + logger: logger, + } + } +} + +func (m *errorLoggingMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Create(ctx, env) +} + +func (m *errorLoggingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Delete(ctx, spaceId, envId) +} + +func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Get(ctx, spaceId, envId) +} + +func (m *errorLoggingMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.List(ctx, spaceId) +} + +func (m *errorLoggingMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Migrate(ctx, spaceId, envId, options...) +} + +func (m *errorLoggingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.RemoveAlias(ctx, spaceId, envId, alias) +} + +func (m *errorLoggingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.SetAlias(ctx, spaceId, envId, alias) +} + +func (m *errorLoggingMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) { + logger := m.logger + defer func() { + if err != nil { + logger.Warn("response error", zap.Error(err)) + } + }() + return m.next.Update(ctx, env) +} diff --git a/pkg/environments/middleware/logging_middleware.go b/pkg/environments/middleware/logging_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..74a2efbf83a466387219ad4fba8e0c0241d71735 --- /dev/null +++ b/pkg/environments/middleware/logging_middleware.go @@ -0,0 +1,325 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../assets/templates/middleware/access_log +// gowrap: http://github.com/hexdigest/gowrap + +package service + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/access_log -o logging_middleware.go -l "" + +import ( + "context" + "fmt" + "time" + + "git.perx.ru/perxis/perxis-go/pkg/auth" + "git.perx.ru/perxis/perxis-go/pkg/environments" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// loggingMiddleware implements environments.Environments that is instrumented with logging +type loggingMiddleware struct { + logger *zap.Logger + next environments.Environments +} + +// LoggingMiddleware instruments an implementation of the environments.Environments with simple logging +func LoggingMiddleware(logger *zap.Logger) Middleware { + return func(next environments.Environments) environments.Environments { + return &loggingMiddleware{ + next: next, + logger: logger, + } + } +} + +func (m *loggingMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "env": env} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Create.Request", fields...) + + created, err = m.next.Create(ctx, env) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "created": created, + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Create.Response", fields...) + + return created, err +} + +func (m *loggingMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "spaceId": spaceId, + "envId": envId} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Delete.Request", fields...) + + err = m.next.Delete(ctx, spaceId, envId) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Delete.Response", fields...) + + return err +} + +func (m *loggingMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "spaceId": spaceId, + "envId": envId} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Get.Request", fields...) + + env, err = m.next.Get(ctx, spaceId, envId) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "env": env, + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Get.Response", fields...) + + return env, err +} + +func (m *loggingMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "spaceId": spaceId} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("List.Request", fields...) + + envs, err = m.next.List(ctx, spaceId) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "envs": envs, + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("List.Response", fields...) + + return envs, err +} + +func (m *loggingMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "spaceId": spaceId, + "envId": envId, + "options": options} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Migrate.Request", fields...) + + err = m.next.Migrate(ctx, spaceId, envId, options...) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Migrate.Response", fields...) + + return err +} + +func (m *loggingMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "spaceId": spaceId, + "envId": envId, + "alias": alias} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("RemoveAlias.Request", fields...) + + err = m.next.RemoveAlias(ctx, spaceId, envId, alias) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("RemoveAlias.Response", fields...) + + return err +} + +func (m *loggingMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "spaceId": spaceId, + "envId": envId, + "alias": alias} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("SetAlias.Request", fields...) + + err = m.next.SetAlias(ctx, spaceId, envId, alias) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("SetAlias.Response", fields...) + + return err +} + +func (m *loggingMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) { + begin := time.Now() + var fields []zapcore.Field + for k, v := range map[string]interface{}{ + "ctx": ctx, + "env": env} { + if k == "ctx" { + fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx)))) + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Update.Request", fields...) + + err = m.next.Update(ctx, env) + + fields = []zapcore.Field{ + zap.Duration("time", time.Since(begin)), + zap.Error(err), + } + + for k, v := range map[string]interface{}{ + "err": err} { + if k == "err" { + continue + } + fields = append(fields, zap.Reflect(k, v)) + } + + m.logger.Debug("Update.Response", fields...) + + return err +} diff --git a/pkg/environments/middleware/middleware.go b/pkg/environments/middleware/middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..b5e29a99db170df7d2cc5003b2d9a206a423fef2 --- /dev/null +++ b/pkg/environments/middleware/middleware.go @@ -0,0 +1,28 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../assets/templates/middleware/middleware +// gowrap: http://github.com/hexdigest/gowrap + +package service + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/middleware -o middleware.go -l "" + +import ( + "git.perx.ru/perxis/perxis-go/pkg/environments" + "go.uber.org/zap" +) + +type Middleware func(environments.Environments) environments.Environments + +func WithLog(s environments.Environments, logger *zap.Logger, log_access bool) environments.Environments { + if logger == nil { + logger = zap.NewNop() + } + + logger = logger.Named("Environments") + s = ErrorLoggingMiddleware(logger)(s) + if log_access { + s = LoggingMiddleware(logger)(s) + } + s = RecoveringMiddleware(logger)(s) + return s +} diff --git a/pkg/environments/middleware/recovering_middleware.go b/pkg/environments/middleware/recovering_middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..bf4bec7d46fa8b3e963c65330c6883c9c7fb0c20 --- /dev/null +++ b/pkg/environments/middleware/recovering_middleware.go @@ -0,0 +1,127 @@ +// Code generated by gowrap. DO NOT EDIT. +// template: ../../../assets/templates/middleware/recovery +// gowrap: http://github.com/hexdigest/gowrap + +package service + +//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/environments -i Environments -t ../../../assets/templates/middleware/recovery -o recovering_middleware.go -l "" + +import ( + "context" + "fmt" + + "git.perx.ru/perxis/perxis-go/pkg/environments" + "go.uber.org/zap" +) + +// recoveringMiddleware implements environments.Environments that is instrumented with logging +type recoveringMiddleware struct { + logger *zap.Logger + next environments.Environments +} + +// RecoveringMiddleware instruments an implementation of the environments.Environments with simple logging +func RecoveringMiddleware(logger *zap.Logger) Middleware { + return func(next environments.Environments) environments.Environments { + return &recoveringMiddleware{ + next: next, + logger: logger, + } + } +} + +func (m *recoveringMiddleware) Create(ctx context.Context, env *environments.Environment) (created *environments.Environment, err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Create(ctx, env) +} + +func (m *recoveringMiddleware) Delete(ctx context.Context, spaceId string, envId string) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Delete(ctx, spaceId, envId) +} + +func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, envId string) (env *environments.Environment, err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Get(ctx, spaceId, envId) +} + +func (m *recoveringMiddleware) List(ctx context.Context, spaceId string) (envs []*environments.Environment, err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.List(ctx, spaceId) +} + +func (m *recoveringMiddleware) Migrate(ctx context.Context, spaceId string, envId string, options ...*environments.MigrateOptions) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Migrate(ctx, spaceId, envId, options...) +} + +func (m *recoveringMiddleware) RemoveAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.RemoveAlias(ctx, spaceId, envId, alias) +} + +func (m *recoveringMiddleware) SetAlias(ctx context.Context, spaceId string, envId string, alias string) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.SetAlias(ctx, spaceId, envId, alias) +} + +func (m *recoveringMiddleware) Update(ctx context.Context, env *environments.Environment) (err error) { + logger := m.logger + defer func() { + if r := recover(); r != nil { + logger.Error("panic", zap.Error(fmt.Errorf("%v", r))) + err = fmt.Errorf("%v", r) + } + }() + + return m.next.Update(ctx, env) +}