Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug/some crdt semantic domain bugs #1098

Merged
merged 3 commits into from
Oct 4, 2024
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
70 changes: 38 additions & 32 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/backend/LexBoxApi/bin/Debug/net8.0/LexBoxApi.dll",
"args": [],
"cwd": "${workspaceFolder}/backend/LexBoxApi",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processName": "LexBoxApi"
}
]
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "C#: LocalWebApp Debug",
"type": "dotnet",
"request": "launch",
"projectPath": "${workspaceFolder}/backend/FwLite/LocalWebApp/LocalWebApp.csproj"
},
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/backend/LexBoxApi/bin/Debug/net8.0/LexBoxApi.dll",
"args": [],
"cwd": "${workspaceFolder}/backend/LexBoxApi",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processName": "LexBoxApi"
}
]
}
57 changes: 34 additions & 23 deletions backend/FwLite/LcmCrdt.Tests/JsonPatchRewriteTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using LcmCrdt.Changes;
using System.Text.Json;
using LcmCrdt.Changes;
using MiniLcm.Models;
using SystemTextJsonPatch;
using SystemTextJsonPatch.Operations;
using SemanticDomain = LcmCrdt.Objects.SemanticDomain;
using Sense = LcmCrdt.Objects.Sense;

namespace LcmCrdt.Tests;

