Skip to content
Draft
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
4 changes: 3 additions & 1 deletion backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,9 @@ await Cache.DoUsingNewOrCurrentUOW("Update WritingSystem",
return await GetWritingSystem(id, type) ?? throw new NullReferenceException($"unable to find writing system with id {id}");
}

public async Task<WritingSystem> UpdateWritingSystem(WritingSystem before, WritingSystem after, IMiniLcmApi? api = null)
public async Task<WritingSystem> UpdateWritingSystem(WritingSystem before,
WritingSystem after,
IMiniLcmWritingSystemApi? api)
{
await Cache.DoUsingNewOrCurrentUOW("Update WritingSystem",
"Revert WritingSystem",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ Task<Publication> IMiniLcmWriteApi.CreatePublication(Publication publication)
ResumableTests.MaybeThrowRandom(random, 0.2);
return _api.CreatePublication(publication);
}
Task<WritingSystem> IMiniLcmWriteApi.CreateWritingSystem(WritingSystem writingSystems, BetweenPosition<WritingSystemId?>? between)
Task<WritingSystem> IMiniLcmWritingSystemApi.CreateWritingSystem(WritingSystem writingSystems, BetweenPosition<WritingSystemId?>? between)
{
ResumableTests.MaybeThrowRandom(random, 0.2);
return _api.CreateWritingSystem(writingSystems, between);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async Task<Publication> IMiniLcmWriteApi.CreatePublication(Publication publicati
{
return await HasCreated(publication, _api.GetPublications(), () => _api.CreatePublication(publication));
}
async Task<WritingSystem> IMiniLcmWriteApi.CreateWritingSystem(WritingSystem writingSystem, BetweenPosition<WritingSystemId?>? between)
async Task<WritingSystem> IMiniLcmWritingSystemApi.CreateWritingSystem(WritingSystem writingSystem, BetweenPosition<WritingSystemId?>? between)
{
return await HasCreated(writingSystem, AsyncWs(), () => _api.CreateWritingSystem(writingSystem, between), ws => ws.Type + ws.WsId.Code);
}
Expand Down
4 changes: 3 additions & 1 deletion backend/FwLite/FwLiteProjectSync/RecordingMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public async Task<WritingSystem> UpdateWritingSystem(WritingSystemId id,
return await _api.UpdateWritingSystem(id, type, update);
}

public async Task<WritingSystem> UpdateWritingSystem(WritingSystem before, WritingSystem after, IMiniLcmApi? api)
public async Task<WritingSystem> UpdateWritingSystem(WritingSystem before,
WritingSystem after,
IMiniLcmWritingSystemApi? api)
{
RunRecords.Add(new RunRecord(nameof(UpdateWritingSystem), $"Update {after.Type} writing system {after.WsId}"));
return await _api.UpdateWritingSystem(before, after, api);
Expand Down
4 changes: 3 additions & 1 deletion backend/FwLite/FwLiteProjectSync/WriteIgnoringMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public async Task<WritingSystem> UpdateWritingSystem(WritingSystemId id,
}).First(w => w.WsId == id);
}

public Task<WritingSystem> UpdateWritingSystem(WritingSystem before, WritingSystem after, IMiniLcmApi? api)
public Task<WritingSystem> UpdateWritingSystem(WritingSystem before,
WritingSystem after,
IMiniLcmWritingSystemApi? api)
{
return Task.FromResult(after);
}
Expand Down
212 changes: 78 additions & 134 deletions backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions backend/FwLite/LcmCrdt/Harmony/HarmonyChangeWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Data;
using Microsoft.Extensions.Options;
using SIL.Harmony;
using SIL.Harmony.Changes;
using SIL.Harmony.Core;

namespace LcmCrdt.Harmony;

public class HarmonyChangeWriter(
DataModel dataModel,
CommitMetadataInterceptor commitMetadataInterceptor,
IOptions<LcmCrdtConfig> config,
CurrentProjectService projectService
)
{
private Guid ClientId { get; } = projectService.ProjectData.ClientId;
public ProjectData ProjectData => projectService.ProjectData;
private CommitMetadata NewMetadata()
{
var metadata = new CommitMetadata
{
ClientVersion = AppVersion.Version,
//todo, if a user logs out and in with another account, this will be out of date until the next sync
AuthorName = ProjectData.LastUserName ?? config.Value.DefaultAuthorForCommits,
AuthorId = ProjectData.LastUserId
};
commitMetadataInterceptor.Apply(metadata);
return metadata;
}

public async Task<Commit> AddChange(IChange change)
{
AssertWritable();
var commit = await dataModel.AddChange(ClientId, change, commitMetadata: NewMetadata());
return commit;
}

public async Task AddChanges(IEnumerable<IChange> changes)
{
AssertWritable();
await dataModel.AddManyChanges(ClientId, changes, commitMetadata: NewMetadata);
}

private void AssertWritable()
{
if (ProjectData.IsReadonly)
throw new ReadOnlyException(
$"project is readonly because you are logged in with the {ProjectData.Role} role. If your role recently changed, try refreshing the server project list on the home page.");
}
}
4 changes: 4 additions & 0 deletions backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
using LcmCrdt.Project;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System.Text.Json.Serialization.Metadata;
using LcmCrdt.Harmony;
using LcmCrdt.MiniLcmImp;
using MiniLcm.Media;

namespace LcmCrdt;
Expand Down Expand Up @@ -72,6 +74,8 @@ public static IServiceCollection AddLcmCrdtClientCore(this IServiceCollection se
harmonyConfig.LocalResourceCachePath = Path.Combine(lcmConfig.Value.ProjectPath, "localResourcesCache");
});
services.AddScoped<IMiniLcmApi, CrdtMiniLcmApi>();
services.AddScoped<HarmonyChangeWriter>();
services.AddScoped<CrdtWritingSystemApi>();
services.AddScoped<CommitMetadataInterceptor>();
services.AddScoped<MiniLcmRepositoryFactory>();
services.AddMiniLcmValidators();
Expand Down
70 changes: 70 additions & 0 deletions backend/FwLite/LcmCrdt/MiniLcmImp/CrdtWritingSystemApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using LcmCrdt.Changes;
using LcmCrdt.Data;
using LcmCrdt.Harmony;
using LinqToDB.Async;
using MiniLcm.Exceptions;
using MiniLcm.SyncHelpers;

namespace LcmCrdt.MiniLcmImp;

public class CrdtWritingSystemApi(
MiniLcmRepositoryFactory repoFactory,
HarmonyChangeWriter harmonyChangeWriter
) : IMiniLcmWritingSystemApi, IMiniLcmReadWritingSystemApi
{
public async Task<WritingSystems> GetWritingSystems()
{
await using var repo = await repoFactory.CreateRepoAsync();
var systems = await repo.WritingSystemsOrdered.ToArrayAsync();
return new WritingSystems
{
Analysis = [.. systems.Where(ws => ws.Type == WritingSystemType.Analysis)],
Vernacular = [.. systems.Where(ws => ws.Type == WritingSystemType.Vernacular)]
};
}

public async Task<WritingSystem?> GetWritingSystem(WritingSystemId id, WritingSystemType type)
{
await using var repo = await repoFactory.CreateRepoAsync();
return await repo.GetWritingSystem(id, type);
}

public async Task<WritingSystem> CreateWritingSystem(WritingSystem writingSystem, BetweenPosition<WritingSystemId?>? between = null)
{
await using var repo = await repoFactory.CreateRepoAsync();
var entityId = writingSystem.MaybeId ?? Guid.NewGuid();
var wsType = writingSystem.Type;
var exists = await repo.WritingSystems.AnyAsync(ws => ws.WsId == writingSystem.WsId && ws.Type == wsType);
if (exists) throw new DuplicateObjectException($"Writing system {writingSystem.WsId.Code} ({wsType}) already exists");
var betweenIds = between is null ? null : await between.MapAsync(async wsId => wsId is null ? null : (await repo.GetWritingSystem(wsId.Value, wsType))?.Id);
var order = await OrderPicker.PickOrder(repo.WritingSystems.Where(ws => ws.Type == wsType), betweenIds);
await harmonyChangeWriter.AddChange(new CreateWritingSystemChange(writingSystem, entityId, order));
return await repo.GetWritingSystem(writingSystem.WsId, wsType) ?? throw NotFoundException.ForWs(writingSystem);
}

public async Task<WritingSystem> UpdateWritingSystem(WritingSystemId id, WritingSystemType type, UpdateObjectInput<WritingSystem> update)
{
await using var repo = await repoFactory.CreateRepoAsync();
var ws = await repo.GetWritingSystem(id, type) ?? throw NotFoundException.ForWs(id, type);
var patchChange = new JsonPatchChange<WritingSystem>(ws.Id, update.Patch);
await harmonyChangeWriter.AddChange(patchChange);
return await repo.GetWritingSystem(id, type) ?? throw NotFoundException.ForWs(id, type);
}

public async Task<WritingSystem> UpdateWritingSystem(WritingSystem before,
WritingSystem after,
IMiniLcmWritingSystemApi? api = null)
{
await WritingSystemSync.Sync(before, after, api ?? this);
return await GetWritingSystem(after.WsId, after.Type) ?? throw NotFoundException.ForWs(after);
}

public async Task MoveWritingSystem(WritingSystemId id, WritingSystemType type, BetweenPosition<WritingSystemId?> between)
{
await using var repo = await repoFactory.CreateRepoAsync();
var ws = await repo.GetWritingSystem(id, type) ?? throw NotFoundException.ForWs(id, type);
var betweenIds = await between.MapAsync(async wsId => wsId is null ? null : (await repo.GetWritingSystem(wsId.Value, type))?.Id);
var order = await OrderPicker.PickOrder(repo.WritingSystems.Where(s => s.Type == type), betweenIds);
await harmonyChangeWriter.AddChange(new Changes.SetOrderChange<WritingSystem>(ws.Id, order));
}
}
6 changes: 5 additions & 1 deletion backend/FwLite/MiniLcm/IMiniLcmReadApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

namespace MiniLcm;

public interface IMiniLcmReadApi
public interface IMiniLcmReadWritingSystemApi
{
Task<WritingSystems> GetWritingSystems();
Task<WritingSystem?> GetWritingSystem(WritingSystemId id, WritingSystemType type);
}

public interface IMiniLcmReadApi : IMiniLcmReadWritingSystemApi
{
IAsyncEnumerable<PartOfSpeech> GetPartsOfSpeech();
IAsyncEnumerable<Publication> GetPublications();
IAsyncEnumerable<SemanticDomain> GetSemanticDomains();
Expand Down
21 changes: 16 additions & 5 deletions backend/FwLite/MiniLcm/IMiniLcmWriteApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,27 @@

namespace MiniLcm;

public interface IMiniLcmWriteApi
// Note there's no Task DeleteWritingSystem(Guid id) because deleting writing systems needs careful consideration, as it can cause a massive cascade of data deletion
public interface IMiniLcmWritingSystemApi
{
Task<WritingSystem> CreateWritingSystem(WritingSystem writingSystem, BetweenPosition<WritingSystemId?>? between = null);

Task<WritingSystem> UpdateWritingSystem(WritingSystemId id,
Task<WritingSystem> UpdateWritingSystem(
WritingSystemId id,
WritingSystemType type,
UpdateObjectInput<WritingSystem> update);
Task<WritingSystem> UpdateWritingSystem(WritingSystem before, WritingSystem after, IMiniLcmApi? api = null);
// Note there's no Task DeleteWritingSystem(Guid id) because deleting writing systems needs careful consideration, as it can cause a massive cascade of data deletion
UpdateObjectInput<WritingSystem> update
);

Task<WritingSystem> UpdateWritingSystem(
WritingSystem before,
WritingSystem after,
IMiniLcmWritingSystemApi? api = null
);
Task MoveWritingSystem(WritingSystemId id, WritingSystemType type, BetweenPosition<WritingSystemId?> between);
}

public interface IMiniLcmWriteApi : IMiniLcmWritingSystemApi
{

#region PartOfSpeech
Task<PartOfSpeech> CreatePartOfSpeech(PartOfSpeech partOfSpeech);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/// JsonElement values are only normalized when they are simple strings; complex JSON values are left as-is
/// to avoid guessing the target type.
/// </summary>
public partial class MiniLcmApiWriteNormalizationWrapper(IMiniLcmApi api) : IMiniLcmApi

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystem(WritingSystemId, WritingSystemType)'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystems()'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystem(WritingSystemId, WritingSystemType)'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystems()'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystem(WritingSystemId, WritingSystemType)'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystems()'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystem(WritingSystemId, WritingSystemType)'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystems()'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / E2E Tests

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystem(WritingSystemId, WritingSystemType)'

Check failure on line 44 in backend/FwLite/MiniLcm/Normalization/MiniLcmApiWriteNormalizationWrapper.cs

View workflow job for this annotation

GitHub Actions / E2E Tests

'MiniLcmApiWriteNormalizationWrapper' does not implement interface member 'IMiniLcmReadWritingSystemApi.GetWritingSystems()'
{
private readonly IMiniLcmApi _api = api;

Expand All @@ -65,7 +65,9 @@
}


public Task<WritingSystem> UpdateWritingSystem(WritingSystem before, WritingSystem after, IMiniLcmApi? api = null)
public Task<WritingSystem> UpdateWritingSystem(WritingSystem before,
WritingSystem after,
IMiniLcmWritingSystemApi? api)
{
return _api.UpdateWritingSystem(before, after, api);
}
Expand Down
2 changes: 1 addition & 1 deletion backend/FwLite/MiniLcm/SyncHelpers/WritingSystemSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static async Task<int> Sync(WritingSystem[] beforeWritingSystems,
return await writingSystemsDiffApi.Diff(beforeWritingSystems, afterWritingSystems);
}

public static async Task<int> Sync(WritingSystem beforeWs, WritingSystem afterWs, IMiniLcmApi api)
public static async Task<int> Sync(WritingSystem beforeWs, WritingSystem afterWs, IMiniLcmWritingSystemApi api)
{
var updateObjectInput = WritingSystemDiffToUpdate(beforeWs, afterWs);
if (updateObjectInput is not null) await api.UpdateWritingSystem(afterWs.WsId, afterWs.Type, updateObjectInput);
Expand Down
Loading