1
0
Fork 0
mirror of https://github.com/anyproto/any-sync.git synced 2025-06-10 01:51:11 +09:00
any-sync/service/sync/document/service.go
2022-08-11 11:04:03 +02:00

258 lines
6.9 KiB
Go

package document
import (
"context"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app"
"github.com/anytypeio/go-anytype-infrastructure-experiments/app/logger"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/aclchanges/aclpb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/testutils/testchanges/testchangepb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/tree"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage"
"github.com/anytypeio/go-anytype-infrastructure-experiments/pkg/acl/treestorage/treepb"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/account"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/node"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/sync/message"
"github.com/anytypeio/go-anytype-infrastructure-experiments/service/treecache"
"github.com/anytypeio/go-anytype-infrastructure-experiments/syncproto"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
)
var CName = "DocumentService"
var log = logger.NewNamed("documentservice")
type service struct {
messageService message.Service
treeCache treecache.Service
account account.Service
treeStorageProvider treestorage.Provider
// to create new documents we need to know all nodes
nodes []*node.Node
}
type Service interface {
UpdateDocumentTree(ctx context.Context, id, text string) error
CreateACLTree(ctx context.Context) (id string, err error)
CreateDocumentTree(ctx context.Context, aclTreeId string, text string) (id string, err error)
}
func New() app.Component {
return &service{}
}
func (s *service) Init(ctx context.Context, a *app.App) (err error) {
s.account = a.MustComponent(account.CName).(account.Service)
s.messageService = a.MustComponent(message.CName).(message.Service)
s.treeCache = a.MustComponent(treecache.CName).(treecache.Service)
// TODO: add TreeStorageProvider service
nodesService := a.MustComponent(node.CName).(node.Service)
s.nodes = nodesService.Nodes()
return nil
}
func (s *service) Name() (name string) {
return CName
}
func (s *service) Run(ctx context.Context) (err error) {
return nil
}
func (s *service) Close(ctx context.Context) (err error) {
return nil
}
func (s *service) UpdateDocumentTree(ctx context.Context, id, text string) (err error) {
var (
ch *aclpb.RawChange
header *treepb.TreeHeader
snapshotPath []string
heads []string
)
log.With(zap.String("id", id), zap.String("text", text)).
Debug("updating document")
err = s.treeCache.Do(ctx, id, func(obj interface{}) error {
docTree := obj.(tree.DocTree)
err = s.treeCache.Do(ctx, docTree.Header().AclTreeId, func(obj interface{}) error {
aclTree := obj.(tree.ACLTree)
aclTree.RLock()
defer aclTree.RUnlock()
content := createAppendTextChange(text)
_, err := docTree.AddContent(ctx, aclTree, content, false)
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
id = docTree.ID()
heads = docTree.Heads()
header = docTree.Header()
snapshotPath = docTree.SnapshotPath()
return nil
})
if err != nil {
return err
}
log.With(
zap.String("id", id),
zap.Strings("heads", heads),
zap.String("header", header.String())).
Debug("document updated in the database")
return s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{
Heads: heads,
Changes: []*aclpb.RawChange{ch},
TreeId: id,
SnapshotPath: snapshotPath,
TreeHeader: header,
}))
}
func (s *service) CreateACLTree(ctx context.Context) (id string, err error) {
acc := s.account.Account()
var (
ch *aclpb.RawChange
header *treepb.TreeHeader
snapshotPath []string
heads []string
)
t, err := tree.CreateNewTreeStorageWithACL(acc, func(builder tree.ACLChangeBuilder) error {
err := builder.UserAdd(acc.Identity, acc.EncKey.GetPublic(), aclpb.ACLChange_Admin)
if err != nil {
return err
}
// adding all predefined nodes to the document as admins
for _, n := range s.nodes {
err = builder.UserAdd(n.SigningKeyString, n.EncryptionKey, aclpb.ACLChange_Admin)
if err != nil {
return err
}
}
return nil
}, s.treeStorageProvider.CreateTreeStorage)
id, err = t.TreeID()
if err != nil {
return "", err
}
header, err = t.Header()
if err != nil {
return "", err
}
heads = []string{header.FirstChangeId}
snapshotPath = []string{header.FirstChangeId}
ch, err = t.GetChange(ctx, header.FirstChangeId)
if err != nil {
return "", err
}
if err != nil {
return "", err
}
err = s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{
Heads: heads,
Changes: []*aclpb.RawChange{ch},
TreeId: id,
SnapshotPath: snapshotPath,
TreeHeader: header,
}))
return id, nil
}
func (s *service) CreateDocumentTree(ctx context.Context, aclTreeId string, text string) (id string, err error) {
acc := s.account.Account()
var (
ch *aclpb.RawChange
header *treepb.TreeHeader
snapshotPath []string
heads []string
)
err = s.treeCache.Do(ctx, aclTreeId, func(obj interface{}) error {
t := obj.(tree.ACLTree)
t.RLock()
defer t.RUnlock()
content := createInitialTextChange(text)
doc, err := tree.CreateNewTreeStorage(acc, t, content, s.treeStorageProvider.CreateTreeStorage)
if err != nil {
return err
}
id, err = doc.TreeID()
if err != nil {
return err
}
header, err = doc.Header()
if err != nil {
return err
}
heads = []string{header.FirstChangeId}
snapshotPath = []string{header.FirstChangeId}
ch, err = doc.GetChange(ctx, header.FirstChangeId)
if err != nil {
return err
}
return nil
})
if err != nil {
return "", err
}
log.With(zap.String("id", id), zap.String("text", text)).
Debug("creating document")
err = s.messageService.SendToSpaceAsync("", syncproto.WrapHeadUpdate(&syncproto.SyncHeadUpdate{
Heads: heads,
Changes: []*aclpb.RawChange{ch},
TreeId: id,
SnapshotPath: snapshotPath,
TreeHeader: header,
}))
if err != nil {
return "", err
}
return id, err
}
func createInitialTextChange(text string) proto.Marshaler {
return &testchangepb.PlainTextChangeData{
Content: []*testchangepb.PlainTextChangeContent{
createAppendTextChangeContent(text),
},
Snapshot: &testchangepb.PlainTextChangeSnapshot{Text: text},
}
}
func createAppendTextChange(text string) proto.Marshaler {
return &testchangepb.PlainTextChangeData{
Content: []*testchangepb.PlainTextChangeContent{
createAppendTextChangeContent(text),
},
}
}
func createAppendTextChangeContent(text string) *testchangepb.PlainTextChangeContent {
return &testchangepb.PlainTextChangeContent{
Value: &testchangepb.PlainTextChangeContentValueOfTextAppend{
TextAppend: &testchangepb.PlainTextChangeTextAppend{
Text: text,
},
},
}
}