1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-09 09:35:03 +09:00

WIP work on components

This commit is contained in:
mcrakhman 2023-06-01 10:28:32 +02:00
parent 467536ac97
commit b0fa43fb14
No known key found for this signature in database
GPG key ID: DED12CFEF5B8396B
34 changed files with 1343 additions and 838 deletions

View file

@ -2,9 +2,9 @@ package settings
import (
"context"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/settings/settingsstate"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"go.uber.org/zap"
)
@ -15,11 +15,11 @@ type Deleter interface {
type deleter struct {
st spacestorage.SpaceStorage
state settingsstate.ObjectDeletionState
state deletionstate.ObjectDeletionState
getter treemanager.TreeManager
}
func newDeleter(st spacestorage.SpaceStorage, state settingsstate.ObjectDeletionState, getter treemanager.TreeManager) Deleter {
func newDeleter(st spacestorage.SpaceStorage, state deletionstate.ObjectDeletionState, getter treemanager.TreeManager) Deleter {
return &deleter{st, state, getter}
}

View file

@ -2,6 +2,7 @@ package settings
import (
"context"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/settings/settingsstate"
"go.uber.org/zap"
@ -20,7 +21,7 @@ func newDeletionManager(
settingsId string,
isResponsible bool,
treeManager treemanager.TreeManager,
deletionState settingsstate.ObjectDeletionState,
deletionState deletionstate.ObjectDeletionState,
provider SpaceIdsProvider,
onSpaceDelete func()) DeletionManager {
return &deletionManager{
@ -35,7 +36,7 @@ func newDeletionManager(
}
type deletionManager struct {
deletionState settingsstate.ObjectDeletionState
deletionState deletionstate.ObjectDeletionState
provider SpaceIdsProvider
treeManager treemanager.TreeManager
spaceId string

View file

@ -1,328 +1,88 @@
//go:generate mockgen -destination mock_settings/mock_settings.go github.com/anyproto/any-sync/commonspace/settings DeletionManager,Deleter,SpaceIdsProvider
package settings
import (
"context"
"errors"
"fmt"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/commonspace/headsync"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/settings/settingsstate"
"github.com/anyproto/any-sync/commonspace/objecttreebuilder"
"github.com/anyproto/any-sync/commonspace/spacestate"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/nodeconf"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
"golang.org/x/exp/slices"
)
var log = logger.NewNamed("common.commonspace.settings")
const CName = "common.commonspace.settings"
type SettingsObject interface {
synctree.SyncTree
Init(ctx context.Context) (err error)
DeleteObject(id string) (err error)
DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error)
SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error)
type Settings interface {
app.ComponentRunnable
}
var (
ErrDeleteSelf = errors.New("cannot delete self")
ErrAlreadyDeleted = errors.New("the object is already deleted")
ErrObjDoesNotExist = errors.New("the object does not exist")
ErrCantDeleteSpace = errors.New("not able to delete space")
)
type settings struct {
account accountservice.Service
treeManager treemanager.TreeManager
storage spacestorage.SpaceStorage
configuration nodeconf.NodeConf
deletionState deletionstate.ObjectDeletionState
headsync headsync.HeadSync
spaceActions spacestate.SpaceActions
treeBuilder objecttreebuilder.TreeBuilder
var (
DoSnapshot = objecttree.DoSnapshot
buildHistoryTree = func(objTree objecttree.ObjectTree) (objecttree.ReadableObjectTree, error) {
return objecttree.BuildHistoryTree(objecttree.HistoryTreeParams{
TreeStorage: objTree.Storage(),
AclList: objTree.AclList(),
BuildFullTree: true,
})
}
)
type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error)
type Deps struct {
BuildFunc BuildTreeFunc
Account accountservice.Service
TreeManager treemanager.TreeManager
Store spacestorage.SpaceStorage
Configuration nodeconf.NodeConf
DeletionState settingsstate.ObjectDeletionState
Provider SpaceIdsProvider
OnSpaceDelete func()
// testing dependencies
builder settingsstate.StateBuilder
del Deleter
delManager DeletionManager
changeFactory settingsstate.ChangeFactory
settingsObject SettingsObject
}
type settingsObject struct {
synctree.SyncTree
account accountservice.Service
spaceId string
treeManager treemanager.TreeManager
store spacestorage.SpaceStorage
builder settingsstate.StateBuilder
buildFunc BuildTreeFunc
loop *deleteLoop
state *settingsstate.State
deletionState settingsstate.ObjectDeletionState
deletionManager DeletionManager
changeFactory settingsstate.ChangeFactory
func (s *settings) Run(ctx context.Context) (err error) {
return s.settingsObject.Init(ctx)
}
func NewSettingsObject(deps Deps, spaceId string) (obj SettingsObject) {
var (
deleter Deleter
deletionManager DeletionManager
builder settingsstate.StateBuilder
changeFactory settingsstate.ChangeFactory
)
if deps.del == nil {
deleter = newDeleter(deps.Store, deps.DeletionState, deps.TreeManager)
} else {
deleter = deps.del
}
if deps.delManager == nil {
deletionManager = newDeletionManager(
spaceId,
deps.Store.SpaceSettingsId(),
deps.Configuration.IsResponsible(spaceId),
deps.TreeManager,
deps.DeletionState,
deps.Provider,
deps.OnSpaceDelete)
} else {
deletionManager = deps.delManager
}
if deps.builder == nil {
builder = settingsstate.NewStateBuilder()
} else {
builder = deps.builder
}
if deps.changeFactory == nil {
changeFactory = settingsstate.NewChangeFactory()
} else {
changeFactory = deps.changeFactory
}
loop := newDeleteLoop(func() {
deleter.Delete()
})
deps.DeletionState.AddObserver(func(ids []string) {
loop.notify()
})
s := &settingsObject{
loop: loop,
spaceId: spaceId,
account: deps.Account,
deletionState: deps.DeletionState,
treeManager: deps.TreeManager,
store: deps.Store,
buildFunc: deps.BuildFunc,
builder: builder,
deletionManager: deletionManager,
changeFactory: changeFactory,
}
obj = s
return
func (s *settings) Close(ctx context.Context) (err error) {
return s.settingsObject.Close()
}
func (s *settingsObject) updateIds(tr objecttree.ObjectTree) {
var err error
s.state, err = s.builder.Build(tr, s.state)
if err != nil {
log.Error("failed to build state", zap.Error(err))
return
}
log.Debug("updating object state", zap.String("deleted by", s.state.DeleterId))
if err = s.deletionManager.UpdateState(context.Background(), s.state); err != nil {
log.Error("failed to update state", zap.Error(err))
}
}
func (s *settings) Init(a *app.App) (err error) {
s.account = a.MustComponent(accountservice.CName).(accountservice.Service)
s.treeManager = a.MustComponent(treemanager.CName).(treemanager.TreeManager)
s.headsync = a.MustComponent(headsync.CName).(headsync.HeadSync)
s.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf)
s.deletionState = a.MustComponent(deletionstate.CName).(deletionstate.ObjectDeletionState)
s.treeBuilder = a.MustComponent(objecttreebuilder.CName).(objecttreebuilder.TreeBuilder)
// Update is called as part of UpdateListener interface
func (s *settingsObject) Update(tr objecttree.ObjectTree) {
s.updateIds(tr)
}
sharedState := a.MustComponent(spacestate.CName).(*spacestate.SpaceState)
s.spaceActions = sharedState.Actions
s.storage = sharedState.SpaceStorage
// Rebuild is called as part of UpdateListener interface (including when the object is built for the first time, e.g. on Init call)
func (s *settingsObject) Rebuild(tr objecttree.ObjectTree) {
// at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter
s.state = nil
s.updateIds(tr)
}
func (s *settingsObject) Init(ctx context.Context) (err error) {
settingsId := s.store.SpaceSettingsId()
log.Debug("space settings id", zap.String("id", settingsId))
s.SyncTree, err = s.buildFunc(ctx, settingsId, s)
if err != nil {
return
}
// TODO: remove this check when everybody updates
if err = s.checkHistoryState(ctx); err != nil {
return
}
s.loop.Run()
return
}
func (s *settingsObject) checkHistoryState(ctx context.Context) (err error) {
historyTree, err := buildHistoryTree(s.SyncTree)
if err != nil {
return
}
fullState, err := s.builder.Build(historyTree, nil)
if err != nil {
return
}
if len(fullState.DeletedIds) != len(s.state.DeletedIds) {
log.WarnCtx(ctx, "state does not have all deleted ids",
zap.Int("fullstate ids", len(fullState.DeletedIds)),
zap.Int("state ids", len(fullState.DeletedIds)))
s.state = fullState
err = s.deletionManager.UpdateState(context.Background(), s.state)
if err != nil {
deps := Deps{
BuildFunc: func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error) {
res, err := s.treeBuilder.BuildTree(ctx, id, objecttreebuilder.BuildTreeOpts{
Listener: listener,
WaitTreeRemoteSync: false,
// space settings document should not have empty data
TreeBuilder: objecttree.BuildObjectTree,
})
log.Debug("building settings tree", zap.String("id", id), zap.String("spaceId", sharedState.SpaceId))
if err != nil {
return
}
t = res.(synctree.SyncTree)
return
}
},
Account: s.account,
TreeManager: s.treeManager,
Store: s.storage,
Configuration: s.configuration,
DeletionState: s.deletionState,
Provider: s.headsync,
OnSpaceDelete: s.spaceActions.OnSpaceDelete,
}
return
s.settingsObject = NewSettingsObject(deps, sharedState.SpaceId)
return nil
}
func (s *settingsObject) Close() error {
s.loop.Close()
return s.SyncTree.Close()
}
func (s *settingsObject) DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error) {
s.Lock()
defer s.Unlock()
defer func() {
log.Debug("finished adding delete change", zap.Error(err))
}()
err = s.verifyDeleteSpace(raw)
if err != nil {
return
}
res, err := s.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: []string{raw.Id},
RawChanges: []*treechangeproto.RawTreeChangeWithId{raw},
})
if err != nil {
return
}
if !slices.Contains(res.Heads, raw.Id) {
err = ErrCantDeleteSpace
return
}
return
}
func (s *settingsObject) SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error) {
accountData := s.account.Account()
data, err := s.changeFactory.CreateSpaceDeleteChange(accountData.PeerId, s.state, false)
if err != nil {
return
}
return s.PrepareChange(objecttree.SignableChangeContent{
Data: data,
Key: accountData.SignKey,
IsSnapshot: false,
IsEncrypted: false,
})
}
func (s *settingsObject) DeleteObject(id string) (err error) {
s.Lock()
defer s.Unlock()
if s.Id() == id {
err = ErrDeleteSelf
return
}
if s.state.Exists(id) {
err = ErrAlreadyDeleted
return nil
}
_, err = s.store.TreeStorage(id)
if err != nil {
err = ErrObjDoesNotExist
return
}
isSnapshot := DoSnapshot(s.Len())
res, err := s.changeFactory.CreateObjectDeleteChange(id, s.state, isSnapshot)
if err != nil {
return
}
return s.addContent(res, isSnapshot)
}
func (s *settingsObject) verifyDeleteSpace(raw *treechangeproto.RawTreeChangeWithId) (err error) {
data, err := s.UnpackChange(raw)
if err != nil {
return
}
return verifyDeleteContent(data, "")
}
func (s *settingsObject) addContent(data []byte, isSnapshot bool) (err error) {
accountData := s.account.Account()
res, err := s.AddContent(context.Background(), objecttree.SignableChangeContent{
Data: data,
Key: accountData.SignKey,
IsSnapshot: isSnapshot,
IsEncrypted: false,
})
if err != nil {
return
}
if res.Mode == objecttree.Rebuild {
s.Rebuild(s)
} else {
s.Update(s)
}
return
}
func VerifyDeleteChange(raw *treechangeproto.RawTreeChangeWithId, identity crypto.PubKey, peerId string) (err error) {
changeBuilder := objecttree.NewChangeBuilder(crypto.NewKeyStorage(), nil)
res, err := changeBuilder.Unmarshall(raw, true)
if err != nil {
return
}
if !res.Identity.Equals(identity) {
return fmt.Errorf("incorrect identity")
}
return verifyDeleteContent(res.Data, peerId)
}
func verifyDeleteContent(data []byte, peerId string) (err error) {
content := &spacesyncproto.SettingsData{}
err = proto.Unmarshal(data, content)
if err != nil {
return
}
if len(content.GetContent()) != 1 ||
content.GetContent()[0].GetSpaceDelete() == nil ||
(peerId == "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() == "") ||
(peerId != "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() != peerId) {
return fmt.Errorf("incorrect delete change payload")
}
return
func (s *settings) Name() (name string) {
return CName
}

View file

@ -0,0 +1,329 @@
//go:generate mockgen -destination mock_settings/mock_settings.go github.com/anyproto/any-sync/commonspace/settings DeletionManager,Deleter,SpaceIdsProvider
package settings
import (
"context"
"errors"
"fmt"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/settings/settingsstate"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/nodeconf"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
"golang.org/x/exp/slices"
)
var log = logger.NewNamed("common.commonspace.settings")
type SettingsObject interface {
synctree.SyncTree
Init(ctx context.Context) (err error)
DeleteObject(id string) (err error)
DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error)
SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error)
}
var (
ErrDeleteSelf = errors.New("cannot delete self")
ErrAlreadyDeleted = errors.New("the object is already deleted")
ErrObjDoesNotExist = errors.New("the object does not exist")
ErrCantDeleteSpace = errors.New("not able to delete space")
)
var (
DoSnapshot = objecttree.DoSnapshot
buildHistoryTree = func(objTree objecttree.ObjectTree) (objecttree.ReadableObjectTree, error) {
return objecttree.BuildHistoryTree(objecttree.HistoryTreeParams{
TreeStorage: objTree.Storage(),
AclList: objTree.AclList(),
BuildFullTree: true,
})
}
)
type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error)
type Deps struct {
BuildFunc BuildTreeFunc
Account accountservice.Service
TreeManager treemanager.TreeManager
Store spacestorage.SpaceStorage
Configuration nodeconf.NodeConf
DeletionState deletionstate.ObjectDeletionState
Provider SpaceIdsProvider
OnSpaceDelete func()
// testing dependencies
builder settingsstate.StateBuilder
del Deleter
delManager DeletionManager
changeFactory settingsstate.ChangeFactory
}
type settingsObject struct {
synctree.SyncTree
account accountservice.Service
spaceId string
treeManager treemanager.TreeManager
store spacestorage.SpaceStorage
builder settingsstate.StateBuilder
buildFunc BuildTreeFunc
loop *deleteLoop
state *settingsstate.State
deletionState deletionstate.ObjectDeletionState
deletionManager DeletionManager
changeFactory settingsstate.ChangeFactory
}
func NewSettingsObject(deps Deps, spaceId string) (obj SettingsObject) {
var (
deleter Deleter
deletionManager DeletionManager
builder settingsstate.StateBuilder
changeFactory settingsstate.ChangeFactory
)
if deps.del == nil {
deleter = newDeleter(deps.Store, deps.DeletionState, deps.TreeManager)
} else {
deleter = deps.del
}
if deps.delManager == nil {
deletionManager = newDeletionManager(
spaceId,
deps.Store.SpaceSettingsId(),
deps.Configuration.IsResponsible(spaceId),
deps.TreeManager,
deps.DeletionState,
deps.Provider,
deps.OnSpaceDelete)
} else {
deletionManager = deps.delManager
}
if deps.builder == nil {
builder = settingsstate.NewStateBuilder()
} else {
builder = deps.builder
}
if deps.changeFactory == nil {
changeFactory = settingsstate.NewChangeFactory()
} else {
changeFactory = deps.changeFactory
}
loop := newDeleteLoop(func() {
deleter.Delete()
})
deps.DeletionState.AddObserver(func(ids []string) {
loop.notify()
})
s := &settingsObject{
loop: loop,
spaceId: spaceId,
account: deps.Account,
deletionState: deps.DeletionState,
treeManager: deps.TreeManager,
store: deps.Store,
buildFunc: deps.BuildFunc,
builder: builder,
deletionManager: deletionManager,
changeFactory: changeFactory,
}
obj = s
return
}
func (s *settingsObject) updateIds(tr objecttree.ObjectTree) {
var err error
s.state, err = s.builder.Build(tr, s.state)
if err != nil {
log.Error("failed to build state", zap.Error(err))
return
}
log.Debug("updating object state", zap.String("deleted by", s.state.DeleterId))
if err = s.deletionManager.UpdateState(context.Background(), s.state); err != nil {
log.Error("failed to update state", zap.Error(err))
}
}
// Update is called as part of UpdateListener interface
func (s *settingsObject) Update(tr objecttree.ObjectTree) {
s.updateIds(tr)
}
// Rebuild is called as part of UpdateListener interface (including when the object is built for the first time, e.g. on Init call)
func (s *settingsObject) Rebuild(tr objecttree.ObjectTree) {
// at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter
s.state = nil
s.updateIds(tr)
}
func (s *settingsObject) Init(ctx context.Context) (err error) {
settingsId := s.store.SpaceSettingsId()
log.Debug("space settings id", zap.String("id", settingsId))
s.SyncTree, err = s.buildFunc(ctx, settingsId, s)
if err != nil {
return
}
// TODO: remove this check when everybody updates
if err = s.checkHistoryState(ctx); err != nil {
return
}
s.loop.Run()
return
}
func (s *settingsObject) checkHistoryState(ctx context.Context) (err error) {
historyTree, err := buildHistoryTree(s.SyncTree)
if err != nil {
return
}
fullState, err := s.builder.Build(historyTree, nil)
if err != nil {
return
}
if len(fullState.DeletedIds) != len(s.state.DeletedIds) {
log.WarnCtx(ctx, "state does not have all deleted ids",
zap.Int("fullstate ids", len(fullState.DeletedIds)),
zap.Int("state ids", len(fullState.DeletedIds)))
s.state = fullState
err = s.deletionManager.UpdateState(context.Background(), s.state)
if err != nil {
return
}
}
return
}
func (s *settingsObject) Close() error {
s.loop.Close()
return s.SyncTree.Close()
}
func (s *settingsObject) DeleteSpace(ctx context.Context, raw *treechangeproto.RawTreeChangeWithId) (err error) {
s.Lock()
defer s.Unlock()
defer func() {
log.Debug("finished adding delete change", zap.Error(err))
}()
err = s.verifyDeleteSpace(raw)
if err != nil {
return
}
res, err := s.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: []string{raw.Id},
RawChanges: []*treechangeproto.RawTreeChangeWithId{raw},
})
if err != nil {
return
}
if !slices.Contains(res.Heads, raw.Id) {
err = ErrCantDeleteSpace
return
}
return
}
func (s *settingsObject) SpaceDeleteRawChange() (raw *treechangeproto.RawTreeChangeWithId, err error) {
accountData := s.account.Account()
data, err := s.changeFactory.CreateSpaceDeleteChange(accountData.PeerId, s.state, false)
if err != nil {
return
}
return s.PrepareChange(objecttree.SignableChangeContent{
Data: data,
Key: accountData.SignKey,
IsSnapshot: false,
IsEncrypted: false,
})
}
func (s *settingsObject) DeleteObject(id string) (err error) {
s.Lock()
defer s.Unlock()
if s.Id() == id {
err = ErrDeleteSelf
return
}
if s.state.Exists(id) {
err = ErrAlreadyDeleted
return nil
}
_, err = s.store.TreeStorage(id)
if err != nil {
err = ErrObjDoesNotExist
return
}
isSnapshot := DoSnapshot(s.Len())
res, err := s.changeFactory.CreateObjectDeleteChange(id, s.state, isSnapshot)
if err != nil {
return
}
return s.addContent(res, isSnapshot)
}
func (s *settingsObject) verifyDeleteSpace(raw *treechangeproto.RawTreeChangeWithId) (err error) {
data, err := s.UnpackChange(raw)
if err != nil {
return
}
return verifyDeleteContent(data, "")
}
func (s *settingsObject) addContent(data []byte, isSnapshot bool) (err error) {
accountData := s.account.Account()
res, err := s.AddContent(context.Background(), objecttree.SignableChangeContent{
Data: data,
Key: accountData.SignKey,
IsSnapshot: isSnapshot,
IsEncrypted: false,
})
if err != nil {
return
}
if res.Mode == objecttree.Rebuild {
s.Rebuild(s)
} else {
s.Update(s)
}
return
}
func VerifyDeleteChange(raw *treechangeproto.RawTreeChangeWithId, identity crypto.PubKey, peerId string) (err error) {
changeBuilder := objecttree.NewChangeBuilder(crypto.NewKeyStorage(), nil)
res, err := changeBuilder.Unmarshall(raw, true)
if err != nil {
return
}
if !res.Identity.Equals(identity) {
return fmt.Errorf("incorrect identity")
}
return verifyDeleteContent(res.Data, peerId)
}
func verifyDeleteContent(data []byte, peerId string) (err error) {
content := &spacesyncproto.SettingsData{}
err = proto.Unmarshal(data, content)
if err != nil {
return
}
if len(content.GetContent()) != 1 ||
content.GetContent()[0].GetSpaceDelete() == nil ||
(peerId == "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() == "") ||
(peerId != "" && content.GetContent()[0].GetSpaceDelete().GetDeleterPeerId() != peerId) {
return fmt.Errorf("incorrect delete change payload")
}
return
}

View file

@ -1,3 +1,4 @@
//go:generate mockgen -destination mock_settingsstate/mock_settingsstate.go github.com/anyproto/any-sync/commonspace/settings/settingsstate ObjectDeletionState,StateBuilder,ChangeFactory
package settingsstate
import "github.com/anyproto/any-sync/commonspace/spacesyncproto"