public class JsonPatchRewriteTests
{
private JsonPatchDocument<MiniLcm.Models.Sense> _patchDocument = new() { Options = new JsonSerializerOptions(JsonSerializerDefaults.Web) };

private Sense _sense = new Sense()
{
Id = Guid.NewGuid(),
Expand All @@ -21,11 +25,10 @@ public class JsonPatchRewriteTests
public void RewritePartOfSpeechChangesIntoSetPartOfSpeechChange()
{
var newPartOfSpeechId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);
patchDocument.Replace(s => s.Gloss["en"], "new gloss");
_patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);
_patchDocument.Replace(s => s.Gloss["en"], "new gloss");

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var setPartOfSpeechChange = changes.OfType<SetPartOfSpeechChange>().Should().ContainSingle().Subject;
setPartOfSpeechChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -40,10 +43,9 @@ public void RewritePartOfSpeechChangesIntoSetPartOfSpeechChange()
public void JsonPatchChangeRewriteDoesNotReturnEmptyPatchChanges()
{
var newPartOfSpeechId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);
_patchDocument.Replace(s => s.PartOfSpeechId, newPartOfSpeechId);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var setPartOfSpeechChange = changes.Should().ContainSingle()
.Subject.Should().BeOfType<SetPartOfSpeechChange>().Subject;
Expand All @@ -55,11 +57,24 @@ public void JsonPatchChangeRewriteDoesNotReturnEmptyPatchChanges()
public void RewritesAddSemanticDomainChangesIntoAddSemanticDomainChange()
{
var newSemanticDomainId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Add(s => s.SemanticDomains,
_patchDocument.Add(s => s.SemanticDomains,
new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() });

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var addSemanticDomainChange = (AddSemanticDomainChange)changes.Should().AllBeOfType<AddSemanticDomainChange>().And.ContainSingle().Subject;
addSemanticDomainChange.EntityId.Should().Be(_sense.Id);
addSemanticDomainChange.SemanticDomain.Id.Should().Be(newSemanticDomainId);
}

[Fact]
public void RewritesAddSemanticDomainChangesIntoAddSemanticDomainChange_JsonElement()
{
var newSemanticDomainId = Guid.Parse("46e4fe08-ffa0-4c8b-bf88-2c56138904d1");
_patchDocument.Operations.Add(new Operation<MiniLcm.Models.Sense>("add", "/semanticDomains/-", null,
JsonSerializer.Deserialize<JsonElement>("""{"deletedAt":null,"predefined":true,"id":"46e4fe08-ffa0-4c8b-bf88-2c56138904d1","name":{"en":"Sky"},"code":"1.1"}""")));

var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var addSemanticDomainChange = (AddSemanticDomainChange)changes.Should().AllBeOfType<AddSemanticDomainChange>().And.ContainSingle().Subject;
addSemanticDomainChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -71,10 +86,9 @@ public void RewritesReplaceSemanticDomainPatchChangesIntoReplaceSemanticDomainCh
{
var oldSemanticDomainId = _sense.SemanticDomains[0].Id;
var newSemanticDomainId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() }, 0);
_patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() }, 0);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var replaceSemanticDomainChange = (ReplaceSemanticDomainChange)changes.Should().AllBeOfType<ReplaceSemanticDomainChange>().And.ContainSingle().Subject;
replaceSemanticDomainChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -86,10 +100,9 @@ public void RewritesReplaceNoIndexSemanticDomainPatchChangesIntoReplaceSemanticD
{
var oldSemanticDomainId = _sense.SemanticDomains[0].Id;
var newSemanticDomainId = Guid.NewGuid();
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() });
_patchDocument.Replace(s => s.SemanticDomains, new SemanticDomain() { Id = newSemanticDomainId, Code = "new code", Name = new MultiString() });

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var replaceSemanticDomainChange = (ReplaceSemanticDomainChange)changes.Should().AllBeOfType<ReplaceSemanticDomainChange>().And.ContainSingle().Subject;
replaceSemanticDomainChange.EntityId.Should().Be(_sense.Id);
Expand All @@ -100,11 +113,10 @@ public void RewritesReplaceNoIndexSemanticDomainPatchChangesIntoReplaceSemanticD
[Fact]
public void RewritesRemoveSemanticDomainPatchChangesIntoReplaceSemanticDomainChange()
{
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
var semanticDomainIdToRemove = _sense.SemanticDomains[0].Id;
patchDocument.Remove(s => s.SemanticDomains, 0);
_patchDocument.Remove(s => s.SemanticDomains, 0);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var removeSemanticDomainChange = (RemoveSemanticDomainChange)changes.Should().AllBeOfType<
RemoveSemanticDomainChange>().And.ContainSingle().Subject;
Expand All @@ -115,11 +127,10 @@ public void RewritesRemoveSemanticDomainPatchChangesIntoReplaceSemanticDomainCha
[Fact]
public void RewritesRemoveNoIndexSemanticDomainPatchChangesIntoReplaceSemanticDomainChange()
{
var patchDocument = new JsonPatchDocument<MiniLcm.Models.Sense>();
var semanticDomainIdToRemove = _sense.SemanticDomains[0].Id;
patchDocument.Remove(s => s.SemanticDomains);
_patchDocument.Remove(s => s.SemanticDomains);

var changes = Sense.ChangesFromJsonPatch(_sense, patchDocument).ToArray();
var changes = Sense.ChangesFromJsonPatch(_sense, _patchDocument).ToArray();

var removeSemanticDomainChange = (RemoveSemanticDomainChange)changes.Should().AllBeOfType<
RemoveSemanticDomainChange>().And.ContainSingle().Subject;
Expand Down
38 changes: 38 additions & 0 deletions backend/FwLite/LcmCrdt.Tests/LexboxApiTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,44 @@ public async Task UpdateSenseSemanticDomain()
semanticDomain.Id.Should().Be(newDomainId);
}

[Fact]
public async Task RemoveSenseSemanticDomain()
{
var newDomainId = Guid.NewGuid();
await DataModel.AddChange(Guid.NewGuid(), new CreateSemanticDomainChange(newDomainId, new MultiString() { { "en", "test" } }, "updated"));
var newSemanticDomain = await DataModel.GetLatest<Objects.SemanticDomain>(newDomainId);
ArgumentNullException.ThrowIfNull(newSemanticDomain);
var entry = await _api.CreateEntry(new Entry
{
LexemeForm = new MultiString
{
Values =
{
{ "en", "test" }
}
},
Senses = new List<Sense>
{
new Sense()
{
SemanticDomains = [newSemanticDomain],
Definition = new MultiString
{
Values =
{
{ "en", "test" }
}
}
}
}
});
var updatedSense = await _api.UpdateSense(entry.Id,
entry.Senses[0].Id,
new UpdateObjectInput<Sense>()
.Remove(e => e.SemanticDomains, 0));
updatedSense.SemanticDomains.Should().BeEmpty();
}

[Fact]
public async Task UpdateExampleSentence()
{
Expand Down
2 changes: 2 additions & 0 deletions backend/FwLite/LcmCrdt/LcmCrdtKernel.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Text.Json;
using SIL.Harmony;
using SIL.Harmony.Core;
using SIL.Harmony;

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

The using directive for 'SIL.Harmony' appeared previously in this namespace

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Linux

The using directive for 'SIL.Harmony' appeared previously in this namespace

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Linux

The using directive for 'SIL.Harmony' appeared previously in this namespace

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

The using directive for 'SIL.Harmony' appeared previously in this namespace

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

The using directive for 'SIL.Harmony' appeared previously in this namespace

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app

The using directive for 'SIL.Harmony' appeared previously in this namespace

Check warning on line 4 in backend/FwLite/LcmCrdt/LcmCrdtKernel.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app

The using directive for 'SIL.Harmony' appeared previously in this namespace
using SIL.Harmony.Changes;
using LcmCrdt.Changes;
using LinqToDB;
Expand Down Expand Up @@ -117,6 +117,8 @@
.Add<DeleteChange<SemanticDomain>>()
.Add<SetPartOfSpeechChange>()
.Add<AddSemanticDomainChange>()
.Add<RemoveSemanticDomainChange>()
.Add<ReplaceSemanticDomainChange>()
.Add<CreateEntryChange>()
.Add<CreateSenseChange>()
.Add<CreateExampleSentenceChange>()
Expand Down
20 changes: 19 additions & 1 deletion backend/FwLite/LcmCrdt/Objects/SemanticDomain.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using SIL.Harmony;
using LcmCrdt.Changes;
using MiniLcm.Models;
using SIL.Harmony;
using SIL.Harmony.Entities;

namespace LcmCrdt.Objects;
Expand Down Expand Up @@ -34,4 +36,20 @@ public IObjectBase Copy()
Predefined = Predefined
};
}

