WIP plain text document
This commit is contained in:
parent
0a118c4891
commit
02d2b948a3
@ -4,16 +4,40 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/account"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/account"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
||||||
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/testchanges/pb"
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/thread"
|
"github.com/anytypeio/go-anytype-infrastructure-experiments/thread"
|
||||||
|
"github.com/gogo/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlainTextDocument struct {
|
type PlainTextDocument interface {
|
||||||
|
Text() string
|
||||||
|
AddText(text string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type plainTextDocument struct {
|
||||||
heads []string
|
heads []string
|
||||||
aclTree acltree.ACLTree
|
aclTree acltree.ACLTree
|
||||||
state *DocumentState
|
state *DocumentState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlainTextDocument) Update(tree acltree.ACLTree) {
|
func (p *plainTextDocument) Text() string {
|
||||||
|
return p.state.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plainTextDocument) AddText(text string) error {
|
||||||
|
_, err := p.aclTree.AddContent(func(builder acltree.ChangeBuilder) {
|
||||||
|
builder.AddChangeContent(
|
||||||
|
&pb.PlainTextChangeData{
|
||||||
|
Content: []*pb.PlainTextChangeContent{
|
||||||
|
createAppendTextChangeContent(text),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *plainTextDocument) Update(tree acltree.ACLTree) {
|
||||||
|
p.aclTree = tree
|
||||||
var err error
|
var err error
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -38,7 +62,8 @@ func (p *PlainTextDocument) Update(tree acltree.ACLTree) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PlainTextDocument) Rebuild(tree acltree.ACLTree) {
|
func (p *plainTextDocument) Rebuild(tree acltree.ACLTree) {
|
||||||
|
p.aclTree = tree
|
||||||
p.heads = tree.Heads()
|
p.heads = tree.Heads()
|
||||||
var startId string
|
var startId string
|
||||||
var err error
|
var err error
|
||||||
@ -79,300 +104,54 @@ func (p *PlainTextDocument) Rebuild(tree acltree.ACLTree) {
|
|||||||
p.state = state
|
p.state = state
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlainTextDocument(t thread.Thread, acc *account.AccountData) (*PlainTextDocument, error) {
|
func NewInMemoryPlainTextDocument(acc *account.AccountData, text string) (PlainTextDocument, error) {
|
||||||
tree, e
|
return NewPlainTextDocument(acc, thread.NewInMemoryThread, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
func NewPlainTextDocument(
|
||||||
//type AccountData struct {
|
acc *account.AccountData,
|
||||||
// Identity string
|
create func(change *thread.RawChange) (thread.Thread, error),
|
||||||
// SignKey threadmodels.SigningPrivKey
|
text string) (PlainTextDocument, error) {
|
||||||
// EncKey threadmodels.EncryptionPrivKey
|
changeBuilder := func(builder acltree.ChangeBuilder) {
|
||||||
//}
|
builder.UserAdd(acc.Identity, acc.EncKey.GetPublic())
|
||||||
//
|
builder.AddChangeContent(createInitialChangeContent(text))
|
||||||
//type Document struct {
|
}
|
||||||
// // TODO: ensure that every operation on Document is synchronized
|
t, err := acltree.BuildThreadWithACL(
|
||||||
// thread threadmodels.Thread
|
acc,
|
||||||
// stateProvider InitialStateProvider
|
changeBuilder,
|
||||||
// accountData *AccountData
|
create)
|
||||||
// decoder threadmodels.SigningPubKeyDecoder
|
if err != nil {
|
||||||
//
|
return nil, err
|
||||||
// treeBuilder *acltree.treeBuilder
|
}
|
||||||
// aclTreeBuilder *acltree.aclTreeBuilder
|
|
||||||
// aclStateBuilder *acltree.aclStateBuilder
|
doc := &plainTextDocument{
|
||||||
// snapshotValidator *acltree.snapshotValidator
|
heads: nil,
|
||||||
// docStateBuilder *acltree.documentStateBuilder
|
aclTree: nil,
|
||||||
//
|
state: nil,
|
||||||
// docContext *acltree.documentContext
|
}
|
||||||
//}
|
tree, err := acltree.BuildACLTree(t, acc, doc)
|
||||||
//
|
if err != nil {
|
||||||
//type UpdateResult int
|
return nil, err
|
||||||
//
|
}
|
||||||
//const (
|
doc.aclTree = tree
|
||||||
// UpdateResultNoAction UpdateResult = iota
|
return doc, nil
|
||||||
// UpdateResultAppend
|
}
|
||||||
// UpdateResultRebuild
|
|
||||||
//)
|
func createInitialChangeContent(text string) proto.Marshaler {
|
||||||
//
|
return &pb.PlainTextChangeData{
|
||||||
//type CreateChangePayload struct {
|
Content: []*pb.PlainTextChangeContent{
|
||||||
// ChangesData proto.Marshaler
|
createAppendTextChangeContent(text),
|
||||||
// ACLData *pb.ACLChangeACLData
|
},
|
||||||
// Id string // TODO: this is just for testing, because id should be created automatically from content
|
Snapshot: &pb.PlainTextChangeSnapshot{Text: text},
|
||||||
//}
|
}
|
||||||
//
|
}
|
||||||
//func NewDocument(
|
|
||||||
// thread threadmodels.Thread,
|
func createAppendTextChangeContent(text string) *pb.PlainTextChangeContent {
|
||||||
// stateProvider InitialStateProvider,
|
return &pb.PlainTextChangeContent{
|
||||||
// accountData *AccountData) *Document {
|
Value: &pb.PlainTextChangeContentValueOfTextAppend{
|
||||||
//
|
TextAppend: &pb.PlainTextChangeTextAppend{
|
||||||
// decoder := threadmodels.NewEd25519Decoder()
|
Text: text,
|
||||||
// return &Document{
|
},
|
||||||
// thread: thread,
|
},
|
||||||
// stateProvider: stateProvider,
|
}
|
||||||
// accountData: accountData,
|
}
|
||||||
// decoder: decoder,
|
|
||||||
// aclTreeBuilder: acltree.newACLTreeBuilder(thread, decoder),
|
|
||||||
// treeBuilder: acltree.newTreeBuilder(thread, decoder),
|
|
||||||
// snapshotValidator: acltree.newSnapshotValidator(decoder, accountData),
|
|
||||||
// aclStateBuilder: acltree.newACLStateBuilder(decoder, accountData),
|
|
||||||
// docStateBuilder: acltree.newDocumentStateBuilder(stateProvider),
|
|
||||||
// docContext: &acltree.documentContext{},
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
////sync layer -> Object cache -> document -> Update (..raw changes)
|
|
||||||
////client layer -> Object cache -> document -> CreateChange(...)
|
|
||||||
////
|
|
||||||
//
|
|
||||||
//// smartblock -> CreateChange(payload)
|
|
||||||
//// SmartTree iterate etc
|
|
||||||
//func (d *Document) CreateChange(payload *CreateChangePayload) error {
|
|
||||||
// // TODO: add snapshot creation logic
|
|
||||||
// marshalled, err := payload.ChangesData.Marshal()
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// encrypted, err := d.docContext.aclState.userReadKeys[d.docContext.aclState.currentReadKeyHash].
|
|
||||||
// Encrypt(marshalled)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// aclChange := &pb.ACLChange{
|
|
||||||
// TreeHeadIds: d.docContext.fullTree.Heads(),
|
|
||||||
// AclHeadIds: d.getACLHeads(),
|
|
||||||
// SnapshotBaseId: d.docContext.fullTree.RootId(),
|
|
||||||
// AclData: payload.ACLData,
|
|
||||||
// ChangesData: encrypted,
|
|
||||||
// CurrentReadKeyHash: d.docContext.aclState.currentReadKeyHash,
|
|
||||||
// Timestamp: 0,
|
|
||||||
// Identity: d.accountData.Identity,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // TODO: add CID creation logic based on content
|
|
||||||
// ch := acltree.NewChange(payload.Id, aclChange)
|
|
||||||
// ch.DecryptedDocumentChange = marshalled
|
|
||||||
//
|
|
||||||
// fullMarshalledChange, err := proto.Marshal(aclChange)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// signature, err := d.accountData.SignKey.Sign(fullMarshalledChange)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if aclChange.AclData != nil {
|
|
||||||
// // we can apply change right away without going through builder, because
|
|
||||||
// err = d.docContext.aclState.ApplyChange(payload.Id, aclChange)
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// d.docContext.fullTree.AddFast(ch)
|
|
||||||
//
|
|
||||||
// err = d.thread.AddChange(&thread.RawChange{
|
|
||||||
// Payload: marshalled,
|
|
||||||
// Signature: signature,
|
|
||||||
// Id: payload.Id,
|
|
||||||
// })
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// d.thread.SetHeads([]string{ch.Id})
|
|
||||||
// d.thread.SetMaybeHeads([]string{ch.Id})
|
|
||||||
//
|
|
||||||
// return nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (d *Document) Update(changes ...*thread.RawChange) (DocumentState, UpdateResult, error) {
|
|
||||||
// var treeChanges []*acltree.Change
|
|
||||||
//
|
|
||||||
// var foundACLChange bool
|
|
||||||
// for _, ch := range changes {
|
|
||||||
// aclChange, err := d.treeBuilder.makeVerifiedACLChange(ch)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, UpdateResultNoAction, fmt.Errorf("change with id %s is incorrect: %w", ch.Id, err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// treeChange := d.treeBuilder.changeCreator(ch.Id, aclChange)
|
|
||||||
// treeChanges = append(treeChanges, treeChange)
|
|
||||||
//
|
|
||||||
// // this already sets PossibleHeads to include new changes
|
|
||||||
// // TODO: change this behaviour as non-obvious, because it is not evident from the interface
|
|
||||||
// err = d.thread.AddChange(ch)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, UpdateResultNoAction, fmt.Errorf("change with id %s cannot be added: %w", ch.Id, err)
|
|
||||||
// }
|
|
||||||
// if treeChange.IsACLChange() {
|
|
||||||
// foundACLChange = true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if foundACLChange {
|
|
||||||
// res, err := d.Build()
|
|
||||||
// return res, UpdateResultRebuild, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// prevHeads := d.docContext.fullTree.Heads()
|
|
||||||
// mode := d.docContext.fullTree.Add(treeChanges...)
|
|
||||||
// switch mode {
|
|
||||||
// case acltree.Nothing:
|
|
||||||
// return d.docContext.docState, UpdateResultNoAction, nil
|
|
||||||
// case acltree.Rebuild:
|
|
||||||
// res, err := d.Build()
|
|
||||||
// return res, UpdateResultRebuild, err
|
|
||||||
// default:
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // TODO: we should still check if the user making those changes are able to write using "aclState"
|
|
||||||
// // decrypting everything, because we have no new keys
|
|
||||||
// for _, ch := range treeChanges {
|
|
||||||
// if ch.Content.GetChangesData() != nil {
|
|
||||||
// key, exists := d.docContext.aclState.userReadKeys[ch.Content.CurrentReadKeyHash]
|
|
||||||
// if !exists {
|
|
||||||
// err := fmt.Errorf("failed to find key with hash: %d", ch.Content.CurrentReadKeyHash)
|
|
||||||
// return nil, UpdateResultNoAction, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// err := ch.DecryptContents(key)
|
|
||||||
// if err != nil {
|
|
||||||
// err = fmt.Errorf("failed to decrypt contents for hash: %d", ch.Content.CurrentReadKeyHash)
|
|
||||||
// return nil, UpdateResultNoAction, err
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // because for every new change we know it was after any of the previous heads
|
|
||||||
// // each of previous heads must have same "Next" nodes
|
|
||||||
// // so it doesn't matter which one we choose
|
|
||||||
// // so we choose first one
|
|
||||||
// newState, err := d.docStateBuilder.appendFrom(prevHeads[0], d.docContext.docState)
|
|
||||||
// if err != nil {
|
|
||||||
// res, _ := d.Build()
|
|
||||||
// return res, UpdateResultRebuild, fmt.Errorf("could not add changes to state, rebuilded")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // setting all heads
|
|
||||||
// d.thread.SetHeads(d.docContext.fullTree.Heads())
|
|
||||||
// // this should be the entrypoint when we build the document
|
|
||||||
// d.thread.SetMaybeHeads(d.docContext.fullTree.Heads())
|
|
||||||
//
|
|
||||||
// return newState, UpdateResultAppend, nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (d *Document) Build() (DocumentState, error) {
|
|
||||||
// return d.build(false)
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//// TODO: this should not be the responsibility of Document, move it somewhere else after testing
|
|
||||||
//func (d *Document) getACLHeads() []string {
|
|
||||||
// var aclTreeHeads []string
|
|
||||||
// for _, head := range d.docContext.fullTree.Heads() {
|
|
||||||
// if slice.FindPos(aclTreeHeads, head) != -1 { // do not scan known heads
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// precedingHeads := d.getPrecedingACLHeads(head)
|
|
||||||
//
|
|
||||||
// for _, aclHead := range precedingHeads {
|
|
||||||
// if slice.FindPos(aclTreeHeads, aclHead) != -1 {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// aclTreeHeads = append(aclTreeHeads, aclHead)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return aclTreeHeads
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (d *Document) getPrecedingACLHeads(head string) []string {
|
|
||||||
// headChange := d.docContext.fullTree.attached[head]
|
|
||||||
//
|
|
||||||
// if headChange.Content.GetAclData() != nil {
|
|
||||||
// return []string{head}
|
|
||||||
// } else {
|
|
||||||
// return headChange.Content.AclHeadIds
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (d *Document) build(fromStart bool) (DocumentState, error) {
|
|
||||||
// d.treeBuilder.init()
|
|
||||||
// d.aclTreeBuilder.init()
|
|
||||||
//
|
|
||||||
// var err error
|
|
||||||
// d.docContext.fullTree, err = d.treeBuilder.build(fromStart)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // TODO: remove this from context as this is used only to validate snapshot
|
|
||||||
// d.docContext.aclTree, err = d.aclTreeBuilder.build()
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if !fromStart {
|
|
||||||
// err = d.snapshotValidator.init(d.docContext.aclTree)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// valid, err := d.snapshotValidator.validateSnapshot(d.docContext.fullTree.root)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// if !valid {
|
|
||||||
// return d.build(true)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// err = d.aclStateBuilder.init(d.docContext.fullTree)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// d.docContext.aclState, err = d.aclStateBuilder.build()
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // tree should be exposed
|
|
||||||
//
|
|
||||||
// d.docStateBuilder.init(d.docContext.aclState, d.docContext.fullTree)
|
|
||||||
// d.docContext.docState, err = d.docStateBuilder.build()
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // setting all heads
|
|
||||||
// d.thread.SetHeads(d.docContext.fullTree.Heads())
|
|
||||||
// // this should be the entrypoint when we build the document
|
|
||||||
// d.thread.SetMaybeHeads(d.docContext.fullTree.Heads())
|
|
||||||
//
|
|
||||||
// return d.docContext.docState, nil
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//func (d *Document) State() DocumentState {
|
|
||||||
// return nil
|
|
||||||
//}
|
|
||||||
|
|||||||
@ -1,52 +1,53 @@
|
|||||||
package plaintextdocument
|
package plaintextdocument
|
||||||
|
|
||||||
import (
|
//
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder"
|
//import (
|
||||||
"github.com/stretchr/testify/assert"
|
// "github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder"
|
||||||
"testing"
|
// "github.com/stretchr/testify/assert"
|
||||||
)
|
// "testing"
|
||||||
|
//)
|
||||||
func TestDocument_Build(t *testing.T) {
|
//
|
||||||
thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml")
|
//func TestDocument_Build(t *testing.T) {
|
||||||
if err != nil {
|
// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml")
|
||||||
t.Fatal(err)
|
// if err != nil {
|
||||||
}
|
// t.Fatal(err)
|
||||||
keychain := thread.GetKeychain()
|
// }
|
||||||
accountData := &AccountData{
|
// keychain := thread.GetKeychain()
|
||||||
Identity: keychain.GetIdentity("A"),
|
// accountData := &AccountData{
|
||||||
EncKey: keychain.EncryptionKeys["A"],
|
// Identity: keychain.GetIdentity("A"),
|
||||||
}
|
// EncKey: keychain.EncryptionKeys["A"],
|
||||||
doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData)
|
// }
|
||||||
res, err := doc.Build()
|
// doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData)
|
||||||
if err != nil {
|
// res, err := doc.Build()
|
||||||
t.Fatal(err)
|
// if err != nil {
|
||||||
}
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
st := res.(*DocumentState)
|
//
|
||||||
assert.Equal(t, st.Text, "some text|first")
|
// st := res.(*DocumentState)
|
||||||
}
|
// assert.Equal(t, st.Text, "some text|first")
|
||||||
|
//}
|
||||||
func TestDocument_Update(t *testing.T) {
|
//
|
||||||
thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml")
|
//func TestDocument_Update(t *testing.T) {
|
||||||
if err != nil {
|
// thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml")
|
||||||
t.Fatal(err)
|
// if err != nil {
|
||||||
}
|
// t.Fatal(err)
|
||||||
keychain := thread.GetKeychain()
|
// }
|
||||||
accountData := &AccountData{
|
// keychain := thread.GetKeychain()
|
||||||
Identity: keychain.GetIdentity("A"),
|
// accountData := &AccountData{
|
||||||
EncKey: keychain.EncryptionKeys["A"],
|
// Identity: keychain.GetIdentity("A"),
|
||||||
}
|
// EncKey: keychain.EncryptionKeys["A"],
|
||||||
doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData)
|
// }
|
||||||
res, err := doc.Build()
|
// doc := NewDocument(thread, NewPlainTextDocumentStateProvider(), accountData)
|
||||||
if err != nil {
|
// res, err := doc.Build()
|
||||||
t.Fatal(err)
|
// if err != nil {
|
||||||
}
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
st := res.(*DocumentState)
|
//
|
||||||
assert.Equal(t, st.Text, "some text|first")
|
// st := res.(*DocumentState)
|
||||||
|
// assert.Equal(t, st.Text, "some text|first")
|
||||||
rawChs := thread.GetUpdatedChanges()
|
//
|
||||||
res, updateResult, err := doc.Update(rawChs...)
|
// rawChs := thread.GetUpdatedChanges()
|
||||||
assert.Equal(t, updateResult, UpdateResultAppend)
|
// res, updateResult, err := doc.Update(rawChs...)
|
||||||
assert.Equal(t, res.(*DocumentState).Text, "some text|first|second")
|
// assert.Equal(t, updateResult, UpdateResultAppend)
|
||||||
}
|
// assert.Equal(t, res.(*DocumentState).Text, "some text|first|second")
|
||||||
|
//}
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
package plaintextdocument
|
|
||||||
|
|
||||||
import "github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
|
||||||
|
|
||||||
type documentContext struct {
|
|
||||||
aclTree *acltree.tree // TODO: remove it, because we don't use it
|
|
||||||
fullTree *acltree.tree
|
|
||||||
aclState *acltree.aclState
|
|
||||||
docState DocumentState
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
package plaintextdocument
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
|
||||||
)
|
|
||||||
|
|
||||||
// example ->
|
|
||||||
|
|
||||||
type documentStateBuilder struct {
|
|
||||||
tree *acltree.tree
|
|
||||||
aclState *acltree.aclState // TODO: decide if this is needed or not
|
|
||||||
stateProvider InitialStateProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDocumentStateBuilder(stateProvider InitialStateProvider) *documentStateBuilder {
|
|
||||||
return &documentStateBuilder{
|
|
||||||
stateProvider: stateProvider,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *documentStateBuilder) init(aclState *acltree.aclState, tree *acltree.tree) {
|
|
||||||
d.tree = tree
|
|
||||||
d.aclState = aclState
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we should probably merge the two builders into one
|
|
||||||
func (d *documentStateBuilder) build() (s DocumentState, err error) {
|
|
||||||
var (
|
|
||||||
startId string
|
|
||||||
count int
|
|
||||||
)
|
|
||||||
rootChange := d.tree.Root()
|
|
||||||
|
|
||||||
if rootChange.DecryptedDocumentChange == nil {
|
|
||||||
err = fmt.Errorf("root doesn't have decrypted change")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err = d.stateProvider.ProvideFromInitialChange(rootChange.DecryptedDocumentChange, rootChange.Id)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t := d.tree
|
|
||||||
startId = rootChange.Id
|
|
||||||
|
|
||||||
t.Iterate(startId, func(c *acltree.Change) (isContinue bool) {
|
|
||||||
count++
|
|
||||||
if startId == c.Id {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c.DecryptedDocumentChange != nil {
|
|
||||||
s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *documentStateBuilder) appendFrom(fromId string, init DocumentState) (s DocumentState, err error) {
|
|
||||||
// TODO: we should do something like state copy probably
|
|
||||||
s = init
|
|
||||||
// TODO: we should have the same logic as in aclStateBuilder, that means we should either pass in both methods state from the outside or save the state inside the builder
|
|
||||||
d.tree.Iterate(fromId, func(c *acltree.Change) (isContinue bool) {
|
|
||||||
if c.Id == fromId {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c.DecryptedDocumentChange != nil {
|
|
||||||
s, err = s.ApplyChange(c.DecryptedDocumentChange, c.Id)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return s, err
|
|
||||||
}
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
package plaintextdocument
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/acltree"
|
|
||||||
"github.com/anytypeio/go-anytype-infrastructure-experiments/testutils/threadbuilder"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDocumentStateBuilder_UserJoinBuild(t *testing.T) {
|
|
||||||
thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userjoinexample.yml")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
keychain := thread.GetKeychain()
|
|
||||||
ctx, err := acltree.createDocumentStateFromThread(
|
|
||||||
thread,
|
|
||||||
keychain.GetIdentity("A"),
|
|
||||||
keychain.EncryptionKeys["A"],
|
|
||||||
NewPlainTextDocumentStateProvider(),
|
|
||||||
threadmodels.NewEd25519Decoder())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
st := ctx.DocState.(*DocumentState)
|
|
||||||
assert.Equal(t, st.Text, "some text|first")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDocumentStateBuilder_UserRemoveBuild(t *testing.T) {
|
|
||||||
thread, err := threadbuilder.NewThreadBuilderFromFile("threadbuilder/userremoveexample.yml")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
keychain := thread.GetKeychain()
|
|
||||||
ctx, err := acltree.createDocumentStateFromThread(
|
|
||||||
thread,
|
|
||||||
keychain.GetIdentity("A"),
|
|
||||||
keychain.EncryptionKeys["A"],
|
|
||||||
NewPlainTextDocumentStateProvider(),
|
|
||||||
threadmodels.NewEd25519Decoder())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should build acl aclState without err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
st := ctx.DocState.(*DocumentState)
|
|
||||||
assert.Equal(t, st.Text, "some text|first")
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user