Merge pull request #17 from anytypeio/object-delete
This commit is contained in:
commit
f749e967f1
@ -23,6 +23,8 @@ type Controller interface {
|
|||||||
|
|
||||||
// CreateDocument creates new document in space
|
// CreateDocument creates new document in space
|
||||||
CreateDocument(spaceId string) (id string, err error)
|
CreateDocument(spaceId string) (id string, err error)
|
||||||
|
// DeleteDocument deletes a document from space
|
||||||
|
DeleteDocument(spaceId, documentId string) (err error)
|
||||||
// AllDocumentIds gets all ids of documents in space
|
// AllDocumentIds gets all ids of documents in space
|
||||||
AllDocumentIds(spaceId string) (ids []string, err error)
|
AllDocumentIds(spaceId string) (ids []string, err error)
|
||||||
// AddText adds text to space document
|
// AddText adds text to space document
|
||||||
@ -97,6 +99,10 @@ func (c *controller) CreateDocument(spaceId string) (id string, err error) {
|
|||||||
return c.docService.CreateDocument(spaceId)
|
return c.docService.CreateDocument(spaceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *controller) DeleteDocument(spaceId, documentId string) (err error) {
|
||||||
|
return c.docService.DeleteDocument(spaceId, documentId)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *controller) AllDocumentIds(spaceId string) (ids []string, err error) {
|
func (c *controller) AllDocumentIds(spaceId string) (ids []string, err error) {
|
||||||
return c.docService.AllDocumentIds(spaceId)
|
return c.docService.AllDocumentIds(spaceId)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,6 +65,7 @@ func (s *service) Run(ctx context.Context) (err error) {
|
|||||||
mux.HandleFunc("/loadSpace", s.loadSpace)
|
mux.HandleFunc("/loadSpace", s.loadSpace)
|
||||||
mux.HandleFunc("/allSpaceIds", s.allSpaceIds)
|
mux.HandleFunc("/allSpaceIds", s.allSpaceIds)
|
||||||
mux.HandleFunc("/createDocument", s.createDocument)
|
mux.HandleFunc("/createDocument", s.createDocument)
|
||||||
|
mux.HandleFunc("/deleteDocument", s.deleteDocument)
|
||||||
mux.HandleFunc("/allDocumentIds", s.allDocumentIds)
|
mux.HandleFunc("/allDocumentIds", s.allDocumentIds)
|
||||||
mux.HandleFunc("/addText", s.addText)
|
mux.HandleFunc("/addText", s.addText)
|
||||||
mux.HandleFunc("/dumpDocumentTree", s.dumpDocumentTree)
|
mux.HandleFunc("/dumpDocumentTree", s.dumpDocumentTree)
|
||||||
@ -134,6 +135,18 @@ func (s *service) createDocument(w http.ResponseWriter, req *http.Request) {
|
|||||||
sendText(w, http.StatusOK, id)
|
sendText(w, http.StatusOK, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) deleteDocument(w http.ResponseWriter, req *http.Request) {
|
||||||
|
query := req.URL.Query()
|
||||||
|
spaceId := query.Get("spaceId")
|
||||||
|
documentId := query.Get("documentId")
|
||||||
|
err := s.controller.DeleteDocument(spaceId, documentId)
|
||||||
|
if err != nil {
|
||||||
|
sendText(w, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendText(w, http.StatusOK, documentId)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) allDocumentIds(w http.ResponseWriter, req *http.Request) {
|
func (s *service) allDocumentIds(w http.ResponseWriter, req *http.Request) {
|
||||||
query := req.URL.Query()
|
query := req.URL.Query()
|
||||||
spaceId := query.Get("spaceId")
|
spaceId := query.Get("spaceId")
|
||||||
|
|||||||
@ -103,6 +103,20 @@ func (c *treeCache) GetTree(ctx context.Context, spaceId, id string) (tr tree.Ob
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tr = doc.Tree()
|
// we have to do this trick, otherwise the compiler won't understand that TextDocument conforms to SyncHandler interface
|
||||||
|
tr = doc.InnerTree()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *treeCache) DeleteTree(ctx context.Context, spaceId, treeId string) (err error) {
|
||||||
|
tr, err := c.GetTree(ctx, spaceId, treeId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tr.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.cache.Remove(treeId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,22 +19,34 @@ func (r *rpcHandler) PullSpace(ctx context.Context, request *spacesyncproto.Pull
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
description := sp.Description()
|
spaceDesc, err := sp.Description()
|
||||||
|
if err != nil {
|
||||||
|
err = spacesyncproto.ErrUnexpected
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp = &spacesyncproto.PullSpaceResponse{
|
resp = &spacesyncproto.PullSpaceResponse{
|
||||||
SpaceHeader: description.SpaceHeader,
|
Payload: &spacesyncproto.SpacePayload{
|
||||||
AclPayload: description.AclPayload,
|
SpaceHeader: spaceDesc.SpaceHeader,
|
||||||
AclPayloadId: description.AclId,
|
AclPayloadId: spaceDesc.AclId,
|
||||||
|
AclPayload: spaceDesc.AclPayload,
|
||||||
|
SpaceSettingsPayload: spaceDesc.SpaceSettingsPayload,
|
||||||
|
SpaceSettingsPayloadId: spaceDesc.SpaceSettingsId,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcHandler) PushSpace(ctx context.Context, req *spacesyncproto.PushSpaceRequest) (resp *spacesyncproto.PushSpaceResponse, err error) {
|
func (r *rpcHandler) PushSpace(ctx context.Context, req *spacesyncproto.PushSpaceRequest) (resp *spacesyncproto.PushSpaceResponse, err error) {
|
||||||
description := commonspace.SpaceDescription{
|
description := commonspace.SpaceDescription{
|
||||||
SpaceHeader: req.SpaceHeader,
|
SpaceHeader: req.Payload.SpaceHeader,
|
||||||
AclId: req.AclPayloadId,
|
AclId: req.Payload.AclPayloadId,
|
||||||
AclPayload: req.AclPayload,
|
AclPayload: req.Payload.AclPayload,
|
||||||
|
SpaceSettingsPayload: req.Payload.SpaceSettingsPayload,
|
||||||
|
SpaceSettingsId: req.Payload.SpaceSettingsPayloadId,
|
||||||
}
|
}
|
||||||
err = r.s.AddSpace(ctx, description)
|
ctx = context.WithValue(ctx, commonspace.AddSpaceCtxKey, description)
|
||||||
|
_, err = r.s.GetSpace(ctx, description.SpaceHeader.GetId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,6 @@ func New() Service {
|
|||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
GetSpace(ctx context.Context, id string) (commonspace.Space, error)
|
GetSpace(ctx context.Context, id string) (commonspace.Space, error)
|
||||||
AddSpace(ctx context.Context, description commonspace.SpaceDescription) (err error)
|
|
||||||
CreateSpace(ctx context.Context, payload commonspace.SpaceCreatePayload) (commonspace.Space, error)
|
CreateSpace(ctx context.Context, payload commonspace.SpaceCreatePayload) (commonspace.Space, error)
|
||||||
DeriveSpace(ctx context.Context, payload commonspace.SpaceDerivePayload) (commonspace.Space, error)
|
DeriveSpace(ctx context.Context, payload commonspace.SpaceDerivePayload) (commonspace.Space, error)
|
||||||
app.ComponentRunnable
|
app.ComponentRunnable
|
||||||
@ -91,10 +90,6 @@ func (s *service) GetSpace(ctx context.Context, id string) (container commonspac
|
|||||||
return v.(commonspace.Space), nil
|
return v.(commonspace.Space), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) AddSpace(ctx context.Context, description commonspace.SpaceDescription) (err error) {
|
|
||||||
return s.commonSpace.AddSpace(ctx, description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) loadSpace(ctx context.Context, id string) (value ocache.Object, err error) {
|
func (s *service) loadSpace(ctx context.Context, id string) (value ocache.Object, err error) {
|
||||||
cc, err := s.commonSpace.NewSpace(ctx, id)
|
cc, err := s.commonSpace.NewSpace(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
type Service interface {
|
type Service interface {
|
||||||
app.Component
|
app.Component
|
||||||
CreateDocument(spaceId string) (id string, err error)
|
CreateDocument(spaceId string) (id string, err error)
|
||||||
|
DeleteDocument(spaceId, documentId string) (err error)
|
||||||
AllDocumentIds(spaceId string) (ids []string, err error)
|
AllDocumentIds(spaceId string) (ids []string, err error)
|
||||||
AddText(spaceId, documentId, text string) (err error)
|
AddText(spaceId, documentId, text string) (err error)
|
||||||
DumpDocumentTree(spaceId, documentId string) (dump string, err error)
|
DumpDocumentTree(spaceId, documentId string) (dump string, err error)
|
||||||
@ -53,10 +54,18 @@ func (s *service) CreateDocument(spaceId string) (id string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
id = doc.Tree().ID()
|
id = doc.ID()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) DeleteDocument(spaceId, documentId string) (err error) {
|
||||||
|
space, err := s.spaceService.GetSpace(context.Background(), spaceId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return space.DeleteTree(context.Background(), documentId)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) AllDocumentIds(spaceId string) (ids []string, err error) {
|
func (s *service) AllDocumentIds(spaceId string) (ids []string, err error) {
|
||||||
space, err := s.spaceService.GetSpace(context.Background(), spaceId)
|
space, err := s.spaceService.GetSpace(context.Background(), spaceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -79,5 +88,5 @@ func (s *service) DumpDocumentTree(spaceId, documentId string) (dump string, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return doc.Tree().DebugDump()
|
return doc.DebugDump()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TextDocument interface {
|
type TextDocument interface {
|
||||||
Tree() tree.ObjectTree
|
tree.ObjectTree
|
||||||
|
InnerTree() tree.ObjectTree
|
||||||
AddText(text string) error
|
AddText(text string) error
|
||||||
Text() (string, error)
|
Text() (string, error)
|
||||||
TreeDump() string
|
TreeDump() string
|
||||||
@ -19,7 +20,7 @@ type TextDocument interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type textDocument struct {
|
type textDocument struct {
|
||||||
objTree tree.ObjectTree
|
tree.ObjectTree
|
||||||
account account.Service
|
account account.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +40,8 @@ func CreateTextDocument(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &textDocument{
|
return &textDocument{
|
||||||
objTree: t,
|
ObjectTree: t,
|
||||||
account: account,
|
account: account,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,13 +51,13 @@ func NewTextDocument(ctx context.Context, space commonspace.Space, id string, li
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
return &textDocument{
|
return &textDocument{
|
||||||
objTree: t,
|
ObjectTree: t,
|
||||||
account: account,
|
account: account,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *textDocument) Tree() tree.ObjectTree {
|
func (t *textDocument) InnerTree() tree.ObjectTree {
|
||||||
return t.objTree
|
return t.ObjectTree
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *textDocument) AddText(text string) (err error) {
|
func (t *textDocument) AddText(text string) (err error) {
|
||||||
@ -73,9 +74,9 @@ func (t *textDocument) AddText(text string) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.objTree.Lock()
|
t.Lock()
|
||||||
defer t.objTree.Unlock()
|
defer t.Unlock()
|
||||||
_, err = t.objTree.AddContent(context.Background(), tree.SignableChangeContent{
|
_, err = t.AddContent(context.Background(), tree.SignableChangeContent{
|
||||||
Data: res,
|
Data: res,
|
||||||
Key: t.account.Account().SignKey,
|
Key: t.account.Account().SignKey,
|
||||||
Identity: t.account.Account().Identity,
|
Identity: t.account.Account().Identity,
|
||||||
@ -85,10 +86,10 @@ func (t *textDocument) AddText(text string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *textDocument) Text() (text string, err error) {
|
func (t *textDocument) Text() (text string, err error) {
|
||||||
t.objTree.RLock()
|
t.RLock()
|
||||||
defer t.objTree.RUnlock()
|
defer t.RUnlock()
|
||||||
|
|
||||||
err = t.objTree.Iterate(
|
err = t.Iterate(
|
||||||
func(decrypted []byte) (any, error) {
|
func(decrypted []byte) (any, error) {
|
||||||
textChange := &testchanges.TextData{}
|
textChange := &testchanges.TextData{}
|
||||||
err = proto.Unmarshal(decrypted, textChange)
|
err = proto.Unmarshal(decrypted, textChange)
|
||||||
@ -110,7 +111,3 @@ func (t *textDocument) Text() (text string, err error) {
|
|||||||
func (t *textDocument) TreeDump() string {
|
func (t *textDocument) TreeDump() string {
|
||||||
return t.TreeDump()
|
return t.TreeDump()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *textDocument) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -31,18 +31,20 @@ func (a aclKeys) RawRecordKey(id string) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type treeKeys struct {
|
type treeKeys struct {
|
||||||
id string
|
id string
|
||||||
spaceId string
|
spaceId string
|
||||||
headsKey []byte
|
headsKey []byte
|
||||||
rootKey []byte
|
rootKey []byte
|
||||||
|
rawChangePrefix []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTreeKeys(spaceId, id string) treeKeys {
|
func newTreeKeys(spaceId, id string) treeKeys {
|
||||||
return treeKeys{
|
return treeKeys{
|
||||||
id: id,
|
id: id,
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
headsKey: storage.JoinStringsToBytes("space", spaceId, "t", id, "heads"),
|
headsKey: storage.JoinStringsToBytes("space", spaceId, "t", id, "heads"),
|
||||||
rootKey: storage.JoinStringsToBytes("space", spaceId, "t", "rootId", id),
|
rootKey: storage.JoinStringsToBytes("space", spaceId, "t", "rootId", id),
|
||||||
|
rawChangePrefix: storage.JoinStringsToBytes("space", spaceId, "t", id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,15 +60,23 @@ func (t treeKeys) RawChangeKey(id string) []byte {
|
|||||||
return storage.JoinStringsToBytes("space", t.spaceId, "t", t.id, id)
|
return storage.JoinStringsToBytes("space", t.spaceId, "t", t.id, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t treeKeys) RawChangePrefix() []byte {
|
||||||
|
return t.rawChangePrefix
|
||||||
|
}
|
||||||
|
|
||||||
type spaceKeys struct {
|
type spaceKeys struct {
|
||||||
headerKey []byte
|
spaceId string
|
||||||
treePrefixKey []byte
|
headerKey []byte
|
||||||
|
treePrefixKey []byte
|
||||||
|
spaceSettingsIdKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSpaceKeys(spaceId string) spaceKeys {
|
func newSpaceKeys(spaceId string) spaceKeys {
|
||||||
return spaceKeys{
|
return spaceKeys{
|
||||||
headerKey: storage.JoinStringsToBytes("space", "header", spaceId),
|
spaceId: spaceId,
|
||||||
treePrefixKey: storage.JoinStringsToBytes("space", spaceId, "t", "rootId"),
|
headerKey: storage.JoinStringsToBytes("space", "header", spaceId),
|
||||||
|
treePrefixKey: storage.JoinStringsToBytes("space", spaceId, "t", "rootId"),
|
||||||
|
spaceSettingsIdKey: storage.JoinStringsToBytes("space", spaceId, "spaceSettingsId"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +88,14 @@ func (s spaceKeys) TreeRootPrefix() []byte {
|
|||||||
return s.treePrefixKey
|
return s.treePrefixKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s spaceKeys) SpaceSettingsId() []byte {
|
||||||
|
return s.spaceSettingsIdKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s spaceKeys) TreeDeletedKey(id string) []byte {
|
||||||
|
return storage.JoinStringsToBytes("space", s.spaceId, "deleted", id)
|
||||||
|
}
|
||||||
|
|
||||||
type storageServiceKeys struct {
|
type storageServiceKeys struct {
|
||||||
spacePrefix []byte
|
spacePrefix []byte
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,19 +4,21 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
"github.com/dgraph-io/badger/v3"
|
"github.com/dgraph-io/badger/v3"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type spaceStorage struct {
|
type spaceStorage struct {
|
||||||
spaceId string
|
spaceId string
|
||||||
objDb *badger.DB
|
spaceSettingsId string
|
||||||
keys spaceKeys
|
objDb *badger.DB
|
||||||
aclStorage storage.ListStorage
|
keys spaceKeys
|
||||||
header *spacesyncproto.RawSpaceHeaderWithId
|
aclStorage storage.ListStorage
|
||||||
mx sync.Mutex
|
header *spacesyncproto.RawSpaceHeaderWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var spaceValidationFunc = spacestorage.ValidateSpaceStorageCreatePayload
|
||||||
|
|
||||||
func newSpaceStorage(objDb *badger.DB, spaceId string) (store spacestorage.SpaceStorage, err error) {
|
func newSpaceStorage(objDb *badger.DB, spaceId string) (store spacestorage.SpaceStorage, err error) {
|
||||||
keys := newSpaceKeys(spaceId)
|
keys := newSpaceKeys(spaceId)
|
||||||
err = objDb.View(func(txn *badger.Txn) error {
|
err = objDb.View(func(txn *badger.Txn) error {
|
||||||
@ -30,10 +32,15 @@ func newSpaceStorage(objDb *badger.DB, spaceId string) (store spacestorage.Space
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spaceSettingsId, err := getTxn(txn, keys.SpaceSettingsId())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
store = &spaceStorage{
|
store = &spaceStorage{
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
objDb: objDb,
|
spaceSettingsId: string(spaceSettingsId),
|
||||||
keys: keys,
|
objDb: objDb,
|
||||||
|
keys: keys,
|
||||||
header: &spacesyncproto.RawSpaceHeaderWithId{
|
header: &spacesyncproto.RawSpaceHeaderWithId{
|
||||||
RawHeader: header,
|
RawHeader: header,
|
||||||
Id: spaceId,
|
Id: spaceId,
|
||||||
@ -54,8 +61,32 @@ func createSpaceStorage(db *badger.DB, payload spacestorage.SpaceStorageCreatePa
|
|||||||
err = spacesyncproto.ErrSpaceExists
|
err = spacesyncproto.ErrSpaceExists
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = spaceValidationFunc(payload)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceStore := &spaceStorage{
|
||||||
|
spaceId: payload.SpaceHeaderWithId.Id,
|
||||||
|
objDb: db,
|
||||||
|
keys: keys,
|
||||||
|
spaceSettingsId: payload.SpaceSettingsWithId.Id,
|
||||||
|
header: payload.SpaceHeaderWithId,
|
||||||
|
}
|
||||||
|
_, err = spaceStore.CreateTreeStorage(storage.TreeStorageCreatePayload{
|
||||||
|
RootRawChange: payload.SpaceSettingsWithId,
|
||||||
|
Changes: []*treechangeproto.RawTreeChangeWithId{payload.SpaceSettingsWithId},
|
||||||
|
Heads: []string{payload.SpaceSettingsWithId.Id},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
err = db.Update(func(txn *badger.Txn) error {
|
err = db.Update(func(txn *badger.Txn) error {
|
||||||
aclStorage, err := createListStorage(payload.SpaceHeaderWithId.Id, db, txn, payload.RecWithId)
|
err = txn.Set(keys.SpaceSettingsId(), []byte(payload.SpaceSettingsWithId.Id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
aclStorage, err := createListStorage(payload.SpaceHeaderWithId.Id, db, txn, payload.AclWithId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -65,15 +96,10 @@ func createSpaceStorage(db *badger.DB, payload spacestorage.SpaceStorageCreatePa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
store = &spaceStorage{
|
spaceStore.aclStorage = aclStorage
|
||||||
spaceId: payload.SpaceHeaderWithId.Id,
|
|
||||||
objDb: db,
|
|
||||||
keys: keys,
|
|
||||||
aclStorage: aclStorage,
|
|
||||||
header: payload.SpaceHeaderWithId,
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
store = spaceStore
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,15 +107,15 @@ func (s *spaceStorage) Id() string {
|
|||||||
return s.spaceId
|
return s.spaceId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *spaceStorage) SpaceSettingsId() string {
|
||||||
|
return s.spaceSettingsId
|
||||||
|
}
|
||||||
|
|
||||||
func (s *spaceStorage) TreeStorage(id string) (storage.TreeStorage, error) {
|
func (s *spaceStorage) TreeStorage(id string) (storage.TreeStorage, error) {
|
||||||
return newTreeStorage(s.objDb, s.spaceId, id)
|
return newTreeStorage(s.objDb, s.spaceId, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *spaceStorage) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
|
func (s *spaceStorage) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
|
||||||
// we have mutex here, so we prevent overwriting the heads of a tree on concurrent creation
|
|
||||||
s.mx.Lock()
|
|
||||||
defer s.mx.Unlock()
|
|
||||||
|
|
||||||
return createTreeStorage(s.objDb, s.spaceId, payload)
|
return createTreeStorage(s.objDb, s.spaceId, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +138,8 @@ func (s *spaceStorage) StoredIds() (ids []string, err error) {
|
|||||||
|
|
||||||
for it.Rewind(); it.Valid(); it.Next() {
|
for it.Rewind(); it.Valid(); it.Next() {
|
||||||
item := it.Item()
|
item := it.Item()
|
||||||
id := item.Key()
|
id := make([]byte, 0, len(item.Key()))
|
||||||
|
id = item.KeyCopy(id)
|
||||||
if len(id) <= len(s.keys.TreeRootPrefix())+1 {
|
if len(id) <= len(s.keys.TreeRootPrefix())+1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -124,6 +151,27 @@ func (s *spaceStorage) StoredIds() (ids []string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *spaceStorage) SetTreeDeletedStatus(id, status string) (err error) {
|
||||||
|
return s.objDb.Update(func(txn *badger.Txn) error {
|
||||||
|
return txn.Set(s.keys.TreeDeletedKey(id), []byte(status))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *spaceStorage) TreeDeletedStatus(id string) (status string, err error) {
|
||||||
|
err = s.objDb.View(func(txn *badger.Txn) error {
|
||||||
|
res, err := getTxn(txn, s.keys.TreeDeletedKey(id))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
status = string(res)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == badger.ErrKeyNotFound {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *spaceStorage) Close() (err error) {
|
func (s *spaceStorage) Close() (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -18,9 +20,14 @@ func spaceTestPayload() spacestorage.SpaceStorageCreatePayload {
|
|||||||
Payload: []byte("aclRoot"),
|
Payload: []byte("aclRoot"),
|
||||||
Id: "aclRootId",
|
Id: "aclRootId",
|
||||||
}
|
}
|
||||||
|
settings := &treechangeproto.RawTreeChangeWithId{
|
||||||
|
RawChange: []byte("settings"),
|
||||||
|
Id: "settingsId",
|
||||||
|
}
|
||||||
return spacestorage.SpaceStorageCreatePayload{
|
return spacestorage.SpaceStorageCreatePayload{
|
||||||
RecWithId: aclRoot,
|
AclWithId: aclRoot,
|
||||||
SpaceHeaderWithId: header,
|
SpaceHeaderWithId: header,
|
||||||
|
SpaceSettingsWithId: settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +38,7 @@ func testSpace(t *testing.T, store spacestorage.SpaceStorage, payload spacestora
|
|||||||
|
|
||||||
aclStorage, err := store.ACLStorage()
|
aclStorage, err := store.ACLStorage()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testList(t, aclStorage, payload.RecWithId, payload.RecWithId.Id)
|
testList(t, aclStorage, payload.AclWithId, payload.AclWithId.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSpaceStorage_Create(t *testing.T) {
|
func TestSpaceStorage_Create(t *testing.T) {
|
||||||
@ -68,7 +75,7 @@ func TestSpaceStorage_NewAndCreateTree(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testSpace(t, store, payload)
|
testSpace(t, store, payload)
|
||||||
|
|
||||||
t.Run("create tree and get tree", func(t *testing.T) {
|
t.Run("create tree, get tree and mark deleted", func(t *testing.T) {
|
||||||
payload := treeTestPayload()
|
payload := treeTestPayload()
|
||||||
treeStore, err := store.CreateTreeStorage(payload)
|
treeStore, err := store.CreateTreeStorage(payload)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -77,6 +84,14 @@ func TestSpaceStorage_NewAndCreateTree(t *testing.T) {
|
|||||||
otherStore, err := store.TreeStorage(payload.RootRawChange.Id)
|
otherStore, err := store.TreeStorage(payload.RootRawChange.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testTreePayload(t, otherStore, payload)
|
testTreePayload(t, otherStore, payload)
|
||||||
|
|
||||||
|
initialStatus := "deleted"
|
||||||
|
err = store.SetTreeDeletedStatus(otherStore.Id(), initialStatus)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
status, err := store.TreeDeletedStatus(otherStore.Id())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, initialStatus, status)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,8 +116,11 @@ func TestSpaceStorage_StoredIds(t *testing.T) {
|
|||||||
_, err := store.CreateTreeStorage(treePayload)
|
_, err := store.CreateTreeStorage(treePayload)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
ids = append(ids, payload.SpaceSettingsWithId.Id)
|
||||||
|
sort.Strings(ids)
|
||||||
|
|
||||||
storedIds, err := store.StoredIds()
|
storedIds, err := store.StoredIds()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
sort.Strings(storedIds)
|
||||||
require.Equal(t, ids, storedIds)
|
require.Equal(t, ids, storedIds)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -136,3 +136,46 @@ func (t *treeStorage) GetRawChange(ctx context.Context, id string) (raw *treecha
|
|||||||
func (t *treeStorage) HasChange(ctx context.Context, id string) (bool, error) {
|
func (t *treeStorage) HasChange(ctx context.Context, id string) (bool, error) {
|
||||||
return hasDB(t.db, t.keys.RawChangeKey(id)), nil
|
return hasDB(t.db, t.keys.RawChangeKey(id)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *treeStorage) Delete() (err error) {
|
||||||
|
storedKeys, err := t.storedKeys()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = t.db.Update(func(txn *badger.Txn) error {
|
||||||
|
for _, k := range storedKeys {
|
||||||
|
err = txn.Delete(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *treeStorage) storedKeys() (keys [][]byte, err error) {
|
||||||
|
err = t.db.View(func(txn *badger.Txn) error {
|
||||||
|
opts := badger.DefaultIteratorOptions
|
||||||
|
opts.PrefetchValues = false
|
||||||
|
// this will get all raw changes and also "heads"
|
||||||
|
opts.Prefix = t.keys.RawChangePrefix()
|
||||||
|
|
||||||
|
it := txn.NewIterator(opts)
|
||||||
|
defer it.Close()
|
||||||
|
|
||||||
|
for it.Rewind(); it.Valid(); it.Next() {
|
||||||
|
item := it.Item()
|
||||||
|
key := item.Key()
|
||||||
|
keyCopy := make([]byte, 0, len(key))
|
||||||
|
keyCopy = item.KeyCopy(keyCopy)
|
||||||
|
keys = append(keys, keyCopy)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keys = append(keys, t.keys.RootIdKey())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -61,6 +61,42 @@ func (fx *fixture) stop(t *testing.T) {
|
|||||||
require.NoError(t, fx.db.Close())
|
require.NoError(t, fx.db.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fx *fixture) testNoKeysExist(t *testing.T, spaceId, treeId string) {
|
||||||
|
treeKeys := newTreeKeys(spaceId, treeId)
|
||||||
|
|
||||||
|
var keys [][]byte
|
||||||
|
err := fx.db.View(func(txn *badger.Txn) error {
|
||||||
|
opts := badger.DefaultIteratorOptions
|
||||||
|
opts.PrefetchValues = false
|
||||||
|
opts.Prefix = treeKeys.RawChangePrefix()
|
||||||
|
|
||||||
|
it := txn.NewIterator(opts)
|
||||||
|
defer it.Close()
|
||||||
|
|
||||||
|
for it.Rewind(); it.Valid(); it.Next() {
|
||||||
|
item := it.Item()
|
||||||
|
key := item.Key()
|
||||||
|
keyCopy := make([]byte, 0, len(key))
|
||||||
|
keyCopy = item.KeyCopy(key)
|
||||||
|
keys = append(keys, keyCopy)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(keys))
|
||||||
|
|
||||||
|
err = fx.db.View(func(txn *badger.Txn) error {
|
||||||
|
_, err = getTxn(txn, treeKeys.RootIdKey())
|
||||||
|
require.Equal(t, err, badger.ErrKeyNotFound)
|
||||||
|
|
||||||
|
_, err = getTxn(txn, treeKeys.HeadsKey())
|
||||||
|
require.Equal(t, err, badger.ErrKeyNotFound)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestTreeStorage_Create(t *testing.T) {
|
func TestTreeStorage_Create(t *testing.T) {
|
||||||
fx := newFixture(t)
|
fx := newFixture(t)
|
||||||
fx.open(t)
|
fx.open(t)
|
||||||
@ -121,3 +157,32 @@ func TestTreeStorage_Methods(t *testing.T) {
|
|||||||
require.False(t, has)
|
require.False(t, has)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTreeStorage_Delete(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
fx.open(t)
|
||||||
|
payload := treeTestPayload()
|
||||||
|
spaceId := "spaceId"
|
||||||
|
_, err := createTreeStorage(fx.db, spaceId, payload)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fx.stop(t)
|
||||||
|
|
||||||
|
fx.open(t)
|
||||||
|
defer fx.stop(t)
|
||||||
|
store, err := newTreeStorage(fx.db, spaceId, payload.RootRawChange.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
testTreePayload(t, store, payload)
|
||||||
|
|
||||||
|
t.Run("add raw change, get change and has change", func(t *testing.T) {
|
||||||
|
newChange := &treechangeproto.RawTreeChangeWithId{RawChange: []byte("ab"), Id: "newId"}
|
||||||
|
require.NoError(t, store.AddRawChange(newChange))
|
||||||
|
|
||||||
|
err = store.Delete()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = newTreeStorage(fx.db, spaceId, payload.RootRawChange.Id)
|
||||||
|
require.Equal(t, err, storage.ErrUnknownTreeId)
|
||||||
|
|
||||||
|
fx.testNoKeysExist(t, spaceId, payload.RootRawChange.Id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
78
common/account/mock_account/mock_account.go
Normal file
78
common/account/mock_account/mock_account.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/account (interfaces: Service)
|
||||||
|
|
||||||
|
// Package mock_account is a generated GoMock package.
|
||||||
|
package mock_account
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
app "github.com/anytypeio/go-anytype-infrastructure-experiments/common/app"
|
||||||
|
account "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/account"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockService is a mock of Service interface.
|
||||||
|
type MockService struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockServiceMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockServiceMockRecorder is the mock recorder for MockService.
|
||||||
|
type MockServiceMockRecorder struct {
|
||||||
|
mock *MockService
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockService creates a new mock instance.
|
||||||
|
func NewMockService(ctrl *gomock.Controller) *MockService {
|
||||||
|
mock := &MockService{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockServiceMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockService) EXPECT() *MockServiceMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account mocks base method.
|
||||||
|
func (m *MockService) Account() *account.AccountData {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Account")
|
||||||
|
ret0, _ := ret[0].(*account.AccountData)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account indicates an expected call of Account.
|
||||||
|
func (mr *MockServiceMockRecorder) Account() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Account", reflect.TypeOf((*MockService)(nil).Account))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init mocks base method.
|
||||||
|
func (m *MockService) Init(arg0 *app.App) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Init", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init indicates an expected call of Init.
|
||||||
|
func (mr *MockServiceMockRecorder) Init(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockService)(nil).Init), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name mocks base method.
|
||||||
|
func (m *MockService) Name() string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Name")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name indicates an expected call of Name.
|
||||||
|
func (mr *MockServiceMockRecorder) Name() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockService)(nil).Name))
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
//go:generate mockgen -destination mock_account/mock_account.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/account Service
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package commonspace
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/objectgetter"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/objectgetter"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncacl"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncacl"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
||||||
)
|
)
|
||||||
@ -11,13 +12,15 @@ type commonSpaceGetter struct {
|
|||||||
spaceId string
|
spaceId string
|
||||||
aclList *syncacl.SyncACL
|
aclList *syncacl.SyncACL
|
||||||
treeGetter treegetter.TreeGetter
|
treeGetter treegetter.TreeGetter
|
||||||
|
settings settingsdocument.SettingsDocument
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCommonSpaceGetter(spaceId string, aclList *syncacl.SyncACL, treeGetter treegetter.TreeGetter) objectgetter.ObjectGetter {
|
func newCommonSpaceGetter(spaceId string, aclList *syncacl.SyncACL, treeGetter treegetter.TreeGetter, settings settingsdocument.SettingsDocument) objectgetter.ObjectGetter {
|
||||||
return &commonSpaceGetter{
|
return &commonSpaceGetter{
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
aclList: aclList,
|
aclList: aclList,
|
||||||
treeGetter: treeGetter,
|
treeGetter: treeGetter,
|
||||||
|
settings: settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +29,10 @@ func (c *commonSpaceGetter) GetObject(ctx context.Context, objectId string) (obj
|
|||||||
obj = c.aclList
|
obj = c.aclList
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if c.settings.ID() == objectId {
|
||||||
|
obj = c.settings.(objectgetter.Object)
|
||||||
|
return
|
||||||
|
}
|
||||||
t, err := c.treeGetter.GetTree(ctx, c.spaceId, objectId)
|
t, err := c.treeGetter.GetTree(ctx, c.spaceId, objectId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
28
common/commonspace/commonstorage.go
Normal file
28
common/commonspace/commonstorage.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package commonspace
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
|
treestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commonStorage struct {
|
||||||
|
storage.SpaceStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCommonStorage(spaceStorage storage.SpaceStorage) storage.SpaceStorage {
|
||||||
|
return &commonStorage{
|
||||||
|
SpaceStorage: spaceStorage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *commonStorage) CreateTreeStorage(payload treestorage.TreeStorageCreatePayload) (store treestorage.TreeStorage, err error) {
|
||||||
|
status, err := c.TreeDeletedStatus(payload.RootRawChange.Id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if status == "" {
|
||||||
|
return c.SpaceStorage.CreateTreeStorage(payload)
|
||||||
|
}
|
||||||
|
err = storage.ErrTreeStorageAlreadyDeleted
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -1,14 +1,16 @@
|
|||||||
//go:generate mockgen -destination mock_diffservice/mock_diffservice.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice DiffSyncer,PeriodicSync
|
//go:generate mockgen -destination mock_diffservice/mock_diffservice.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice DiffSyncer
|
||||||
package diffservice
|
package diffservice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/periodicsync"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -16,19 +18,20 @@ import (
|
|||||||
type DiffService interface {
|
type DiffService interface {
|
||||||
HeadNotifiable
|
HeadNotifiable
|
||||||
HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error)
|
HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error)
|
||||||
RemoveObject(id string)
|
RemoveObjects(ids []string)
|
||||||
AllIds() []string
|
AllIds() []string
|
||||||
|
|
||||||
Init(objectIds []string)
|
Init(objectIds []string, deletionState deletionstate.DeletionState)
|
||||||
Close() (err error)
|
Close() (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type diffService struct {
|
type diffService struct {
|
||||||
spaceId string
|
spaceId string
|
||||||
periodicSync PeriodicSync
|
periodicSync periodicsync.PeriodicSync
|
||||||
storage storage.SpaceStorage
|
storage storage.SpaceStorage
|
||||||
diff ldiff.Diff
|
diff ldiff.Diff
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
|
syncer DiffSyncer
|
||||||
|
|
||||||
syncPeriod int
|
syncPeriod int
|
||||||
}
|
}
|
||||||
@ -45,11 +48,12 @@ func NewDiffService(
|
|||||||
l := log.With(zap.String("spaceId", spaceId))
|
l := log.With(zap.String("spaceId", spaceId))
|
||||||
factory := spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceClient)
|
factory := spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceClient)
|
||||||
syncer := newDiffSyncer(spaceId, diff, confConnector, cache, storage, factory, l)
|
syncer := newDiffSyncer(spaceId, diff, confConnector, cache, storage, factory, l)
|
||||||
periodicSync := newPeriodicSync(syncPeriod, syncer, l)
|
periodicSync := periodicsync.NewPeriodicSync(syncPeriod, syncer.Sync, l)
|
||||||
|
|
||||||
return &diffService{
|
return &diffService{
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
|
syncer: syncer,
|
||||||
periodicSync: periodicSync,
|
periodicSync: periodicSync,
|
||||||
diff: diff,
|
diff: diff,
|
||||||
log: log,
|
log: log,
|
||||||
@ -57,8 +61,9 @@ func NewDiffService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diffService) Init(objectIds []string) {
|
func (d *diffService) Init(objectIds []string, deletionState deletionstate.DeletionState) {
|
||||||
d.fillDiff(objectIds)
|
d.fillDiff(objectIds)
|
||||||
|
d.syncer.Init(deletionState)
|
||||||
d.periodicSync.Run()
|
d.periodicSync.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,19 +72,15 @@ func (d *diffService) HandleRangeRequest(ctx context.Context, req *spacesyncprot
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *diffService) UpdateHeads(id string, heads []string) {
|
func (d *diffService) UpdateHeads(id string, heads []string) {
|
||||||
d.diff.Set(ldiff.Element{
|
d.syncer.UpdateHeads(id, heads)
|
||||||
Id: id,
|
|
||||||
Head: concatStrings(heads),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diffService) AllIds() []string {
|
func (d *diffService) AllIds() []string {
|
||||||
return d.diff.Ids()
|
return d.diff.Ids()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diffService) RemoveObject(id string) {
|
func (d *diffService) RemoveObjects(ids []string) {
|
||||||
// TODO: add space document to remove ids
|
d.syncer.RemoveObjects(ids)
|
||||||
d.diff.RemoveId(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diffService) Close() (err error) {
|
func (d *diffService) Close() (err error) {
|
||||||
|
|||||||
@ -3,10 +3,12 @@ package diffservice
|
|||||||
import (
|
import (
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice/mock_diffservice"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice/mock_diffservice"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
||||||
mock_storage2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage/mock_storage"
|
mock_storage2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage/mock_storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff/mock_ldiff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff/mock_ldiff"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/periodicsync/mock_periodicsync"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -17,10 +19,12 @@ func TestDiffService(t *testing.T) {
|
|||||||
|
|
||||||
spaceId := "spaceId"
|
spaceId := "spaceId"
|
||||||
l := logger.NewNamed("sync")
|
l := logger.NewNamed("sync")
|
||||||
pSyncMock := mock_diffservice.NewMockPeriodicSync(ctrl)
|
pSyncMock := mock_periodicsync.NewMockPeriodicSync(ctrl)
|
||||||
storageMock := mock_storage.NewMockSpaceStorage(ctrl)
|
storageMock := mock_storage.NewMockSpaceStorage(ctrl)
|
||||||
treeStorageMock := mock_storage2.NewMockTreeStorage(ctrl)
|
treeStorageMock := mock_storage2.NewMockTreeStorage(ctrl)
|
||||||
diffMock := mock_ldiff.NewMockDiff(ctrl)
|
diffMock := mock_ldiff.NewMockDiff(ctrl)
|
||||||
|
syncer := mock_diffservice.NewMockDiffSyncer(ctrl)
|
||||||
|
delState := mock_deletionstate.NewMockDeletionState(ctrl)
|
||||||
syncPeriod := 1
|
syncPeriod := 1
|
||||||
initId := "initId"
|
initId := "initId"
|
||||||
|
|
||||||
@ -28,6 +32,7 @@ func TestDiffService(t *testing.T) {
|
|||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
storage: storageMock,
|
storage: storageMock,
|
||||||
periodicSync: pSyncMock,
|
periodicSync: pSyncMock,
|
||||||
|
syncer: syncer,
|
||||||
diff: diffMock,
|
diff: diffMock,
|
||||||
log: l,
|
log: l,
|
||||||
syncPeriod: syncPeriod,
|
syncPeriod: syncPeriod,
|
||||||
@ -36,22 +41,25 @@ func TestDiffService(t *testing.T) {
|
|||||||
t.Run("init", func(t *testing.T) {
|
t.Run("init", func(t *testing.T) {
|
||||||
storageMock.EXPECT().TreeStorage(initId).Return(treeStorageMock, nil)
|
storageMock.EXPECT().TreeStorage(initId).Return(treeStorageMock, nil)
|
||||||
treeStorageMock.EXPECT().Heads().Return([]string{"h1", "h2"}, nil)
|
treeStorageMock.EXPECT().Heads().Return([]string{"h1", "h2"}, nil)
|
||||||
|
syncer.EXPECT().Init(delState)
|
||||||
diffMock.EXPECT().Set(ldiff.Element{
|
diffMock.EXPECT().Set(ldiff.Element{
|
||||||
Id: initId,
|
Id: initId,
|
||||||
Head: "h1h2",
|
Head: "h1h2",
|
||||||
})
|
})
|
||||||
pSyncMock.EXPECT().Run()
|
pSyncMock.EXPECT().Run()
|
||||||
service.Init([]string{initId})
|
service.Init([]string{initId}, delState)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update heads", func(t *testing.T) {
|
t.Run("update heads", func(t *testing.T) {
|
||||||
diffMock.EXPECT().Set(ldiff.Element{
|
syncer.EXPECT().UpdateHeads(initId, []string{"h1", "h2"})
|
||||||
Id: initId,
|
|
||||||
Head: "h1h2",
|
|
||||||
})
|
|
||||||
service.UpdateHeads(initId, []string{"h1", "h2"})
|
service.UpdateHeads(initId, []string{"h1", "h2"})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("remove objects", func(t *testing.T) {
|
||||||
|
syncer.EXPECT().RemoveObjects([]string{"h1", "h2"})
|
||||||
|
service.RemoveObjects([]string{"h1", "h2"})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("close", func(t *testing.T) {
|
t.Run("close", func(t *testing.T) {
|
||||||
pSyncMock.EXPECT().Close()
|
pSyncMock.EXPECT().Close()
|
||||||
service.Close()
|
service.Close()
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package diffservice
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
||||||
@ -16,6 +17,9 @@ import (
|
|||||||
|
|
||||||
type DiffSyncer interface {
|
type DiffSyncer interface {
|
||||||
Sync(ctx context.Context) error
|
Sync(ctx context.Context) error
|
||||||
|
RemoveObjects(ids []string)
|
||||||
|
UpdateHeads(id string, heads []string)
|
||||||
|
Init(deletionState deletionstate.DeletionState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDiffSyncer(
|
func newDiffSyncer(
|
||||||
@ -45,6 +49,28 @@ type diffSyncer struct {
|
|||||||
storage storage.SpaceStorage
|
storage storage.SpaceStorage
|
||||||
clientFactory spacesyncproto.ClientFactory
|
clientFactory spacesyncproto.ClientFactory
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
|
deletionState deletionstate.DeletionState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *diffSyncer) Init(deletionState deletionstate.DeletionState) {
|
||||||
|
d.deletionState = deletionState
|
||||||
|
d.deletionState.AddObserver(d.RemoveObjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *diffSyncer) RemoveObjects(ids []string) {
|
||||||
|
for _, id := range ids {
|
||||||
|
d.diff.RemoveId(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *diffSyncer) UpdateHeads(id string, heads []string) {
|
||||||
|
if d.deletionState.Exists(id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.diff.Set(ldiff.Element{
|
||||||
|
Id: id,
|
||||||
|
Head: concatStrings(heads),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diffSyncer) Sync(ctx context.Context) error {
|
func (d *diffSyncer) Sync(ctx context.Context) error {
|
||||||
@ -74,15 +100,17 @@ func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error)
|
|||||||
if err == spacesyncproto.ErrSpaceMissing {
|
if err == spacesyncproto.ErrSpaceMissing {
|
||||||
return d.sendPushSpaceRequest(ctx, cl)
|
return d.sendPushSpaceRequest(ctx, cl)
|
||||||
}
|
}
|
||||||
|
totalLen := len(newIds) + len(changedIds) + len(removedIds)
|
||||||
|
// not syncing ids which were removed through settings document
|
||||||
|
filteredIds := d.deletionState.FilterJoin(newIds, changedIds, removedIds)
|
||||||
|
|
||||||
ctx = peer.CtxWithPeerId(ctx, p.Id())
|
ctx = peer.CtxWithPeerId(ctx, p.Id())
|
||||||
d.pingTreesInCache(ctx, newIds)
|
d.pingTreesInCache(ctx, filteredIds)
|
||||||
d.pingTreesInCache(ctx, changedIds)
|
|
||||||
d.pingTreesInCache(ctx, removedIds)
|
|
||||||
|
|
||||||
d.log.Info("sync done:", zap.Int("newIds", len(newIds)),
|
d.log.Info("sync done:", zap.Int("newIds", len(newIds)),
|
||||||
zap.Int("changedIds", len(changedIds)),
|
zap.Int("changedIds", len(changedIds)),
|
||||||
zap.Int("removedIds", len(removedIds)))
|
zap.Int("removedIds", len(removedIds)),
|
||||||
|
zap.Int("already deleted ids", totalLen-len(filteredIds)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,10 +136,24 @@ func (d *diffSyncer) sendPushSpaceRequest(ctx context.Context, cl spacesyncproto
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsStorage, err := d.storage.TreeStorage(d.storage.SpaceSettingsId())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
spaceSettingsRoot, err := settingsStorage.Root()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
spacePayload := &spacesyncproto.SpacePayload{
|
||||||
|
SpaceHeader: header,
|
||||||
|
AclPayload: root.Payload,
|
||||||
|
AclPayloadId: root.Id,
|
||||||
|
SpaceSettingsPayload: spaceSettingsRoot.RawChange,
|
||||||
|
SpaceSettingsPayloadId: spaceSettingsRoot.Id,
|
||||||
|
}
|
||||||
_, err = cl.PushSpace(ctx, &spacesyncproto.PushSpaceRequest{
|
_, err = cl.PushSpace(ctx, &spacesyncproto.PushSpaceRequest{
|
||||||
SpaceHeader: header,
|
Payload: spacePayload,
|
||||||
AclPayload: root.Payload,
|
|
||||||
AclPayloadId: root.Id,
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/remotediff"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto/mock_spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto/mock_spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
||||||
@ -13,6 +14,9 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf/mock_nodeconf"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf/mock_nodeconf"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
||||||
mock_aclstorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage/mock_storage"
|
mock_aclstorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage/mock_storage"
|
||||||
|
mock_treestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage/mock_storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff/mock_ldiff"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/ldiff/mock_ldiff"
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/libp2p/go-libp2p/core/sec"
|
"github.com/libp2p/go-libp2p/core/sec"
|
||||||
@ -25,6 +29,7 @@ import (
|
|||||||
type pushSpaceRequestMatcher struct {
|
type pushSpaceRequestMatcher struct {
|
||||||
spaceId string
|
spaceId string
|
||||||
aclRootId string
|
aclRootId string
|
||||||
|
settingsId string
|
||||||
spaceHeader *spacesyncproto.RawSpaceHeaderWithId
|
spaceHeader *spacesyncproto.RawSpaceHeaderWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +39,7 @@ func (p pushSpaceRequestMatcher) Matches(x interface{}) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.AclPayloadId == p.aclRootId && res.SpaceHeader == p.spaceHeader
|
return res.Payload.AclPayloadId == p.aclRootId && res.Payload.SpaceHeader == p.spaceHeader && res.Payload.SpaceSettingsPayloadId == p.settingsId
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p pushSpaceRequestMatcher) String() string {
|
func (p pushSpaceRequestMatcher) String() string {
|
||||||
@ -77,10 +82,12 @@ func (m mockPeer) NewStream(ctx context.Context, rpc string, enc drpc.Encoding)
|
|||||||
func newPushSpaceRequestMatcher(
|
func newPushSpaceRequestMatcher(
|
||||||
spaceId string,
|
spaceId string,
|
||||||
aclRootId string,
|
aclRootId string,
|
||||||
|
settingsId string,
|
||||||
spaceHeader *spacesyncproto.RawSpaceHeaderWithId) *pushSpaceRequestMatcher {
|
spaceHeader *spacesyncproto.RawSpaceHeaderWithId) *pushSpaceRequestMatcher {
|
||||||
return &pushSpaceRequestMatcher{
|
return &pushSpaceRequestMatcher{
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
aclRootId: aclRootId,
|
aclRootId: aclRootId,
|
||||||
|
settingsId: settingsId,
|
||||||
spaceHeader: spaceHeader,
|
spaceHeader: spaceHeader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,18 +106,22 @@ func TestDiffSyncer_Sync(t *testing.T) {
|
|||||||
factory := spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceClient {
|
factory := spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceClient {
|
||||||
return clientMock
|
return clientMock
|
||||||
})
|
})
|
||||||
|
delState := mock_deletionstate.NewMockDeletionState(ctrl)
|
||||||
spaceId := "spaceId"
|
spaceId := "spaceId"
|
||||||
aclRootId := "aclRootId"
|
aclRootId := "aclRootId"
|
||||||
l := logger.NewNamed(spaceId)
|
l := logger.NewNamed(spaceId)
|
||||||
diffSyncer := newDiffSyncer(spaceId, diffMock, connectorMock, cacheMock, stMock, factory, l)
|
diffSyncer := newDiffSyncer(spaceId, diffMock, connectorMock, cacheMock, stMock, factory, l)
|
||||||
|
delState.EXPECT().AddObserver(gomock.Any())
|
||||||
|
diffSyncer.Init(delState)
|
||||||
|
|
||||||
t.Run("diff syncer sync simple", func(t *testing.T) {
|
t.Run("diff syncer sync", func(t *testing.T) {
|
||||||
connectorMock.EXPECT().
|
connectorMock.EXPECT().
|
||||||
GetResponsiblePeers(gomock.Any(), spaceId).
|
GetResponsiblePeers(gomock.Any(), spaceId).
|
||||||
Return([]peer.Peer{mockPeer{}}, nil)
|
Return([]peer.Peer{mockPeer{}}, nil)
|
||||||
diffMock.EXPECT().
|
diffMock.EXPECT().
|
||||||
Diff(gomock.Any(), gomock.Eq(remotediff.NewRemoteDiff(spaceId, clientMock))).
|
Diff(gomock.Any(), gomock.Eq(remotediff.NewRemoteDiff(spaceId, clientMock))).
|
||||||
Return([]string{"new"}, []string{"changed"}, nil, nil)
|
Return([]string{"new"}, []string{"changed"}, nil, nil)
|
||||||
|
delState.EXPECT().FilterJoin(gomock.Any()).Return([]string{"new", "changed"})
|
||||||
for _, arg := range []string{"new", "changed"} {
|
for _, arg := range []string{"new", "changed"} {
|
||||||
cacheMock.EXPECT().
|
cacheMock.EXPECT().
|
||||||
GetTree(gomock.Any(), spaceId, arg).
|
GetTree(gomock.Any(), spaceId, arg).
|
||||||
@ -127,12 +138,37 @@ func TestDiffSyncer_Sync(t *testing.T) {
|
|||||||
require.Error(t, diffSyncer.Sync(ctx))
|
require.Error(t, diffSyncer.Sync(ctx))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("deletion state remove objects", func(t *testing.T) {
|
||||||
|
deletedId := "id"
|
||||||
|
delState.EXPECT().Exists(deletedId).Return(true)
|
||||||
|
|
||||||
|
// this should not result in any mock being called
|
||||||
|
diffSyncer.UpdateHeads(deletedId, []string{"someHead"})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("update heads updates diff", func(t *testing.T) {
|
||||||
|
newId := "newId"
|
||||||
|
newHeads := []string{"h1", "h2"}
|
||||||
|
diffMock.EXPECT().Set(ldiff.Element{
|
||||||
|
Id: newId,
|
||||||
|
Head: concatStrings(newHeads),
|
||||||
|
})
|
||||||
|
delState.EXPECT().Exists(newId).Return(false)
|
||||||
|
diffSyncer.UpdateHeads(newId, newHeads)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("diff syncer sync space missing", func(t *testing.T) {
|
t.Run("diff syncer sync space missing", func(t *testing.T) {
|
||||||
aclStorageMock := mock_aclstorage.NewMockListStorage(ctrl)
|
aclStorageMock := mock_aclstorage.NewMockListStorage(ctrl)
|
||||||
|
settingsStorage := mock_treestorage.NewMockTreeStorage(ctrl)
|
||||||
|
settingsId := "settingsId"
|
||||||
aclRoot := &aclrecordproto.RawACLRecordWithId{
|
aclRoot := &aclrecordproto.RawACLRecordWithId{
|
||||||
Id: aclRootId,
|
Id: aclRootId,
|
||||||
}
|
}
|
||||||
|
settingsRoot := &treechangeproto.RawTreeChangeWithId{
|
||||||
|
Id: settingsId,
|
||||||
|
}
|
||||||
spaceHeader := &spacesyncproto.RawSpaceHeaderWithId{}
|
spaceHeader := &spacesyncproto.RawSpaceHeaderWithId{}
|
||||||
|
spaceSettingsId := "spaceSettingsId"
|
||||||
|
|
||||||
connectorMock.EXPECT().
|
connectorMock.EXPECT().
|
||||||
GetResponsiblePeers(gomock.Any(), spaceId).
|
GetResponsiblePeers(gomock.Any(), spaceId).
|
||||||
@ -140,17 +176,18 @@ func TestDiffSyncer_Sync(t *testing.T) {
|
|||||||
diffMock.EXPECT().
|
diffMock.EXPECT().
|
||||||
Diff(gomock.Any(), gomock.Eq(remotediff.NewRemoteDiff(spaceId, clientMock))).
|
Diff(gomock.Any(), gomock.Eq(remotediff.NewRemoteDiff(spaceId, clientMock))).
|
||||||
Return(nil, nil, nil, spacesyncproto.ErrSpaceMissing)
|
Return(nil, nil, nil, spacesyncproto.ErrSpaceMissing)
|
||||||
stMock.EXPECT().
|
|
||||||
ACLStorage().
|
stMock.EXPECT().ACLStorage().Return(aclStorageMock, nil)
|
||||||
Return(aclStorageMock, nil)
|
stMock.EXPECT().SpaceHeader().Return(spaceHeader, nil)
|
||||||
stMock.EXPECT().
|
stMock.EXPECT().SpaceSettingsId().Return(spaceSettingsId)
|
||||||
SpaceHeader().
|
stMock.EXPECT().TreeStorage(spaceSettingsId).Return(settingsStorage, nil)
|
||||||
Return(spaceHeader, nil)
|
|
||||||
|
settingsStorage.EXPECT().Root().Return(settingsRoot, nil)
|
||||||
aclStorageMock.EXPECT().
|
aclStorageMock.EXPECT().
|
||||||
Root().
|
Root().
|
||||||
Return(aclRoot, nil)
|
Return(aclRoot, nil)
|
||||||
clientMock.EXPECT().
|
clientMock.EXPECT().
|
||||||
PushSpace(gomock.Any(), newPushSpaceRequestMatcher(spaceId, aclRootId, spaceHeader)).
|
PushSpace(gomock.Any(), newPushSpaceRequestMatcher(spaceId, aclRootId, settingsId, spaceHeader)).
|
||||||
Return(nil, nil)
|
Return(nil, nil)
|
||||||
|
|
||||||
require.NoError(t, diffSyncer.Sync(ctx))
|
require.NoError(t, diffSyncer.Sync(ctx))
|
||||||
|
|||||||
@ -3,3 +3,9 @@ package diffservice
|
|||||||
type HeadNotifiable interface {
|
type HeadNotifiable interface {
|
||||||
UpdateHeads(id string, heads []string)
|
UpdateHeads(id string, heads []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HeadNotifiableFunc func(id string, heads []string)
|
||||||
|
|
||||||
|
func (h HeadNotifiableFunc) UpdateHeads(id string, heads []string) {
|
||||||
|
h(id, heads)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice (interfaces: DiffSyncer,PeriodicSync)
|
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice (interfaces: DiffSyncer)
|
||||||
|
|
||||||
// Package mock_diffservice is a generated GoMock package.
|
// Package mock_diffservice is a generated GoMock package.
|
||||||
package mock_diffservice
|
package mock_diffservice
|
||||||
@ -8,6 +8,7 @@ import (
|
|||||||
context "context"
|
context "context"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
|
deletionstate "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,6 +35,30 @@ func (m *MockDiffSyncer) EXPECT() *MockDiffSyncerMockRecorder {
|
|||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init mocks base method.
|
||||||
|
func (m *MockDiffSyncer) Init(arg0 deletionstate.DeletionState) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Init", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init indicates an expected call of Init.
|
||||||
|
func (mr *MockDiffSyncerMockRecorder) Init(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockDiffSyncer)(nil).Init), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveObjects mocks base method.
|
||||||
|
func (m *MockDiffSyncer) RemoveObjects(arg0 []string) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "RemoveObjects", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveObjects indicates an expected call of RemoveObjects.
|
||||||
|
func (mr *MockDiffSyncerMockRecorder) RemoveObjects(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObjects", reflect.TypeOf((*MockDiffSyncer)(nil).RemoveObjects), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
// Sync mocks base method.
|
// Sync mocks base method.
|
||||||
func (m *MockDiffSyncer) Sync(arg0 context.Context) error {
|
func (m *MockDiffSyncer) Sync(arg0 context.Context) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -48,49 +73,14 @@ func (mr *MockDiffSyncerMockRecorder) Sync(arg0 interface{}) *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sync", reflect.TypeOf((*MockDiffSyncer)(nil).Sync), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sync", reflect.TypeOf((*MockDiffSyncer)(nil).Sync), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockPeriodicSync is a mock of PeriodicSync interface.
|
// UpdateHeads mocks base method.
|
||||||
type MockPeriodicSync struct {
|
func (m *MockDiffSyncer) UpdateHeads(arg0 string, arg1 []string) {
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockPeriodicSyncMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPeriodicSyncMockRecorder is the mock recorder for MockPeriodicSync.
|
|
||||||
type MockPeriodicSyncMockRecorder struct {
|
|
||||||
mock *MockPeriodicSync
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockPeriodicSync creates a new mock instance.
|
|
||||||
func NewMockPeriodicSync(ctrl *gomock.Controller) *MockPeriodicSync {
|
|
||||||
mock := &MockPeriodicSync{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockPeriodicSyncMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockPeriodicSync) EXPECT() *MockPeriodicSyncMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close mocks base method.
|
|
||||||
func (m *MockPeriodicSync) Close() {
|
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
m.ctrl.Call(m, "Close")
|
m.ctrl.Call(m, "UpdateHeads", arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close indicates an expected call of Close.
|
// UpdateHeads indicates an expected call of UpdateHeads.
|
||||||
func (mr *MockPeriodicSyncMockRecorder) Close() *gomock.Call {
|
func (mr *MockDiffSyncerMockRecorder) UpdateHeads(arg0, arg1 interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPeriodicSync)(nil).Close))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHeads", reflect.TypeOf((*MockDiffSyncer)(nil).UpdateHeads), arg0, arg1)
|
||||||
}
|
|
||||||
|
|
||||||
// Run mocks base method.
|
|
||||||
func (m *MockPeriodicSync) Run() {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "Run")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run indicates an expected call of Run.
|
|
||||||
func (mr *MockPeriodicSyncMockRecorder) Run() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockPeriodicSync)(nil).Run))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,9 @@ package commonspace
|
|||||||
import (
|
import (
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
aclrecordproto2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
aclrecordproto "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/common"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/cid"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/cid"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
@ -11,6 +13,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SpaceSettingsChangeType = "reserved.spacesettings"
|
||||||
|
SpaceDerivationScheme = "derivation.standard"
|
||||||
|
)
|
||||||
|
|
||||||
func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload storage.SpaceStorageCreatePayload, err error) {
|
func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload storage.SpaceStorageCreatePayload, err error) {
|
||||||
// unmarshalling signing and encryption keys
|
// unmarshalling signing and encryption keys
|
||||||
identity, err := payload.SigningKey.GetPublic().Raw()
|
identity, err := payload.SigningKey.GetPublic().Raw()
|
||||||
@ -23,8 +30,8 @@ func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// preparing header and space id
|
// preparing header and space id
|
||||||
bytes := make([]byte, 32)
|
spaceHeaderSeed := make([]byte, 32)
|
||||||
_, err = rand.Read(bytes)
|
_, err = rand.Read(spaceHeaderSeed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -33,7 +40,7 @@ func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload st
|
|||||||
Timestamp: time.Now().UnixNano(),
|
Timestamp: time.Now().UnixNano(),
|
||||||
SpaceType: payload.SpaceType,
|
SpaceType: payload.SpaceType,
|
||||||
ReplicationKey: payload.ReplicationKey,
|
ReplicationKey: payload.ReplicationKey,
|
||||||
Seed: bytes,
|
Seed: spaceHeaderSeed,
|
||||||
}
|
}
|
||||||
marshalled, err := header.Marshal()
|
marshalled, err := header.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -68,12 +75,11 @@ func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// preparing acl
|
// preparing acl
|
||||||
aclRoot := &aclrecordproto2.ACLRoot{
|
aclRoot := &aclrecordproto.ACLRoot{
|
||||||
Identity: identity,
|
Identity: identity,
|
||||||
EncryptionKey: encPubKey,
|
EncryptionKey: encPubKey,
|
||||||
SpaceId: spaceId,
|
SpaceId: spaceId,
|
||||||
EncryptedReadKey: encReadKey,
|
EncryptedReadKey: encReadKey,
|
||||||
DerivationScheme: "",
|
|
||||||
CurrentReadKeyHash: readKeyHash,
|
CurrentReadKeyHash: readKeyHash,
|
||||||
Timestamp: time.Now().UnixNano(),
|
Timestamp: time.Now().UnixNano(),
|
||||||
}
|
}
|
||||||
@ -82,10 +88,31 @@ func storagePayloadForSpaceCreate(payload SpaceCreatePayload) (storagePayload st
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder := tree.NewChangeBuilder(common.NewKeychain(), nil)
|
||||||
|
spaceSettingsSeed := make([]byte, 32)
|
||||||
|
_, err = rand.Read(spaceSettingsSeed)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, settingsRoot, err := builder.BuildInitialContent(tree.InitialContent{
|
||||||
|
AclHeadId: rawWithId.Id,
|
||||||
|
Identity: aclRoot.Identity,
|
||||||
|
SigningKey: payload.SigningKey,
|
||||||
|
SpaceId: spaceId,
|
||||||
|
Seed: spaceSettingsSeed,
|
||||||
|
ChangeType: SpaceSettingsChangeType,
|
||||||
|
Timestamp: time.Now().UnixNano(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// creating storage
|
// creating storage
|
||||||
storagePayload = storage.SpaceStorageCreatePayload{
|
storagePayload = storage.SpaceStorageCreatePayload{
|
||||||
RecWithId: rawWithId,
|
AclWithId: rawWithId,
|
||||||
SpaceHeaderWithId: rawHeaderWithId,
|
SpaceHeaderWithId: rawHeaderWithId,
|
||||||
|
SpaceSettingsWithId: settingsRoot,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -144,7 +171,7 @@ func storagePayloadForSpaceDerive(payload SpaceDerivePayload) (storagePayload st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deriving and encrypting read key
|
// deriving and encrypting read key
|
||||||
readKey, err := aclrecordproto2.ACLReadKeyDerive(signPrivKey, encPrivKey)
|
readKey, err := aclrecordproto.ACLReadKeyDerive(signPrivKey, encPrivKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -160,29 +187,41 @@ func storagePayloadForSpaceDerive(payload SpaceDerivePayload) (storagePayload st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// preparing acl
|
// preparing acl
|
||||||
aclRoot := &aclrecordproto2.ACLRoot{
|
aclRoot := &aclrecordproto.ACLRoot{
|
||||||
Identity: identity,
|
Identity: identity,
|
||||||
EncryptionKey: encPubKey,
|
EncryptionKey: encPubKey,
|
||||||
SpaceId: spaceId,
|
SpaceId: spaceId,
|
||||||
EncryptedReadKey: encReadKey,
|
EncryptedReadKey: encReadKey,
|
||||||
DerivationScheme: "",
|
DerivationScheme: SpaceDerivationScheme,
|
||||||
CurrentReadKeyHash: readKeyHash,
|
CurrentReadKeyHash: readKeyHash,
|
||||||
Timestamp: time.Now().UnixNano(),
|
|
||||||
}
|
}
|
||||||
rawWithId, err := marshalACLRoot(aclRoot, payload.SigningKey)
|
rawWithId, err := marshalACLRoot(aclRoot, payload.SigningKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder := tree.NewChangeBuilder(common.NewKeychain(), nil)
|
||||||
|
_, settingsRoot, err := builder.BuildInitialContent(tree.InitialContent{
|
||||||
|
AclHeadId: rawWithId.Id,
|
||||||
|
Identity: aclRoot.Identity,
|
||||||
|
SigningKey: payload.SigningKey,
|
||||||
|
SpaceId: spaceId,
|
||||||
|
ChangeType: SpaceSettingsChangeType,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// creating storage
|
// creating storage
|
||||||
storagePayload = storage.SpaceStorageCreatePayload{
|
storagePayload = storage.SpaceStorageCreatePayload{
|
||||||
RecWithId: rawWithId,
|
AclWithId: rawWithId,
|
||||||
SpaceHeaderWithId: rawHeaderWithId,
|
SpaceHeaderWithId: rawHeaderWithId,
|
||||||
|
SpaceSettingsWithId: settingsRoot,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func marshalACLRoot(aclRoot *aclrecordproto2.ACLRoot, key signingkey.PrivKey) (rawWithId *aclrecordproto2.RawACLRecordWithId, err error) {
|
func marshalACLRoot(aclRoot *aclrecordproto.ACLRoot, key signingkey.PrivKey) (rawWithId *aclrecordproto.RawACLRecordWithId, err error) {
|
||||||
marshalledRoot, err := aclRoot.Marshal()
|
marshalledRoot, err := aclRoot.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -191,7 +230,7 @@ func marshalACLRoot(aclRoot *aclrecordproto2.ACLRoot, key signingkey.PrivKey) (r
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
raw := &aclrecordproto2.RawACLRecord{
|
raw := &aclrecordproto.RawACLRecord{
|
||||||
Payload: marshalledRoot,
|
Payload: marshalledRoot,
|
||||||
Signature: signature,
|
Signature: signature,
|
||||||
}
|
}
|
||||||
@ -203,7 +242,7 @@ func marshalACLRoot(aclRoot *aclrecordproto2.ACLRoot, key signingkey.PrivKey) (r
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rawWithId = &aclrecordproto2.RawACLRecordWithId{
|
rawWithId = &aclrecordproto.RawACLRecordWithId{
|
||||||
Payload: marshalledRaw,
|
Payload: marshalledRaw,
|
||||||
Id: aclHeadId,
|
Id: aclHeadId,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/net/pool"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/nodeconf"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CName = "common.commonspace"
|
const CName = "common.commonspace"
|
||||||
@ -25,11 +26,14 @@ func New() Service {
|
|||||||
return &service{}
|
return &service{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ctxKey int
|
||||||
|
|
||||||
|
const AddSpaceCtxKey ctxKey = 0
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (string, error)
|
DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (string, error)
|
||||||
CreateSpace(ctx context.Context, payload SpaceCreatePayload) (string, error)
|
CreateSpace(ctx context.Context, payload SpaceCreatePayload) (string, error)
|
||||||
NewSpace(ctx context.Context, id string) (sp Space, err error)
|
NewSpace(ctx context.Context, id string) (sp Space, err error)
|
||||||
AddSpace(ctx context.Context, spaceDescription SpaceDescription) (err error)
|
|
||||||
app.Component
|
app.Component
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,47 +86,23 @@ func (s *service) DeriveSpace(ctx context.Context, payload SpaceDerivePayload) (
|
|||||||
return store.Id(), nil
|
return store.Id(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) AddSpace(ctx context.Context, spaceDescription SpaceDescription) (err error) {
|
|
||||||
_, err = s.storageProvider.SpaceStorage(spaceDescription.SpaceHeader.Id)
|
|
||||||
if err == nil {
|
|
||||||
err = spacesyncproto.ErrSpaceExists
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err != storage.ErrSpaceStorageMissing {
|
|
||||||
err = spacesyncproto.ErrUnexpected
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := storage.SpaceStorageCreatePayload{
|
|
||||||
RecWithId: &aclrecordproto.RawACLRecordWithId{
|
|
||||||
Payload: spaceDescription.AclPayload,
|
|
||||||
Id: spaceDescription.AclId,
|
|
||||||
},
|
|
||||||
SpaceHeaderWithId: spaceDescription.SpaceHeader,
|
|
||||||
}
|
|
||||||
st, err := s.storageProvider.CreateSpaceStorage(payload)
|
|
||||||
if err != nil {
|
|
||||||
err = spacesyncproto.ErrUnexpected
|
|
||||||
if err == storage.ErrSpaceStorageExists {
|
|
||||||
err = spacesyncproto.ErrSpaceExists
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = st.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) NewSpace(ctx context.Context, id string) (Space, error) {
|
func (s *service) NewSpace(ctx context.Context, id string) (Space, error) {
|
||||||
st, err := s.storageProvider.SpaceStorage(id)
|
st, err := s.storageProvider.SpaceStorage(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != spacesyncproto.ErrSpaceMissing {
|
if err != storage.ErrSpaceStorageMissing {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err = s.getSpaceStorageFromRemote(ctx, id)
|
if description, ok := ctx.Value(AddSpaceCtxKey).(SpaceDescription); ok {
|
||||||
if err != nil {
|
st, err = s.addSpaceStorage(ctx, description)
|
||||||
err = storage.ErrSpaceStorageMissing
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
st, err = s.getSpaceStorageFromRemote(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,10 +122,33 @@ func (s *service) NewSpace(ctx context.Context, id string) (Space, error) {
|
|||||||
return sp, nil
|
return sp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) addSpaceStorage(ctx context.Context, spaceDescription SpaceDescription) (st storage.SpaceStorage, err error) {
|
||||||
|
payload := storage.SpaceStorageCreatePayload{
|
||||||
|
AclWithId: &aclrecordproto.RawACLRecordWithId{
|
||||||
|
Payload: spaceDescription.AclPayload,
|
||||||
|
Id: spaceDescription.AclId,
|
||||||
|
},
|
||||||
|
SpaceHeaderWithId: spaceDescription.SpaceHeader,
|
||||||
|
SpaceSettingsWithId: &treechangeproto.RawTreeChangeWithId{
|
||||||
|
RawChange: spaceDescription.SpaceSettingsPayload,
|
||||||
|
Id: spaceDescription.SpaceSettingsId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
st, err = s.storageProvider.CreateSpaceStorage(payload)
|
||||||
|
if err != nil {
|
||||||
|
err = spacesyncproto.ErrUnexpected
|
||||||
|
if err == storage.ErrSpaceStorageExists {
|
||||||
|
err = spacesyncproto.ErrSpaceExists
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) getSpaceStorageFromRemote(ctx context.Context, id string) (st storage.SpaceStorage, err error) {
|
func (s *service) getSpaceStorageFromRemote(ctx context.Context, id string) (st storage.SpaceStorage, err error) {
|
||||||
var p peer.Peer
|
var p peer.Peer
|
||||||
lastConfiguration := s.configurationService.GetLast()
|
lastConfiguration := s.configurationService.GetLast()
|
||||||
// for nodes we always get remote space only if we have id in the context
|
// we can't connect to client if it is a node
|
||||||
if lastConfiguration.IsResponsible(id) {
|
if lastConfiguration.IsResponsible(id) {
|
||||||
err = spacesyncproto.ErrSpaceMissing
|
err = spacesyncproto.ErrSpaceMissing
|
||||||
return
|
return
|
||||||
@ -161,12 +164,17 @@ func (s *service) getSpaceStorageFromRemote(ctx context.Context, id string) (st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
st, err = s.storageProvider.CreateSpaceStorage(storage.SpaceStorageCreatePayload{
|
st, err = s.storageProvider.CreateSpaceStorage(storage.SpaceStorageCreatePayload{
|
||||||
RecWithId: &aclrecordproto.RawACLRecordWithId{
|
AclWithId: &aclrecordproto.RawACLRecordWithId{
|
||||||
Payload: res.AclPayload,
|
Payload: res.Payload.AclPayload,
|
||||||
Id: res.AclPayloadId,
|
Id: res.Payload.AclPayloadId,
|
||||||
},
|
},
|
||||||
SpaceHeaderWithId: res.SpaceHeader,
|
SpaceSettingsWithId: &treechangeproto.RawTreeChangeWithId{
|
||||||
|
RawChange: res.Payload.SpaceSettingsPayload,
|
||||||
|
Id: res.Payload.SpaceSettingsPayloadId,
|
||||||
|
},
|
||||||
|
SpaceHeaderWithId: res.Payload.SpaceHeader,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
53
common/commonspace/settingsdocument/deleteloop.go
Normal file
53
common/commonspace/settingsdocument/deleteloop.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deleteLoop struct {
|
||||||
|
deleteCtx context.Context
|
||||||
|
deleteCancel context.CancelFunc
|
||||||
|
deleteChan chan struct{}
|
||||||
|
deleteFunc func()
|
||||||
|
loopDone chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeleteLoop(deleteFunc func()) *deleteLoop {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
return &deleteLoop{
|
||||||
|
deleteCtx: ctx,
|
||||||
|
deleteCancel: cancel,
|
||||||
|
deleteChan: make(chan struct{}, 1),
|
||||||
|
deleteFunc: deleteFunc,
|
||||||
|
loopDone: make(chan struct{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *deleteLoop) Run() {
|
||||||
|
go dl.loop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *deleteLoop) loop() {
|
||||||
|
defer close(dl.loopDone)
|
||||||
|
dl.deleteFunc()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-dl.deleteCtx.Done():
|
||||||
|
return
|
||||||
|
case <-dl.deleteChan:
|
||||||
|
dl.deleteFunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *deleteLoop) notify() {
|
||||||
|
select {
|
||||||
|
case dl.deleteChan <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dl *deleteLoop) Close() {
|
||||||
|
dl.deleteCancel()
|
||||||
|
<-dl.loopDone
|
||||||
|
}
|
||||||
39
common/commonspace/settingsdocument/deleter.go
Normal file
39
common/commonspace/settingsdocument/deleter.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Deleter interface {
|
||||||
|
Delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
type deleter struct {
|
||||||
|
st storage.SpaceStorage
|
||||||
|
state deletionstate.DeletionState
|
||||||
|
getter treegetter.TreeGetter
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeleter(st storage.SpaceStorage, state deletionstate.DeletionState, getter treegetter.TreeGetter) Deleter {
|
||||||
|
return &deleter{st, state, getter}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deleter) Delete() {
|
||||||
|
allQueued := d.state.GetQueued()
|
||||||
|
for _, id := range allQueued {
|
||||||
|
err := d.getter.DeleteTree(context.Background(), d.st.Id(), id)
|
||||||
|
if err != nil && err != storage.ErrTreeStorageAlreadyDeleted {
|
||||||
|
log.With(zap.String("id", id), zap.Error(err)).Error("failed to delete object")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = d.state.Delete(id)
|
||||||
|
if err != nil {
|
||||||
|
log.With(zap.String("id", id), zap.Error(err)).Error("failed to mark object as deleted")
|
||||||
|
}
|
||||||
|
log.With(zap.String("id", id), zap.Error(err)).Debug("object successfully deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
52
common/commonspace/settingsdocument/deleter_test.go
Normal file
52
common/commonspace/settingsdocument/deleter_test.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter/mock_treegetter"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeleter_Delete(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
treeGetter := mock_treegetter.NewMockTreeGetter(ctrl)
|
||||||
|
st := mock_storage.NewMockSpaceStorage(ctrl)
|
||||||
|
delState := mock_deletionstate.NewMockDeletionState(ctrl)
|
||||||
|
|
||||||
|
deleter := newDeleter(st, delState, treeGetter)
|
||||||
|
|
||||||
|
t.Run("deleter delete queued", func(t *testing.T) {
|
||||||
|
id := "id"
|
||||||
|
spaceId := "spaceId"
|
||||||
|
delState.EXPECT().GetQueued().Return([]string{id})
|
||||||
|
st.EXPECT().Id().Return(spaceId)
|
||||||
|
treeGetter.EXPECT().DeleteTree(gomock.Any(), spaceId, id).Return(nil)
|
||||||
|
delState.EXPECT().Delete(id).Return(nil)
|
||||||
|
|
||||||
|
deleter.Delete()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("deleter delete already deleted", func(t *testing.T) {
|
||||||
|
id := "id"
|
||||||
|
spaceId := "spaceId"
|
||||||
|
delState.EXPECT().GetQueued().Return([]string{id})
|
||||||
|
st.EXPECT().Id().Return(spaceId)
|
||||||
|
treeGetter.EXPECT().DeleteTree(gomock.Any(), spaceId, id).Return(storage.ErrTreeStorageAlreadyDeleted)
|
||||||
|
delState.EXPECT().Delete(id).Return(nil)
|
||||||
|
|
||||||
|
deleter.Delete()
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("deleter delete error", func(t *testing.T) {
|
||||||
|
id := "id"
|
||||||
|
spaceId := "spaceId"
|
||||||
|
delState.EXPECT().GetQueued().Return([]string{id})
|
||||||
|
st.EXPECT().Id().Return(spaceId)
|
||||||
|
treeGetter.EXPECT().DeleteTree(gomock.Any(), spaceId, id).Return(fmt.Errorf("some error"))
|
||||||
|
|
||||||
|
deleter.Delete()
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
//go:generate mockgen -destination mock_deletionstate/mock_deletionstate.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate DeletionState
|
||||||
|
package deletionstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StateUpdateObserver func(ids []string)
|
||||||
|
|
||||||
|
type DeletionState interface {
|
||||||
|
AddObserver(observer StateUpdateObserver)
|
||||||
|
Add(ids []string) (err error)
|
||||||
|
GetQueued() (ids []string)
|
||||||
|
Delete(id string) (err error)
|
||||||
|
Exists(id string) bool
|
||||||
|
FilterJoin(ids ...[]string) (filtered []string)
|
||||||
|
CreateDeleteChange(id string, isSnapshot bool) (res []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type deletionState struct {
|
||||||
|
sync.RWMutex
|
||||||
|
queued map[string]struct{}
|
||||||
|
deleted map[string]struct{}
|
||||||
|
stateUpdateObservers []StateUpdateObserver
|
||||||
|
storage storage.SpaceStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeletionState(storage storage.SpaceStorage) DeletionState {
|
||||||
|
return &deletionState{
|
||||||
|
queued: map[string]struct{}{},
|
||||||
|
deleted: map[string]struct{}{},
|
||||||
|
storage: storage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) AddObserver(observer StateUpdateObserver) {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
st.stateUpdateObservers = append(st.stateUpdateObservers, observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) Add(ids []string) (err error) {
|
||||||
|
st.Lock()
|
||||||
|
defer func() {
|
||||||
|
st.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, ob := range st.stateUpdateObservers {
|
||||||
|
ob(ids)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
if _, exists := st.deleted[id]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, exists := st.queued[id]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var status string
|
||||||
|
status, err = st.storage.TreeDeletedStatus(id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case storage.TreeDeletedStatusQueued:
|
||||||
|
st.queued[id] = struct{}{}
|
||||||
|
case storage.TreeDeletedStatusDeleted:
|
||||||
|
st.deleted[id] = struct{}{}
|
||||||
|
default:
|
||||||
|
st.queued[id] = struct{}{}
|
||||||
|
err = st.storage.SetTreeDeletedStatus(id, storage.TreeDeletedStatusQueued)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) GetQueued() (ids []string) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
ids = make([]string, 0, len(st.queued))
|
||||||
|
for id := range st.queued {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) Delete(id string) (err error) {
|
||||||
|
st.Lock()
|
||||||
|
defer st.Unlock()
|
||||||
|
delete(st.queued, id)
|
||||||
|
st.deleted[id] = struct{}{}
|
||||||
|
err = st.storage.SetTreeDeletedStatus(id, storage.TreeDeletedStatusDeleted)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) Exists(id string) bool {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
return st.exists(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) FilterJoin(ids ...[]string) (filtered []string) {
|
||||||
|
st.RLock()
|
||||||
|
defer st.RUnlock()
|
||||||
|
filter := func(ids []string) {
|
||||||
|
for _, id := range ids {
|
||||||
|
if !st.exists(id) {
|
||||||
|
filtered = append(filtered, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, arr := range ids {
|
||||||
|
filter(arr)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) CreateDeleteChange(id string, isSnapshot bool) (res []byte, err error) {
|
||||||
|
content := &spacesyncproto.SpaceSettingsContent_ObjectDelete{
|
||||||
|
ObjectDelete: &spacesyncproto.ObjectDelete{Id: id},
|
||||||
|
}
|
||||||
|
change := &spacesyncproto.SettingsData{
|
||||||
|
Content: []*spacesyncproto.SpaceSettingsContent{
|
||||||
|
{content},
|
||||||
|
},
|
||||||
|
Snapshot: nil,
|
||||||
|
}
|
||||||
|
// TODO: add snapshot logic
|
||||||
|
res, err = change.Marshal()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *deletionState) exists(id string) bool {
|
||||||
|
if _, exists := st.deleted[id]; exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, exists := st.queued[id]; exists {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@ -0,0 +1,127 @@
|
|||||||
|
package deletionstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fixture struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
delState *deletionState
|
||||||
|
spaceStorage *mock_storage.MockSpaceStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFixture(t *testing.T) *fixture {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
spaceStorage := mock_storage.NewMockSpaceStorage(ctrl)
|
||||||
|
delState := NewDeletionState(spaceStorage).(*deletionState)
|
||||||
|
return &fixture{
|
||||||
|
ctrl: ctrl,
|
||||||
|
delState: delState,
|
||||||
|
spaceStorage: spaceStorage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fx *fixture) stop() {
|
||||||
|
fx.ctrl.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionState_Add(t *testing.T) {
|
||||||
|
t.Run("add new", func(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
id := "newId"
|
||||||
|
fx.spaceStorage.EXPECT().TreeDeletedStatus(id).Return("", nil)
|
||||||
|
fx.spaceStorage.EXPECT().SetTreeDeletedStatus(id, storage.TreeDeletedStatusQueued).Return(nil)
|
||||||
|
err := fx.delState.Add([]string{id})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, fx.delState.queued, id)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("add existing queued", func(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
id := "newId"
|
||||||
|
fx.spaceStorage.EXPECT().TreeDeletedStatus(id).Return(storage.TreeDeletedStatusQueued, nil)
|
||||||
|
err := fx.delState.Add([]string{id})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, fx.delState.queued, id)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("add existing deleted", func(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
id := "newId"
|
||||||
|
fx.spaceStorage.EXPECT().TreeDeletedStatus(id).Return(storage.TreeDeletedStatusDeleted, nil)
|
||||||
|
err := fx.delState.Add([]string{id})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, fx.delState.deleted, id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionState_GetQueued(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
fx.delState.queued["id1"] = struct{}{}
|
||||||
|
fx.delState.queued["id2"] = struct{}{}
|
||||||
|
|
||||||
|
queued := fx.delState.GetQueued()
|
||||||
|
require.Equal(t, []string{"id1", "id2"}, queued)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionState_FilterJoin(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
fx.delState.queued["id1"] = struct{}{}
|
||||||
|
fx.delState.queued["id2"] = struct{}{}
|
||||||
|
|
||||||
|
filtered := fx.delState.FilterJoin([]string{"id1"}, []string{"id3", "id2"}, []string{"id4"})
|
||||||
|
require.Equal(t, []string{"id3", "id4"}, filtered)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionState_AddObserver(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
var queued []string
|
||||||
|
|
||||||
|
fx.delState.AddObserver(func(ids []string) {
|
||||||
|
queued = ids
|
||||||
|
})
|
||||||
|
id := "newId"
|
||||||
|
fx.spaceStorage.EXPECT().TreeDeletedStatus(id).Return("", nil)
|
||||||
|
fx.spaceStorage.EXPECT().SetTreeDeletedStatus(id, storage.TreeDeletedStatusQueued).Return(nil)
|
||||||
|
err := fx.delState.Add([]string{id})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, fx.delState.queued, id)
|
||||||
|
require.Equal(t, []string{id}, queued)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionState_Delete(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
id := "deletedId"
|
||||||
|
fx.delState.queued[id] = struct{}{}
|
||||||
|
fx.spaceStorage.EXPECT().SetTreeDeletedStatus(id, storage.TreeDeletedStatusDeleted).Return(nil)
|
||||||
|
err := fx.delState.Delete(id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, fx.delState.deleted, id)
|
||||||
|
require.NotContains(t, fx.delState.queued, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeletionState_Exists(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
fx.delState.queued["id1"] = struct{}{}
|
||||||
|
fx.delState.deleted["id2"] = struct{}{}
|
||||||
|
require.True(t, fx.delState.Exists("id1"))
|
||||||
|
require.True(t, fx.delState.Exists("id2"))
|
||||||
|
require.False(t, fx.delState.Exists("id3"))
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate (interfaces: DeletionState)
|
||||||
|
|
||||||
|
// Package mock_deletionstate is a generated GoMock package.
|
||||||
|
package mock_deletionstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
deletionstate "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockDeletionState is a mock of DeletionState interface.
|
||||||
|
type MockDeletionState struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockDeletionStateMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDeletionStateMockRecorder is the mock recorder for MockDeletionState.
|
||||||
|
type MockDeletionStateMockRecorder struct {
|
||||||
|
mock *MockDeletionState
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockDeletionState creates a new mock instance.
|
||||||
|
func NewMockDeletionState(ctrl *gomock.Controller) *MockDeletionState {
|
||||||
|
mock := &MockDeletionState{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockDeletionStateMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockDeletionState) EXPECT() *MockDeletionStateMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add mocks base method.
|
||||||
|
func (m *MockDeletionState) Add(arg0 []string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Add", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add indicates an expected call of Add.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) Add(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockDeletionState)(nil).Add), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddObserver mocks base method.
|
||||||
|
func (m *MockDeletionState) AddObserver(arg0 deletionstate.StateUpdateObserver) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "AddObserver", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddObserver indicates an expected call of AddObserver.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) AddObserver(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddObserver", reflect.TypeOf((*MockDeletionState)(nil).AddObserver), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeleteChange mocks base method.
|
||||||
|
func (m *MockDeletionState) CreateDeleteChange(arg0 string, arg1 bool) ([]byte, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateDeleteChange", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]byte)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDeleteChange indicates an expected call of CreateDeleteChange.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) CreateDeleteChange(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDeleteChange", reflect.TypeOf((*MockDeletionState)(nil).CreateDeleteChange), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete mocks base method.
|
||||||
|
func (m *MockDeletionState) Delete(arg0 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Delete", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete indicates an expected call of Delete.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) Delete(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDeletionState)(nil).Delete), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exists mocks base method.
|
||||||
|
func (m *MockDeletionState) Exists(arg0 string) bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Exists", arg0)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exists indicates an expected call of Exists.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) Exists(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockDeletionState)(nil).Exists), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterJoin mocks base method.
|
||||||
|
func (m *MockDeletionState) FilterJoin(arg0 ...[]string) []string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
varargs := []interface{}{}
|
||||||
|
for _, a := range arg0 {
|
||||||
|
varargs = append(varargs, a)
|
||||||
|
}
|
||||||
|
ret := m.ctrl.Call(m, "FilterJoin", varargs...)
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterJoin indicates an expected call of FilterJoin.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) FilterJoin(arg0 ...interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FilterJoin", reflect.TypeOf((*MockDeletionState)(nil).FilterJoin), arg0...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueued mocks base method.
|
||||||
|
func (m *MockDeletionState) GetQueued() []string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "GetQueued")
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueued indicates an expected call of GetQueued.
|
||||||
|
func (mr *MockDeletionStateMockRecorder) GetQueued() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueued", reflect.TypeOf((*MockDeletionState)(nil).GetQueued))
|
||||||
|
}
|
||||||
59
common/commonspace/settingsdocument/idprovider.go
Normal file
59
common/commonspace/settingsdocument/idprovider.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeletedIdsProvider interface {
|
||||||
|
ProvideIds(tr tree.ObjectTree, startId string) (ids []string, lastId string, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type provider struct{}
|
||||||
|
|
||||||
|
func (p *provider) processChange(change *tree.Change, rootId, startId string, ids []string) []string {
|
||||||
|
// ignoring root change which has empty model or startId change
|
||||||
|
if change.Model == nil || (change.Id == startId && startId != "") {
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteChange := change.Model.(*spacesyncproto.SettingsData)
|
||||||
|
// getting data from snapshot if we start from it
|
||||||
|
if change.Id == rootId {
|
||||||
|
ids = deleteChange.Snapshot.DeletedIds
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise getting data from content
|
||||||
|
for _, cnt := range deleteChange.Content {
|
||||||
|
if cnt.GetObjectDelete() != nil {
|
||||||
|
ids = append(ids, cnt.GetObjectDelete().GetId())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *provider) ProvideIds(tr tree.ObjectTree, startId string) (ids []string, lastId string, err error) {
|
||||||
|
rootId := tr.Root().Id
|
||||||
|
process := func(change *tree.Change) bool {
|
||||||
|
lastId = change.Id
|
||||||
|
ids = p.processChange(change, rootId, startId, ids)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
convert := func(decrypted []byte) (res any, err error) {
|
||||||
|
deleteChange := &spacesyncproto.SettingsData{}
|
||||||
|
err = proto.Unmarshal(decrypted, deleteChange)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return deleteChange, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if startId == "" {
|
||||||
|
err = tr.IterateFrom(tr.ID(), convert, process)
|
||||||
|
} else {
|
||||||
|
err = tr.IterateFrom(startId, convert, process)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
94
common/commonspace/settingsdocument/idprovider_test.go
Normal file
94
common/commonspace/settingsdocument/idprovider_test.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
|
mock_tree "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree/mock_objecttree"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProvider_ProcessChange(t *testing.T) {
|
||||||
|
//ctrl := gomock.NewController(t)
|
||||||
|
//objTree := mock_tree.NewMockObjectTree(ctrl)
|
||||||
|
prov := &provider{}
|
||||||
|
//defer ctrl.Finish()
|
||||||
|
|
||||||
|
t.Run("empty model", func(t *testing.T) {
|
||||||
|
ch := &tree.Change{}
|
||||||
|
startId := "startId"
|
||||||
|
rootId := "rootId"
|
||||||
|
ids := []string{startId}
|
||||||
|
otherIds := prov.processChange(ch, rootId, startId, ids)
|
||||||
|
require.Equal(t, []string{startId}, otherIds)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("changeId is equal to startId", func(t *testing.T) {
|
||||||
|
ch := &tree.Change{}
|
||||||
|
ch.Model = &spacesyncproto.SettingsData{}
|
||||||
|
ch.Id = "startId"
|
||||||
|
|
||||||
|
startId := "startId"
|
||||||
|
rootId := "rootId"
|
||||||
|
ids := []string{startId}
|
||||||
|
otherIds := prov.processChange(ch, rootId, startId, ids)
|
||||||
|
require.Equal(t, []string{startId}, otherIds)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("changeId is equal to rootId, startId is empty", func(t *testing.T) {
|
||||||
|
ch := &tree.Change{}
|
||||||
|
ch.Model = &spacesyncproto.SettingsData{
|
||||||
|
Snapshot: &spacesyncproto.SpaceSettingsSnapshot{
|
||||||
|
DeletedIds: []string{"id1", "id2"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ch.Id = "rootId"
|
||||||
|
|
||||||
|
startId := ""
|
||||||
|
rootId := "rootId"
|
||||||
|
otherIds := prov.processChange(ch, rootId, startId, nil)
|
||||||
|
require.Equal(t, []string{"id1", "id2"}, otherIds)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("changeId is equal to rootId, startId is empty", func(t *testing.T) {
|
||||||
|
ch := &tree.Change{}
|
||||||
|
ch.Model = &spacesyncproto.SettingsData{
|
||||||
|
Content: []*spacesyncproto.SpaceSettingsContent{
|
||||||
|
{&spacesyncproto.SpaceSettingsContent_ObjectDelete{
|
||||||
|
ObjectDelete: &spacesyncproto.ObjectDelete{Id: "id1"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ch.Id = "someId"
|
||||||
|
|
||||||
|
startId := "startId"
|
||||||
|
rootId := "rootId"
|
||||||
|
otherIds := prov.processChange(ch, rootId, startId, nil)
|
||||||
|
require.Equal(t, []string{"id1"}, otherIds)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvider_ProvideIds(t *testing.T) {
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
objTree := mock_tree.NewMockObjectTree(ctrl)
|
||||||
|
prov := &provider{}
|
||||||
|
defer ctrl.Finish()
|
||||||
|
|
||||||
|
t.Run("startId is empty", func(t *testing.T) {
|
||||||
|
ch := &tree.Change{Id: "rootId"}
|
||||||
|
objTree.EXPECT().Root().Return(ch)
|
||||||
|
objTree.EXPECT().ID().Return("id")
|
||||||
|
objTree.EXPECT().IterateFrom("id", gomock.Any(), gomock.Any()).Return(nil)
|
||||||
|
_, _, err := prov.ProvideIds(objTree, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("startId is not empty", func(t *testing.T) {
|
||||||
|
ch := &tree.Change{Id: "rootId"}
|
||||||
|
objTree.EXPECT().Root().Return(ch)
|
||||||
|
objTree.EXPECT().IterateFrom("startId", gomock.Any(), gomock.Any()).Return(nil)
|
||||||
|
_, _, err := prov.ProvideIds(objTree, "startId")
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument (interfaces: DeletedIdsProvider,Deleter)
|
||||||
|
|
||||||
|
// Package mock_settingsdocument is a generated GoMock package.
|
||||||
|
package mock_settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
tree "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockDeletedIdsProvider is a mock of DeletedIdsProvider interface.
|
||||||
|
type MockDeletedIdsProvider struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockDeletedIdsProviderMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDeletedIdsProviderMockRecorder is the mock recorder for MockDeletedIdsProvider.
|
||||||
|
type MockDeletedIdsProviderMockRecorder struct {
|
||||||
|
mock *MockDeletedIdsProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockDeletedIdsProvider creates a new mock instance.
|
||||||
|
func NewMockDeletedIdsProvider(ctrl *gomock.Controller) *MockDeletedIdsProvider {
|
||||||
|
mock := &MockDeletedIdsProvider{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockDeletedIdsProviderMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockDeletedIdsProvider) EXPECT() *MockDeletedIdsProviderMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvideIds mocks base method.
|
||||||
|
func (m *MockDeletedIdsProvider) ProvideIds(arg0 tree.ObjectTree, arg1 string) ([]string, string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ProvideIds", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
ret1, _ := ret[1].(string)
|
||||||
|
ret2, _ := ret[2].(error)
|
||||||
|
return ret0, ret1, ret2
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProvideIds indicates an expected call of ProvideIds.
|
||||||
|
func (mr *MockDeletedIdsProviderMockRecorder) ProvideIds(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProvideIds", reflect.TypeOf((*MockDeletedIdsProvider)(nil).ProvideIds), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDeleter is a mock of Deleter interface.
|
||||||
|
type MockDeleter struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockDeleterMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockDeleterMockRecorder is the mock recorder for MockDeleter.
|
||||||
|
type MockDeleterMockRecorder struct {
|
||||||
|
mock *MockDeleter
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockDeleter creates a new mock instance.
|
||||||
|
func NewMockDeleter(ctrl *gomock.Controller) *MockDeleter {
|
||||||
|
mock := &MockDeleter{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockDeleterMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockDeleter) EXPECT() *MockDeleterMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete mocks base method.
|
||||||
|
func (m *MockDeleter) Delete() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Delete")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete indicates an expected call of Delete.
|
||||||
|
func (mr *MockDeleterMockRecorder) Delete() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockDeleter)(nil).Delete))
|
||||||
|
}
|
||||||
154
common/commonspace/settingsdocument/settingsdocument.go
Normal file
154
common/commonspace/settingsdocument/settingsdocument.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
//go:generate mockgen -destination mock_settingsdocument/mock_settingsdocument.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument DeletedIdsProvider,Deleter
|
||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
|
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logger.NewNamed("commonspace.settingsdocument")
|
||||||
|
|
||||||
|
type SettingsDocument interface {
|
||||||
|
synctree.SyncTree
|
||||||
|
Init(ctx context.Context) (err error)
|
||||||
|
DeleteObject(id string) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BuildTreeFunc func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error)
|
||||||
|
|
||||||
|
type Deps struct {
|
||||||
|
BuildFunc BuildTreeFunc
|
||||||
|
Account account.Service
|
||||||
|
TreeGetter treegetter.TreeGetter
|
||||||
|
Store spacestorage.SpaceStorage
|
||||||
|
DeletionState deletionstate.DeletionState
|
||||||
|
// testing dependencies
|
||||||
|
prov DeletedIdsProvider
|
||||||
|
del Deleter
|
||||||
|
}
|
||||||
|
|
||||||
|
type settingsDocument struct {
|
||||||
|
synctree.SyncTree
|
||||||
|
account account.Service
|
||||||
|
spaceId string
|
||||||
|
treeGetter treegetter.TreeGetter
|
||||||
|
store spacestorage.SpaceStorage
|
||||||
|
prov DeletedIdsProvider
|
||||||
|
buildFunc BuildTreeFunc
|
||||||
|
loop *deleteLoop
|
||||||
|
|
||||||
|
deletionState deletionstate.DeletionState
|
||||||
|
lastChangeId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSettingsDocument(deps Deps, spaceId string) (doc SettingsDocument) {
|
||||||
|
var deleter Deleter
|
||||||
|
if deps.del == nil {
|
||||||
|
deleter = newDeleter(deps.Store, deps.DeletionState, deps.TreeGetter)
|
||||||
|
} else {
|
||||||
|
deleter = deps.del
|
||||||
|
}
|
||||||
|
|
||||||
|
loop := newDeleteLoop(func() {
|
||||||
|
deleter.Delete()
|
||||||
|
})
|
||||||
|
deps.DeletionState.AddObserver(func(ids []string) {
|
||||||
|
loop.notify()
|
||||||
|
})
|
||||||
|
|
||||||
|
s := &settingsDocument{
|
||||||
|
loop: loop,
|
||||||
|
spaceId: spaceId,
|
||||||
|
account: deps.Account,
|
||||||
|
deletionState: deps.DeletionState,
|
||||||
|
treeGetter: deps.TreeGetter,
|
||||||
|
store: deps.Store,
|
||||||
|
buildFunc: deps.BuildFunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is needed mainly for testing
|
||||||
|
if deps.prov == nil {
|
||||||
|
s.prov = &provider{}
|
||||||
|
} else {
|
||||||
|
s.prov = deps.prov
|
||||||
|
}
|
||||||
|
|
||||||
|
doc = s
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settingsDocument) updateIds(tr tree.ObjectTree, lastChangeId string) {
|
||||||
|
s.lastChangeId = lastChangeId
|
||||||
|
ids, lastId, err := s.prov.ProvideIds(tr, s.lastChangeId)
|
||||||
|
if err != nil {
|
||||||
|
log.With(zap.Strings("ids", ids), zap.Error(err)).Error("failed to update state")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.lastChangeId = lastId
|
||||||
|
if err = s.deletionState.Add(ids); err != nil {
|
||||||
|
log.With(zap.Strings("ids", ids), zap.Error(err)).Error("failed to queue ids to delete")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update is called as part of UpdateListener interface
|
||||||
|
func (s *settingsDocument) Update(tr tree.ObjectTree) {
|
||||||
|
s.updateIds(tr, s.lastChangeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *settingsDocument) Rebuild(tr tree.ObjectTree) {
|
||||||
|
// at initial build "s" may not contain the object tree, so it is safer to provide it from the function parameter
|
||||||
|
s.updateIds(tr, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settingsDocument) Init(ctx context.Context) (err error) {
|
||||||
|
s.SyncTree, err = s.buildFunc(ctx, s.store.SpaceSettingsId(), s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.loop.Run()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settingsDocument) Close() error {
|
||||||
|
s.loop.Close()
|
||||||
|
return s.SyncTree.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *settingsDocument) DeleteObject(id string) (err error) {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
if s.deletionState.Exists(id) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add snapshot logic
|
||||||
|
res, err := s.deletionState.CreateDeleteChange(id, false)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accountData := s.account.Account()
|
||||||
|
_, err = s.AddContent(context.Background(), tree.SignableChangeContent{
|
||||||
|
Data: res,
|
||||||
|
Key: accountData.SignKey,
|
||||||
|
Identity: accountData.Identity,
|
||||||
|
IsSnapshot: false,
|
||||||
|
IsEncrypted: false,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Update(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
188
common/commonspace/settingsdocument/settingsdocument_test.go
Normal file
188
common/commonspace/settingsdocument/settingsdocument_test.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package settingsdocument
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account/mock_account"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate/mock_deletionstate"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/mock_settingsdocument"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/mock_synctree"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/treegetter/mock_treegetter"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/account"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/asymmetric/signingkey"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testSyncTreeMock struct {
|
||||||
|
*mock_synctree.MockSyncTree
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestObjMock(mockTree *mock_synctree.MockSyncTree) *testSyncTreeMock {
|
||||||
|
return &testSyncTreeMock{
|
||||||
|
MockSyncTree: mockTree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSyncTreeMock) Lock() {
|
||||||
|
t.m.Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSyncTreeMock) Unlock() {
|
||||||
|
t.m.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
type settingsFixture struct {
|
||||||
|
spaceId string
|
||||||
|
docId string
|
||||||
|
doc *settingsDocument
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
treeGetter *mock_treegetter.MockTreeGetter
|
||||||
|
spaceStorage *mock_storage.MockSpaceStorage
|
||||||
|
provider *mock_settingsdocument.MockDeletedIdsProvider
|
||||||
|
deleter *mock_settingsdocument.MockDeleter
|
||||||
|
syncTree *mock_synctree.MockSyncTree
|
||||||
|
delState *mock_deletionstate.MockDeletionState
|
||||||
|
account *mock_account.MockService
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSettingsFixture(t *testing.T) *settingsFixture {
|
||||||
|
spaceId := "spaceId"
|
||||||
|
docId := "documentId"
|
||||||
|
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
acc := mock_account.NewMockService(ctrl)
|
||||||
|
treeGetter := mock_treegetter.NewMockTreeGetter(ctrl)
|
||||||
|
st := mock_storage.NewMockSpaceStorage(ctrl)
|
||||||
|
delState := mock_deletionstate.NewMockDeletionState(ctrl)
|
||||||
|
prov := mock_settingsdocument.NewMockDeletedIdsProvider(ctrl)
|
||||||
|
syncTree := mock_synctree.NewMockSyncTree(ctrl)
|
||||||
|
del := mock_settingsdocument.NewMockDeleter(ctrl)
|
||||||
|
|
||||||
|
delState.EXPECT().AddObserver(gomock.Any())
|
||||||
|
|
||||||
|
buildFunc := BuildTreeFunc(func(ctx context.Context, id string, listener updatelistener.UpdateListener) (synctree.SyncTree, error) {
|
||||||
|
require.Equal(t, docId, id)
|
||||||
|
return newTestObjMock(syncTree), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
deps := Deps{
|
||||||
|
BuildFunc: buildFunc,
|
||||||
|
Account: acc,
|
||||||
|
TreeGetter: treeGetter,
|
||||||
|
Store: st,
|
||||||
|
DeletionState: delState,
|
||||||
|
prov: prov,
|
||||||
|
del: del,
|
||||||
|
}
|
||||||
|
doc := NewSettingsDocument(deps, spaceId).(*settingsDocument)
|
||||||
|
return &settingsFixture{
|
||||||
|
spaceId: spaceId,
|
||||||
|
docId: docId,
|
||||||
|
doc: doc,
|
||||||
|
ctrl: ctrl,
|
||||||
|
treeGetter: treeGetter,
|
||||||
|
spaceStorage: st,
|
||||||
|
provider: prov,
|
||||||
|
deleter: del,
|
||||||
|
syncTree: syncTree,
|
||||||
|
account: acc,
|
||||||
|
delState: delState,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fx *settingsFixture) stop() {
|
||||||
|
fx.ctrl.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSettingsDocument_Init(t *testing.T) {
|
||||||
|
fx := newSettingsFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
fx.spaceStorage.EXPECT().SpaceSettingsId().Return(fx.docId)
|
||||||
|
fx.deleter.EXPECT().Delete()
|
||||||
|
fx.syncTree.EXPECT().Close().Return(nil)
|
||||||
|
|
||||||
|
err := fx.doc.Init(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = fx.doc.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSettingsDocument_DeleteObject(t *testing.T) {
|
||||||
|
fx := newSettingsFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
fx.spaceStorage.EXPECT().SpaceSettingsId().Return(fx.docId)
|
||||||
|
fx.deleter.EXPECT().Delete()
|
||||||
|
|
||||||
|
err := fx.doc.Init(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
delId := "delId"
|
||||||
|
|
||||||
|
fx.delState.EXPECT().Exists(delId).Return(false)
|
||||||
|
res := []byte("settingsData")
|
||||||
|
fx.delState.EXPECT().CreateDeleteChange(delId, false).Return(res, nil)
|
||||||
|
|
||||||
|
accountData := &account.AccountData{
|
||||||
|
Identity: []byte("id"),
|
||||||
|
PeerKey: nil,
|
||||||
|
SignKey: &signingkey.Ed25519PrivateKey{},
|
||||||
|
EncKey: nil,
|
||||||
|
}
|
||||||
|
fx.account.EXPECT().Account().Return(accountData)
|
||||||
|
fx.syncTree.EXPECT().AddContent(gomock.Any(), tree.SignableChangeContent{
|
||||||
|
Data: res,
|
||||||
|
Key: accountData.SignKey,
|
||||||
|
Identity: accountData.Identity,
|
||||||
|
IsSnapshot: false,
|
||||||
|
IsEncrypted: false,
|
||||||
|
}).Return(tree.AddResult{}, nil)
|
||||||
|
|
||||||
|
lastChangeId := "someId"
|
||||||
|
retIds := []string{"id1", "id2"}
|
||||||
|
fx.doc.lastChangeId = lastChangeId
|
||||||
|
fx.provider.EXPECT().ProvideIds(gomock.Not(nil), lastChangeId).Return(retIds, retIds[len(retIds)-1], nil)
|
||||||
|
fx.delState.EXPECT().Add(retIds).Return(nil)
|
||||||
|
err = fx.doc.DeleteObject(delId)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, retIds[len(retIds)-1], fx.doc.lastChangeId)
|
||||||
|
|
||||||
|
fx.syncTree.EXPECT().Close().Return(nil)
|
||||||
|
err = fx.doc.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSettingsDocument_Rebuild(t *testing.T) {
|
||||||
|
fx := newSettingsFixture(t)
|
||||||
|
defer fx.stop()
|
||||||
|
|
||||||
|
fx.spaceStorage.EXPECT().SpaceSettingsId().Return(fx.docId)
|
||||||
|
fx.deleter.EXPECT().Delete()
|
||||||
|
|
||||||
|
err := fx.doc.Init(context.Background())
|
||||||
|
require.NoError(t, err)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
lastChangeId := "someId"
|
||||||
|
retIds := []string{"id1", "id2"}
|
||||||
|
fx.doc.lastChangeId = lastChangeId
|
||||||
|
fx.provider.EXPECT().ProvideIds(gomock.Not(nil), "").Return(retIds, retIds[len(retIds)-1], nil)
|
||||||
|
fx.delState.EXPECT().Add(retIds).Return(nil)
|
||||||
|
|
||||||
|
fx.doc.Rebuild(fx.doc)
|
||||||
|
require.Equal(t, retIds[len(retIds)-1], fx.doc.lastChangeId)
|
||||||
|
|
||||||
|
fx.syncTree.EXPECT().Close().Return(nil)
|
||||||
|
err = fx.doc.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/account"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/settingsdocument/deletionstate"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncacl"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncacl"
|
||||||
@ -40,7 +42,10 @@ type SpaceCreatePayload struct {
|
|||||||
ReplicationKey uint64
|
ReplicationKey uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpaceTypeDerived = "derived.space"
|
const (
|
||||||
|
SpaceTypeDerived = "derived.space"
|
||||||
|
SettingsSyncPeriodSeconds = 10
|
||||||
|
)
|
||||||
|
|
||||||
type SpaceDerivePayload struct {
|
type SpaceDerivePayload struct {
|
||||||
SigningKey signingkey.PrivKey
|
SigningKey signingkey.PrivKey
|
||||||
@ -48,9 +53,11 @@ type SpaceDerivePayload struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SpaceDescription struct {
|
type SpaceDescription struct {
|
||||||
SpaceHeader *spacesyncproto.RawSpaceHeaderWithId
|
SpaceHeader *spacesyncproto.RawSpaceHeaderWithId
|
||||||
AclId string
|
AclId string
|
||||||
AclPayload []byte
|
AclPayload []byte
|
||||||
|
SpaceSettingsId string
|
||||||
|
SpaceSettingsPayload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSpaceId(id string, repKey uint64) string {
|
func NewSpaceId(id string, repKey uint64) string {
|
||||||
@ -62,13 +69,14 @@ type Space interface {
|
|||||||
Init(ctx context.Context) error
|
Init(ctx context.Context) error
|
||||||
|
|
||||||
StoredIds() []string
|
StoredIds() []string
|
||||||
Description() SpaceDescription
|
Description() (SpaceDescription, error)
|
||||||
|
|
||||||
SpaceSyncRpc() RpcHandler
|
SpaceSyncRpc() RpcHandler
|
||||||
|
|
||||||
DeriveTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener updatelistener.UpdateListener) (tree.ObjectTree, error)
|
DeriveTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener updatelistener.UpdateListener) (tree.ObjectTree, error)
|
||||||
CreateTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener updatelistener.UpdateListener) (tree.ObjectTree, error)
|
CreateTree(ctx context.Context, payload tree.ObjectTreeCreatePayload, listener updatelistener.UpdateListener) (tree.ObjectTree, error)
|
||||||
BuildTree(ctx context.Context, id string, listener updatelistener.UpdateListener) (tree.ObjectTree, error)
|
BuildTree(ctx context.Context, id string, listener updatelistener.UpdateListener) (tree.ObjectTree, error)
|
||||||
|
DeleteTree(ctx context.Context, id string) (err error)
|
||||||
|
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
@ -80,13 +88,14 @@ type space struct {
|
|||||||
|
|
||||||
rpc *rpcHandler
|
rpc *rpcHandler
|
||||||
|
|
||||||
syncService syncservice.SyncService
|
syncService syncservice.SyncService
|
||||||
diffService diffservice.DiffService
|
diffService diffservice.DiffService
|
||||||
storage storage.SpaceStorage
|
storage storage.SpaceStorage
|
||||||
cache treegetter.TreeGetter
|
cache treegetter.TreeGetter
|
||||||
account account.Service
|
account account.Service
|
||||||
aclList *syncacl.SyncACL
|
aclList *syncacl.SyncACL
|
||||||
configuration nodeconf.Configuration
|
configuration nodeconf.Configuration
|
||||||
|
settingsDocument settingsdocument.SettingsDocument
|
||||||
|
|
||||||
isClosed atomic.Bool
|
isClosed atomic.Bool
|
||||||
}
|
}
|
||||||
@ -99,16 +108,30 @@ func (s *space) Id() string {
|
|||||||
return s.id
|
return s.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *space) Description() SpaceDescription {
|
func (s *space) Description() (desc SpaceDescription, err error) {
|
||||||
root := s.aclList.Root()
|
root := s.aclList.Root()
|
||||||
return SpaceDescription{
|
settingsStorage, err := s.storage.TreeStorage(s.storage.SpaceSettingsId())
|
||||||
SpaceHeader: s.header,
|
if err != nil {
|
||||||
AclId: root.Id,
|
return
|
||||||
AclPayload: root.Payload,
|
|
||||||
}
|
}
|
||||||
|
settingsRoot, err := settingsStorage.Root()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
desc = SpaceDescription{
|
||||||
|
SpaceHeader: s.header,
|
||||||
|
AclId: root.Id,
|
||||||
|
AclPayload: root.Payload,
|
||||||
|
SpaceSettingsId: settingsRoot.Id,
|
||||||
|
SpaceSettingsPayload: settingsRoot.RawChange,
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *space) Init(ctx context.Context) (err error) {
|
func (s *space) Init(ctx context.Context) (err error) {
|
||||||
|
s.storage = newCommonStorage(s.storage)
|
||||||
|
|
||||||
header, err := s.storage.SpaceHeader()
|
header, err := s.storage.SpaceHeader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -128,9 +151,32 @@ func (s *space) Init(ctx context.Context) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.aclList = syncacl.NewSyncACL(aclList, s.syncService.StreamPool())
|
s.aclList = syncacl.NewSyncACL(aclList, s.syncService.StreamPool())
|
||||||
objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache)
|
|
||||||
|
deletionState := deletionstate.NewDeletionState(s.storage)
|
||||||
|
deps := settingsdocument.Deps{
|
||||||
|
BuildFunc: func(ctx context.Context, id string, listener updatelistener.UpdateListener) (t synctree.SyncTree, err error) {
|
||||||
|
res, err := s.BuildTree(ctx, id, listener)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t = res.(synctree.SyncTree)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
Account: s.account,
|
||||||
|
TreeGetter: s.cache,
|
||||||
|
Store: s.storage,
|
||||||
|
DeletionState: deletionState,
|
||||||
|
}
|
||||||
|
s.settingsDocument = settingsdocument.NewSettingsDocument(deps, s.id)
|
||||||
|
|
||||||
|
objectGetter := newCommonSpaceGetter(s.id, s.aclList, s.cache, s.settingsDocument)
|
||||||
s.syncService.Init(objectGetter)
|
s.syncService.Init(objectGetter)
|
||||||
s.diffService.Init(initialIds)
|
s.diffService.Init(initialIds, deletionState)
|
||||||
|
err = s.settingsDocument.Init(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +209,7 @@ func (s *space) DeriveTree(ctx context.Context, payload tree.ObjectTreeCreatePay
|
|||||||
HeadNotifiable: s.diffService,
|
HeadNotifiable: s.diffService,
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
AclList: s.aclList,
|
AclList: s.aclList,
|
||||||
CreateStorage: s.storage.CreateTreeStorage,
|
SpaceStorage: s.storage,
|
||||||
}
|
}
|
||||||
return synctree.DeriveSyncTree(ctx, deps)
|
return synctree.DeriveSyncTree(ctx, deps)
|
||||||
}
|
}
|
||||||
@ -181,7 +227,7 @@ func (s *space) CreateTree(ctx context.Context, payload tree.ObjectTreeCreatePay
|
|||||||
HeadNotifiable: s.diffService,
|
HeadNotifiable: s.diffService,
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
AclList: s.aclList,
|
AclList: s.aclList,
|
||||||
CreateStorage: s.storage.CreateTreeStorage,
|
SpaceStorage: s.storage,
|
||||||
}
|
}
|
||||||
return synctree.CreateSyncTree(ctx, deps)
|
return synctree.CreateSyncTree(ctx, deps)
|
||||||
}
|
}
|
||||||
@ -203,6 +249,10 @@ func (s *space) BuildTree(ctx context.Context, id string, listener updatelistene
|
|||||||
return synctree.BuildSyncTreeOrGetRemote(ctx, id, deps)
|
return synctree.BuildSyncTreeOrGetRemote(ctx, id, deps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *space) DeleteTree(ctx context.Context, id string) (err error) {
|
||||||
|
return s.settingsDocument.DeleteObject(id)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *space) Close() error {
|
func (s *space) Close() error {
|
||||||
log.With(zap.String("id", s.id)).Debug("space is closing")
|
log.With(zap.String("id", s.id)).Debug("space is closing")
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -216,6 +266,9 @@ func (s *space) Close() error {
|
|||||||
if err := s.syncService.Close(); err != nil {
|
if err := s.syncService.Close(); err != nil {
|
||||||
mError.Add(err)
|
mError.Add(err)
|
||||||
}
|
}
|
||||||
|
if err := s.settingsDocument.Close(); err != nil {
|
||||||
|
mError.Add(err)
|
||||||
|
}
|
||||||
if err := s.aclList.Close(); err != nil {
|
if err := s.aclList.Close(); err != nil {
|
||||||
mError.Add(err)
|
mError.Add(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,9 +64,7 @@ message ObjectSyncMessage {
|
|||||||
|
|
||||||
// PushSpaceRequest is a request to add space on a node containing only one acl record
|
// PushSpaceRequest is a request to add space on a node containing only one acl record
|
||||||
message PushSpaceRequest {
|
message PushSpaceRequest {
|
||||||
RawSpaceHeaderWithId spaceHeader = 1;
|
SpacePayload payload = 1;
|
||||||
bytes aclPayload = 2;
|
|
||||||
string aclPayloadId = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushSpaceResponse is an empty response
|
// PushSpaceResponse is an empty response
|
||||||
@ -79,9 +77,15 @@ message PullSpaceRequest {
|
|||||||
|
|
||||||
// PullSpaceResponse is a response with header and acl root
|
// PullSpaceResponse is a response with header and acl root
|
||||||
message PullSpaceResponse {
|
message PullSpaceResponse {
|
||||||
|
SpacePayload payload = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SpacePayload {
|
||||||
RawSpaceHeaderWithId spaceHeader = 1;
|
RawSpaceHeaderWithId spaceHeader = 1;
|
||||||
bytes aclPayload = 2;
|
bytes aclPayload = 2;
|
||||||
string aclPayloadId = 3;
|
string aclPayloadId = 3;
|
||||||
|
bytes spaceSettingsPayload = 4;
|
||||||
|
string spaceSettingsPayloadId = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SpaceHeader is a header for a space
|
// SpaceHeader is a header for a space
|
||||||
@ -102,3 +106,23 @@ message RawSpaceHeaderWithId {
|
|||||||
bytes rawHeader = 1;
|
bytes rawHeader = 1;
|
||||||
string id = 2;
|
string id = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message SpaceSettingsContent {
|
||||||
|
oneof value {
|
||||||
|
ObjectDelete objectDelete = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message ObjectDelete {
|
||||||
|
string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SpaceSettingsSnapshot {
|
||||||
|
repeated string deletedIds = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SettingsData {
|
||||||
|
repeated SpaceSettingsContent content = 1;
|
||||||
|
SpaceSettingsSnapshot snapshot = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -176,6 +176,20 @@ func (mr *MockSpaceStorageMockRecorder) Id() *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockSpaceStorage)(nil).Id))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Id", reflect.TypeOf((*MockSpaceStorage)(nil).Id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTreeDeletedStatus mocks base method.
|
||||||
|
func (m *MockSpaceStorage) SetTreeDeletedStatus(arg0, arg1 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SetTreeDeletedStatus", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTreeDeletedStatus indicates an expected call of SetTreeDeletedStatus.
|
||||||
|
func (mr *MockSpaceStorageMockRecorder) SetTreeDeletedStatus(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetTreeDeletedStatus", reflect.TypeOf((*MockSpaceStorage)(nil).SetTreeDeletedStatus), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// SpaceHeader mocks base method.
|
// SpaceHeader mocks base method.
|
||||||
func (m *MockSpaceStorage) SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error) {
|
func (m *MockSpaceStorage) SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -191,6 +205,20 @@ func (mr *MockSpaceStorageMockRecorder) SpaceHeader() *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceHeader", reflect.TypeOf((*MockSpaceStorage)(nil).SpaceHeader))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceHeader", reflect.TypeOf((*MockSpaceStorage)(nil).SpaceHeader))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpaceSettingsId mocks base method.
|
||||||
|
func (m *MockSpaceStorage) SpaceSettingsId() string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SpaceSettingsId")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpaceSettingsId indicates an expected call of SpaceSettingsId.
|
||||||
|
func (mr *MockSpaceStorageMockRecorder) SpaceSettingsId() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceSettingsId", reflect.TypeOf((*MockSpaceStorage)(nil).SpaceSettingsId))
|
||||||
|
}
|
||||||
|
|
||||||
// StoredIds mocks base method.
|
// StoredIds mocks base method.
|
||||||
func (m *MockSpaceStorage) StoredIds() ([]string, error) {
|
func (m *MockSpaceStorage) StoredIds() ([]string, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -206,6 +234,21 @@ func (mr *MockSpaceStorageMockRecorder) StoredIds() *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoredIds", reflect.TypeOf((*MockSpaceStorage)(nil).StoredIds))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoredIds", reflect.TypeOf((*MockSpaceStorage)(nil).StoredIds))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TreeDeletedStatus mocks base method.
|
||||||
|
func (m *MockSpaceStorage) TreeDeletedStatus(arg0 string) (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "TreeDeletedStatus", arg0)
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// TreeDeletedStatus indicates an expected call of TreeDeletedStatus.
|
||||||
|
func (mr *MockSpaceStorageMockRecorder) TreeDeletedStatus(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TreeDeletedStatus", reflect.TypeOf((*MockSpaceStorage)(nil).TreeDeletedStatus), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
// TreeStorage mocks base method.
|
// TreeStorage mocks base method.
|
||||||
func (m *MockSpaceStorage) TreeStorage(arg0 string) (storage0.TreeStorage, error) {
|
func (m *MockSpaceStorage) TreeStorage(arg0 string) (storage0.TreeStorage, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@ -7,16 +7,29 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
const CName = "commonspace.storage"
|
const CName = "commonspace.storage"
|
||||||
|
|
||||||
var ErrSpaceStorageExists = errors.New("space storage exists")
|
var (
|
||||||
var ErrSpaceStorageMissing = errors.New("space storage missing")
|
ErrSpaceStorageExists = errors.New("space storage exists")
|
||||||
|
ErrSpaceStorageMissing = errors.New("space storage missing")
|
||||||
|
|
||||||
|
ErrTreeStorageAlreadyDeleted = errors.New("tree storage already deleted")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TreeDeletedStatusQueued = "queued"
|
||||||
|
TreeDeletedStatusDeleted = "deleted"
|
||||||
|
)
|
||||||
|
|
||||||
type SpaceStorage interface {
|
type SpaceStorage interface {
|
||||||
storage.Provider
|
storage.Provider
|
||||||
Id() string
|
Id() string
|
||||||
|
SetTreeDeletedStatus(id, state string) error
|
||||||
|
TreeDeletedStatus(id string) (string, error)
|
||||||
|
SpaceSettingsId() string
|
||||||
ACLStorage() (storage.ListStorage, error)
|
ACLStorage() (storage.ListStorage, error)
|
||||||
SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error)
|
SpaceHeader() (*spacesyncproto.RawSpaceHeaderWithId, error)
|
||||||
StoredIds() ([]string, error)
|
StoredIds() ([]string, error)
|
||||||
@ -24,8 +37,9 @@ type SpaceStorage interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SpaceStorageCreatePayload struct {
|
type SpaceStorageCreatePayload struct {
|
||||||
RecWithId *aclrecordproto.RawACLRecordWithId
|
AclWithId *aclrecordproto.RawACLRecordWithId
|
||||||
SpaceHeaderWithId *spacesyncproto.RawSpaceHeaderWithId
|
SpaceHeaderWithId *spacesyncproto.RawSpaceHeaderWithId
|
||||||
|
SpaceSettingsWithId *treechangeproto.RawTreeChangeWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
type SpaceStorageProvider interface {
|
type SpaceStorageProvider interface {
|
||||||
@ -33,3 +47,8 @@ type SpaceStorageProvider interface {
|
|||||||
SpaceStorage(id string) (SpaceStorage, error)
|
SpaceStorage(id string) (SpaceStorage, error)
|
||||||
CreateSpaceStorage(payload SpaceStorageCreatePayload) (SpaceStorage, error)
|
CreateSpaceStorage(payload SpaceStorageCreatePayload) (SpaceStorage, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ValidateSpaceStorageCreatePayload(payload SpaceStorageCreatePayload) (err error) {
|
||||||
|
// TODO: add proper validation
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree (interfaces: SyncClient)
|
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree (interfaces: SyncClient,SyncTree)
|
||||||
|
|
||||||
// Package mock_synctree is a generated GoMock package.
|
// Package mock_synctree is a generated GoMock package.
|
||||||
package mock_synctree
|
package mock_synctree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
context "context"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
|
|
||||||
|
spacesyncproto "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
|
storage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
tree "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
tree "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/tree"
|
||||||
treechangeproto "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
treechangeproto "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
@ -134,3 +137,311 @@ func (mr *MockSyncClientMockRecorder) SendAsync(arg0, arg1, arg2 interface{}) *g
|
|||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendAsync", reflect.TypeOf((*MockSyncClient)(nil).SendAsync), arg0, arg1, arg2)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendAsync", reflect.TypeOf((*MockSyncClient)(nil).SendAsync), arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockSyncTree is a mock of SyncTree interface.
|
||||||
|
type MockSyncTree struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockSyncTreeMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockSyncTreeMockRecorder is the mock recorder for MockSyncTree.
|
||||||
|
type MockSyncTreeMockRecorder struct {
|
||||||
|
mock *MockSyncTree
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockSyncTree creates a new mock instance.
|
||||||
|
func NewMockSyncTree(ctrl *gomock.Controller) *MockSyncTree {
|
||||||
|
mock := &MockSyncTree{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockSyncTreeMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockSyncTree) EXPECT() *MockSyncTreeMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddContent mocks base method.
|
||||||
|
func (m *MockSyncTree) AddContent(arg0 context.Context, arg1 tree.SignableChangeContent) (tree.AddResult, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "AddContent", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(tree.AddResult)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddContent indicates an expected call of AddContent.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) AddContent(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddContent", reflect.TypeOf((*MockSyncTree)(nil).AddContent), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRawChanges mocks base method.
|
||||||
|
func (m *MockSyncTree) AddRawChanges(arg0 context.Context, arg1 ...*treechangeproto.RawTreeChangeWithId) (tree.AddResult, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
varargs := []interface{}{arg0}
|
||||||
|
for _, a := range arg1 {
|
||||||
|
varargs = append(varargs, a)
|
||||||
|
}
|
||||||
|
ret := m.ctrl.Call(m, "AddRawChanges", varargs...)
|
||||||
|
ret0, _ := ret[0].(tree.AddResult)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRawChanges indicates an expected call of AddRawChanges.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) AddRawChanges(arg0 interface{}, arg1 ...interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
varargs := append([]interface{}{arg0}, arg1...)
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawChanges", reflect.TypeOf((*MockSyncTree)(nil).AddRawChanges), varargs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangesAfterCommonSnapshot mocks base method.
|
||||||
|
func (m *MockSyncTree) ChangesAfterCommonSnapshot(arg0, arg1 []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ChangesAfterCommonSnapshot", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].([]*treechangeproto.RawTreeChangeWithId)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangesAfterCommonSnapshot indicates an expected call of ChangesAfterCommonSnapshot.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) ChangesAfterCommonSnapshot(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChangesAfterCommonSnapshot", reflect.TypeOf((*MockSyncTree)(nil).ChangesAfterCommonSnapshot), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mocks base method.
|
||||||
|
func (m *MockSyncTree) Close() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Close")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close indicates an expected call of Close.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Close() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSyncTree)(nil).Close))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugDump mocks base method.
|
||||||
|
func (m *MockSyncTree) DebugDump() (string, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "DebugDump")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugDump indicates an expected call of DebugDump.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) DebugDump() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DebugDump", reflect.TypeOf((*MockSyncTree)(nil).DebugDump))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete mocks base method.
|
||||||
|
func (m *MockSyncTree) Delete() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Delete")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete indicates an expected call of Delete.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Delete() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSyncTree)(nil).Delete))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleMessage mocks base method.
|
||||||
|
func (m *MockSyncTree) HandleMessage(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "HandleMessage", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleMessage indicates an expected call of HandleMessage.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) HandleMessage(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockSyncTree)(nil).HandleMessage), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasChanges mocks base method.
|
||||||
|
func (m *MockSyncTree) HasChanges(arg0 ...string) bool {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
varargs := []interface{}{}
|
||||||
|
for _, a := range arg0 {
|
||||||
|
varargs = append(varargs, a)
|
||||||
|
}
|
||||||
|
ret := m.ctrl.Call(m, "HasChanges", varargs...)
|
||||||
|
ret0, _ := ret[0].(bool)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasChanges indicates an expected call of HasChanges.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) HasChanges(arg0 ...interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasChanges", reflect.TypeOf((*MockSyncTree)(nil).HasChanges), arg0...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header mocks base method.
|
||||||
|
func (m *MockSyncTree) Header() *treechangeproto.RawTreeChangeWithId {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Header")
|
||||||
|
ret0, _ := ret[0].(*treechangeproto.RawTreeChangeWithId)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header indicates an expected call of Header.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Header() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Header", reflect.TypeOf((*MockSyncTree)(nil).Header))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heads mocks base method.
|
||||||
|
func (m *MockSyncTree) Heads() []string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Heads")
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heads indicates an expected call of Heads.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Heads() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Heads", reflect.TypeOf((*MockSyncTree)(nil).Heads))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID mocks base method.
|
||||||
|
func (m *MockSyncTree) ID() string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ID")
|
||||||
|
ret0, _ := ret[0].(string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID indicates an expected call of ID.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) ID() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockSyncTree)(nil).ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate mocks base method.
|
||||||
|
func (m *MockSyncTree) Iterate(arg0 func([]byte) (interface{}, error), arg1 func(*tree.Change) bool) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Iterate", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate indicates an expected call of Iterate.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Iterate(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Iterate", reflect.TypeOf((*MockSyncTree)(nil).Iterate), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IterateFrom mocks base method.
|
||||||
|
func (m *MockSyncTree) IterateFrom(arg0 string, arg1 func([]byte) (interface{}, error), arg2 func(*tree.Change) bool) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "IterateFrom", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IterateFrom indicates an expected call of IterateFrom.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) IterateFrom(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateFrom", reflect.TypeOf((*MockSyncTree)(nil).IterateFrom), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock mocks base method.
|
||||||
|
func (m *MockSyncTree) Lock() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Lock")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock indicates an expected call of Lock.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Lock() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lock", reflect.TypeOf((*MockSyncTree)(nil).Lock))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock mocks base method.
|
||||||
|
func (m *MockSyncTree) RLock() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "RLock")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RLock indicates an expected call of RLock.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) RLock() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RLock", reflect.TypeOf((*MockSyncTree)(nil).RLock))
|
||||||
|
}
|
||||||
|
|
||||||
|
// RUnlock mocks base method.
|
||||||
|
func (m *MockSyncTree) RUnlock() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "RUnlock")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RUnlock indicates an expected call of RUnlock.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) RUnlock() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RUnlock", reflect.TypeOf((*MockSyncTree)(nil).RUnlock))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root mocks base method.
|
||||||
|
func (m *MockSyncTree) Root() *tree.Change {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Root")
|
||||||
|
ret0, _ := ret[0].(*tree.Change)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root indicates an expected call of Root.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Root() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Root", reflect.TypeOf((*MockSyncTree)(nil).Root))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotPath mocks base method.
|
||||||
|
func (m *MockSyncTree) SnapshotPath() []string {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SnapshotPath")
|
||||||
|
ret0, _ := ret[0].([]string)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotPath indicates an expected call of SnapshotPath.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) SnapshotPath() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SnapshotPath", reflect.TypeOf((*MockSyncTree)(nil).SnapshotPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage mocks base method.
|
||||||
|
func (m *MockSyncTree) Storage() storage.TreeStorage {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Storage")
|
||||||
|
ret0, _ := ret[0].(storage.TreeStorage)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage indicates an expected call of Storage.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Storage() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Storage", reflect.TypeOf((*MockSyncTree)(nil).Storage))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock mocks base method.
|
||||||
|
func (m *MockSyncTree) Unlock() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Unlock")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock indicates an expected call of Unlock.
|
||||||
|
func (mr *MockSyncTreeMockRecorder) Unlock() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockSyncTree)(nil).Unlock))
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree SyncClient
|
//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree SyncClient,SyncTree
|
||||||
package synctree
|
package synctree
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@ -20,15 +20,24 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrSyncTreeClosed = errors.New("sync tree is closed")
|
var (
|
||||||
|
ErrSyncTreeClosed = errors.New("sync tree is closed")
|
||||||
|
ErrSyncTreeDeleted = errors.New("sync tree is deleted")
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyncTree interface {
|
||||||
|
tree.ObjectTree
|
||||||
|
synchandler.SyncHandler
|
||||||
|
}
|
||||||
|
|
||||||
// SyncTree sends head updates to sync service and also sends new changes to update listener
|
// SyncTree sends head updates to sync service and also sends new changes to update listener
|
||||||
type SyncTree struct {
|
type syncTree struct {
|
||||||
tree.ObjectTree
|
tree.ObjectTree
|
||||||
synchandler.SyncHandler
|
synchandler.SyncHandler
|
||||||
syncClient SyncClient
|
syncClient SyncClient
|
||||||
listener updatelistener.UpdateListener
|
listener updatelistener.UpdateListener
|
||||||
isClosed bool
|
isClosed bool
|
||||||
|
isDeleted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var log = logger.NewNamed("commonspace.synctree").Sugar()
|
var log = logger.NewNamed("commonspace.synctree").Sugar()
|
||||||
@ -46,7 +55,7 @@ type CreateDeps struct {
|
|||||||
StreamPool syncservice.StreamPool
|
StreamPool syncservice.StreamPool
|
||||||
Listener updatelistener.UpdateListener
|
Listener updatelistener.UpdateListener
|
||||||
AclList list.ACLList
|
AclList list.ACLList
|
||||||
CreateStorage storage.TreeStorageCreatorFunc
|
SpaceStorage spacestorage.SpaceStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
type BuildDeps struct {
|
type BuildDeps struct {
|
||||||
@ -60,8 +69,8 @@ type BuildDeps struct {
|
|||||||
TreeStorage storage.TreeStorage
|
TreeStorage storage.TreeStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeriveSyncTree(ctx context.Context, deps CreateDeps) (t tree.ObjectTree, err error) {
|
func DeriveSyncTree(ctx context.Context, deps CreateDeps) (t SyncTree, err error) {
|
||||||
t, err = createDerivedObjectTree(deps.Payload, deps.AclList, deps.CreateStorage)
|
objTree, err := createDerivedObjectTree(deps.Payload, deps.AclList, deps.SpaceStorage.CreateTreeStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -71,22 +80,27 @@ func DeriveSyncTree(ctx context.Context, deps CreateDeps) (t tree.ObjectTree, er
|
|||||||
deps.HeadNotifiable,
|
deps.HeadNotifiable,
|
||||||
sharedFactory,
|
sharedFactory,
|
||||||
deps.Configuration)
|
deps.Configuration)
|
||||||
syncTree := &SyncTree{
|
syncTree := &syncTree{
|
||||||
ObjectTree: t,
|
ObjectTree: objTree,
|
||||||
syncClient: syncClient,
|
syncClient: syncClient,
|
||||||
listener: deps.Listener,
|
listener: deps.Listener,
|
||||||
}
|
}
|
||||||
syncHandler := newSyncTreeHandler(syncTree, syncClient)
|
syncHandler := newSyncTreeHandler(syncTree, syncClient)
|
||||||
syncTree.SyncHandler = syncHandler
|
syncTree.SyncHandler = syncHandler
|
||||||
t = syncTree
|
t = syncTree
|
||||||
|
syncTree.Lock()
|
||||||
|
defer syncTree.Unlock()
|
||||||
|
if syncTree.listener != nil {
|
||||||
|
syncTree.listener.Rebuild(syncTree)
|
||||||
|
}
|
||||||
|
|
||||||
headUpdate := syncClient.CreateHeadUpdate(t, nil)
|
headUpdate := syncClient.CreateHeadUpdate(t, nil)
|
||||||
err = syncClient.BroadcastAsync(headUpdate)
|
err = syncClient.BroadcastAsync(headUpdate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSyncTree(ctx context.Context, deps CreateDeps) (t tree.ObjectTree, err error) {
|
func CreateSyncTree(ctx context.Context, deps CreateDeps) (t SyncTree, err error) {
|
||||||
t, err = createObjectTree(deps.Payload, deps.AclList, deps.CreateStorage)
|
objTree, err := createObjectTree(deps.Payload, deps.AclList, deps.SpaceStorage.CreateTreeStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -96,21 +110,27 @@ func CreateSyncTree(ctx context.Context, deps CreateDeps) (t tree.ObjectTree, er
|
|||||||
deps.HeadNotifiable,
|
deps.HeadNotifiable,
|
||||||
GetRequestFactory(),
|
GetRequestFactory(),
|
||||||
deps.Configuration)
|
deps.Configuration)
|
||||||
syncTree := &SyncTree{
|
syncTree := &syncTree{
|
||||||
ObjectTree: t,
|
ObjectTree: objTree,
|
||||||
syncClient: syncClient,
|
syncClient: syncClient,
|
||||||
listener: deps.Listener,
|
listener: deps.Listener,
|
||||||
}
|
}
|
||||||
syncHandler := newSyncTreeHandler(syncTree, syncClient)
|
syncHandler := newSyncTreeHandler(syncTree, syncClient)
|
||||||
syncTree.SyncHandler = syncHandler
|
syncTree.SyncHandler = syncHandler
|
||||||
t = syncTree
|
t = syncTree
|
||||||
|
syncTree.Lock()
|
||||||
|
defer syncTree.Unlock()
|
||||||
|
// TODO: refactor here because the code is duplicated, when we create a tree we should only create a storage and then build a tree
|
||||||
|
if syncTree.listener != nil {
|
||||||
|
syncTree.listener.Rebuild(syncTree)
|
||||||
|
}
|
||||||
|
|
||||||
headUpdate := syncClient.CreateHeadUpdate(t, nil)
|
headUpdate := syncClient.CreateHeadUpdate(t, nil)
|
||||||
err = syncClient.BroadcastAsync(headUpdate)
|
err = syncClient.BroadcastAsync(headUpdate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t tree.ObjectTree, err error) {
|
func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t SyncTree, err error) {
|
||||||
getTreeRemote := func() (msg *treechangeproto.TreeSyncMessage, err error) {
|
getTreeRemote := func() (msg *treechangeproto.TreeSyncMessage, err error) {
|
||||||
peerId, err := peer.CtxPeerId(ctx)
|
peerId, err := peer.CtxPeerId(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -140,6 +160,15 @@ func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status, err := deps.SpaceStorage.TreeDeletedStatus(id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if status != "" {
|
||||||
|
err = spacestorage.ErrTreeStorageAlreadyDeleted
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := getTreeRemote()
|
resp, err := getTreeRemote()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -170,9 +199,8 @@ func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t
|
|||||||
return buildSyncTree(ctx, true, deps)
|
return buildSyncTree(ctx, true, deps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSyncTree(ctx context.Context, isFirstBuild bool, deps BuildDeps) (t tree.ObjectTree, err error) {
|
func buildSyncTree(ctx context.Context, isFirstBuild bool, deps BuildDeps) (t SyncTree, err error) {
|
||||||
|
objTree, err := buildObjectTree(deps.TreeStorage, deps.AclList)
|
||||||
t, err = buildObjectTree(deps.TreeStorage, deps.AclList)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -182,14 +210,19 @@ func buildSyncTree(ctx context.Context, isFirstBuild bool, deps BuildDeps) (t tr
|
|||||||
deps.HeadNotifiable,
|
deps.HeadNotifiable,
|
||||||
GetRequestFactory(),
|
GetRequestFactory(),
|
||||||
deps.Configuration)
|
deps.Configuration)
|
||||||
syncTree := &SyncTree{
|
syncTree := &syncTree{
|
||||||
ObjectTree: t,
|
ObjectTree: objTree,
|
||||||
syncClient: syncClient,
|
syncClient: syncClient,
|
||||||
listener: deps.Listener,
|
listener: deps.Listener,
|
||||||
}
|
}
|
||||||
syncHandler := newSyncTreeHandler(syncTree, syncClient)
|
syncHandler := newSyncTreeHandler(syncTree, syncClient)
|
||||||
syncTree.SyncHandler = syncHandler
|
syncTree.SyncHandler = syncHandler
|
||||||
t = syncTree
|
t = syncTree
|
||||||
|
syncTree.Lock()
|
||||||
|
defer syncTree.Unlock()
|
||||||
|
if syncTree.listener != nil {
|
||||||
|
syncTree.listener.Rebuild(syncTree)
|
||||||
|
}
|
||||||
|
|
||||||
headUpdate := syncTree.syncClient.CreateHeadUpdate(t, nil)
|
headUpdate := syncTree.syncClient.CreateHeadUpdate(t, nil)
|
||||||
// here we will have different behaviour based on who is sending this update
|
// here we will have different behaviour based on who is sending this update
|
||||||
@ -203,9 +236,22 @@ func buildSyncTree(ctx context.Context, isFirstBuild bool, deps BuildDeps) (t tr
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncTree) AddContent(ctx context.Context, content tree.SignableChangeContent) (res tree.AddResult, err error) {
|
func (s *syncTree) IterateFrom(id string, convert tree.ChangeConvertFunc, iterate tree.ChangeIterateFunc) (err error) {
|
||||||
if s.isClosed {
|
if err = s.checkAlive(); err != nil {
|
||||||
err = ErrSyncTreeClosed
|
return
|
||||||
|
}
|
||||||
|
return s.ObjectTree.IterateFrom(id, convert, iterate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syncTree) Iterate(convert tree.ChangeConvertFunc, iterate tree.ChangeIterateFunc) (err error) {
|
||||||
|
if err = s.checkAlive(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return s.ObjectTree.Iterate(convert, iterate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syncTree) AddContent(ctx context.Context, content tree.SignableChangeContent) (res tree.AddResult, err error) {
|
||||||
|
if err = s.checkAlive(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, err = s.ObjectTree.AddContent(ctx, content)
|
res, err = s.ObjectTree.AddContent(ctx, content)
|
||||||
@ -217,9 +263,8 @@ func (s *SyncTree) AddContent(ctx context.Context, content tree.SignableChangeCo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncTree) AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (res tree.AddResult, err error) {
|
func (s *syncTree) AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (res tree.AddResult, err error) {
|
||||||
if s.isClosed {
|
if err = s.checkAlive(); err != nil {
|
||||||
err = ErrSyncTreeClosed
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, err = s.ObjectTree.AddRawChanges(ctx, changes...)
|
res, err = s.ObjectTree.AddRawChanges(ctx, changes...)
|
||||||
@ -243,15 +288,38 @@ func (s *SyncTree) AddRawChanges(ctx context.Context, changes ...*treechangeprot
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncTree) Close() (err error) {
|
func (s *syncTree) Delete() (err error) {
|
||||||
|
log.With("id", s.ID()).Debug("deleting sync tree")
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
if err = s.checkAlive(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = s.ObjectTree.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.isDeleted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *syncTree) Close() (err error) {
|
||||||
log.With("id", s.ID()).Debug("closing sync tree")
|
log.With("id", s.ID()).Debug("closing sync tree")
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
defer s.Unlock()
|
||||||
log.With("id", s.ID()).Debug("taken lock on sync tree")
|
|
||||||
if s.isClosed {
|
if s.isClosed {
|
||||||
err = ErrSyncTreeClosed
|
return ErrSyncTreeClosed
|
||||||
return
|
|
||||||
}
|
}
|
||||||
s.isClosed = true
|
s.isClosed = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *syncTree) checkAlive() (err error) {
|
||||||
|
if s.isClosed {
|
||||||
|
err = ErrSyncTreeClosed
|
||||||
|
}
|
||||||
|
if s.isDeleted {
|
||||||
|
err = ErrSyncTreeDeleted
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package synctree
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage/mock_storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/syncservice"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/mock_synctree"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/mock_synctree"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/synctree/updatelistener"
|
||||||
@ -26,7 +27,7 @@ type syncTreeMatcher struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s syncTreeMatcher) Matches(x interface{}) bool {
|
func (s syncTreeMatcher) Matches(x interface{}) bool {
|
||||||
t, ok := x.(*SyncTree)
|
t, ok := x.(*syncTree)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -45,7 +46,8 @@ func Test_DeriveSyncTree(t *testing.T) {
|
|||||||
updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl)
|
updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl)
|
||||||
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
|
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
|
||||||
aclListMock := mock_list.NewMockACLList(ctrl)
|
aclListMock := mock_list.NewMockACLList(ctrl)
|
||||||
objTreeMock := mock_tree.NewMockObjectTree(ctrl)
|
objTreeMock := newTestObjMock(mock_tree.NewMockObjectTree(ctrl))
|
||||||
|
spaceStorageMock := mock_storage.NewMockSpaceStorage(ctrl)
|
||||||
spaceId := "spaceId"
|
spaceId := "spaceId"
|
||||||
expectedPayload := tree.ObjectTreeCreatePayload{SpaceId: spaceId}
|
expectedPayload := tree.ObjectTreeCreatePayload{SpaceId: spaceId}
|
||||||
createDerivedObjectTree = func(payload tree.ObjectTreeCreatePayload, l list.ACLList, create storage2.TreeStorageCreatorFunc) (objTree tree.ObjectTree, err error) {
|
createDerivedObjectTree = func(payload tree.ObjectTreeCreatePayload, l list.ACLList, create storage2.TreeStorageCreatorFunc) (objTree tree.ObjectTree, err error) {
|
||||||
@ -59,7 +61,14 @@ func Test_DeriveSyncTree(t *testing.T) {
|
|||||||
headUpdate := &treechangeproto.TreeSyncMessage{}
|
headUpdate := &treechangeproto.TreeSyncMessage{}
|
||||||
syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate)
|
syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate)
|
||||||
syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil)
|
syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil)
|
||||||
deps := CreateDeps{AclList: aclListMock, SpaceId: spaceId, Payload: expectedPayload, Listener: updateListenerMock}
|
updateListenerMock.EXPECT().Rebuild(gomock.Any())
|
||||||
|
deps := CreateDeps{
|
||||||
|
AclList: aclListMock,
|
||||||
|
SpaceId: spaceId,
|
||||||
|
Payload: expectedPayload,
|
||||||
|
Listener: updateListenerMock,
|
||||||
|
SpaceStorage: spaceStorageMock,
|
||||||
|
}
|
||||||
|
|
||||||
_, err := DeriveSyncTree(ctx, deps)
|
_, err := DeriveSyncTree(ctx, deps)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -73,7 +82,8 @@ func Test_CreateSyncTree(t *testing.T) {
|
|||||||
updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl)
|
updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl)
|
||||||
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
|
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
|
||||||
aclListMock := mock_list.NewMockACLList(ctrl)
|
aclListMock := mock_list.NewMockACLList(ctrl)
|
||||||
objTreeMock := mock_tree.NewMockObjectTree(ctrl)
|
objTreeMock := newTestObjMock(mock_tree.NewMockObjectTree(ctrl))
|
||||||
|
spaceStorageMock := mock_storage.NewMockSpaceStorage(ctrl)
|
||||||
spaceId := "spaceId"
|
spaceId := "spaceId"
|
||||||
expectedPayload := tree.ObjectTreeCreatePayload{SpaceId: spaceId}
|
expectedPayload := tree.ObjectTreeCreatePayload{SpaceId: spaceId}
|
||||||
createObjectTree = func(payload tree.ObjectTreeCreatePayload, l list.ACLList, create storage2.TreeStorageCreatorFunc) (objTree tree.ObjectTree, err error) {
|
createObjectTree = func(payload tree.ObjectTreeCreatePayload, l list.ACLList, create storage2.TreeStorageCreatorFunc) (objTree tree.ObjectTree, err error) {
|
||||||
@ -87,7 +97,14 @@ func Test_CreateSyncTree(t *testing.T) {
|
|||||||
headUpdate := &treechangeproto.TreeSyncMessage{}
|
headUpdate := &treechangeproto.TreeSyncMessage{}
|
||||||
syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate)
|
syncClientMock.EXPECT().CreateHeadUpdate(syncTreeMatcher{objTreeMock, syncClientMock, updateListenerMock}, gomock.Nil()).Return(headUpdate)
|
||||||
syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil)
|
syncClientMock.EXPECT().BroadcastAsync(gomock.Eq(headUpdate)).Return(nil)
|
||||||
deps := CreateDeps{AclList: aclListMock, SpaceId: spaceId, Payload: expectedPayload, Listener: updateListenerMock}
|
updateListenerMock.EXPECT().Rebuild(gomock.Any())
|
||||||
|
deps := CreateDeps{
|
||||||
|
AclList: aclListMock,
|
||||||
|
SpaceId: spaceId,
|
||||||
|
Payload: expectedPayload,
|
||||||
|
Listener: updateListenerMock,
|
||||||
|
SpaceStorage: spaceStorageMock,
|
||||||
|
}
|
||||||
|
|
||||||
_, err := CreateSyncTree(ctx, deps)
|
_, err := CreateSyncTree(ctx, deps)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -100,8 +117,8 @@ func Test_BuildSyncTree(t *testing.T) {
|
|||||||
|
|
||||||
updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl)
|
updateListenerMock := mock_updatelistener.NewMockUpdateListener(ctrl)
|
||||||
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
|
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
|
||||||
objTreeMock := mock_tree.NewMockObjectTree(ctrl)
|
objTreeMock := newTestObjMock(mock_tree.NewMockObjectTree(ctrl))
|
||||||
tr := &SyncTree{
|
tr := &syncTree{
|
||||||
ObjectTree: objTreeMock,
|
ObjectTree: objTreeMock,
|
||||||
SyncHandler: nil,
|
SyncHandler: nil,
|
||||||
syncClient: syncClientMock,
|
syncClient: syncClientMock,
|
||||||
|
|||||||
@ -50,6 +50,20 @@ func (mr *MockTreeGetterMockRecorder) Close(arg0 interface{}) *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockTreeGetter)(nil).Close), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockTreeGetter)(nil).Close), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteTree mocks base method.
|
||||||
|
func (m *MockTreeGetter) DeleteTree(arg0 context.Context, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "DeleteTree", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTree indicates an expected call of DeleteTree.
|
||||||
|
func (mr *MockTreeGetterMockRecorder) DeleteTree(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTree", reflect.TypeOf((*MockTreeGetter)(nil).DeleteTree), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
// GetTree mocks base method.
|
// GetTree mocks base method.
|
||||||
func (m *MockTreeGetter) GetTree(arg0 context.Context, arg1, arg2 string) (tree.ObjectTree, error) {
|
func (m *MockTreeGetter) GetTree(arg0 context.Context, arg1, arg2 string) (tree.ObjectTree, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@ -15,4 +15,5 @@ var ErrSpaceNotFound = errors.New("space not found")
|
|||||||
type TreeGetter interface {
|
type TreeGetter interface {
|
||||||
app.ComponentRunnable
|
app.ComponentRunnable
|
||||||
GetTree(ctx context.Context, spaceId, treeId string) (tree.ObjectTree, error)
|
GetTree(ctx context.Context, spaceId, treeId string) (tree.ObjectTree, error)
|
||||||
|
DeleteTree(ctx context.Context, spaceId, treeId string) error
|
||||||
}
|
}
|
||||||
|
|||||||
@ -157,6 +157,10 @@ func (t *inMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string)
|
|||||||
return nil, fmt.Errorf("could not get change with id: %s", changeId)
|
return nil, fmt.Errorf("could not get change with id: %s", changeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *inMemoryTreeStorage) Delete() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type inMemoryStorageProvider struct {
|
type inMemoryStorageProvider struct {
|
||||||
objects map[string]TreeStorage
|
objects map[string]TreeStorage
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
|||||||
@ -160,6 +160,20 @@ func (mr *MockTreeStorageMockRecorder) AddRawChange(arg0 interface{}) *gomock.Ca
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawChange", reflect.TypeOf((*MockTreeStorage)(nil).AddRawChange), arg0)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawChange", reflect.TypeOf((*MockTreeStorage)(nil).AddRawChange), arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete mocks base method.
|
||||||
|
func (m *MockTreeStorage) Delete() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Delete")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete indicates an expected call of Delete.
|
||||||
|
func (mr *MockTreeStorageMockRecorder) Delete() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockTreeStorage)(nil).Delete))
|
||||||
|
}
|
||||||
|
|
||||||
// GetRawChange mocks base method.
|
// GetRawChange mocks base method.
|
||||||
func (m *MockTreeStorage) GetRawChange(arg0 context.Context, arg1 string) (*treechangeproto.RawTreeChangeWithId, error) {
|
func (m *MockTreeStorage) GetRawChange(arg0 context.Context, arg1 string) (*treechangeproto.RawTreeChangeWithId, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@ -5,9 +5,11 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrUnknownTreeId = errors.New("tree does not exist")
|
var (
|
||||||
var ErrTreeExists = errors.New("tree already exists")
|
ErrUnknownTreeId = errors.New("tree does not exist")
|
||||||
var ErrUnkownChange = errors.New("change doesn't exist")
|
ErrTreeExists = errors.New("tree already exists")
|
||||||
|
ErrUnknownChange = errors.New("change doesn't exist")
|
||||||
|
)
|
||||||
|
|
||||||
type TreeStorageCreatePayload struct {
|
type TreeStorageCreatePayload struct {
|
||||||
RootRawChange *treechangeproto.RawTreeChangeWithId
|
RootRawChange *treechangeproto.RawTreeChangeWithId
|
||||||
|
|||||||
@ -14,6 +14,7 @@ type TreeStorage interface {
|
|||||||
AddRawChange(change *treechangeproto.RawTreeChangeWithId) error
|
AddRawChange(change *treechangeproto.RawTreeChangeWithId) error
|
||||||
GetRawChange(ctx context.Context, id string) (*treechangeproto.RawTreeChangeWithId, error)
|
GetRawChange(ctx context.Context, id string) (*treechangeproto.RawTreeChangeWithId, error)
|
||||||
HasChange(ctx context.Context, id string) (bool, error)
|
HasChange(ctx context.Context, id string) (bool, error)
|
||||||
|
Delete() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type TreeStorageCreatorFunc = func(payload TreeStorageCreatePayload) (TreeStorage, error)
|
type TreeStorageCreatorFunc = func(payload TreeStorageCreatePayload) (TreeStorage, error)
|
||||||
|
|||||||
@ -48,7 +48,7 @@ type changeBuilder struct {
|
|||||||
keys *common.Keychain
|
keys *common.Keychain
|
||||||
}
|
}
|
||||||
|
|
||||||
func newChangeBuilder(keys *common.Keychain, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
|
func NewChangeBuilder(keys *common.Keychain, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
|
||||||
return &changeBuilder{keys: keys, rootChange: rootChange}
|
return &changeBuilder{keys: keys, rootChange: rootChange}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,12 +155,16 @@ func (c *changeBuilder) BuildContent(payload BuilderContent) (ch *Change, rawIdC
|
|||||||
Identity: payload.Identity,
|
Identity: payload.Identity,
|
||||||
IsSnapshot: payload.IsSnapshot,
|
IsSnapshot: payload.IsSnapshot,
|
||||||
}
|
}
|
||||||
|
if payload.ReadKey != nil {
|
||||||
encrypted, err := payload.ReadKey.Encrypt(payload.Content)
|
var encrypted []byte
|
||||||
if err != nil {
|
encrypted, err = payload.ReadKey.Encrypt(payload.Content)
|
||||||
return
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
change.ChangesData = encrypted
|
||||||
|
} else {
|
||||||
|
change.ChangesData = payload.Content
|
||||||
}
|
}
|
||||||
change.ChangesData = encrypted
|
|
||||||
|
|
||||||
marshalledChange, err := proto.Marshal(change)
|
marshalledChange, err := proto.Marshal(change)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -188,7 +192,6 @@ func (c *changeBuilder) BuildContent(payload BuilderContent) (ch *Change, rawIdC
|
|||||||
}
|
}
|
||||||
|
|
||||||
ch = NewChange(id, change, signature)
|
ch = NewChange(id, change, signature)
|
||||||
ch.Model = payload.Content
|
|
||||||
|
|
||||||
rawIdChange = &treechangeproto.RawTreeChangeWithId{
|
rawIdChange = &treechangeproto.RawTreeChangeWithId{
|
||||||
RawChange: marshalledRawChange,
|
RawChange: marshalledRawChange,
|
||||||
|
|||||||
@ -116,6 +116,20 @@ func (mr *MockObjectTreeMockRecorder) DebugDump() *gomock.Call {
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DebugDump", reflect.TypeOf((*MockObjectTree)(nil).DebugDump))
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DebugDump", reflect.TypeOf((*MockObjectTree)(nil).DebugDump))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete mocks base method.
|
||||||
|
func (m *MockObjectTree) Delete() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Delete")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete indicates an expected call of Delete.
|
||||||
|
func (mr *MockObjectTreeMockRecorder) Delete() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockObjectTree)(nil).Delete))
|
||||||
|
}
|
||||||
|
|
||||||
// HasChanges mocks base method.
|
// HasChanges mocks base method.
|
||||||
func (m *MockObjectTree) HasChanges(arg0 ...string) bool {
|
func (m *MockObjectTree) HasChanges(arg0 ...string) bool {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/common"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/common"
|
||||||
list2 "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/list"
|
list "github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/list"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/keys/symmetric"
|
||||||
@ -57,6 +57,7 @@ type ObjectTree interface {
|
|||||||
AddContent(ctx context.Context, content SignableChangeContent) (AddResult, error)
|
AddContent(ctx context.Context, content SignableChangeContent) (AddResult, error)
|
||||||
AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (AddResult, error)
|
AddRawChanges(ctx context.Context, changes ...*treechangeproto.RawTreeChangeWithId) (AddResult, error)
|
||||||
|
|
||||||
|
Delete() error
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ type objectTree struct {
|
|||||||
validator ObjectTreeValidator
|
validator ObjectTreeValidator
|
||||||
rawChangeLoader *rawChangeLoader
|
rawChangeLoader *rawChangeLoader
|
||||||
treeBuilder *treeBuilder
|
treeBuilder *treeBuilder
|
||||||
aclList list2.ACLList
|
aclList list.ACLList
|
||||||
|
|
||||||
id string
|
id string
|
||||||
root *treechangeproto.RawTreeChangeWithId
|
root *treechangeproto.RawTreeChangeWithId
|
||||||
@ -91,16 +92,16 @@ type objectTreeDeps struct {
|
|||||||
treeStorage storage.TreeStorage
|
treeStorage storage.TreeStorage
|
||||||
validator ObjectTreeValidator
|
validator ObjectTreeValidator
|
||||||
rawChangeLoader *rawChangeLoader
|
rawChangeLoader *rawChangeLoader
|
||||||
aclList list2.ACLList
|
aclList list.ACLList
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultObjectTreeDeps(
|
func defaultObjectTreeDeps(
|
||||||
rootChange *treechangeproto.RawTreeChangeWithId,
|
rootChange *treechangeproto.RawTreeChangeWithId,
|
||||||
treeStorage storage.TreeStorage,
|
treeStorage storage.TreeStorage,
|
||||||
aclList list2.ACLList) objectTreeDeps {
|
aclList list.ACLList) objectTreeDeps {
|
||||||
|
|
||||||
keychain := common.NewKeychain()
|
keychain := common.NewKeychain()
|
||||||
changeBuilder := newChangeBuilder(keychain, rootChange)
|
changeBuilder := NewChangeBuilder(keychain, rootChange)
|
||||||
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
|
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
|
||||||
return objectTreeDeps{
|
return objectTreeDeps{
|
||||||
changeBuilder: changeBuilder,
|
changeBuilder: changeBuilder,
|
||||||
@ -186,16 +187,23 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
|
|||||||
ot.aclList.RLock()
|
ot.aclList.RLock()
|
||||||
defer ot.aclList.RUnlock()
|
defer ot.aclList.RUnlock()
|
||||||
|
|
||||||
state := ot.aclList.ACLState() // special method for own keys
|
var (
|
||||||
readKey, err := state.CurrentReadKey()
|
state = ot.aclList.ACLState() // special method for own keys
|
||||||
if err != nil {
|
readKey *symmetric.Key
|
||||||
return
|
readKeyHash uint64
|
||||||
|
)
|
||||||
|
if content.IsEncrypted {
|
||||||
|
readKeyHash = state.CurrentReadKeyHash()
|
||||||
|
readKey, err = state.CurrentReadKey()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cnt = BuilderContent{
|
cnt = BuilderContent{
|
||||||
TreeHeadIds: ot.tree.Heads(),
|
TreeHeadIds: ot.tree.Heads(),
|
||||||
AclHeadId: ot.aclList.Head().Id,
|
AclHeadId: ot.aclList.Head().Id,
|
||||||
SnapshotBaseId: ot.tree.RootId(),
|
SnapshotBaseId: ot.tree.RootId(),
|
||||||
CurrentReadKeyHash: state.CurrentReadKeyHash(),
|
CurrentReadKeyHash: readKeyHash,
|
||||||
Identity: content.Identity,
|
Identity: content.Identity,
|
||||||
IsSnapshot: content.IsSnapshot,
|
IsSnapshot: content.IsSnapshot,
|
||||||
SigningKey: content.Key,
|
SigningKey: content.Key,
|
||||||
@ -439,9 +447,25 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate
|
|||||||
ot.tree.Iterate(id, iterate)
|
ot.tree.Iterate(id, iterate)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
decrypt := func(c *Change) (decrypted []byte, err error) {
|
||||||
|
// the change is not encrypted
|
||||||
|
if c.ReadKeyHash == 0 {
|
||||||
|
decrypted = c.Data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
readKey, exists := ot.keys[c.ReadKeyHash]
|
||||||
|
if !exists {
|
||||||
|
err = list.ErrNoReadKey
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ot.tree.Iterate(ot.tree.RootId(), func(c *Change) (isContinue bool) {
|
decrypted, err = readKey.Decrypt(c.Data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ot.tree.Iterate(id, func(c *Change) (isContinue bool) {
|
||||||
var model any
|
var model any
|
||||||
|
// if already saved as a model
|
||||||
if c.Model != nil {
|
if c.Model != nil {
|
||||||
return iterate(c)
|
return iterate(c)
|
||||||
}
|
}
|
||||||
@ -449,14 +473,9 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate
|
|||||||
if c.Id == ot.id {
|
if c.Id == ot.id {
|
||||||
return iterate(c)
|
return iterate(c)
|
||||||
}
|
}
|
||||||
readKey, exists := ot.keys[c.ReadKeyHash]
|
|
||||||
if !exists {
|
|
||||||
err = list2.ErrNoReadKey
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var decrypted []byte
|
var decrypted []byte
|
||||||
decrypted, err = readKey.Decrypt(c.Data)
|
decrypted, err = decrypt(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -508,6 +527,10 @@ func (ot *objectTree) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ot *objectTree) Delete() error {
|
||||||
|
return ot.treeStorage.Delete()
|
||||||
|
}
|
||||||
|
|
||||||
func (ot *objectTree) SnapshotPath() []string {
|
func (ot *objectTree) SnapshotPath() []string {
|
||||||
// TODO: Add error as return parameter
|
// TODO: Add error as return parameter
|
||||||
if ot.snapshotPathIsActual() {
|
if ot.snapshotPathIsActual() {
|
||||||
|
|||||||
@ -116,7 +116,7 @@ func prepareTreeContext(t *testing.T, aclList list.ACLList) testTreeContext {
|
|||||||
treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
|
treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
|
||||||
root, _ := treeStorage.Root()
|
root, _ := treeStorage.Root()
|
||||||
changeBuilder := &mockChangeBuilder{
|
changeBuilder := &mockChangeBuilder{
|
||||||
originalBuilder: newChangeBuilder(nil, root),
|
originalBuilder: NewChangeBuilder(nil, root),
|
||||||
}
|
}
|
||||||
deps := objectTreeDeps{
|
deps := objectTreeDeps{
|
||||||
changeBuilder: changeBuilder,
|
changeBuilder: changeBuilder,
|
||||||
|
|||||||
@ -14,10 +14,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ObjectTreeCreatePayload struct {
|
type ObjectTreeCreatePayload struct {
|
||||||
SignKey signingkey.PrivKey
|
SignKey signingkey.PrivKey
|
||||||
ChangeType string
|
ChangeType string
|
||||||
SpaceId string
|
SpaceId string
|
||||||
Identity []byte
|
Identity []byte
|
||||||
|
IsEncrypted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuildObjectTree(treeStorage storage.TreeStorage, aclList list.ACLList) (ObjectTree, error) {
|
func BuildObjectTree(treeStorage storage.TreeStorage, aclList list.ACLList) (ObjectTree, error) {
|
||||||
@ -71,7 +72,7 @@ func createObjectTree(
|
|||||||
Seed: seed,
|
Seed: seed,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, raw, err := newChangeBuilder(common.NewKeychain(), nil).BuildInitialContent(cnt)
|
_, raw, err := NewChangeBuilder(common.NewKeychain(), nil).BuildInitialContent(cnt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SignableChangeContent struct {
|
type SignableChangeContent struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
Key signingkey.PrivKey
|
Key signingkey.PrivKey
|
||||||
Identity []byte
|
Identity []byte
|
||||||
IsSnapshot bool
|
IsSnapshot bool
|
||||||
|
IsEncrypted bool
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/periodicsync (interfaces: PeriodicSync)
|
||||||
|
|
||||||
|
// Package mock_periodicsync is a generated GoMock package.
|
||||||
|
package mock_periodicsync
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockPeriodicSync is a mock of PeriodicSync interface.
|
||||||
|
type MockPeriodicSync struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockPeriodicSyncMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockPeriodicSyncMockRecorder is the mock recorder for MockPeriodicSync.
|
||||||
|
type MockPeriodicSyncMockRecorder struct {
|
||||||
|
mock *MockPeriodicSync
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockPeriodicSync creates a new mock instance.
|
||||||
|
func NewMockPeriodicSync(ctrl *gomock.Controller) *MockPeriodicSync {
|
||||||
|
mock := &MockPeriodicSync{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockPeriodicSyncMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockPeriodicSync) EXPECT() *MockPeriodicSyncMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mocks base method.
|
||||||
|
func (m *MockPeriodicSync) Close() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Close")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close indicates an expected call of Close.
|
||||||
|
func (mr *MockPeriodicSyncMockRecorder) Close() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPeriodicSync)(nil).Close))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run mocks base method.
|
||||||
|
func (m *MockPeriodicSync) Run() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Run")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run indicates an expected call of Run.
|
||||||
|
func (mr *MockPeriodicSyncMockRecorder) Run() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockPeriodicSync)(nil).Run))
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
package diffservice
|
//go:generate mockgen -destination mock_periodicsync/mock_periodicsync.go github.com/anytypeio/go-anytype-infrastructure-experiments/common/util/periodicsync PeriodicSync
|
||||||
|
package periodicsync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -11,7 +12,9 @@ type PeriodicSync interface {
|
|||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeriodicSync(periodSeconds int, syncer DiffSyncer, l *zap.Logger) *periodicSync {
|
type SyncerFunc func(ctx context.Context) error
|
||||||
|
|
||||||
|
func NewPeriodicSync(periodSeconds int, syncer SyncerFunc, l *zap.Logger) PeriodicSync {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &periodicSync{
|
return &periodicSync{
|
||||||
syncer: syncer,
|
syncer: syncer,
|
||||||
@ -25,7 +28,7 @@ func newPeriodicSync(periodSeconds int, syncer DiffSyncer, l *zap.Logger) *perio
|
|||||||
|
|
||||||
type periodicSync struct {
|
type periodicSync struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
syncer DiffSyncer
|
syncer SyncerFunc
|
||||||
syncCtx context.Context
|
syncCtx context.Context
|
||||||
syncCancel context.CancelFunc
|
syncCancel context.CancelFunc
|
||||||
syncLoopDone chan struct{}
|
syncLoopDone chan struct{}
|
||||||
@ -42,7 +45,7 @@ func (p *periodicSync) syncLoop(periodSeconds int) {
|
|||||||
doSync := func() {
|
doSync := func() {
|
||||||
ctx, cancel := context.WithTimeout(p.syncCtx, time.Minute)
|
ctx, cancel := context.WithTimeout(p.syncCtx, time.Minute)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := p.syncer.Sync(ctx); err != nil {
|
if err := p.syncer(ctx); err != nil {
|
||||||
p.log.Warn("periodic sync error", zap.Error(err))
|
p.log.Warn("periodic sync error", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
package diffservice
|
package periodicsync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/app/logger"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/diffservice/mock_diffservice"
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -14,25 +15,34 @@ func TestPeriodicSync_Run(t *testing.T) {
|
|||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
l := logger.NewNamed("sync")
|
l := logger.NewNamed("sync")
|
||||||
diffSyncer := mock_diffservice.NewMockDiffSyncer(ctrl)
|
|
||||||
t.Run("diff syncer 1 time", func(t *testing.T) {
|
t.Run("diff syncer 1 time", func(t *testing.T) {
|
||||||
secs := 0
|
secs := 0
|
||||||
pSync := newPeriodicSync(secs, diffSyncer, l)
|
times := 0
|
||||||
|
diffSyncer := func(ctx context.Context) (err error) {
|
||||||
diffSyncer.EXPECT().Sync(gomock.Any()).Times(1).Return(nil)
|
times += 1
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pSync := NewPeriodicSync(secs, diffSyncer, l)
|
||||||
|
|
||||||
pSync.Run()
|
pSync.Run()
|
||||||
pSync.Close()
|
pSync.Close()
|
||||||
|
require.Equal(t, 1, times)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("diff syncer 2 times", func(t *testing.T) {
|
t.Run("diff syncer 2 times", func(t *testing.T) {
|
||||||
secs := 1
|
secs := 1
|
||||||
|
|
||||||
pSync := newPeriodicSync(secs, diffSyncer, l)
|
times := 0
|
||||||
diffSyncer.EXPECT().Sync(gomock.Any()).Times(2).Return(nil)
|
diffSyncer := func(ctx context.Context) (err error) {
|
||||||
|
times += 1
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
pSync := NewPeriodicSync(secs, diffSyncer, l)
|
||||||
|
|
||||||
pSync.Run()
|
pSync.Run()
|
||||||
time.Sleep(time.Second * time.Duration(secs))
|
time.Sleep(time.Second * time.Duration(secs))
|
||||||
pSync.Close()
|
pSync.Close()
|
||||||
|
require.Equal(t, 2, times)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -10,6 +10,9 @@ require (
|
|||||||
github.com/akrylysov/pogreb v0.10.1
|
github.com/akrylysov/pogreb v0.10.1
|
||||||
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-00010101000000-000000000000
|
github.com/anytypeio/go-anytype-infrastructure-experiments/common v0.0.0-00010101000000-000000000000
|
||||||
github.com/anytypeio/go-anytype-infrastructure-experiments/consensus v0.0.0-00010101000000-000000000000
|
github.com/anytypeio/go-anytype-infrastructure-experiments/consensus v0.0.0-00010101000000-000000000000
|
||||||
|
github.com/golang/mock v1.6.0
|
||||||
|
github.com/ipfs/go-cid v0.3.2
|
||||||
|
github.com/stretchr/testify v1.8.0
|
||||||
go.uber.org/zap v1.23.0
|
go.uber.org/zap v1.23.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,6 +22,7 @@ require (
|
|||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
github.com/cespare/xxhash v1.1.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/cheggaaa/mb/v2 v2.0.1 // indirect
|
github.com/cheggaaa/mb/v2 v2.0.1 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
|
||||||
github.com/fogleman/gg v1.3.0 // indirect
|
github.com/fogleman/gg v1.3.0 // indirect
|
||||||
github.com/goccy/go-graphviz v0.0.9 // indirect
|
github.com/goccy/go-graphviz v0.0.9 // indirect
|
||||||
@ -26,12 +30,10 @@ require (
|
|||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/huandu/skiplist v1.2.0 // indirect
|
github.com/huandu/skiplist v1.2.0 // indirect
|
||||||
github.com/ipfs/go-cid v0.3.2 // indirect
|
|
||||||
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
github.com/klauspost/cpuid/v2 v2.1.1 // indirect
|
||||||
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||||
github.com/libp2p/go-libp2p v0.23.2 // indirect
|
github.com/libp2p/go-libp2p v0.23.2 // indirect
|
||||||
github.com/libp2p/go-libp2p-core v0.20.1 // indirect
|
|
||||||
github.com/libp2p/go-openssl v0.1.0 // indirect
|
github.com/libp2p/go-openssl v0.1.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
github.com/mattn/go-pointer v0.0.1 // indirect
|
github.com/mattn/go-pointer v0.0.1 // indirect
|
||||||
@ -46,6 +48,7 @@ require (
|
|||||||
github.com/multiformats/go-multihash v0.2.1 // indirect
|
github.com/multiformats/go-multihash v0.2.1 // indirect
|
||||||
github.com/multiformats/go-varint v0.0.6 // indirect
|
github.com/multiformats/go-varint v0.0.6 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.13.0 // indirect
|
github.com/prometheus/client_golang v1.13.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.2.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
|
|||||||
@ -109,6 +109,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@ -191,8 +192,6 @@ github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6
|
|||||||
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||||
github.com/libp2p/go-libp2p v0.23.2 h1:yqyTeKQJyofWXxEv/eEVUvOrGdt/9x+0PIQ4N1kaxmE=
|
github.com/libp2p/go-libp2p v0.23.2 h1:yqyTeKQJyofWXxEv/eEVUvOrGdt/9x+0PIQ4N1kaxmE=
|
||||||
github.com/libp2p/go-libp2p v0.23.2/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
|
github.com/libp2p/go-libp2p v0.23.2/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
|
||||||
github.com/libp2p/go-libp2p-core v0.20.1 h1:fQz4BJyIFmSZAiTbKV8qoYhEH5Dtv/cVhZbG3Ib/+Cw=
|
|
||||||
github.com/libp2p/go-libp2p-core v0.20.1/go.mod h1:6zR8H7CvQWgYLsbG4on6oLNSGcyKaYFSEYyDt51+bIY=
|
|
||||||
github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo=
|
github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo=
|
||||||
github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc=
|
github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
@ -271,11 +270,14 @@ github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0b
|
|||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -497,6 +499,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
|||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|||||||
@ -70,3 +70,16 @@ func (c *treeCache) GetTree(ctx context.Context, spaceId, id string) (tr tree.Ob
|
|||||||
tr = value.(tree.ObjectTree)
|
tr = value.(tree.ObjectTree)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *treeCache) DeleteTree(ctx context.Context, spaceId, treeId string) (err error) {
|
||||||
|
tr, err := c.GetTree(ctx, spaceId, treeId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = tr.Delete()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = c.cache.Remove(treeId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -19,22 +19,34 @@ func (r *rpcHandler) PullSpace(ctx context.Context, request *spacesyncproto.Pull
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
description := sp.Description()
|
spaceDesc, err := sp.Description()
|
||||||
|
if err != nil {
|
||||||
|
err = spacesyncproto.ErrUnexpected
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp = &spacesyncproto.PullSpaceResponse{
|
resp = &spacesyncproto.PullSpaceResponse{
|
||||||
SpaceHeader: description.SpaceHeader,
|
Payload: &spacesyncproto.SpacePayload{
|
||||||
AclPayload: description.AclPayload,
|
SpaceHeader: spaceDesc.SpaceHeader,
|
||||||
AclPayloadId: description.AclId,
|
AclPayloadId: spaceDesc.AclId,
|
||||||
|
AclPayload: spaceDesc.AclPayload,
|
||||||
|
SpaceSettingsPayload: spaceDesc.SpaceSettingsPayload,
|
||||||
|
SpaceSettingsPayloadId: spaceDesc.SpaceSettingsId,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rpcHandler) PushSpace(ctx context.Context, req *spacesyncproto.PushSpaceRequest) (resp *spacesyncproto.PushSpaceResponse, err error) {
|
func (r *rpcHandler) PushSpace(ctx context.Context, req *spacesyncproto.PushSpaceRequest) (resp *spacesyncproto.PushSpaceResponse, err error) {
|
||||||
description := commonspace.SpaceDescription{
|
description := commonspace.SpaceDescription{
|
||||||
SpaceHeader: req.SpaceHeader,
|
SpaceHeader: req.Payload.SpaceHeader,
|
||||||
AclId: req.AclPayloadId,
|
AclId: req.Payload.AclPayloadId,
|
||||||
AclPayload: req.AclPayload,
|
AclPayload: req.Payload.AclPayload,
|
||||||
|
SpaceSettingsPayload: req.Payload.SpaceSettingsPayload,
|
||||||
|
SpaceSettingsId: req.Payload.SpaceSettingsPayloadId,
|
||||||
}
|
}
|
||||||
err = r.s.AddSpace(ctx, description)
|
ctx = context.WithValue(ctx, commonspace.AddSpaceCtxKey, description)
|
||||||
|
_, err = r.s.GetSpace(ctx, description.SpaceHeader.GetId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,6 @@ func New() Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Service interface {
|
type Service interface {
|
||||||
AddSpace(ctx context.Context, description commonspace.SpaceDescription) (err error)
|
|
||||||
GetSpace(ctx context.Context, id string) (commonspace.Space, error)
|
GetSpace(ctx context.Context, id string) (commonspace.Space, error)
|
||||||
app.ComponentRunnable
|
app.ComponentRunnable
|
||||||
}
|
}
|
||||||
@ -63,10 +62,6 @@ func (s *service) GetSpace(ctx context.Context, id string) (commonspace.Space, e
|
|||||||
return v.(commonspace.Space), nil
|
return v.(commonspace.Space), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) AddSpace(ctx context.Context, description commonspace.SpaceDescription) (err error) {
|
|
||||||
return s.commonSpace.AddSpace(ctx, description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) loadSpace(ctx context.Context, id string) (value ocache.Object, err error) {
|
func (s *service) loadSpace(ctx context.Context, id string) (value ocache.Object, err error) {
|
||||||
cc, err := s.commonSpace.NewSpace(ctx, id)
|
cc, err := s.commonSpace.NewSpace(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -25,6 +26,7 @@ func (a aclKeys) RawRecordKey(id string) []byte {
|
|||||||
|
|
||||||
type treeKeys struct {
|
type treeKeys struct {
|
||||||
id string
|
id string
|
||||||
|
prefix string
|
||||||
headsKey []byte
|
headsKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ func newTreeKeys(id string) treeKeys {
|
|||||||
return treeKeys{
|
return treeKeys{
|
||||||
id: id,
|
id: id,
|
||||||
headsKey: storage.JoinStringsToBytes("t", id, "heads"),
|
headsKey: storage.JoinStringsToBytes("t", id, "heads"),
|
||||||
|
prefix: fmt.Sprintf("t/%s", id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +46,10 @@ func (t treeKeys) RawChangeKey(id string) []byte {
|
|||||||
return storage.JoinStringsToBytes("t", t.id, id)
|
return storage.JoinStringsToBytes("t", t.id, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t treeKeys) isTreeRelatedKey(key string) bool {
|
||||||
|
return strings.HasPrefix(key, t.prefix)
|
||||||
|
}
|
||||||
|
|
||||||
type spaceKeys struct {
|
type spaceKeys struct {
|
||||||
headerKey []byte
|
headerKey []byte
|
||||||
}
|
}
|
||||||
@ -51,7 +58,10 @@ func newSpaceKeys(spaceId string) spaceKeys {
|
|||||||
return spaceKeys{headerKey: storage.JoinStringsToBytes("s", spaceId)}
|
return spaceKeys{headerKey: storage.JoinStringsToBytes("s", spaceId)}
|
||||||
}
|
}
|
||||||
|
|
||||||
var spaceIdKey = []byte("spaceId")
|
var (
|
||||||
|
spaceIdKey = []byte("spaceId")
|
||||||
|
spaceSettingsIdKey = []byte("spaceSettingsId")
|
||||||
|
)
|
||||||
|
|
||||||
func (s spaceKeys) SpaceIdKey() []byte {
|
func (s spaceKeys) SpaceIdKey() []byte {
|
||||||
return spaceIdKey
|
return spaceIdKey
|
||||||
@ -61,7 +71,15 @@ func (s spaceKeys) HeaderKey() []byte {
|
|||||||
return s.headerKey
|
return s.headerKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func isRootIdKey(key string) bool {
|
func (s spaceKeys) SpaceSettingsIdKey() []byte {
|
||||||
|
return spaceSettingsIdKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s spaceKeys) TreeDeletedKey(id string) []byte {
|
||||||
|
return storage.JoinStringsToBytes("del", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTreeHeadsKey(key string) bool {
|
||||||
return strings.HasPrefix(key, "t/") && strings.HasSuffix(key, "/heads")
|
return strings.HasPrefix(key, "t/") && strings.HasSuffix(key, "/heads")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,25 +6,25 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/storage"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"path"
|
"path"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defPogrebOptions = &pogreb.Options{
|
var (
|
||||||
BackgroundCompactionInterval: time.Minute * 5,
|
defPogrebOptions = &pogreb.Options{BackgroundCompactionInterval: time.Minute * 5}
|
||||||
}
|
log = logger.NewNamed("storage.spacestorage")
|
||||||
|
spaceValidationFunc = spacestorage.ValidateSpaceStorageCreatePayload
|
||||||
var log = logger.NewNamed("storage.spacestorage")
|
)
|
||||||
|
|
||||||
type spaceStorage struct {
|
type spaceStorage struct {
|
||||||
spaceId string
|
spaceId string
|
||||||
objDb *pogreb.DB
|
spaceSettingsId string
|
||||||
keys spaceKeys
|
objDb *pogreb.DB
|
||||||
aclStorage storage.ListStorage
|
keys spaceKeys
|
||||||
header *spacesyncproto.RawSpaceHeaderWithId
|
aclStorage storage.ListStorage
|
||||||
mx sync.Mutex
|
header *spacesyncproto.RawSpaceHeaderWithId
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSpaceStorage(rootPath string, spaceId string) (store spacestorage.SpaceStorage, err error) {
|
func newSpaceStorage(rootPath string, spaceId string) (store spacestorage.SpaceStorage, err error) {
|
||||||
@ -61,15 +61,25 @@ func newSpaceStorage(rootPath string, spaceId string) (store spacestorage.SpaceS
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spaceSettingsId, err := objDb.Get(keys.SpaceSettingsIdKey())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if spaceSettingsId == nil {
|
||||||
|
err = spacestorage.ErrSpaceStorageMissing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
aclStorage, err := newListStorage(objDb)
|
aclStorage, err := newListStorage(objDb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store = &spaceStorage{
|
store = &spaceStorage{
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
objDb: objDb,
|
spaceSettingsId: string(spaceSettingsId),
|
||||||
keys: keys,
|
objDb: objDb,
|
||||||
|
keys: keys,
|
||||||
header: &spacesyncproto.RawSpaceHeaderWithId{
|
header: &spacesyncproto.RawSpaceHeaderWithId{
|
||||||
RawHeader: header,
|
RawHeader: header,
|
||||||
Id: spaceId,
|
Id: spaceId,
|
||||||
@ -103,8 +113,35 @@ func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreate
|
|||||||
err = spacesyncproto.ErrSpaceExists
|
err = spacesyncproto.ErrSpaceExists
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = spaceValidationFunc(payload)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
aclStorage, err := createListStorage(db, payload.RecWithId)
|
aclStorage, err := createListStorage(db, payload.AclWithId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
store = &spaceStorage{
|
||||||
|
spaceId: payload.SpaceHeaderWithId.Id,
|
||||||
|
objDb: db,
|
||||||
|
keys: keys,
|
||||||
|
aclStorage: aclStorage,
|
||||||
|
spaceSettingsId: payload.SpaceSettingsWithId.Id,
|
||||||
|
header: payload.SpaceHeaderWithId,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = store.CreateTreeStorage(storage.TreeStorageCreatePayload{
|
||||||
|
RootRawChange: payload.SpaceSettingsWithId,
|
||||||
|
Changes: []*treechangeproto.RawTreeChangeWithId{payload.SpaceSettingsWithId},
|
||||||
|
Heads: []string{payload.SpaceSettingsWithId.Id},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Put(keys.SpaceSettingsIdKey(), []byte(payload.SpaceSettingsWithId.Id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -119,13 +156,6 @@ func createSpaceStorage(rootPath string, payload spacestorage.SpaceStorageCreate
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store = &spaceStorage{
|
|
||||||
spaceId: payload.SpaceHeaderWithId.Id,
|
|
||||||
objDb: db,
|
|
||||||
keys: keys,
|
|
||||||
aclStorage: aclStorage,
|
|
||||||
header: payload.SpaceHeaderWithId,
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,15 +163,15 @@ func (s *spaceStorage) Id() string {
|
|||||||
return s.spaceId
|
return s.spaceId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *spaceStorage) SpaceSettingsId() string {
|
||||||
|
return s.spaceSettingsId
|
||||||
|
}
|
||||||
|
|
||||||
func (s *spaceStorage) TreeStorage(id string) (storage.TreeStorage, error) {
|
func (s *spaceStorage) TreeStorage(id string) (storage.TreeStorage, error) {
|
||||||
return newTreeStorage(s.objDb, id)
|
return newTreeStorage(s.objDb, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *spaceStorage) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
|
func (s *spaceStorage) CreateTreeStorage(payload storage.TreeStorageCreatePayload) (ts storage.TreeStorage, err error) {
|
||||||
// we have mutex here, so we prevent overwriting the heads of a tree on concurrent creation
|
|
||||||
s.mx.Lock()
|
|
||||||
defer s.mx.Unlock()
|
|
||||||
|
|
||||||
return createTreeStorage(s.objDb, payload)
|
return createTreeStorage(s.objDb, payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,13 +183,26 @@ func (s *spaceStorage) SpaceHeader() (header *spacesyncproto.RawSpaceHeaderWithI
|
|||||||
return s.header, nil
|
return s.header, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *spaceStorage) SetTreeDeletedStatus(id, state string) (err error) {
|
||||||
|
return s.objDb.Put(s.keys.TreeDeletedKey(id), []byte(state))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *spaceStorage) TreeDeletedStatus(id string) (status string, err error) {
|
||||||
|
res, err := s.objDb.Get(s.keys.TreeDeletedKey(id))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
status = string(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (s *spaceStorage) StoredIds() (ids []string, err error) {
|
func (s *spaceStorage) StoredIds() (ids []string, err error) {
|
||||||
index := s.objDb.Items()
|
index := s.objDb.Items()
|
||||||
|
|
||||||
key, _, err := index.Next()
|
key, _, err := index.Next()
|
||||||
for err == nil {
|
for err == nil {
|
||||||
strKey := string(key)
|
strKey := string(key)
|
||||||
if isRootIdKey(strKey) {
|
if isTreeHeadsKey(strKey) {
|
||||||
ids = append(ids, getRootId(strKey))
|
ids = append(ids, getRootId(strKey))
|
||||||
}
|
}
|
||||||
key, _, err = index.Next()
|
key, _, err = index.Next()
|
||||||
|
|||||||
@ -4,8 +4,10 @@ import (
|
|||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/spacesyncproto"
|
||||||
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
spacestorage "github.com/anytypeio/go-anytype-infrastructure-experiments/common/commonspace/storage"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/aclrecordproto"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/common/pkg/acl/treechangeproto"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -19,9 +21,14 @@ func spaceTestPayload() spacestorage.SpaceStorageCreatePayload {
|
|||||||
Payload: []byte("aclRoot"),
|
Payload: []byte("aclRoot"),
|
||||||
Id: "aclRootId",
|
Id: "aclRootId",
|
||||||
}
|
}
|
||||||
|
settings := &treechangeproto.RawTreeChangeWithId{
|
||||||
|
RawChange: []byte("settings"),
|
||||||
|
Id: "settingsId",
|
||||||
|
}
|
||||||
return spacestorage.SpaceStorageCreatePayload{
|
return spacestorage.SpaceStorageCreatePayload{
|
||||||
RecWithId: aclRoot,
|
AclWithId: aclRoot,
|
||||||
SpaceHeaderWithId: header,
|
SpaceHeaderWithId: header,
|
||||||
|
SpaceSettingsWithId: settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +39,7 @@ func testSpace(t *testing.T, store spacestorage.SpaceStorage, payload spacestora
|
|||||||
|
|
||||||
aclStorage, err := store.ACLStorage()
|
aclStorage, err := store.ACLStorage()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testList(t, aclStorage, payload.RecWithId, payload.RecWithId.Id)
|
testList(t, aclStorage, payload.AclWithId, payload.AclWithId.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSpaceStorage_Create(t *testing.T) {
|
func TestSpaceStorage_Create(t *testing.T) {
|
||||||
@ -68,7 +75,7 @@ func TestSpaceStorage_NewAndCreateTree(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
testSpace(t, store, payload)
|
testSpace(t, store, payload)
|
||||||
|
|
||||||
t.Run("create tree and get tree", func(t *testing.T) {
|
t.Run("create tree, get tree and mark deleted", func(t *testing.T) {
|
||||||
payload := treeTestPayload()
|
payload := treeTestPayload()
|
||||||
treeStore, err := store.CreateTreeStorage(payload)
|
treeStore, err := store.CreateTreeStorage(payload)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -77,6 +84,14 @@ func TestSpaceStorage_NewAndCreateTree(t *testing.T) {
|
|||||||
otherStore, err := store.TreeStorage(payload.RootRawChange.Id)
|
otherStore, err := store.TreeStorage(payload.RootRawChange.Id)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
testTreePayload(t, otherStore, payload)
|
testTreePayload(t, otherStore, payload)
|
||||||
|
|
||||||
|
initialStatus := "deleted"
|
||||||
|
err = store.SetTreeDeletedStatus(otherStore.Id(), initialStatus)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
status, err := store.TreeDeletedStatus(otherStore.Id())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, initialStatus, status)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +115,11 @@ func TestSpaceStorage_StoredIds(t *testing.T) {
|
|||||||
_, err := store.CreateTreeStorage(treePayload)
|
_, err := store.CreateTreeStorage(treePayload)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
ids = append(ids, payload.SpaceSettingsWithId.Id)
|
||||||
|
sort.Strings(ids)
|
||||||
|
|
||||||
storedIds, err := store.StoredIds()
|
storedIds, err := store.StoredIds()
|
||||||
|
sort.Strings(storedIds)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ids, storedIds)
|
require.Equal(t, ids, storedIds)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,7 +122,7 @@ func (t *treeStorage) GetRawChange(ctx context.Context, id string) (raw *treecha
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if res == nil {
|
if res == nil {
|
||||||
err = storage.ErrUnkownChange
|
err = storage.ErrUnknownChange
|
||||||
}
|
}
|
||||||
|
|
||||||
raw = &treechangeproto.RawTreeChangeWithId{
|
raw = &treechangeproto.RawTreeChangeWithId{
|
||||||
@ -135,3 +135,36 @@ func (t *treeStorage) GetRawChange(ctx context.Context, id string) (raw *treecha
|
|||||||
func (t *treeStorage) HasChange(ctx context.Context, id string) (bool, error) {
|
func (t *treeStorage) HasChange(ctx context.Context, id string) (bool, error) {
|
||||||
return t.db.Has(t.keys.RawChangeKey(id))
|
return t.db.Has(t.keys.RawChangeKey(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *treeStorage) Delete() (err error) {
|
||||||
|
storedKeys, err := t.storedKeys()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, k := range storedKeys {
|
||||||
|
err = t.db.Delete(k)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *treeStorage) storedKeys() (keys [][]byte, err error) {
|
||||||
|
index := t.db.Items()
|
||||||
|
|
||||||
|
key, _, err := index.Next()
|
||||||
|
for err == nil {
|
||||||
|
strKey := string(key)
|
||||||
|
if t.keys.isTreeRelatedKey(strKey) {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
key, _, err = index.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != pogreb.ErrIterationDone {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@ -42,6 +42,26 @@ func (fx *fixture) stop(t *testing.T) {
|
|||||||
require.NoError(t, fx.db.Close())
|
require.NoError(t, fx.db.Close())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fx *fixture) testNoKeysExist(t *testing.T, treeId string) {
|
||||||
|
index := fx.db.Items()
|
||||||
|
treeKeys := newTreeKeys(treeId)
|
||||||
|
var keys [][]byte
|
||||||
|
key, _, err := index.Next()
|
||||||
|
for err == nil {
|
||||||
|
strKey := string(key)
|
||||||
|
if treeKeys.isTreeRelatedKey(strKey) {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
key, _, err = index.Next()
|
||||||
|
}
|
||||||
|
require.Equal(t, pogreb.ErrIterationDone, err)
|
||||||
|
require.Equal(t, 0, len(keys))
|
||||||
|
|
||||||
|
res, err := fx.db.Has(treeKeys.HeadsKey())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
func testTreePayload(t *testing.T, store storage.TreeStorage, payload storage.TreeStorageCreatePayload) {
|
func testTreePayload(t *testing.T, store storage.TreeStorage, payload storage.TreeStorageCreatePayload) {
|
||||||
require.Equal(t, payload.RootRawChange.Id, store.Id())
|
require.Equal(t, payload.RootRawChange.Id, store.Id())
|
||||||
|
|
||||||
@ -119,3 +139,31 @@ func TestTreeStorage_Methods(t *testing.T) {
|
|||||||
require.False(t, has)
|
require.False(t, has)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTreeStorage_Delete(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
fx.open(t)
|
||||||
|
payload := treeTestPayload()
|
||||||
|
_, err := createTreeStorage(fx.db, payload)
|
||||||
|
require.NoError(t, err)
|
||||||
|
fx.stop(t)
|
||||||
|
|
||||||
|
fx.open(t)
|
||||||
|
defer fx.stop(t)
|
||||||
|
store, err := newTreeStorage(fx.db, payload.RootRawChange.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
testTreePayload(t, store, payload)
|
||||||
|
|
||||||
|
t.Run("add raw change, get change and has change", func(t *testing.T) {
|
||||||
|
newChange := &treechangeproto.RawTreeChangeWithId{RawChange: []byte("ab"), Id: "newId"}
|
||||||
|
require.NoError(t, store.AddRawChange(newChange))
|
||||||
|
|
||||||
|
err = store.Delete()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = newTreeStorage(fx.db, payload.RootRawChange.Id)
|
||||||
|
require.Equal(t, err, storage.ErrUnknownTreeId)
|
||||||
|
|
||||||
|
fx.testNoKeysExist(t, payload.RootRawChange.Id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user