internal static async Task PredefinedSemanticDomains(DataModel dataModel, Guid clientId)
{
//todo load from xml instead of hardcoding and use real IDs
await dataModel.AddChanges(clientId,
[
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d0"), new MultiString() { { "en", "Universe, Creation" } }, "1", true),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d1"), new MultiString() { { "en", "Sky" } }, "1.1", true),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d2"), new MultiString() { { "en", "World" } }, "1.2", true),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d3"), new MultiString() { { "en", "Person" } }, "2", true),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d4"), new MultiString() { { "en", "Body" } }, "2.1", true),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d5"), new MultiString() { { "en", "Head" } }, "2.1.1", true),
new CreateSemanticDomainChange(new Guid("46e4fe08-ffa0-4c8b-bf88-2c56f38904d6"), new MultiString() { { "en", "Eye" } }, "2.1.1.1", true),
],
new Guid("023faebb-711b-4d2f-a14f-a15621fc66bc"));
}
}
2 changes: 2 additions & 0 deletions backend/FwLite/LcmCrdt/ProjectsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.Options;
using MiniLcm;
using PartOfSpeech = LcmCrdt.Objects.PartOfSpeech;
using LcmCrdt.Objects;

namespace LcmCrdt;

Expand Down Expand Up @@ -83,6 +84,7 @@ internal static async Task InitProjectDb(LcmCrdtDbContext db, ProjectData data)
internal static async Task SeedSystemData(DataModel dataModel, Guid clientId)
{
await PartOfSpeech.PredefinedPartsOfSpeech(dataModel, clientId);
await SemanticDomain.PredefinedSemanticDomains(dataModel, clientId);
}

public AsyncServiceScope CreateProjectScope(CrdtProject crdtProject)
Expand Down
17 changes: 16 additions & 1 deletion backend/FwLite/LcmCrdt/Utils/JsonPatchRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,22 @@ public static IEnumerable<IChange> RewriteChanges<T, TProp>(this JsonPatchDocume
}
}
patchDocument.Operations.Remove(operation);
yield return changeFactory((TProp?)operation.Value, index, operation.OperationType);
if (operation.Value is TProp value)
{
yield return changeFactory(value, index, operation.OperationType);
}
else if (operation.Value is JsonElement element)
{
yield return changeFactory(JsonSerializer.Deserialize<TProp>(element, patchDocument.Options), index, operation.OperationType);
}
else if (operation.Value is null)
{
yield return changeFactory(default, index, operation.OperationType);
}
else
{
throw new InvalidOperationException($"Unexpected value type {operation.Value?.GetType()}");
}
}
}

Expand Down
Loading