diff --git a/pkg/setup/collection.go b/pkg/setup/collection.go index f8d119db2b54bc8a9c44c389aafc3e041aafcd1d..cee9d6203091efdb18f4d484efd8be4eb093a7da 100644 --- a/pkg/setup/collection.go +++ b/pkg/setup/collection.go @@ -10,6 +10,7 @@ import ( "git.perx.ru/perxis/perxis-go/pkg/environments" "git.perx.ru/perxis/perxis-go/pkg/errors" "git.perx.ru/perxis/perxis-go/pkg/extension" + "git.perx.ru/perxis/perxis-go/pkg/schema" "go.uber.org/zap" ) @@ -19,6 +20,11 @@ var ( ErrUninstallCollections = errors.New("failed to uninstall collections") ) +// todo добавлено в релизе v0.0.16: временный флаг для установки метадаты во все коллекции расширений +const ( + _alwaysSetSchema = true +) + type CollectionsOption func(c *CollectionConfig) type UpdateCollectionFn func(s *Setup, exist, new *collections.Collection) (coll *collections.Collection, upd bool, setSchema bool, err error) type DeleteCollectionFn func(s *Setup, col *collections.Collection) (bool, error) @@ -70,13 +76,24 @@ func DeleteCollectionIfRemove() CollectionsOption { } } +func isMetadataExtensionEqual(s1, s2 *schema.Schema) bool { + if s1.Metadata == nil && s2.Metadata == nil { + return true + } + + if s1.Metadata == nil || s2.Metadata == nil { + return false + } + + return s1.Metadata[extension.ExtensionMetadataKey] == s2.Metadata[extension.ExtensionMetadataKey] +} + func DefaultUpdateCollectionStrategy() CollectionsOption { return func(c *CollectionConfig) { c.UpdateFn = func(s *Setup, exist, collection *collections.Collection) (*collections.Collection, bool, bool, error) { - if !s.IsForce() && !collection.IsView() && !exist.IsView() && collection.Schema.Metadata != nil && collection.Schema.Metadata[extension.ExtensionMetadataKey] != "" { - if exist.Schema.Metadata == nil || exist.Schema.Metadata[extension.ExtensionMetadataKey] != collection.Schema.Metadata[extension.ExtensionMetadataKey] { - return nil, false, false, collections.ErrAlreadyExists - } + if !_alwaysSetSchema && !s.IsForce() && !collection.IsView() && !exist.IsView() && !isMetadataExtensionEqual(collection.Schema, exist.Schema) { + return nil, false, false, errors.WithDetailf(collections.ErrAlreadyExists, "Коллекция с идентификатором '%s' "+ + "уже существует. Удалите ее или вызовите установку расширения с флагом Force", collection.ID) } if len(exist.Tags) > 0 { @@ -90,7 +107,8 @@ func DefaultUpdateCollectionStrategy() CollectionsOption { if exist.View != nil && collection.View != nil { update = update && *exist.View == *collection.View } - setSchema = !collection.IsView() && !reflect.DeepEqual(exist.Schema, collection.Schema) + + setSchema = _alwaysSetSchema || !collection.IsView() && !reflect.DeepEqual(exist.Schema, collection.Schema) return collection, update, setSchema, nil } diff --git a/pkg/setup/collection_test.go b/pkg/setup/collection_test.go index b249048970b153d08f30f8aecf6f732c827cf47e..f62ba3b42a752f5b0b43680966d42037b5d56378 100644 --- a/pkg/setup/collection_test.go +++ b/pkg/setup/collection_test.go @@ -99,7 +99,7 @@ func TestSetup_InstallCollections(t *testing.T) { collectionsCall: func(svc *mockscollections.Collections) { svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}, nil).Once() svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}).Return(nil).Once() - svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String())).Return(nil).Once() + svc.On("SetSchema", mock.Anything, "sp", "env", "1", schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")).Return(nil).Once() }, envsCall: func(svc *envmocks.Environments) { svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once() @@ -108,17 +108,18 @@ func TestSetup_InstallCollections(t *testing.T) { assert.NoError(t, err) }, }, - { - name: "Fail to update user collection with same id as in extensions collection", - collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}}, - collectionsCall: func(svc *mockscollections.Collections) { - svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String())}, nil).Once() - }, - wantErr: func(t *testing.T, err error) { - assert.Error(t, err) - assert.ErrorIs(t, err, collections.ErrAlreadyExists) - }, - }, + //todo после удаления флага _alwaysSetSchema данный тест будет работать, сейчас он будет всегда падать, поэтому пока закомментирован + //{ + //name: "Fail to update user collection with same id as in extensions collection", + //collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}}, + //collectionsCall: func(svc *mockscollections.Collections) { + // svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String())}, nil).Once() + //}, + //wantErr: func(t *testing.T, err error) { + // assert.Error(t, err) + // assert.ErrorIs(t, err, collections.ErrAlreadyExists) + //}, + //}, { name: "Update user collection with same id as in extensions collection with force", collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", Schema: schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test-extension")}}, @@ -150,20 +151,21 @@ func TestSetup_InstallCollections(t *testing.T) { assert.NoError(t, err) }, }, - { - name: "Update view collection with the same id to new view collection", - collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}}, - collectionsCall: func(svc *mockscollections.Collections) { - svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: environments.DefaultEnvironment, CollectionID: "2"}}, nil).Once() - svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}).Return(nil).Once() - }, - envsCall: func(svc *envmocks.Environments) { - svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once() - }, - wantErr: func(t *testing.T, err error) { - assert.NoError(t, err) - }, - }, + //todo после удаления флага _alwaysSetSchema данный тест будет работать, сейчас он будет всегда падать, поэтому пока закомментирован + //{ + // name: "Update view collection with the same id to new view collection", + // collections: []*collections.Collection{{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}}, + // collectionsCall: func(svc *mockscollections.Collections) { + // svc.On("Get", mock.Anything, "sp", "env", "1").Return(&collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp2", EnvID: environments.DefaultEnvironment, CollectionID: "2"}}, nil).Once() + // svc.On("Update", mock.Anything, &collections.Collection{ID: "1", SpaceID: "sp", EnvID: "env", View: &collections.View{SpaceID: "sp3", EnvID: environments.DefaultEnvironment, CollectionID: "3"}}).Return(nil).Once() + // }, + // envsCall: func(svc *envmocks.Environments) { + // svc.On("Migrate", mock.Anything, "sp", "env", &environments.MigrateOptions{Wait: true}).Return(nil).Once() + // }, + // wantErr: func(t *testing.T, err error) { + // assert.NoError(t, err) + // }, + //}, { name: "Fail to install collection on migrate", collections: []*collections.Collection{{ID: "1", SpaceID: "sp", Name: "space", EnvID: "env", Schema: schema.New("name", field.String())}}, @@ -200,3 +202,42 @@ func TestSetup_InstallCollections(t *testing.T) { }) } } + +func Test_isMetadataExtensionEqual(t *testing.T) { + tests := []struct { + name string + s1 *schema.Schema + s2 *schema.Schema + want bool + }{ + { + "Not equal #1 (no metadata)", + schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"), + schema.New("name", field.String()), + false, + }, + { + "Not equal #2 (different metadata)", + schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"), + schema.New("name", field.String()).WithMetadata("test", "test"), + false, + }, + { + "Equal #1 (no metadata)", + schema.New("name", field.String()), + schema.New("name", field.String()), + true, + }, + { + "Equal #2 (equal metadata)", + schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"), + schema.New("name", field.String()).WithMetadata(extension.ExtensionMetadataKey, "test"), + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, isMetadataExtensionEqual(tt.s1, tt.s2), "isMetadataExtensionEqual(%v, %v)", tt.s1, tt.s2) + }) + } +} diff --git a/pkg/setup/setup_test.go b/pkg/setup/setup_test.go index 7c2059732443bd697f4e9b618f36569e45626fa9..6a1b6f7ba6884fa2f09a826a08a695219b23e142 100644 --- a/pkg/setup/setup_test.go +++ b/pkg/setup/setup_test.go @@ -179,11 +179,16 @@ func TestSetupInstall(t *testing.T) { t.Run("Success, update existing records", func(t *testing.T) { envMocks := &environmentMock.Environments{} + //todo после удаления флага _alwaysSetSchema убрать лишний вызов + envMocks.On("Migrate", mock.Anything, spaceID, envID, &environments.MigrateOptions{Wait: true}). + Return(nil).Once() collsMock := &collectionMock.Collections{} collsMock.On("Get", mock.Anything, spaceID, envID, "coll1"). Return(&collections.Collection{ID: "coll1", SpaceID: spaceID, EnvID: envID, Schema: schema.New(), Name: "Коллекция старая"}, nil).Once() collsMock.On("Update", mock.Anything, &collections.Collection{ID: "coll1", SpaceID: spaceID, EnvID: envID, Schema: schema.New(), Name: "Коллекция"}).Return(nil).Once() + //todo после удаления флага _alwaysSetSchema убрать лишний вызов + collsMock.On("SetSchema", mock.Anything, "sp", "env", "coll1", schema.New()).Return(nil).Once() rMock := &rolesMock.Roles{} for _, role := range getRoles() { @@ -240,10 +245,17 @@ func TestSetupInstall(t *testing.T) { }) t.Run("Success, with force", func(t *testing.T) { + //todo после удаления флага _alwaysSetSchema убрать лишний вызов + envMocks := &environmentMock.Environments{} + envMocks.On("Migrate", mock.Anything, spaceID, envID, &environments.MigrateOptions{Wait: true}). + Return(nil).Once() + collsMock := &collectionMock.Collections{} collsMock.On("Get", mock.Anything, spaceID, envID, "coll1"). Return(&collections.Collection{ID: "coll1", SpaceID: spaceID, EnvID: envID, Schema: schema.New(), Name: "Коллекция старая"}, nil).Once() collsMock.On("Update", mock.Anything, &collections.Collection{ID: "coll1", SpaceID: spaceID, EnvID: envID, Schema: schema.New(), Name: "Коллекция"}).Return(nil).Once() + //todo после удаления флага _alwaysSetSchema убрать лишний вызов + collsMock.On("SetSchema", mock.Anything, "sp", "env", "coll1", schema.New()).Return(nil).Once() rMock := &rolesMock.Roles{} for _, role := range getRoles() { @@ -278,6 +290,8 @@ func TestSetupInstall(t *testing.T) { Clients: clMock, Roles: rMock, Items: itmMock, + //todo после удаления флага _alwaysSetSchema убрать + Environments: envMocks, }, t) setup = setup.WithForce(true)