Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ public async Task UpdateEntry_CanUpdateExampleSentenceTranslations_WhenNoTransla

var before = entry.Copy();
var exampleSentence = entry.Senses[0].ExampleSentences[0];
exampleSentence.Translations = [new() { Text = { { "en", new RichString("updated") } } }];
exampleSentence.Translations =
[
new() { Id = Guid.NewGuid(), Text = { { "en", new RichString("updated") } } }
];

// Act
var updatedEntry = await Api.UpdateEntry(before, entry);
Expand Down
10 changes: 5 additions & 5 deletions backend/FwLite/FwLiteProjectSync/CrdtFwdataProjectSyncService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@
using LexCore.Sync;
using Microsoft.Extensions.Logging;
using MiniLcm;
using MiniLcm.Normalization;
using MiniLcm.SyncHelpers;
using MiniLcm.Validators;

namespace FwLiteProjectSync;

public class CrdtFwdataProjectSyncService(MiniLcmImport miniLcmImport,
ILogger<CrdtFwdataProjectSyncService> logger,
MiniLcmApiValidationWrapperFactory validationWrapperFactory,
MiniLcmApiStringNormalizationWrapperFactory normalizationWrapperFactory)
MiniLcmApiValidationWrapperFactory validationWrapperFactory)
{
public record DryRunSyncResult(
int CrdtChanges,
Expand Down Expand Up @@ -63,8 +61,10 @@ private async Task<SyncResult> SyncOrImportInternal(IMiniLcmApi crdtApi, IMiniLc
throw new InvalidOperationException("Project sync state does not match presence of snapshot.");
}

crdtApi = normalizationWrapperFactory.Create(validationWrapperFactory.Create(crdtApi));
fwdataApi = normalizationWrapperFactory.Create(validationWrapperFactory.Create(fwdataApi));
// No write normalization: Data is already normalised on both sides.
// No query normalization: The sync doesn't do any querying.
crdtApi = validationWrapperFactory.Create(crdtApi);
fwdataApi = validationWrapperFactory.Create(fwdataApi);

if (dryRun)
{
Expand Down
7 changes: 2 additions & 5 deletions backend/FwLite/FwLiteShared/Services/MiniLcmJsInvokable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
using MiniLcm;
using MiniLcm.Media;
using MiniLcm.Models;
using MiniLcm.Normalization;
using MiniLcm.Validators;
using MiniLcm.Wrappers;
using Reinforced.Typings.Attributes;

Expand All @@ -18,11 +16,10 @@ public class MiniLcmJsInvokable(
IProjectIdentifier project,
ILogger<MiniLcmJsInvokable> logger,
MiniLcmApiNotifyWrapperFactory notificationWrapperFactory,
MiniLcmApiValidationWrapperFactory validationWrapperFactory,
MiniLcmApiStringNormalizationWrapperFactory normalizationWrapperFactory
MiniLcmApiUserFacingWrappers userFacingWrappers
) : IDisposable
{
private readonly IMiniLcmApi _wrappedApi = api.WrapWith([normalizationWrapperFactory, validationWrapperFactory, notificationWrapperFactory], project);
private readonly IMiniLcmApi _wrappedApi = userFacingWrappers.Apply(api, project, notificationWrapperFactory);

public record MiniLcmFeatures(bool? History, bool? Write, bool? OpenWithFlex, bool? Feedback, bool? Sync, bool? Audio, bool? CustomViews);
private bool SupportsSync => project.DataFormat == ProjectDataFormat.Harmony && api is CrdtMiniLcmApi;
Expand Down
8 changes: 3 additions & 5 deletions backend/FwLite/FwLiteWeb/Hubs/CrdtMiniLcmApiHub.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
using FwLiteShared;
using FwLiteShared.Events;
using FwLiteShared.Projects;
using FwLiteShared.Sync;
using LcmCrdt;
using LcmCrdt.Data;
using FwLiteWeb.Services;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Memory;
using MiniLcm;
using MiniLcm.Models;
using MiniLcm.Validators;
using MiniLcm.Wrappers;
using SystemTextJsonPatch;

namespace FwLiteWeb.Hubs;
Expand All @@ -23,8 +21,8 @@ public class CrdtMiniLcmApiHub(
LexboxProjectService lexboxProjectService,
IMemoryCache memoryCache,
IHubContext<CrdtMiniLcmApiHub, ILexboxHubClient> hubContext,
MiniLcmApiValidationWrapperFactory validationWrapperFactory
) : MiniLcmApiHubBase(miniLcmApi, validationWrapperFactory)
MiniLcmApiUserFacingWrappers userFacingWrappers
) : MiniLcmApiHubBase(miniLcmApi, userFacingWrappers, projectContext.Project)
{
public const string ProjectRouteKey = "project";
public static string ProjectGroup(string projectName) => "crdt-" + projectName;
Expand Down
10 changes: 4 additions & 6 deletions backend/FwLite/FwLiteWeb/Hubs/FwDataMiniLcmHub.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using FwDataMiniLcmBridge;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options;
using MiniLcm;
using MiniLcm.Validators;
using MiniLcm.Wrappers;
using SIL.LCModel;
using SystemTextJsonPatch;

namespace FwLiteWeb.Hubs;

Expand All @@ -13,8 +10,9 @@ public class FwDataMiniLcmHub(
IMiniLcmApi miniLcmApi,
FwDataFactory fwDataFactory,
FwDataProjectContext context,
MiniLcmApiValidationWrapperFactory validationWrapperFactory
) : MiniLcmApiHubBase(miniLcmApi, validationWrapperFactory)
MiniLcmApiUserFacingWrappers userFacingWrappers
)
: MiniLcmApiHubBase(miniLcmApi, userFacingWrappers, context.Project ?? throw new InvalidOperationException("No project is set in the context."))
{
public const string ProjectRouteKey = "fwdata";
public override async Task OnConnectedAsync()
Expand Down
11 changes: 6 additions & 5 deletions backend/FwLite/FwLiteWeb/Hubs/MiniLcmApiHubBase.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Options;
using MiniLcm;
using MiniLcm.Models;
using MiniLcm.Validators;
using MiniLcm.Wrappers;
using SystemTextJsonPatch;

namespace FwLiteWeb.Hubs;

public abstract class MiniLcmApiHubBase(IMiniLcmApi miniLcmApi,
MiniLcmApiValidationWrapperFactory validationWrapperFactory) : Hub<ILexboxHubClient>
public abstract class MiniLcmApiHubBase(
IMiniLcmApi miniLcmApi,
MiniLcmApiUserFacingWrappers userFacingWrappers,
IProjectIdentifier projectIdentifier) : Hub<ILexboxHubClient>
{
private readonly IMiniLcmApi _miniLcmApi = validationWrapperFactory.Create(miniLcmApi);
private readonly IMiniLcmApi _miniLcmApi = userFacingWrappers.Apply(miniLcmApi, projectIdentifier);

public async Task<WritingSystems> GetWritingSystems()
{
Expand Down
11 changes: 5 additions & 6 deletions backend/FwLite/FwLiteWeb/Routes/MiniLcmRoutes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using MiniLcm.Filtering;
using MiniLcm.Models;
using MiniLcm.Project;
using MiniLcm.Validators;
using MiniLcm.Wrappers;

namespace FwLiteWeb.Routes;

Expand Down Expand Up @@ -86,12 +86,11 @@ public static IEndpointConventionBuilder MapMiniLcmRoutes(this IEndpointRouteBui
return Results.Problem($"Project {projectCode} not found");
}

var validationWrapperFactory = context.HttpContext.RequestServices
.GetRequiredService<MiniLcmApiValidationWrapperFactory>();
var services = context.HttpContext.RequestServices;
var userFacingWrappers = services.GetRequiredService<MiniLcmApiUserFacingWrappers>();

miniLcmHolder.MiniLcmApi = validationWrapperFactory.Create(
await projectProvider.OpenProject(project, context.HttpContext.RequestServices)
);
miniLcmHolder.MiniLcmApi = userFacingWrappers.Apply(
await projectProvider.OpenProject(project, services), project);
return await next(context);
});

Expand Down
3 changes: 2 additions & 1 deletion backend/FwLite/MiniLcm.Tests/CreateEntryTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ public async Task CanCreateEntry_AutoFaker()
var createdEntry = await Api.CreateEntry(entry);
createdEntry.Should().BeEquivalentTo(entry, options => options
.For(e => e.Components).Exclude(e => e.Id)
.For(e => e.ComplexForms).Exclude(e => e.Id));
.For(e => e.ComplexForms).Exclude(e => e.Id)
.Excluding(member => member.Name == nameof(IOrderable.Order)));
}

[Fact]
Expand Down
16 changes: 13 additions & 3 deletions backend/FwLite/MiniLcm.Tests/ExampleSentenceTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,34 @@ public async Task CanCreateExampleSentence()
{
var expectedExampleSentence = new ExampleSentence()
{
Id = Guid.NewGuid(),
SenseId = _senseId,
Reference = new RichString("This is a reference", "en"),
Sentence = { { "en", new RichString("test", "en") } },
Translations = { new Translation() { Text = { { "en", new RichString("test", "en") } } } }
Translations = {
new Translation()
{
Id = Guid.NewGuid(),
Text = { { "en", new RichString("test", "en") } },
}
}
};
var actualSentence = await Api.CreateExampleSentence(_entryId, _senseId, expectedExampleSentence);
actualSentence.Should().BeEquivalentTo(expectedExampleSentence);
actualSentence.Should().BeEquivalentTo(expectedExampleSentence,
options => options.Excluding(s => s.Order));
}

[Fact]
public async Task CanCreateEmptyExampleSentence()
{
var expectedExampleSentence = new ExampleSentence()
{
Id = Guid.NewGuid(),
SenseId = _senseId
};
var actualSentence = await Api.CreateExampleSentence(_entryId, _senseId, expectedExampleSentence);
actualSentence.Should().BeEquivalentTo(expectedExampleSentence);
actualSentence.Should().BeEquivalentTo(expectedExampleSentence,
options => options.Excluding(s => s.Order));
}

[Fact]
Expand Down
Loading
Loading