Skip to content

Commit

Permalink
Minilcm diff API (#1181)
Browse files Browse the repository at this point in the history
* always using DoUsingNewOrCurrentUOW as we've got a lot of recursive calls now

* allow defining copy which returns the correct type instead of IObjectWithId

* change UpdateEntry to use before and after instead of depending on Version

* fix test issue due to parts of speech and domains not being created when an entry is created, fix issue with ordering of adding complex forms to an entry
  • Loading branch information
hahn-kev authored Nov 14, 2024
1 parent 3656265 commit b188a5d
Show file tree
Hide file tree
Showing 31 changed files with 694 additions and 62 deletions.
104 changes: 104 additions & 0 deletions .idea/.idea.LexBox/.idea/dataSources.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FwDataMiniLcmBridge.Tests.Fixtures;
using FwDataMiniLcmBridge.Api;
using FwDataMiniLcmBridge.Tests.Fixtures;
using MiniLcm.Models;

namespace FwDataMiniLcmBridge.Tests.MiniLcmTests;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using FwDataMiniLcmBridge.Tests.Fixtures;

namespace FwDataMiniLcmBridge.Tests.MiniLcmTests;

[Collection(ProjectLoaderFixture.Name)]
public class UpdateEntryTests(ProjectLoaderFixture fixture) : UpdateEntryTestsBase
{
protected override Task<IMiniLcmApi> NewApi()
{
return Task.FromResult<IMiniLcmApi>(fixture.NewProjectApi("update-entry-test", "en", "en"));
}
}
22 changes: 17 additions & 5 deletions backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,12 @@ public async Task<Entry> CreateEntry(Entry entry)
CreateSense(lexEntry, sense);
}

//form types should be created before components, otherwise the form type "unspecified" will be added
foreach (var complexFormType in entry.ComplexFormTypes)
{
AddComplexFormType(lexEntry, complexFormType.Id);
}

foreach (var component in entry.Components)
{
AddComplexFormComponent(lexEntry, component);
Expand All @@ -586,11 +592,6 @@ public async Task<Entry> CreateEntry(Entry entry)
var complexLexEntry = EntriesRepository.GetObject(complexForm.ComplexFormEntryId);
AddComplexFormComponent(complexLexEntry, complexForm);
}

foreach (var complexFormType in entry.ComplexFormTypes)
{
AddComplexFormType(lexEntry, complexFormType.Id);
}
});

return await GetEntry(entry.Id) ?? throw new InvalidOperationException("Entry was not created");
Expand Down Expand Up @@ -739,6 +740,17 @@ public Task<Entry> UpdateEntry(Guid id, UpdateObjectInput<Entry> update)
return Task.FromResult(FromLexEntry(lexEntry));
}

public async Task<Entry> UpdateEntry(Entry before, Entry after)
{
await Cache.DoUsingNewOrCurrentUOW("Update Entry",
"Revert entry",
async () =>
{
await EntrySync.Sync(after, before, this);
});
return await GetEntry(after.Id) ?? throw new NullReferenceException("unable to find entry with id " + after.Id);
}

public Task DeleteEntry(Guid id)
{
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Delete Entry",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace FwDataMiniLcmBridge.Api;

public class VersionInvalidException(string type, Exception? innerException = null) : Exception(
$"version of {type} is invalid, it has been changed since this version was fetched",
innerException);
20 changes: 15 additions & 5 deletions backend/FwLite/FwDataMiniLcmBridge/LcmUtils/ProjectLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class ProjectLoader(IOptions<FwDataBridgeConfig> config) : IProjectLoader
{
protected string TemplatesFolder => config.Value.TemplatesFolder;
private static bool _init;
private static readonly object _initLock = new();

public static void Init()
{
Expand All @@ -34,13 +35,22 @@ public static void Init()
return;
}

Icu.Wrapper.Init();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
lock (_initLock)
{
Debug.Assert(Icu.Wrapper.IcuVersion == "72.1.0.3");
if (_init)
{
return;
}

Icu.Wrapper.Init();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Debug.Assert(Icu.Wrapper.IcuVersion == "72.1.0.3");
}

Sldr.Initialize();
_init = true;
}
Sldr.Initialize();
_init = true;
}

public virtual LcmCache LoadCache(FwDataProject project)
Expand Down
108 changes: 108 additions & 0 deletions backend/FwLite/FwLiteProjectSync.Tests/EntrySyncTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using FluentAssertions.Equivalency;
using FwLiteProjectSync.Tests.Fixtures;
using MiniLcm.Models;
using MiniLcm.SyncHelpers;
using MiniLcm.Tests.AutoFakerHelpers;
using Soenneker.Utils.AutoBogus;

namespace FwLiteProjectSync.Tests;

