Skip to content
This repository has been archived by the owner on Jan 2, 2025. It is now read-only.

Commit

Permalink
feat(rebase): WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
horacioh committed May 28, 2024
1 parent e15d6e3 commit 5f9cf8c
Show file tree
Hide file tree
Showing 37 changed files with 1,861 additions and 453 deletions.
45 changes: 45 additions & 0 deletions backend/daemon/api/documents/v1alpha/documents.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,52 @@ func (api *Server) MergeChanges(ctx context.Context, in *documents.MergeChangesR
Version: hb.CID.String(),
LocalOnly: true,
})
}

// RebaseChanges implements the corresponding gRPC method.
func (api *Server) RebaseChanges(ctx context.Context, in *documents.RebaseChangesRequest) (*documents.Document, error) {
me, err := api.getMe()
if err != nil {
return nil, err
}

del, err := api.getDelegation(ctx)
if err != nil {
return nil, err
}

allHeads := []cid.Cid{}
draft, err := api.blobs.LoadDraft(ctx, hyper.EntityID(in.BaseDraftId))
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "Could not load draft with provided ID [%s]: %v", in.BaseDraftId, err)
}

for _, version := range in.Versions {
heads, err := hyper.Version(version).Parse()
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "unable to parse version %s: %v", version, err)
}
allHeads = append(allHeads, heads...)
}
entity, err := api.blobs.LoadEntityFromHeads(ctx, hyper.EntityID(in.BaseDraftId), allHeads...)
if err != nil {
return nil, err
}
if entity == nil {
return nil, fmt.Errorf("nothing to rebase, aborting")
}

_, err = entity.CreateChange(entity.NextTimestamp(), me.DeviceKey(), del, draft.Change.Patch)
if err != nil {
return nil, err
}

mut, err := docmodel.New(entity, me.DeviceKey(), del)
if err != nil {
return nil, err
}

return mut.Hydrate(ctx, api.blobs)
}

func (api *Server) getMe() (core.Identity, error) {
Expand Down
62 changes: 62 additions & 0 deletions backend/daemon/api/documents/v1alpha/documents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,68 @@ func TestMerge(t *testing.T) {
})
require.Error(t, err)
}

func TestRebase(t *testing.T) {
api := newTestDocsAPI(t, "alice")
ctx := context.Background()

draft, err := api.CreateDraft(ctx, &documents.CreateDraftRequest{})
require.NoError(t, err)

updated := updateDraft(ctx, t, api, draft.Id, []*documents.DocumentChange{
{Op: &documents.DocumentChange_SetTitle{SetTitle: "Fisrt Doc"}},
{Op: &documents.DocumentChange_MoveBlock_{MoveBlock: &documents.DocumentChange_MoveBlock{BlockId: "b1"}}},
{Op: &documents.DocumentChange_ReplaceBlock{ReplaceBlock: &documents.Block{
Id: "b1",
Type: "statement",
Text: "Hello world 1!",
}}},
})
require.NoError(t, err)
pub1, err := api.PublishDraft(ctx, &documents.PublishDraftRequest{
DocumentId: updated.Id,
})
require.NoError(t, err)

draft, err = api.CreateDraft(ctx, &documents.CreateDraftRequest{ExistingDocumentId: pub1.Document.Id})
require.NoError(t, err)
updated2 := updateDraft(ctx, t, api, draft.Id, []*documents.DocumentChange{
{Op: &documents.DocumentChange_SetTitle{SetTitle: "Second Doc"}},
{Op: &documents.DocumentChange_MoveBlock_{MoveBlock: &documents.DocumentChange_MoveBlock{BlockId: "b1"}}},
{Op: &documents.DocumentChange_ReplaceBlock{ReplaceBlock: &documents.Block{
Id: "b1",
Type: "statement",
Text: "Hello world 2!",
}}},
})
require.NoError(t, err)
pub2, err := api.PublishDraft(ctx, &documents.PublishDraftRequest{
DocumentId: updated2.Id,
})
require.NoError(t, err)
require.Equal(t, pub1.Document.Id, pub2.Document.Id)

draft, err = api.CreateDraft(ctx, &documents.CreateDraftRequest{ExistingDocumentId: pub1.Document.Id})
require.NoError(t, err)
updated3 := updateDraft(ctx, t, api, draft.Id, []*documents.DocumentChange{
{Op: &documents.DocumentChange_SetTitle{SetTitle: "Linked with the first"}},
{Op: &documents.DocumentChange_MoveBlock_{MoveBlock: &documents.DocumentChange_MoveBlock{BlockId: "b1"}}},
{Op: &documents.DocumentChange_ReplaceBlock{ReplaceBlock: &documents.Block{
Id: "b1",
Type: "statement",
Text: "Some content",
}}},
})
require.NoError(t, err)

doc, err := api.RebaseChanges(ctx, &documents.RebaseChangesRequest{
BaseDraftId: updated3.Id,
Versions: []string{pub1.Version, pub2.Version},
})
require.NoError(t, err)
require.Equal(t, updated3.Id, doc.Id)
}

func TestAPIUpdateDraft_WithList(t *testing.T) {
api := newTestDocsAPI(t, "alice")
ctx := context.Background()
Expand Down
1 change: 1 addition & 0 deletions backend/daemon/api/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (s Server) Register(srv *grpc.Server) {
documents.RegisterPublicationsServer(srv, s.Documents)
documents.RegisterChangesServer(srv, s.Documents)
documents.RegisterCommentsServer(srv, s.Documents)
documents.RegisterMergeServer(srv, s.Documents)

activity.RegisterActivityFeedServer(srv, s.Activity)
networking.RegisterNetworkingServer(srv, s.Networking)
Expand Down
62 changes: 62 additions & 0 deletions backend/daemon/daemon_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,68 @@ func TestMergeE2E(t *testing.T) {
require.NoError(t, err)
require.Contains(t, mergedPub.Document.PreviousVersion, secondVersion.Version)
require.Contains(t, mergedPub.Document.PreviousVersion, forkedVersion.Version)
require.Contains(t, mergedPub.Document.Editors, bob.Storage.Identity().MustGet().Account().String())
require.Contains(t, mergedPub.Document.Editors, alice.Storage.Identity().MustGet().Account().String())
}

func TestRebaseE2E(t *testing.T) {
t.Parallel()
acfg := makeTestConfig(t)
bcfg := makeTestConfig(t)

acfg.Syncing.WarmupDuration = 1 * time.Millisecond
bcfg.Syncing.WarmupDuration = 1 * time.Millisecond

acfg.Syncing.Interval = 150 * time.Millisecond
bcfg.Syncing.Interval = 150 * time.Millisecond

acfg.Syncing.RefreshInterval = 50 * time.Millisecond
bcfg.Syncing.RefreshInterval = 50 * time.Millisecond

alice := makeTestApp(t, "alice", acfg, true)
bob := makeTestApp(t, "bob", bcfg, true)
ctx := context.Background()

_, err := alice.RPC.Networking.Connect(ctx, &networking.ConnectRequest{
Addrs: getAddrs(t, bob),
})
require.NoError(t, err)

require.NoError(t, alice.Blobs.SetAccountTrust(ctx, bob.Storage.Identity().MustGet().Account().Principal()))
require.NoError(t, bob.Blobs.SetAccountTrust(ctx, alice.Storage.Identity().MustGet().Account().Principal()))

time.Sleep(200 * time.Millisecond)

initialVersion := publishDocument(t, ctx, alice, "", "", "")
time.Sleep(200 * time.Millisecond)

// so Bob gets Alice's document
_, err = bob.RPC.Daemon.ForceSync(ctx, &daemon.ForceSyncRequest{})
require.NoError(t, err)
time.Sleep(200 * time.Millisecond)

version1 := publishDocument(t, ctx, bob, "", initialVersion.Document.Id, initialVersion.Version)
version2 := publishDocument(t, ctx, bob, "", initialVersion.Document.Id, initialVersion.Version)
time.Sleep(200 * time.Millisecond)

// so Alice gets Bobs's changes
_, err = alice.RPC.Daemon.ForceSync(ctx, &daemon.ForceSyncRequest{})
require.NoError(t, err)
time.Sleep(200 * time.Millisecond)
draft, err := alice.RPC.Documents.CreateDraft(ctx, &documents.CreateDraftRequest{
ExistingDocumentId: initialVersion.Document.Id,
Version: initialVersion.Version,
})
require.NoError(t, err)
rebasedDoc, err := alice.RPC.Documents.RebaseChanges(ctx, &documents.RebaseChangesRequest{
BaseDraftId: draft.Id,
Versions: []string{version1.Version, version2.Version},
})
require.NoError(t, err)
require.Contains(t, rebasedDoc.PreviousVersion, version1.Version)
require.Contains(t, rebasedDoc.PreviousVersion, version2.Version)
require.Contains(t, rebasedDoc.Editors, bob.Storage.Identity().MustGet().Account().String())
require.Contains(t, rebasedDoc.Editors, alice.Storage.Identity().MustGet().Account().String())
}

func TestAPIGetRemotePublication(t *testing.T) {
Expand Down
Loading

0 comments on commit 5f9cf8c

Please sign in to comment.