diff --git a/pkg/items/item.go b/pkg/items/item.go index f0fc81809947e1b6282c72ef514a2ecb730aa33f..7c34d2bb3563b338d6c3cb0baf97c4e49f58b535 100644 --- a/pkg/items/item.go +++ b/pkg/items/item.go @@ -69,6 +69,7 @@ var SystemFields = []string{ "updated_at", "updated_by", "revision_id", + "revision_description", "data", "translations", "locale", @@ -86,21 +87,22 @@ type Permissions struct { } type Item struct { - ID string `json:"id" bson:"_id"` // ID - Рдентификатор записи. Автоматически генерируется системой РїСЂРё сохранении первой ревизии. - SpaceID string `json:"spaceId" bson:"-"` - EnvID string `json:"envId" bson:"-"` - CollectionID string `json:"collectionId" bson:"-"` - State State `json:"state" bson:"state"` - CreatedRevAt time.Time `json:"createdRevAt,omitempty" bson:"created_rev_at,omitempty"` - CreatedBy string `json:"createdBy,omitempty" bson:"created_by,omitempty"` - CreatedAt time.Time `json:"createdAt,omitempty" bson:"created_at,omitempty"` - UpdatedAt time.Time `json:"updatedAt,omitempty" bson:"updated_at,omitempty"` - UpdatedBy string `json:"updatedBy,omitempty" bson:"updated_by,omitempty"` - Data map[string]interface{} `json:"data" bson:"data"` - Locale string `json:"locale" bson:"-"` - Translations map[string]map[string]interface{} `json:"translations" bson:"translations,omitempty"` - RevisionID string `json:"revId,omitempty" bson:"revision_id"` - Permissions *Permissions `json:"permissions,omitempty" bson:"-"` + ID string `json:"id" bson:"_id"` // ID - Рдентификатор записи. Автоматически генерируется системой РїСЂРё сохранении первой ревизии. + SpaceID string `json:"spaceId" bson:"-"` + EnvID string `json:"envId" bson:"-"` + CollectionID string `json:"collectionId" bson:"-"` + State State `json:"state" bson:"state"` + CreatedRevAt time.Time `json:"createdRevAt,omitempty" bson:"created_rev_at,omitempty"` + CreatedBy string `json:"createdBy,omitempty" bson:"created_by,omitempty"` + CreatedAt time.Time `json:"createdAt,omitempty" bson:"created_at,omitempty"` + UpdatedAt time.Time `json:"updatedAt,omitempty" bson:"updated_at,omitempty"` + UpdatedBy string `json:"updatedBy,omitempty" bson:"updated_by,omitempty"` + Data map[string]interface{} `json:"data" bson:"data"` + Locale string `json:"locale" bson:"-"` + Translations map[string]map[string]interface{} `json:"translations" bson:"translations,omitempty"` + RevisionID string `json:"revId,omitempty" bson:"revision_id"` + RevisionDescription string `json:"revDescription,omitempty" bson:"revision_description"` + Permissions *Permissions `json:"permissions,omitempty" bson:"-"` // Флаги записи Deleted bool `json:"deleted" bson:"deleted,omitempty"` @@ -135,23 +137,24 @@ func (i *Item) Clone() *Item { func (i *Item) ToMap() map[string]interface{} { return map[string]interface{}{ - "id": i.ID, - "space_id": i.SpaceID, - "env_id": i.EnvID, - "collection_id": i.CollectionID, - "state": i.State, - "created_rev_at": i.CreatedRevAt, - "created_by": i.CreatedBy, - "created_at": i.CreatedAt, - "updated_at": i.UpdatedAt, - "updated_by": i.UpdatedBy, - "revision_id": i.RevisionID, - "data": i.Data, - "translations": i.Translations, - "locale": i.Locale, - "deleted": i.Deleted, - "hidden": i.Hidden, - "template": i.Template, + "id": i.ID, + "space_id": i.SpaceID, + "env_id": i.EnvID, + "collection_id": i.CollectionID, + "state": i.State, + "created_rev_at": i.CreatedRevAt, + "created_by": i.CreatedBy, + "created_at": i.CreatedAt, + "updated_at": i.UpdatedAt, + "updated_by": i.UpdatedBy, + "revision_id": i.RevisionID, + "revision_description": i.RevisionDescription, + "data": i.Data, + "translations": i.Translations, + "locale": i.Locale, + "deleted": i.Deleted, + "hidden": i.Hidden, + "template": i.Template, } } @@ -304,6 +307,8 @@ func (i *Item) SetSystemField(field string, value interface{}) error { i.UpdatedAt, ok = value.(time.Time) case "revision_id": i.RevisionID, ok = value.(string) + case "revision_description": + i.RevisionDescription, ok = value.(string) case "hidden": i.Hidden, ok = value.(bool) case "deleted": @@ -344,6 +349,8 @@ func (i *Item) GetSystem(field string) (any, error) { return i.UpdatedAt, nil case "revision_id": return i.RevisionID, nil + case "revision_description": + return i.RevisionDescription, nil case "hidden": return i.Hidden, nil case "deleted": @@ -394,7 +401,7 @@ func (i *Item) Get(field string) (any, error) { // GetSystemField возвращает описание поля для системных аттрибутов Item func GetSystemField(fld string) (*field.Field, error) { switch fld { - case "id", "space_id", "env_id", "collection_id", "revision_id": + case "id", "space_id", "env_id", "collection_id", "revision_id", "revision_description": return field.String(), nil case "created_rev_at", "created_at", "updated_at", "published_at": return field.Time(), nil @@ -439,18 +446,19 @@ func ItemToProto(item *Item) *pb.Item { } protoItem := &pb.Item{ - Id: item.ID, - SpaceId: item.SpaceID, - EnvId: item.EnvID, - CollectionId: item.CollectionID, - State: pb.Item_State(item.State), - CreatedBy: item.CreatedBy, - UpdatedBy: item.UpdatedBy, - RevisionId: item.RevisionID, - Locale: item.Locale, - Hidden: item.Hidden, - Template: item.Template, - Deleted: item.Deleted, + Id: item.ID, + SpaceId: item.SpaceID, + EnvId: item.EnvID, + CollectionId: item.CollectionID, + State: pb.Item_State(item.State), + CreatedBy: item.CreatedBy, + UpdatedBy: item.UpdatedBy, + RevisionId: item.RevisionID, + RevisionDescription: item.RevisionDescription, + Locale: item.Locale, + Hidden: item.Hidden, + Template: item.Template, + Deleted: item.Deleted, } if item.Data != nil { @@ -487,18 +495,19 @@ func ItemFromProto(protoItem *pb.Item) *Item { } item := &Item{ - ID: protoItem.Id, - SpaceID: protoItem.SpaceId, - EnvID: protoItem.EnvId, - CollectionID: protoItem.CollectionId, - State: State(protoItem.State), - CreatedBy: protoItem.CreatedBy, - UpdatedBy: protoItem.UpdatedBy, - RevisionID: protoItem.RevisionId, - Locale: protoItem.Locale, - Hidden: protoItem.Hidden, - Template: protoItem.Template, - Deleted: protoItem.Deleted, + ID: protoItem.Id, + SpaceID: protoItem.SpaceId, + EnvID: protoItem.EnvId, + CollectionID: protoItem.CollectionId, + State: State(protoItem.State), + CreatedBy: protoItem.CreatedBy, + UpdatedBy: protoItem.UpdatedBy, + RevisionID: protoItem.RevisionId, + RevisionDescription: protoItem.RevisionDescription, + Locale: protoItem.Locale, + Hidden: protoItem.Hidden, + Template: protoItem.Template, + Deleted: protoItem.Deleted, } if protoItem.Data != nil { diff --git a/pkg/schema/walk/fn.go b/pkg/schema/walk/fn.go index c3eaf35c569c18ba8ead7c7ff48c08d03f2a9d90..9b1d4f22316cf37cae1af824d98f611b7dc0bcf7 100644 --- a/pkg/schema/walk/fn.go +++ b/pkg/schema/walk/fn.go @@ -1,10 +1,15 @@ package walk +import "reflect" + func GenericMerge(c *WalkContext) (err error) { return } func KeepSrc(c *WalkContext) (err error) { + if reflect.DeepEqual(c.Src, c.Dst) { + return + } c.Dst = c.Src c.Changed = true return diff --git a/pkg/schema/walk/walk_test.go b/pkg/schema/walk/walk_test.go index e20adb462735e83c92c8b04f5c159e336e49f2b0..3fd01bd577c3fe16abb1cae7b2e9785e0edc659c 100644 --- a/pkg/schema/walk/walk_test.go +++ b/pkg/schema/walk/walk_test.go @@ -46,6 +46,54 @@ func TestWalker_DataWalk(t *testing.T) { wantErr bool }{ {"generic", + &WalkConfig{ + Fields: map[string]FieldConfig{}, + }, + map[string]interface{}{ + "a": "src_a", + "b": "src_b", + "obj1": map[string]interface{}{ + "a": "src_obj1_a", + "b": "src_obj1_b", + "obj2": map[string]interface{}{ + "a": "dst_obj1_obj2_a", + }, + "obj3": map[string]interface{}{ + "e": "dst_obj1_obj3_e", + }, + }, + "inline_str_1": "src_inline_1", + "inline_str_2": "src_inline_2", + "slice": []interface{}{"src_s1", "src_s2"}, + }, + map[string]interface{}{ + "a": "dst_a", + "field_not_extists": "remove", + "obj1": map[string]interface{}{ + "a": "dst_obj1_a", + "obj2": map[string]interface{}{ + "a": "dst_obj1_obj2_a", + }, + }, + "inline_str_1": "dst_inline_1", + "inline_str_2": "dst_inline_2", + "slice": []interface{}{"dst_s1", "dst_s2", "dst_s3"}, + }, + map[string]interface{}{ + "a": "dst_a", + "obj1": map[string]interface{}{ + "a": "dst_obj1_a", + "obj2": map[string]interface{}{ + "a": "dst_obj1_obj2_a", + }, + }, + "inline_str_1": "dst_inline_1", + "inline_str_2": "dst_inline_2", + "slice": []interface{}{"dst_s1", "dst_s2", "dst_s3"}, + }, + false, false, + }, + {"keep src changed", &WalkConfig{ Fields: map[string]FieldConfig{ "obj1.a": {Fn: KeepSrc}, @@ -96,6 +144,68 @@ func TestWalker_DataWalk(t *testing.T) { "inline_str_2": "src_inline_2", "slice": []interface{}{"dst_s1", "src_s2", "dst_s3"}, }, + true, false, + }, + {"keep src not changed", + &WalkConfig{ + Fields: map[string]FieldConfig{ + "obj1.a": {Fn: KeepSrc}, + "slice.1": {Fn: KeepSrc}, + "inline_str_1": {Fn: KeepSrc}, + "inline_str_2": {Fn: KeepSrc}, + }, + }, + map[string]interface{}{ + "a": "src_a", + "b": "src_b", + "obj1": map[string]interface{}{ + "a": "src_obj1_a", + "b": "src_obj1_b", + "obj2": map[string]interface{}{ + "a": "dst_obj1_obj2_a", + }, + "obj3": map[string]interface{}{ + "e": "dst_obj1_obj3_e", + }, + }, + "inline_str_1": "src_inline_1", + "inline_str_2": "src_inline_2", + "slice": []interface{}{"src_s1", "src_s2"}, + }, + map[string]interface{}{ + "a": "src_a", + "b": "src_b", + "obj1": map[string]interface{}{ + "a": "src_obj1_a", + "b": "src_obj1_b", + "obj2": map[string]interface{}{ + "a": "dst_obj1_obj2_a", + }, + "obj3": map[string]interface{}{ + "e": "dst_obj1_obj3_e", + }, + }, + "inline_str_1": "src_inline_1", + "inline_str_2": "src_inline_2", + "slice": []interface{}{"src_s1", "src_s2"}, + }, + map[string]interface{}{ + "a": "src_a", + "b": "src_b", + "obj1": map[string]interface{}{ + "a": "src_obj1_a", + "b": "src_obj1_b", + "obj2": map[string]interface{}{ + "a": "dst_obj1_obj2_a", + }, + "obj3": map[string]interface{}{ + "e": "dst_obj1_obj3_e", + }, + }, + "inline_str_1": "src_inline_1", + "inline_str_2": "src_inline_2", + "slice": []interface{}{"src_s1", "src_s2"}, + }, false, false, }, } @@ -103,11 +213,12 @@ func TestWalker_DataWalk(t *testing.T) { t.Run(tt.name, func(t *testing.T) { m := NewWalker(s, tt.config) dst := tt.dst - res, _, err := m.DataWalk(context.Background(), dst, tt.src) - assert.Equal(t, tt.res, res) + res, chg, err := m.DataWalk(context.Background(), dst, tt.src) if tt.wantErr { require.Error(t, err) } else { + assert.Equal(t, tt.res, res) + assert.Equal(t, tt.wantChanged, chg) require.NoError(t, err) } })