public class EntrySyncTests : IClassFixture<SyncFixture>
{
private readonly AutoFaker _autoFaker = new(builder => builder.WithOverride(new MultiStringOverride()).WithOverride(new ObjectWithIdOverride()));
public EntrySyncTests(SyncFixture fixture)
{
_fixture = fixture;
}

private readonly SyncFixture _fixture;

[Fact]
public async Task CanSyncRandomEntries()
{
var createdEntry = await _fixture.CrdtApi.CreateEntry(await _autoFaker.EntryReadyForCreation(_fixture.CrdtApi));
var after = await _autoFaker.EntryReadyForCreation(_fixture.CrdtApi, entryId: createdEntry.Id);
await EntrySync.Sync(after, createdEntry, _fixture.CrdtApi);
var actual = await _fixture.CrdtApi.GetEntry(after.Id);
actual.Should().NotBeNull();
actual.Should().BeEquivalentTo(after, options => options);
}

[Fact]
public async Task CanChangeComplexFormVisSync_Components()
{
var component1 = await _fixture.CrdtApi.CreateEntry(new() { LexemeForm = { { "en", "component1" } } });
var component2 = await _fixture.CrdtApi.CreateEntry(new() { LexemeForm = { { "en", "component2" } } });
var complexFormId = Guid.NewGuid();
var complexForm = await _fixture.CrdtApi.CreateEntry(new()
{
Id = complexFormId,
LexemeForm = { { "en", "complex form" } },
Components =
[
new ComplexFormComponent()
{
ComponentEntryId = component1.Id,
ComponentHeadword = component1.Headword(),
ComplexFormEntryId = complexFormId,
ComplexFormHeadword = "complex form"
}
]
});
Entry after = (Entry) complexForm.Copy();
after.Components[0].ComponentEntryId = component2.Id;
after.Components[0].ComponentHeadword = component2.Headword();

await EntrySync.Sync(after, complexForm, _fixture.CrdtApi);

var actual = await _fixture.CrdtApi.GetEntry(after.Id);
actual.Should().NotBeNull();
actual.Should().BeEquivalentTo(after, options => options);
}

[Fact]
public async Task CanChangeComplexFormViaSync_ComplexForms()
{
var complexForm1 = await _fixture.CrdtApi.CreateEntry(new() { LexemeForm = { { "en", "complexForm1" } } });
var complexForm2 = await _fixture.CrdtApi.CreateEntry(new() { LexemeForm = { { "en", "complexForm2" } } });
var componentId = Guid.NewGuid();
var component = await _fixture.CrdtApi.CreateEntry(new()
{
Id = componentId,
LexemeForm = { { "en", "component" } },
ComplexForms =
[
new ComplexFormComponent()
{
ComponentEntryId = componentId,
ComponentHeadword = "component",
ComplexFormEntryId = complexForm1.Id,
ComplexFormHeadword = complexForm1.Headword()
}
]
});
Entry after = (Entry) component.Copy();
after.ComplexForms[0].ComplexFormEntryId = complexForm2.Id;
after.ComplexForms[0].ComplexFormHeadword = complexForm2.Headword();

await EntrySync.Sync(after, component, _fixture.CrdtApi);

var actual = await _fixture.CrdtApi.GetEntry(after.Id);
actual.Should().NotBeNull();
actual.Should().BeEquivalentTo(after, options => options);
}

[Fact]
public async Task CanChangeComplexFormTypeViaSync()
{
var entry = await _fixture.CrdtApi.CreateEntry(new() { LexemeForm = { { "en", "complexForm1" } } });
var complexFormType = await _fixture.CrdtApi.GetComplexFormTypes().FirstAsync();
var after = (Entry) entry.Copy();
after.ComplexFormTypes = [complexFormType];
await EntrySync.Sync(after, entry, _fixture.CrdtApi);

var actual = await _fixture.CrdtApi.GetEntry(after.Id);
actual.Should().NotBeNull();
actual.Should().BeEquivalentTo(after, options => options);
}
}
3 changes: 2 additions & 1 deletion backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FwLiteProjectSync.Tests.Fixtures;
using FluentAssertions.Equivalency;
using FwLiteProjectSync.Tests.Fixtures;
using LcmCrdt;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
Expand Down
1 change: 1 addition & 0 deletions backend/FwLite/FwLiteProjectSync.Tests/UpdateDiffTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FwLiteProjectSync.Tests.Fixtures;
using MiniLcm.Models;
using MiniLcm.SyncHelpers;
using MiniLcm.Tests.AutoFakerHelpers;
using Soenneker.Utils.AutoBogus;
using Soenneker.Utils.AutoBogus.Config;

Expand Down
6 changes: 6 additions & 0 deletions backend/FwLite/FwLiteProjectSync/DryRunMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ public Task<Entry> UpdateEntry(Guid id, UpdateObjectInput<Entry> update)
return GetEntry(id)!;
}

public Task<Entry> UpdateEntry(Entry before, Entry after)
{
DryRunRecords.Add(new DryRunRecord(nameof(UpdateEntry), $"Update entry {after.Id}"));
return Task.FromResult(after);
}

public Task DeleteEntry(Guid id)
{
DryRunRecords.Add(new DryRunRecord(nameof(DeleteEntry), $"Delete entry {id}"));
Expand Down
Loading

0 comments on commit b188a5d

Please sign in to comment.