diff --git a/core/acl/aclservice.go b/core/acl/aclservice.go index 684ed8e45..6c6c9da86 100644 --- a/core/acl/aclservice.go +++ b/core/acl/aclservice.go @@ -387,7 +387,7 @@ func (a *aclService) ViewInvite(ctx context.Context, inviteCid cid.Cid, inviteFi if err != nil { return inviteservice.InviteView{}, convertedOrAclRequestError(err) } - lst, err := list.BuildAclList(store, list.NoOpAcceptorVerifier{}) + lst, err := list.BuildAclListWithIdentity(a.accountService.Keys(), store, list.NoOpAcceptorVerifier{}) if err != nil { return inviteservice.InviteView{}, convertedOrAclRequestError(err) } diff --git a/core/acl/aclservice_test.go b/core/acl/aclservice_test.go index a44b9cccb..bd29e5abc 100644 --- a/core/acl/aclservice_test.go +++ b/core/acl/aclservice_test.go @@ -282,6 +282,7 @@ func TestService_ViewInvite(t *testing.T) { defer fx.finish(t) keys, err := accountdata.NewRandom() require.NoError(t, err) + fx.mockAccountService.EXPECT().Keys().Return(keys) aclList, err := list.NewTestDerivedAcl("spaceId", keys) require.NoError(t, err) inv, err := aclList.RecordBuilder().BuildInvite() @@ -312,6 +313,7 @@ func TestService_ViewInvite(t *testing.T) { defer fx.finish(t) keys, err := accountdata.NewRandom() require.NoError(t, err) + fx.mockAccountService.EXPECT().Keys().Return(keys) aclList, err := list.NewTestDerivedAcl("spaceId", keys) require.NoError(t, err) inv, err := aclList.RecordBuilder().BuildInvite() diff --git a/core/anytype/account/mock_account/mock_Service.go b/core/anytype/account/mock_account/mock_Service.go index d389928d0..e21056f7e 100644 --- a/core/anytype/account/mock_account/mock_Service.go +++ b/core/anytype/account/mock_account/mock_Service.go @@ -3,9 +3,11 @@ package mock_account import ( - app "github.com/anyproto/any-sync/app" + accountdata "github.com/anyproto/any-sync/commonspace/object/accountdata" account "github.com/anyproto/anytype-heart/core/anytype/account" + app "github.com/anyproto/any-sync/app" + context "context" mock "github.com/stretchr/testify/mock" @@ -232,6 +234,53 @@ func (_c *MockService_Init_Call) RunAndReturn(run func(*app.App) error) *MockSer return _c } +// Keys provides a mock function with given fields: +func (_m *MockService) Keys() *accountdata.AccountKeys { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Keys") + } + + var r0 *accountdata.AccountKeys + if rf, ok := ret.Get(0).(func() *accountdata.AccountKeys); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*accountdata.AccountKeys) + } + } + + return r0 +} + +// MockService_Keys_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Keys' +type MockService_Keys_Call struct { + *mock.Call +} + +// Keys is a helper method to define mock.On call +func (_e *MockService_Expecter) Keys() *MockService_Keys_Call { + return &MockService_Keys_Call{Call: _e.mock.On("Keys")} +} + +func (_c *MockService_Keys_Call) Run(run func()) *MockService_Keys_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockService_Keys_Call) Return(_a0 *accountdata.AccountKeys) *MockService_Keys_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockService_Keys_Call) RunAndReturn(run func() *accountdata.AccountKeys) *MockService_Keys_Call { + _c.Call.Return(run) + return _c +} + // MyParticipantId provides a mock function with given fields: _a0 func (_m *MockService) MyParticipantId(_a0 string) string { ret := _m.Called(_a0) diff --git a/core/anytype/account/service.go b/core/anytype/account/service.go index 5621a9065..67bb6b68e 100644 --- a/core/anytype/account/service.go +++ b/core/anytype/account/service.go @@ -6,7 +6,9 @@ import ( "path/filepath" "sync" + "github.com/anyproto/any-sync/accountservice" "github.com/anyproto/any-sync/app" + "github.com/anyproto/any-sync/commonspace/object/accountdata" "github.com/anyproto/any-sync/coordinator/coordinatorclient" "github.com/anyproto/any-sync/coordinator/coordinatorproto" "github.com/anyproto/any-sync/nodeconf" @@ -42,6 +44,7 @@ type Service interface { // ProfileObjectId returns id of Profile object stored in personal space ProfileObjectId() (string, error) ProfileInfo() (Profile, error) + Keys() *accountdata.AccountKeys } type service struct { @@ -51,9 +54,9 @@ type service struct { gateway gateway.Gateway config *config.Config objectStore objectstore.ObjectStore - - nodeConf nodeconf.Service - coordClient coordinatorclient.CoordinatorClient + keyProvider accountservice.Service + nodeConf nodeconf.Service + coordClient coordinatorclient.CoordinatorClient picker cache.ObjectGetter once sync.Once @@ -71,6 +74,7 @@ func (s *service) Init(a *app.App) (err error) { s.gateway = app.MustComponent[gateway.Gateway](a) s.nodeConf = app.MustComponent[nodeconf.Service](a) s.coordClient = app.MustComponent[coordinatorclient.CoordinatorClient](a) + s.keyProvider = app.MustComponent[accountservice.Service](a) s.config = app.MustComponent[*config.Config](a) s.picker = app.MustComponent[cache.ObjectGetter](a) s.objectStore = app.MustComponent[objectstore.ObjectStore](a) @@ -78,6 +82,10 @@ func (s *service) Init(a *app.App) (err error) { return } +func (s *service) Keys() *accountdata.AccountKeys { + return s.keyProvider.Account() +} + func (s *service) Delete(ctx context.Context) (toBeDeleted int64, err error) { confirm, err := coordinatorproto.PrepareAccountDeleteConfirmation(s.wallet.GetAccountPrivkey(), s.wallet.GetDevicePrivkey().GetPublic().PeerId(), s.nodeConf.Configuration().NetworkId) if err != nil { diff --git a/core/block/editor/participant.go b/core/block/editor/participant.go index c1abdc568..42f0c71c4 100644 --- a/core/block/editor/participant.go +++ b/core/block/editor/participant.go @@ -82,7 +82,7 @@ func (p *participant) TryClose(objectTTL time.Duration) (bool, error) { func (p *participant) modifyDetails(newDetails *types.Struct) (err error) { return p.DetailsUpdatable.UpdateDetails(func(current *types.Struct) (*types.Struct, error) { - return pbtypes.StructMerge(p.CombinedDetails(), newDetails, false), nil + return pbtypes.StructMerge(current, newDetails, false), nil }) } diff --git a/core/block/import/common/collection.go b/core/block/import/common/collection.go index 3ece3eed0..7c89aa743 100644 --- a/core/block/import/common/collection.go +++ b/core/block/import/common/collection.go @@ -124,7 +124,7 @@ func ReplaceRelationsInDataView(st *state.State, rel *model.RelationLink) error err := dv.ReplaceViewRelation(view.Id, rel.Key, &model.BlockContentDataviewRelation{ Key: rel.Key, IsVisible: true, - Width: 192, + Width: simpleDataview.DefaultViewRelationWidth, }) if err != nil { return true diff --git a/core/block/import/common/common.go b/core/block/import/common/common.go index a037e95bd..ab499dbf2 100644 --- a/core/block/import/common/common.go +++ b/core/block/import/common/common.go @@ -346,7 +346,7 @@ func AddRelationsToDataView(collectionState *state.State, relationLink *model.Re err := dataView.AddViewRelation(view.GetId(), &model.BlockContentDataviewRelation{ Key: relationLink.Key, IsVisible: true, - Width: 192, + Width: dataview.DefaultViewRelationWidth, }) if err != nil { return true diff --git a/core/block/import/csv/tablestrategy.go b/core/block/import/csv/tablestrategy.go index 5336abcb5..0c3983f58 100644 --- a/core/block/import/csv/tablestrategy.go +++ b/core/block/import/csv/tablestrategy.go @@ -1,6 +1,7 @@ package csv import ( + "github.com/globalsign/mgo/bson" "github.com/google/uuid" "github.com/anyproto/anytype-heart/core/block/editor/state" @@ -97,7 +98,7 @@ func (c *TableStrategy) createEmptyHeader(st *state.State, tableID string, colum } for _, colID := range columnIDs { textBlock := &model.Block{ - Id: uuid.New().String(), + Id: bson.NewObjectId().Hex(), Content: &model.BlockContentOfText{ Text: &model.BlockContentText{Text: ""}, }, @@ -123,7 +124,7 @@ func (c *TableStrategy) createCells(columns []string, st *state.State, rowID str continue } textBlock := &model.Block{ - Id: uuid.New().String(), + Id: bson.NewObjectId().Hex(), Content: &model.BlockContentOfText{ Text: &model.BlockContentText{Text: columns[i]}, }, diff --git a/core/block/import/markdown/anymark/blocks_renderer.go b/core/block/import/markdown/anymark/blocks_renderer.go index 57b9d0e0a..b8df7ac21 100644 --- a/core/block/import/markdown/anymark/blocks_renderer.go +++ b/core/block/import/markdown/anymark/blocks_renderer.go @@ -10,7 +10,6 @@ import ( "github.com/globalsign/mgo/bson" "github.com/gogo/protobuf/types" - "github.com/google/uuid" "github.com/anyproto/anytype-heart/pkg/lib/pb/model" "github.com/anyproto/anytype-heart/util/text" @@ -125,7 +124,7 @@ func (r *blocksRenderer) OpenNewTextBlock(style model.BlockContentTextStyle, fie r.curStyledBlock = style } - id := uuid.New().String() + id := bson.NewObjectId().Hex() newBlock := model.Block{ Id: id, @@ -277,7 +276,7 @@ func (r *blocksRenderer) CloseTextBlock(content model.BlockContentTextStyle) { var closingBlock *textBlock var parentBlock *textBlock - id := uuid.New().String() + id := bson.NewObjectId().Hex() if len(r.openedTextBlocks) > 0 { closingBlock = r.openedTextBlocks[len(r.openedTextBlocks)-1] diff --git a/core/block/import/markdown/import.go b/core/block/import/markdown/import.go index 5a25e1e88..5804835a6 100644 --- a/core/block/import/markdown/import.go +++ b/core/block/import/markdown/import.go @@ -9,7 +9,6 @@ import ( "github.com/globalsign/mgo/bson" "github.com/gogo/protobuf/types" - "github.com/google/uuid" "github.com/anyproto/anytype-heart/core/block/collection" "github.com/anyproto/anytype-heart/core/block/import/common" @@ -524,7 +523,7 @@ func (m *Markdown) setNewID(files map[string]*FileInfo, progress process.Progres } if strings.EqualFold(filepath.Ext(name), ".md") || strings.EqualFold(filepath.Ext(name), ".csv") { - file.PageID = uuid.New().String() + file.PageID = bson.NewObjectId().Hex() m.setDetails(file, name, details) } diff --git a/core/block/import/notion/api/page/page.go b/core/block/import/notion/api/page/page.go index 46d32913f..9ddd126a2 100644 --- a/core/block/import/notion/api/page/page.go +++ b/core/block/import/notion/api/page/page.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/google/uuid" + "github.com/globalsign/mgo/bson" "github.com/anyproto/anytype-heart/core/block/import/common" "github.com/anyproto/anytype-heart/core/block/import/common/workerpool" @@ -152,7 +152,7 @@ func (ds *Service) fillNotionImportContext(pages []Page, progress process.Progre if err := progress.TryStep(1); err != nil { return common.NewCancelError(err) } - importContext.NotionPageIdsToAnytype[p.ID] = uuid.New().String() + importContext.NotionPageIdsToAnytype[p.ID] = bson.NewObjectId().Hex() if p.Parent.PageID != "" { importContext.PageTree.ParentPageToChildIDs[p.Parent.PageID] = append(importContext.PageTree.ParentPageToChildIDs[p.Parent.PageID], p.ID) } diff --git a/core/block/simple/dataview/views.go b/core/block/simple/dataview/views.go index 1de890454..a44ca9bda 100644 --- a/core/block/simple/dataview/views.go +++ b/core/block/simple/dataview/views.go @@ -2,11 +2,14 @@ package dataview import ( "github.com/globalsign/mgo/bson" + "golang.org/x/exp/slices" "github.com/anyproto/anytype-heart/pkg/lib/pb/model" "github.com/anyproto/anytype-heart/util/slice" ) +const DefaultViewRelationWidth = 192 + func (l *Dataview) AddFilter(viewID string, filter *model.BlockContentDataviewFilter) error { l.resetObjectOrderForView(viewID) @@ -155,6 +158,7 @@ func (l *Dataview) AddViewRelation(viewID string, relation *model.BlockContentDa if err != nil { return err } + l.syncViewRelationWithRelationLinks(view) view.Relations = append(view.Relations, relation) return nil @@ -165,6 +169,7 @@ func (l *Dataview) RemoveViewRelations(viewID string, relationKeys []string) err if err != nil { return err } + l.syncViewRelationWithRelationLinks(view) view.Relations = slice.Filter(view.Relations, func(f *model.BlockContentDataviewRelation) bool { return slice.FindPos(relationKeys, f.Key) == -1 @@ -177,6 +182,7 @@ func (l *Dataview) ReplaceViewRelation(viewID string, relationKey string, relati if err != nil { return err } + l.syncViewRelationWithRelationLinks(view) idx := slice.Find(view.Relations, func(f *model.BlockContentDataviewRelation) bool { return f.Key == relationKey @@ -195,21 +201,58 @@ func (l *Dataview) ReorderViewRelations(viewID string, relationKeys []string) er if err != nil { return err } + l.syncViewRelationWithRelationLinks(view) relationsMap := make(map[string]*model.BlockContentDataviewRelation) for _, r := range view.Relations { relationsMap[r.Key] = r - } - view.Relations = view.Relations[:0] - for _, key := range relationKeys { - if r, ok := relationsMap[key]; ok { - view.Relations = append(view.Relations, r) + // Add missing relation keys to requested order + if !slices.Contains(relationKeys, r.Key) { + relationKeys = append(relationKeys, r.Key) } } + + newRelations := make([]*model.BlockContentDataviewRelation, 0, len(view.Relations)) + for _, key := range relationKeys { + // Ignore relations that don't present in view's relations + if r, ok := relationsMap[key]; ok { + newRelations = append(newRelations, r) + } + } + view.Relations = newRelations return nil } +func (l *Dataview) syncViewRelationWithRelationLinks(view *model.BlockContentDataviewView) { + relationLinksKeys := map[string]struct{}{} + for _, relLink := range l.content.RelationLinks { + relationLinksKeys[relLink.Key] = struct{}{} + } + + currentViewKeys := map[string]struct{}{} + newViewRelations := view.Relations[:0] + for _, rel := range view.Relations { + // Don't add relations that are not in relation links + if _, ok := relationLinksKeys[rel.Key]; ok { + newViewRelations = append(newViewRelations, rel) + currentViewKeys[rel.Key] = struct{}{} + } + } + + for _, relLink := range l.content.RelationLinks { + _, ok := currentViewKeys[relLink.Key] + if !ok { + newViewRelations = append(newViewRelations, &model.BlockContentDataviewRelation{ + Key: relLink.Key, + Width: DefaultViewRelationWidth, + IsVisible: false, + }) + } + } + view.Relations = newViewRelations +} + func (l *Dataview) setRelationFormat(filter *model.BlockContentDataviewFilter) { for _, relLink := range l.content.RelationLinks { if relLink.Key == filter.RelationKey { diff --git a/core/block/simple/dataview/views_test.go b/core/block/simple/dataview/views_test.go new file mode 100644 index 000000000..2b88040cc --- /dev/null +++ b/core/block/simple/dataview/views_test.go @@ -0,0 +1,93 @@ +package dataview + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anyproto/anytype-heart/pkg/lib/bundle" + "github.com/anyproto/anytype-heart/pkg/lib/pb/model" +) + +const testViewId = "viewId" + +func makeDataviewForReorderTest(relationLinks []*model.RelationLink, relations []*model.BlockContentDataviewRelation) Block { + return NewDataview(&model.Block{ + Content: &model.BlockContentOfDataview{ + Dataview: &model.BlockContentDataview{ + RelationLinks: relationLinks, + Views: []*model.BlockContentDataviewView{ + { + Id: testViewId, + Relations: relations, + }, + }, + }, + }, + }).(Block) +} + +func TestReorderViewRelations(t *testing.T) { + t.Run("reorder: add missing relation from relation links", func(t *testing.T) { + dv := makeDataviewForReorderTest( + []*model.RelationLink{ + {Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext}, + {Key: bundle.RelationKeyCreator.String(), Format: model.RelationFormat_object}, + {Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date}, + }, + []*model.BlockContentDataviewRelation{ + {Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth}, + }, + ) + + err := dv.ReorderViewRelations(testViewId, []string{bundle.RelationKeyCreator.String(), bundle.RelationKeyName.String()}) + require.NoError(t, err) + + want := makeDataviewForReorderTest( + []*model.RelationLink{ + {Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext}, + {Key: bundle.RelationKeyCreator.String(), Format: model.RelationFormat_object}, + {Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date}, + }, + []*model.BlockContentDataviewRelation{ + {Key: bundle.RelationKeyCreator.String(), IsVisible: false, Width: DefaultViewRelationWidth}, + {Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth}, + {Key: bundle.RelationKeyCreatedDate.String(), IsVisible: false, Width: DefaultViewRelationWidth}, + }, + ) + + assert.Equal(t, want, dv) + }) + + t.Run("reorder: remove extra relation that don't exist in relation links", func(t *testing.T) { + dv := makeDataviewForReorderTest( + []*model.RelationLink{ + {Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext}, + {Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date}, + }, + []*model.BlockContentDataviewRelation{ + {Key: bundle.RelationKeyCreator.String(), IsVisible: false, Width: DefaultViewRelationWidth}, + {Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth}, + {Key: bundle.RelationKeyCreatedDate.String(), IsVisible: false, Width: DefaultViewRelationWidth}, + }, + ) + + err := dv.ReorderViewRelations(testViewId, []string{bundle.RelationKeyName.String(), bundle.RelationKeyCreator.String(), bundle.RelationKeyDescription.String()}) + require.NoError(t, err) + + want := makeDataviewForReorderTest( + []*model.RelationLink{ + {Key: bundle.RelationKeyName.String(), Format: model.RelationFormat_longtext}, + {Key: bundle.RelationKeyCreatedDate.String(), Format: model.RelationFormat_date}, + }, + []*model.BlockContentDataviewRelation{ + {Key: bundle.RelationKeyName.String(), IsVisible: true, Width: DefaultViewRelationWidth}, + {Key: bundle.RelationKeyCreatedDate.String(), IsVisible: false, Width: DefaultViewRelationWidth}, + }, + ) + + assert.Equal(t, want, dv) + }) + +} diff --git a/core/block/source/source.go b/core/block/source/source.go index 5db5e1cc4..ef20b5909 100644 --- a/core/block/source/source.go +++ b/core/block/source/source.go @@ -95,6 +95,10 @@ func UnmarshalChange(treeChange *objecttree.Change, data []byte) (result any, er return } +func UnmarshalChangeWithDataType(dataType string, decrypted []byte) (res any, err error) { + return UnmarshalChange(&objecttree.Change{DataType: dataType}, decrypted) +} + type ChangeReceiver interface { StateAppend(func(d state.Doc) (s *state.State, changes []*pb.ChangeContent, err error)) error StateRebuild(d state.Doc) (err error) diff --git a/core/debug/changedataconverter.go b/core/debug/changedataconverter.go index 716ca64bd..9d905e445 100644 --- a/core/debug/changedataconverter.go +++ b/core/debug/changedataconverter.go @@ -3,8 +3,7 @@ package debug import ( "fmt" - "github.com/gogo/protobuf/proto" - + "github.com/anyproto/anytype-heart/core/block/source" "github.com/anyproto/anytype-heart/pb" "github.com/anyproto/anytype-heart/util/anonymize" ) @@ -13,22 +12,18 @@ type changeDataConverter struct { anonymize bool } -func (c *changeDataConverter) Unmarshall(decrypted []byte) (res any, err error) { - ch := &pb.Change{} - err = proto.Unmarshal(decrypted, ch) - if err != nil { - return nil, err - } - return ch, nil +func (c *changeDataConverter) Unmarshall(dataType string, decrypted []byte) (res any, err error) { + return source.UnmarshalChangeWithDataType(dataType, decrypted) } -func (c *changeDataConverter) Marshall(model any) ([]byte, error) { +func (c *changeDataConverter) Marshall(model any) (data []byte, dataType string, err error) { ch, ok := model.(*pb.Change) if !ok { - return nil, fmt.Errorf("can't convert the model") + return nil, "", fmt.Errorf("can't convert the model") } if c.anonymize { ch = anonymize.Change(ch) } - return ch.Marshal() + data, err = ch.Marshal() + return } diff --git a/core/files/fileobject/service_test.go b/core/files/fileobject/service_test.go index eee81b37b..d7b6fb06d 100644 --- a/core/files/fileobject/service_test.go +++ b/core/files/fileobject/service_test.go @@ -60,6 +60,7 @@ func newFixture(t *testing.T) *fixture { commonFileService := fileservice.New() fileSyncService := filesync.New() eventSender := mock_event.NewMockSender(t) + eventSender.EXPECT().Broadcast(mock.Anything).Return().Maybe() fileService := files.New() spaceService := mock_space.NewMockService(t) spaceIdResolver := mock_idresolver.NewMockResolver(t) diff --git a/core/files/files_test.go b/core/files/files_test.go index a8fff969b..645887b01 100644 --- a/core/files/files_test.go +++ b/core/files/files_test.go @@ -56,6 +56,7 @@ func newFixture(t *testing.T) *fixture { fileSyncService := filesync.New() objectStore := objectstore.NewStoreFixture(t) eventSender := mock_event.NewMockSender(t) + eventSender.EXPECT().Broadcast(mock.Anything).Return().Maybe() ctx := context.Background() a := new(app.App) diff --git a/core/files/fileuploader/uploader_test.go b/core/files/fileuploader/uploader_test.go index cfbdc67f2..5a61a4dc4 100644 --- a/core/files/fileuploader/uploader_test.go +++ b/core/files/fileuploader/uploader_test.go @@ -187,6 +187,7 @@ func newFileServiceFixture(t *testing.T) files.Service { fileSyncService := filesync.New() objectStore := objectstore.NewStoreFixture(t) eventSender := mock_event.NewMockSender(t) + eventSender.EXPECT().Broadcast(mock.Anything).Return().Maybe() ctx := context.Background() a := new(app.App) diff --git a/core/filestorage/filesync/stats.go b/core/filestorage/filesync/stats.go index e89ac5b7f..97dab6755 100644 --- a/core/filestorage/filesync/stats.go +++ b/core/filestorage/filesync/stats.go @@ -185,14 +185,15 @@ func (s *fileSync) getAndUpdateNodeUsage(ctx context.Context) (NodeUsage, error) return NodeUsage{}, fmt.Errorf("save node usage info to store: %w", err) } + if !prevUsageFound || prevUsage.AccountBytesLimit != usage.AccountBytesLimit { + s.sendLimitUpdatedEvent(uint64(usage.AccountBytesLimit)) + } + for _, space := range spaces { if !prevUsageFound || prevUsage.GetSpaceUsage(space.SpaceId).SpaceBytesUsage != space.SpaceBytesUsage { s.sendSpaceUsageEvent(space.SpaceId, uint64(space.SpaceBytesUsage)) } } - if !prevUsageFound || prevUsage.AccountBytesLimit != usage.AccountBytesLimit { - s.sendLimitUpdatedEvent(uint64(usage.AccountBytesLimit)) - } return usage, nil } diff --git a/core/filestorage/filesync/stats_test.go b/core/filestorage/filesync/stats_test.go index 2ecb32102..242e71004 100644 --- a/core/filestorage/filesync/stats_test.go +++ b/core/filestorage/filesync/stats_test.go @@ -9,6 +9,10 @@ import ( "github.com/anyproto/anytype-heart/pb" ) +type limitSetter interface { + SetLimit(limit int) +} + func TestSpaceUsageUpdate(t *testing.T) { const limit = 1024 * 1024 * 1024 fx := newFixture(t, limit) @@ -71,13 +75,27 @@ func TestSpaceUsageUpdate(t *testing.T) { assert.Equal(t, fileSize2, uint64(spaceUsage.SpaceBytesUsage)) }) + t.Run("update limit", func(t *testing.T) { + setter, ok := fx.rpcStore.(limitSetter) + require.True(t, ok) + + setter.SetLimit(limit * 10) + + err = fx.UpdateNodeUsage(ctx) + require.NoError(t, err) + + // Event is expected to be sent + }) + t.Run("events sent", func(t *testing.T) { fx.eventsLock.Lock() defer fx.eventsLock.Unlock() wantEvents := []*pb.Event{ + makeLimitUpdatedEvent(limit), makeSpaceUsageEvent("space1", fileSize1), makeSpaceUsageEvent("space2", fileSize2), + makeLimitUpdatedEvent(limit * 10), } assert.Equal(t, wantEvents, fx.events) diff --git a/core/filestorage/rpcstore/inmemory.go b/core/filestorage/rpcstore/inmemory.go index 7f1f5d481..537f22730 100644 --- a/core/filestorage/rpcstore/inmemory.go +++ b/core/filestorage/rpcstore/inmemory.go @@ -180,6 +180,12 @@ func (t *inMemoryStore) SpaceInfo(ctx context.Context, spaceId string) (*filepro panic("not implemented") } +func (t *inMemoryStore) SetLimit(limit int) { + t.mu.Lock() + defer t.mu.Unlock() + t.limit = limit +} + func (t *inMemoryStore) AccountInfo(ctx context.Context) (*fileproto.AccountInfoResponse, error) { var info fileproto.AccountInfoResponse t.mu.Lock() diff --git a/go.mod b/go.mod index 0673d2e98..3935f2172 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/PuerkitoBio/goquery v1.9.1 github.com/VividCortex/ewma v1.2.0 github.com/adrium/goheif v0.0.0-20230113233934-ca402e77a786 - github.com/anyproto/any-sync v0.4.5-0.20240417114039-321a9ca30bd1 + github.com/anyproto/any-sync v0.4.5 github.com/anyproto/go-naturaldate/v2 v2.0.2-0.20230524105841-9829cfd13438 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/avast/retry-go/v4 v4.5.1 diff --git a/go.sum b/go.sum index dacb1ed84..0945f2095 100644 --- a/go.sum +++ b/go.sum @@ -89,12 +89,10 @@ github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxB github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/anyproto/any-sync v0.4.4 h1:/nlgv8oItRptlmvM2ctn7WyriQ4U4ztW5fhfAiWBJtU= -github.com/anyproto/any-sync v0.4.4/go.mod h1:C/byc9JTwLSqhaHd0wCf1+ncU4iDQCMaXuZj4yOKDYw= -github.com/anyproto/any-sync v0.4.5-0.20240417104247-8514732467b2 h1:wQND3pN/cLiy2EDVE9dheAvRYDWSh0xU4soGgRyQRKs= -github.com/anyproto/any-sync v0.4.5-0.20240417104247-8514732467b2/go.mod h1:C/byc9JTwLSqhaHd0wCf1+ncU4iDQCMaXuZj4yOKDYw= -github.com/anyproto/any-sync v0.4.5-0.20240417114039-321a9ca30bd1 h1:MPFx7ra5wK7ANu8n0WSPOh7/J4U3Neq4VXhWUbvDVRA= -github.com/anyproto/any-sync v0.4.5-0.20240417114039-321a9ca30bd1/go.mod h1:C/byc9JTwLSqhaHd0wCf1+ncU4iDQCMaXuZj4yOKDYw= +github.com/anyproto/any-sync v0.4.5-0.20240417164638-dcc4a1124a13 h1:wpBInpIoNS0xLH8m7dw7GaH2skDEh/6sOD6RfK42lSk= +github.com/anyproto/any-sync v0.4.5-0.20240417164638-dcc4a1124a13/go.mod h1:C/byc9JTwLSqhaHd0wCf1+ncU4iDQCMaXuZj4yOKDYw= +github.com/anyproto/any-sync v0.4.5 h1:E+FATSRxMpaBt1GgmHfi3VkNKSC5SftpwgLa883Wy3M= +github.com/anyproto/any-sync v0.4.5/go.mod h1:C/byc9JTwLSqhaHd0wCf1+ncU4iDQCMaXuZj4yOKDYw= github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580 h1:Ba80IlCCxkZ9H1GF+7vFu/TSpPvbpDCxXJ5ogc4euYc= github.com/anyproto/badger/v4 v4.2.1-0.20240110160636-80743fa3d580/go.mod h1:T/uWAYxrXdaXw64ihI++9RMbKTCpKd/yE9+saARew7k= github.com/anyproto/go-chash v0.1.0 h1:I9meTPjXFRfXZHRJzjOHC/XF7Q5vzysKkiT/grsogXY= diff --git a/pkg/lib/datastore/clientds/clientds.go b/pkg/lib/datastore/clientds/clientds.go index bb4cc6258..d03a7f54a 100644 --- a/pkg/lib/datastore/clientds/clientds.go +++ b/pkg/lib/datastore/clientds/clientds.go @@ -42,7 +42,8 @@ type clientds struct { repoPath string spaceStoreWasMissing, localStoreWasMissing bool spentOnInit time.Duration - closed chan struct{} + closing chan struct{} + syncerFinished chan struct{} } type Config struct { @@ -106,7 +107,8 @@ func openBadgerWithRecover(opts badger.Options) (db *badger.DB, err error) { func (r *clientds) Init(a *app.App) (err error) { // TODO: looks like we do a lot of stuff on Init here. We should consider moving it to the Run - r.closed = make(chan struct{}) + r.closing = make(chan struct{}) + r.syncerFinished = make(chan struct{}) start := time.Now() wl := a.Component(wallet.CName) if wl == nil { @@ -204,7 +206,9 @@ func (r *clientds) Name() (name string) { } func (r *clientds) Close(ctx context.Context) (err error) { - close(r.closed) + close(r.closing) + // wait syncer goroutine to finish to make sure we don't have in-progress requests, because it may cause panics + <-r.syncerFinished if r.localstoreDS != nil { err2 := r.localstoreDS.Close() if err2 != nil { diff --git a/pkg/lib/datastore/clientds/syncer.go b/pkg/lib/datastore/clientds/syncer.go index 88c795562..504c01b90 100644 --- a/pkg/lib/datastore/clientds/syncer.go +++ b/pkg/lib/datastore/clientds/syncer.go @@ -54,6 +54,7 @@ func newDbSyncer(db *badger.DB) *dbSyncer { } func (r *clientds) syncer() error { + defer close(r.syncerFinished) var syncers []*dbSyncer if r.spaceDS != nil { syncers = append(syncers, newDbSyncer(r.spaceDS)) @@ -64,10 +65,16 @@ func (r *clientds) syncer() error { for { select { - case <-r.closed: + case <-r.closing: return nil case <-time.After(SyncDbAfterInactivity): for _, syncer := range syncers { + select { + case <-r.closing: + // exit fast in case Close() is already called + return nil + default: + } maxVersion := syncer.db.MaxVersion() if syncer.LastMaxVersionSynced == maxVersion { continue