Skip to content
Snippets Groups Projects
Commit b2615723 authored by Valera Shaitorov's avatar Valera Shaitorov :alien:
Browse files

Добавлены Collections middlewares

parent 022d89ed
No related branches found
No related tags found
No related merge requests found
package service
import (
"context"
"git.perx.ru/perxis/perxis-go/pkg/cache"
service "git.perx.ru/perxis/perxis-go/pkg/collections"
envService "git.perx.ru/perxis/perxis-go/pkg/environments"
"git.perx.ru/perxis/perxis-go/pkg/schema"
)
func makeKey(spaceId, envId, collectionId string, disableSchemaIncludes bool) string {
s := spaceId + "-" + envId + "-" + collectionId + "-"
if disableSchemaIncludes {
s += "1"
} else {
s += "0"
}
return s
}
func CachingMiddleware(cache *cache.Cache, envs envService.Environments) Middleware {
return func(next service.Collections) service.Collections {
return &cachingMiddleware{
cache: cache,
next: next,
envs: envs,
}
}
}
type cachingMiddleware struct {
cache *cache.Cache
next service.Collections
envs envService.Environments
}
func (m cachingMiddleware) Create(ctx context.Context, collection *service.Collection) (coll *service.Collection, err error) {
return m.next.Create(ctx, collection)
}
func (m cachingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*service.GetOptions) (coll *service.Collection, err error) {
opts := service.MergeGetOptions(options...)
value, e := m.cache.Get(makeKey(spaceId, envId, collectionId, opts.DisableSchemaIncludes))
if e == nil {
return value.(*service.Collection), err
}
coll, err = m.next.Get(ctx, spaceId, envId, collectionId, options...)
if err == nil {
env, err := m.envs.Get(ctx, coll.SpaceID, coll.EnvID)
if err != nil {
return nil, err
}
m.cache.Set(makeKey(coll.SpaceID, env.ID, coll.ID, opts.DisableSchemaIncludes), coll)
for _, al := range env.Aliases {
m.cache.Set(makeKey(coll.SpaceID, al, coll.ID, opts.DisableSchemaIncludes), coll)
}
}
return coll, err
}
func (m cachingMiddleware) List(ctx context.Context, spaceId, envId string, filter *service.Filter) (collections []*service.Collection, err error) {
return m.next.List(ctx, spaceId, envId, filter)
}
func (m cachingMiddleware) Update(ctx context.Context, coll *service.Collection) (err error) {
err = m.next.Update(ctx, coll)
if err == nil {
env, err := m.envs.Get(ctx, coll.SpaceID, coll.EnvID)
if err != nil {
return err
}
m.cache.Remove(makeKey(env.SpaceID, env.ID, coll.ID, true))
m.cache.Remove(makeKey(env.SpaceID, env.ID, coll.ID, false))
for _, al := range env.Aliases {
m.cache.Remove(makeKey(env.SpaceID, al, coll.ID, true))
m.cache.Remove(makeKey(env.SpaceID, al, coll.ID, false))
}
}
return err
}
func (m cachingMiddleware) SetSchema(ctx context.Context, spaceId, envId, collectionId string, schema *schema.Schema) (err error) {
err = m.next.SetSchema(ctx, spaceId, envId, collectionId, schema)
if err == nil {
env, err := m.envs.Get(ctx, spaceId, envId)
if err != nil {
return err
}
m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
for _, al := range env.Aliases {
m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
}
}
return err
}
func (m cachingMiddleware) SetState(ctx context.Context, spaceId, envId, collectionId string, state *service.StateInfo) (err error) {
err = m.next.SetState(ctx, spaceId, envId, collectionId, state)
if err == nil {
env, err := m.envs.Get(ctx, spaceId, envId)
if err != nil {
return err
}
m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
for _, al := range env.Aliases {
m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
}
}
return err
}
func (m cachingMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId string) (err error) {
err = m.next.Delete(ctx, spaceId, envId, collectionId)
if err == nil {
env, err := m.envs.Get(ctx, spaceId, envId)
if err != nil {
return err
}
m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, true))
m.cache.Remove(makeKey(env.SpaceID, env.ID, collectionId, false))
for _, al := range env.Aliases {
m.cache.Remove(makeKey(env.SpaceID, al, collectionId, true))
m.cache.Remove(makeKey(env.SpaceID, al, collectionId, false))
}
}
return err
}
package service
import (
"context"
"testing"
"time"
"git.perx.ru/perxis/perxis-go/pkg/cache"
"git.perx.ru/perxis/perxis-go/pkg/collections"
colsmocks "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
"git.perx.ru/perxis/perxis-go/pkg/environments"
envmocks "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
"git.perx.ru/perxis/perxis-go/pkg/errors"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestCollections_Cache(t *testing.T) {
const (
colID = "colID"
spaceID = "spaceID"
envID = "envId"
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) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
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(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе по ID окружения.")
v3, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по alias окружения.")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("Get from cache(by Alias)", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша при повторном запросе по Alias окружения.")
v3, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v3, v2, "Ожидается получение объекта из кеша, при запросе того же объекта по ID окружения.")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("Get from cache with options", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
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, mock.Anything).Run(func(args mock.Arguments) {
require.Len(t, args, 5)
opt := args.Get(4).(*collections.GetOptions)
assert.True(t, opt.DisableSchemaIncludes)
}).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
_, err := svc.Get(ctx, spaceID, envID, colID, []*collections.GetOptions{{DisableSchemaIncludes: true}}...)
require.NoError(t, err)
env.AssertExpectations(t)
col.AssertExpectations(t)
})
//t.Run("List from cache", func(t *testing.T) {
// col := &colsmocks.Collections{}
// env := &envmocks.Environments{}
//
// svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
//
// col.On("List", mock.Anything, spaceID, envID).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
// env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
//
// vl1, err := svc.List(ctx, spaceID, envID, nil)
// require.NoError(t, err)
//
// vl2, err := svc.List(ctx, spaceID, envID, nil)
// require.NoError(t, err)
// assert.Len(t, vl2, 1)
// assert.Same(t, vl1[0], vl2[0], "При повторном запросе по ID окружения, ожидается получение списка объектов из кеша.")
//
// vl3, err := svc.List(ctx, spaceID, envAlias, nil)
// require.NoError(t, err)
// assert.Len(t, vl3, 1)
// assert.Same(t, vl3[0], vl2[0], "При повторном запросе по Alias окружения, ожидается получение списка объектов из кеша.")
//
// env.AssertExpectations(t)
// col.AssertExpectations(t)
//})
t.Run("List", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
_, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
_, err = svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("Invalidate cache", func(t *testing.T) {
t.Run("After Update", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
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(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
v3, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
vl1, err := svc.List(ctx, spaceID, envID, nil)
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("Update", mock.Anything, mock.Anything).Return(nil).Once()
err = svc.Update(ctx, &collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"})
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(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}}, nil).Once()
v4, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.NotSame(t, v3, v4, "Ожидает что элемент после обновления был удален из кэша и будет запрошен заново из сервиса.")
v5, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
vl2, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.NotSame(t, vl1[0], vl2[0], "Ожидает что после обновления элементы будут запрошены заново из сервиса.")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("After Update(by Alias)", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
//env.On("Get", mock.Anything, spaceID, envAlias).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по Alias окружения.")
v3, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по ID окружения.")
vl1, err := svc.List(ctx, spaceID, envAlias, nil)
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("Update", mock.Anything, mock.Anything).Return(nil).Once()
err = svc.Update(ctx, &collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"})
require.NoError(t, err)
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
//env.On("Get", mock.Anything, spaceID, envAlias).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}, nil).Once()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}}, nil).Once()
v4, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.NotSame(t, v3, v4, "Ожидает что элемент после обновления был удален из кэша и будет запрошен заново из сервиса.")
v5, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
vl4, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
assert.NotSame(t, vl1[0], vl4[0], "Ожидает что после обновления элементы будут запрошены заново из сервиса.")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("After Set Schema", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Twice()
col.On("Get", mock.Anything, spaceID, envID, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Twice()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
v3, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
vl1, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
vl2, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.Len(t, vl2, 1)
assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
vl3, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
assert.Len(t, vl2, 1)
assert.Equal(t, vl2[0], vl3[0], "Ожидается получение объектов из кеша по Alias окружения.")
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
col.On("SetSchema", mock.Anything, spaceID, envID, colID, mock.Anything).Return(nil).Once()
err = svc.SetSchema(ctx, spaceID, envID, colID, &schema.Schema{})
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(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}}, nil).Once()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "nameUPD"}}, nil).Once()
v4, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.NotSame(t, v3, v4, "Ожидает что элемент после обновления схемы был удален из кэша и будет запрошен заново из сервиса.")
v5, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v4, v5, "Ожидается получение объекта из кеша по Alias окружения.")
vl4, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.NotSame(t, vl4[0], vl3[0], "Ожидает что после обновления схемы элементы будут запрошены заново из сервиса.")
vl5, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
assert.Equal(t, vl4[0], vl5[0], "Ожидается получение объектов из кеша по Alias окружения..")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("After Delete", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Twice()
col.On("Get", mock.Anything, spaceID, envID, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Twice()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша по ID окружения.")
v3, err := svc.Get(ctx, spaceID, envAlias, colID)
require.NoError(t, err)
assert.Same(t, v2, v3, "Ожидается получение объекта из кеша по Alias окружения.")
vl1, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
vl2, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.Len(t, vl2, 1)
assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
vl3, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
assert.Len(t, vl2, 1)
assert.Equal(t, vl2[0], vl3[0], "Ожидается получение объектов из кеша по Alias окружения.")
//env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
col.On("Delete", mock.Anything, spaceID, envID, colID).Return(nil).Once()
err = svc.Delete(ctx, spaceID, envID, colID)
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("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{}, nil).Once()
_, err = svc.Get(ctx, spaceID, envID, colID)
require.Error(t, err)
assert.EqualError(t, err, "not found", "Ожидает что элемент был удален из кэша и получена ошибка от сервиса.")
vl4, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.Len(t, vl4, 0, "Ожидает что элементы были удалены из кэша.")
col.On("Get", mock.Anything, spaceID, envAlias, colID).Return(nil, ErrNotFound).Once()
_, err = svc.Get(ctx, spaceID, envAlias, colID)
require.Error(t, err)
assert.EqualError(t, err, "not found", "Ожидает что элемент был удален из кэша и получена ошибка от сервиса.")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("After Create", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
//env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
col.On("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Twice()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}}, nil).Once()
vl1, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
vl2, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.Len(t, vl2, 1)
assert.Same(t, vl1[0], vl2[0], "Ожидается получение объектов из кеша по ID окружения.")
vl3, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
assert.Len(t, vl2, 1)
assert.Equal(t, vl2[0], vl3[0], "Ожидается получение объектов из кеша по Alias окружения.")
//env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil).Once()
col.On("Create", mock.Anything, mock.Anything).Return(&collections.Collection{ID: "colID2", SpaceID: spaceID, EnvID: envID, Name: "name2"}, nil).Once()
_, err = svc.Create(ctx, &collections.Collection{ID: "colID2", SpaceID: spaceID, EnvID: envID, Name: "name2"})
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("List", mock.Anything, spaceID, envID, mock.Anything).Return([]*collections.Collection{
{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"},
{ID: "colID2", SpaceID: spaceID, EnvID: envID, Name: "name2"},
}, nil).Once()
col.On("List", mock.Anything, spaceID, envAlias, mock.Anything).Return([]*collections.Collection{
{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"},
{ID: "colID2", SpaceID: spaceID, EnvID: envID, Name: "name2"},
}, nil).Once()
vl4, err := svc.List(ctx, spaceID, envID, nil)
require.NoError(t, err)
assert.Len(t, vl4, 2, "Ожидает что элементы были удалены из кэша и получены заново из сервиса.")
vl5, err := svc.List(ctx, spaceID, envAlias, nil)
require.NoError(t, err)
assert.Len(t, vl5, 2)
assert.Equal(t, vl4[0], vl5[0], "Ожидается получение объектов из кеша по Alias окружения..")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
t.Run("After TTL expired", func(t *testing.T) {
col := &colsmocks.Collections{}
env := &envmocks.Environments{}
svc := CachingMiddleware(cache.NewCache(size, ttl), env)(col)
env.On("Get", mock.Anything, spaceID, envID).Return(&environments.Environment{ID: envID, SpaceID: spaceID, Aliases: []string{envAlias}}, nil)
col.On("Get", mock.Anything, spaceID, envID, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
v1, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
v2, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.Same(t, v1, v2, "Ожидается получение объекта из кеша.")
time.Sleep(2 * ttl)
col.On("Get", mock.Anything, spaceID, envID, colID).Return(&collections.Collection{ID: colID, SpaceID: spaceID, EnvID: envID, Name: "name"}, nil).Once()
v3, err := svc.Get(ctx, spaceID, envID, colID)
require.NoError(t, err)
assert.NotSame(t, v3, v2, "Ожидает что элемент был удален из кэша и будет запрошен заново из сервиса.")
env.AssertExpectations(t)
col.AssertExpectations(t)
})
})
}
// 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/collections -i Collections -t ../../../assets/templates/middleware/error_log -o error_logging_middleware.go -l ""
import (
"context"
"git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"go.uber.org/zap"
)
// errorLoggingMiddleware implements collections.Collections that is instrumented with logging
type errorLoggingMiddleware struct {
logger *zap.Logger
next collections.Collections
}
// ErrorLoggingMiddleware instruments an implementation of the collections.Collections with simple logging
func ErrorLoggingMiddleware(logger *zap.Logger) Middleware {
return func(next collections.Collections) collections.Collections {
return &errorLoggingMiddleware{
next: next,
logger: logger,
}
}
}
func (m *errorLoggingMiddleware) Create(ctx context.Context, collection *collections.Collection) (created *collections.Collection, err error) {
logger := m.logger
defer func() {
if err != nil {
logger.Warn("response error", zap.Error(err))
}
}()
return m.next.Create(ctx, collection)
}
func (m *errorLoggingMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId 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, collectionId)
}
func (m *errorLoggingMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, err error) {
logger := m.logger
defer func() {
if err != nil {
logger.Warn("response error", zap.Error(err))
}
}()
return m.next.Get(ctx, spaceId, envId, collectionId, options...)
}
func (m *errorLoggingMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, err error) {
logger := m.logger
defer func() {
if err != nil {
logger.Warn("response error", zap.Error(err))
}
}()
return m.next.List(ctx, spaceId, envId, filter)
}
func (m *errorLoggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (err error) {
logger := m.logger
defer func() {
if err != nil {
logger.Warn("response error", zap.Error(err))
}
}()
return m.next.SetSchema(ctx, spaceId, envId, collectionId, schema)
}
func (m *errorLoggingMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (err error) {
logger := m.logger
defer func() {
if err != nil {
logger.Warn("response error", zap.Error(err))
}
}()
return m.next.SetState(ctx, spaceId, envId, collectionId, state)
}
func (m *errorLoggingMiddleware) Update(ctx context.Context, coll *collections.Collection) (err error) {
logger := m.logger
defer func() {
if err != nil {
logger.Warn("response error", zap.Error(err))
}
}()
return m.next.Update(ctx, coll)
}
// 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/collections -i Collections -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/collections"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// loggingMiddleware implements collections.Collections that is instrumented with logging
type loggingMiddleware struct {
logger *zap.Logger
next collections.Collections
}
// LoggingMiddleware instruments an implementation of the collections.Collections with simple logging
func LoggingMiddleware(logger *zap.Logger) Middleware {
return func(next collections.Collections) collections.Collections {
return &loggingMiddleware{
next: next,
logger: logger,
}
}
}
func (m *loggingMiddleware) Create(ctx context.Context, collection *collections.Collection) (created *collections.Collection, err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"collection": collection} {
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, collection)
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, collectionId string) (err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"spaceId": spaceId,
"envId": envId,
"collectionId": collectionId} {
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, collectionId)
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, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"spaceId": spaceId,
"envId": envId,
"collectionId": collectionId,
"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("Get.Request", fields...)
collection, err = m.next.Get(ctx, spaceId, envId, collectionId, options...)
fields = []zapcore.Field{
zap.Duration("time", time.Since(begin)),
zap.Error(err),
}
for k, v := range map[string]interface{}{
"collection": collection,
"err": err} {
if k == "err" {
continue
}
fields = append(fields, zap.Reflect(k, v))
}
m.logger.Debug("Get.Response", fields...)
return collection, err
}
func (m *loggingMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"spaceId": spaceId,
"envId": envId,
"filter": filter} {
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...)
collections, err = m.next.List(ctx, spaceId, envId, filter)
fields = []zapcore.Field{
zap.Duration("time", time.Since(begin)),
zap.Error(err),
}
for k, v := range map[string]interface{}{
"collections": collections,
"err": err} {
if k == "err" {
continue
}
fields = append(fields, zap.Reflect(k, v))
}
m.logger.Debug("List.Response", fields...)
return collections, err
}
func (m *loggingMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"spaceId": spaceId,
"envId": envId,
"collectionId": collectionId,
"schema": schema} {
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("SetSchema.Request", fields...)
err = m.next.SetSchema(ctx, spaceId, envId, collectionId, schema)
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("SetSchema.Response", fields...)
return err
}
func (m *loggingMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"spaceId": spaceId,
"envId": envId,
"collectionId": collectionId,
"state": state} {
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("SetState.Request", fields...)
err = m.next.SetState(ctx, spaceId, envId, collectionId, state)
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("SetState.Response", fields...)
return err
}
func (m *loggingMiddleware) Update(ctx context.Context, coll *collections.Collection) (err error) {
begin := time.Now()
var fields []zapcore.Field
for k, v := range map[string]interface{}{
"ctx": ctx,
"coll": coll} {
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, coll)
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
}
// 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/collections -i Collections -t ../../../assets/templates/middleware/middleware -o middleware.go -l ""
import (
"git.perx.ru/perxis/perxis-go/pkg/collections"
"go.uber.org/zap"
)
type Middleware func(collections.Collections) collections.Collections
func WithLog(s collections.Collections, logger *zap.Logger, log_access bool) collections.Collections {
if logger == nil {
logger = zap.NewNop()
}
logger = logger.Named("Collections")
s = ErrorLoggingMiddleware(logger)(s)
if log_access {
s = LoggingMiddleware(logger)(s)
}
s = RecoveringMiddleware(logger)(s)
return s
}
// 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/collections -i Collections -t ../../../assets/templates/middleware/recovery -o recovering_middleware.go -l ""
import (
"context"
"fmt"
"git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/schema"
"go.uber.org/zap"
)
// recoveringMiddleware implements collections.Collections that is instrumented with logging
type recoveringMiddleware struct {
logger *zap.Logger
next collections.Collections
}
// RecoveringMiddleware instruments an implementation of the collections.Collections with simple logging
func RecoveringMiddleware(logger *zap.Logger) Middleware {
return func(next collections.Collections) collections.Collections {
return &recoveringMiddleware{
next: next,
logger: logger,
}
}
}
func (m *recoveringMiddleware) Create(ctx context.Context, collection *collections.Collection) (created *collections.Collection, 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, collection)
}
func (m *recoveringMiddleware) Delete(ctx context.Context, spaceId string, envId string, collectionId 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, collectionId)
}
func (m *recoveringMiddleware) Get(ctx context.Context, spaceId string, envId string, collectionId string, options ...*collections.GetOptions) (collection *collections.Collection, 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, collectionId, options...)
}
func (m *recoveringMiddleware) List(ctx context.Context, spaceId string, envId string, filter *collections.Filter) (collections []*collections.Collection, 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, envId, filter)
}
func (m *recoveringMiddleware) SetSchema(ctx context.Context, spaceId string, envId string, collectionId string, schema *schema.Schema) (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.SetSchema(ctx, spaceId, envId, collectionId, schema)
}
func (m *recoveringMiddleware) SetState(ctx context.Context, spaceId string, envId string, collectionId string, state *collections.StateInfo) (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.SetState(ctx, spaceId, envId, collectionId, state)
}
func (m *recoveringMiddleware) Update(ctx context.Context, coll *collections.Collection) (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, coll)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment