Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/SIL.Harmony.Tests/ModelSnapshotTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,28 @@ public async Task CanGetWordForASpecificCommit()
thirdWord.Text.Should().Be("third");
}

[Fact]
public async Task CanGetWordBeforeASpecificCommit()
{
var entityId = Guid.NewGuid();
var firstCommit = await WriteNextChange(SetWord(entityId, "first"));
var secondCommit = await WriteNextChange(SetWord(entityId, "second"));
var thirdCommit = await WriteNextChange(SetWord(entityId, "third"));
await ClearNonRootSnapshots();

//there's no state before the first commit created the entity
var beforeFirst = await DataModel.GetBeforeCommit<Word>(firstCommit.Id, entityId);
beforeFirst.Should().BeNull();

var beforeSecond = await DataModel.GetBeforeCommit<Word>(secondCommit.Id, entityId);
beforeSecond.Should().NotBeNull();
beforeSecond!.Text.Should().Be("first");

var beforeThird = await DataModel.GetBeforeCommit<Word>(thirdCommit.Id, entityId);
beforeThird.Should().NotBeNull();
beforeThird!.Text.Should().Be("second");
}

[Fact]
public async Task CanGetWordForASpecificTime()
{
Expand Down
44 changes: 40 additions & 4 deletions src/SIL.Harmony/DataModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,52 @@ public async Task<T> GetAtTime<T>(DateTimeOffset time, Guid entityId)
public async Task<T> GetAtCommit<T>(Guid commitId, Guid entityId)
{
await using var repo = await _crdtRepositoryFactory.CreateRepository();
return await GetAtCommit<T>(await repo.CurrentCommits().SingleAsync(c => c.Id == commitId),
entityId);
var commit = await repo.CurrentCommits().SingleAsync(c => c.Id == commitId);
return await GetAtCommit<T>(commit, entityId, repo);
}

public async Task<T> GetAtCommit<T>(Commit commit, Guid entityId)
{
await using var repo = await _crdtRepositoryFactory.CreateRepository();
return await GetAtCommit<T>(commit, entityId, repo);
}

private async Task<T> GetAtCommit<T>(Commit commit, Guid entityId, CrdtRepository repo)
{
var snapshot = await GetSnapshotAtCommit(commit, entityId, repo);
ArgumentNullException.ThrowIfNull(snapshot);
return (T)snapshot.Entity.DbObject;
}

public async Task<T?> GetBeforeCommit<T>(Guid commitId, Guid entityId)
{
await using var repo = await _crdtRepositoryFactory.CreateRepository();
var commit = await repo.CurrentCommits().SingleAsync(c => c.Id == commitId);
return await GetBeforeCommit<T>(commit, entityId, repo);
}

public async Task<T?> GetBeforeCommit<T>(Commit commit, Guid entityId)
{
await using var repo = await _crdtRepositoryFactory.CreateRepository();
return await GetBeforeCommit<T>(commit, entityId, repo);
}

private async Task<T?> GetBeforeCommit<T>(Commit commit, Guid entityId, CrdtRepository repo)
{
var previousCommit = await repo.FindPreviousCommit(commit);
//there's no state before the first commit
if (previousCommit is null) return default;
var snapshot = await GetSnapshotAtCommit(previousCommit, entityId, repo);
//the entity did not exist before the given commit
if (snapshot is null) return default;
return (T)snapshot.Entity.DbObject;
}

private async Task<ObjectSnapshot?> GetSnapshotAtCommit(Commit commit, Guid entityId, CrdtRepository repo)
{
var repository = repo.GetScopedRepository(commit);
var snapshot = await repository.GetCurrentSnapshotByObjectId(entityId, false);
ArgumentNullException.ThrowIfNull(snapshot);
if (snapshot is null) return null;
var newCommits = await repository.CurrentCommits()
.Include(c => c.ChangeEntities)
.WhereAfter(snapshot.Commit)
Expand All @@ -347,7 +383,7 @@ public async Task<T> GetAtCommit<T>(Commit commit, Guid entityId)
snapshot = snapshots[snapshot.EntityId];
}

return (T)snapshot.Entity.DbObject;
return snapshot;
}

public async Task<SyncState> GetSyncState()
Expand Down
Loading