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

Validate MiniLcm types #1344

Merged
merged 25 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
01a619e
Add validation for entries and most entry fields
rmunn Jan 6, 2025
8499ff5
Update entry validation tests to pass again
rmunn Jan 6, 2025
dcf2aef
Add entry validator tests for lexeme, citation form
rmunn Jan 6, 2025
4dd4032
Add entry validation tests for literal meaning, note
rmunn Jan 6, 2025
04b8b26
Add validation tests for senses, example sentences
rmunn Jan 6, 2025
87b2f8b
Address review comments so far
rmunn Jan 7, 2025
7577e83
Add list of canonical GUIDs for parts of speech
rmunn Jan 7, 2025
98fd92b
Add list of canonical GUIDs for semantic domains
rmunn Jan 7, 2025
68fe485
Mark FwLiteProjectSync tests as integration tests
rmunn Jan 7, 2025
b8e6b30
Add more entry validation tests
rmunn Jan 7, 2025
379d4d4
Also validate complex form types on updates
rmunn Jan 7, 2025
3bcd237
Actually use validators in MiniLCM API
rmunn Jan 7, 2025
7f38246
Don't check part of speech GUIDs yet
rmunn Jan 7, 2025
1cedbec
Adjust some test data to make it valid
rmunn Jan 7, 2025
e5b02b7
Fix test failures around semantic domain IDs
rmunn Jan 7, 2025
d046bdc
Push two missing files
rmunn Jan 7, 2025
8d5a2fe
Make EntryReadyForCreation create valid data
rmunn Jan 7, 2025
e894d40
Temporarily skip part of speech validation
rmunn Jan 7, 2025
9082936
Better comment
rmunn Jan 8, 2025
922f0a0
Example sentences may have empty Sentence fields
rmunn Jan 8, 2025
c3f0908
Address most review comments
rmunn Jan 8, 2025
0a39681
Rename PartOfSpeechIdValidator
rmunn Jan 8, 2025
7e0e604
Rename one overload that was missed earlier
rmunn Jan 9, 2025
913400a
Fix complex form test - this one should fail
rmunn Jan 9, 2025
9e4e4f1
Fix renames that the code rename somehow missed
rmunn Jan 9, 2025
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
33 changes: 23 additions & 10 deletions backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@
}
}

public Task<WritingSystem> CreateWritingSystem(WritingSystemType type, WritingSystem writingSystem)
public async Task<WritingSystem> CreateWritingSystem(WritingSystemType type, WritingSystem writingSystem)
{
await validators.ValidateAndThrow(writingSystem);
var exitingWs = type == WritingSystemType.Analysis ? Cache.ServiceLocator.WritingSystems.AnalysisWritingSystems : Cache.ServiceLocator.WritingSystems.VernacularWritingSystems;
if (exitingWs.Any(ws => ws.Id == writingSystem.WsId))
{
Expand Down Expand Up @@ -194,7 +195,7 @@
WritingSystemType.Vernacular => WritingSystemContainer.CurrentVernacularWritingSystems.Count,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
} - 1;
return Task.FromResult(FromLcmWritingSystem(ws, index, type));
return FromLcmWritingSystem(ws, index, type);
}

public async Task<WritingSystem> UpdateWritingSystem(WritingSystemId id, WritingSystemType type, UpdateObjectInput<WritingSystem> update)
Expand All @@ -205,7 +206,7 @@
}
await Cache.DoUsingNewOrCurrentUOW("Update WritingSystem",
"Revert WritingSystem",
async () =>

Check warning on line 209 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs

View workflow job for this annotation

GitHub Actions / Build FwHeadless / publish-fw-headless

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 209 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs

View workflow job for this annotation

GitHub Actions / Build FW Lite and run tests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 209 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Linux

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 209 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 209 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Mac

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 209 in backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs

View workflow job for this annotation

GitHub Actions / Publish FW Lite app for Windows

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
var updateProxy = new UpdateWritingSystemProxy(lcmWritingSystem, this)
{
Expand All @@ -220,6 +221,7 @@

public async Task<WritingSystem> UpdateWritingSystem(WritingSystem before, WritingSystem after)
{
await validators.ValidateAndThrow(after);
await Cache.DoUsingNewOrCurrentUOW("Update WritingSystem",
"Revert WritingSystem",
async () =>
Expand All @@ -246,8 +248,9 @@
? FromLcmPartOfSpeech(partOfSpeech) : null);
}

public Task<PartOfSpeech> CreatePartOfSpeech(PartOfSpeech partOfSpeech)
public async Task<PartOfSpeech> CreatePartOfSpeech(PartOfSpeech partOfSpeech)
{
await validators.ValidateAndThrow(partOfSpeech);
IPartOfSpeech? lcmPartOfSpeech = null;
if (partOfSpeech.Id == default) partOfSpeech.Id = Guid.NewGuid();
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Part of Speech",
Expand All @@ -259,7 +262,7 @@
.Create(partOfSpeech.Id, Cache.LangProject.PartsOfSpeechOA);
UpdateLcmMultiString(lcmPartOfSpeech.Name, partOfSpeech.Name);
});
return Task.FromResult(FromLcmPartOfSpeech(lcmPartOfSpeech ?? throw new InvalidOperationException("Part of speech was not created")));
return FromLcmPartOfSpeech(lcmPartOfSpeech ?? throw new InvalidOperationException("Part of speech was not created"));
}

public Task<PartOfSpeech> UpdatePartOfSpeech(Guid id, UpdateObjectInput<PartOfSpeech> update)
Expand All @@ -278,6 +281,7 @@

public async Task<PartOfSpeech> UpdatePartOfSpeech(PartOfSpeech before, PartOfSpeech after)
{
await validators.ValidateAndThrow(after);
await PartOfSpeechSync.Sync(before, after, this);
return await GetPartOfSpeech(after.Id) ?? throw new NullReferenceException($"unable to find part of speech with id {after.Id}");
}
Expand All @@ -301,7 +305,7 @@
Id = semanticDomain.Guid,
Name = FromLcmMultiString(semanticDomain.Name),
Code = semanticDomain.Abbreviation.UiString ?? "",
Predefined = true, // TODO: Look up in a GUID list of predefined data
Predefined = CanonicalGuidsSemanticDomain.CanonicalSemDomGuids.Contains(semanticDomain.Guid),
hahn-kev marked this conversation as resolved.
Show resolved Hide resolved
};
}

Expand All @@ -323,6 +327,7 @@

public async Task<SemanticDomain> CreateSemanticDomain(SemanticDomain semanticDomain)
{
await validators.ValidateAndThrow(semanticDomain);
if (semanticDomain.Id == Guid.Empty) semanticDomain.Id = Guid.NewGuid();
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Semantic Domain",
"Remove semantic domain",
Expand Down Expand Up @@ -355,6 +360,7 @@

public async Task<SemanticDomain> UpdateSemanticDomain(SemanticDomain before, SemanticDomain after)
{
await validators.ValidateAndThrow(after);
await SemanticDomainSync.Sync(before, after, this);
return await GetSemanticDomain(after.Id) ?? throw new NullReferenceException($"unable to find semantic domain with id {after.Id}");
}
Expand Down Expand Up @@ -429,6 +435,7 @@

public async Task<ComplexFormType> UpdateComplexFormType(ComplexFormType before, ComplexFormType after)
{
await validators.ValidateAndThrow(after);
await ComplexFormTypeSync.Sync(before, after, this);
return ToComplexFormType(ComplexFormTypesFlattened.Single(c => c.Guid == after.Id));
}
Expand Down Expand Up @@ -460,7 +467,7 @@
Id = lcmPos.Guid,
Name = FromLcmMultiString(lcmPos.Name),
// TODO: Abreviation = FromLcmMultiString(partOfSpeech.Abreviation),
Predefined = true, // NOTE: the !string.IsNullOrEmpty(lcmPos.CatalogSourceId) check doesn't work if the PoS originated in CRDT
Predefined = CanonicalGuidsPartOfSpeech.CanonicalPosGuids.Contains(lcmPos.Guid),
};
}

Expand Down Expand Up @@ -660,6 +667,7 @@
public async Task<Entry> CreateEntry(Entry entry)
{
entry.Id = entry.Id == default ? Guid.NewGuid() : entry.Id;
await validators.ValidateAndThrow(entry);
try
{
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Entry",
Expand Down Expand Up @@ -852,6 +860,7 @@

public async Task<Entry> UpdateEntry(Entry before, Entry after)
{
await validators.ValidateAndThrow(after);
await Cache.DoUsingNewOrCurrentUOW("Update Entry",
"Revert entry",
async () =>
Expand Down Expand Up @@ -957,16 +966,17 @@
return Task.FromResult(lcmSense is null ? null : FromLexSense(lcmSense));
}

public Task<Sense> CreateSense(Guid entryId, Sense sense, BetweenPosition? between = null)
public async Task<Sense> CreateSense(Guid entryId, Sense sense, BetweenPosition? between = null)
{
if (sense.Id == default) sense.Id = Guid.NewGuid();
if (!EntriesRepository.TryGetObject(entryId, out var lexEntry))
throw new InvalidOperationException("Entry not found");
await validators.ValidateAndThrow(sense);
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Sense",
"Remove sense",
Cache.ServiceLocator.ActionHandler,
() => CreateSense(lexEntry, sense, between));
return Task.FromResult(FromLexSense(SenseRepository.GetObject(sense.Id)));
return FromLexSense(SenseRepository.GetObject(sense.Id));
}

public Task<Sense> UpdateSense(Guid entryId, Guid senseId, UpdateObjectInput<Sense> update)
Expand All @@ -986,6 +996,7 @@

public async Task<Sense> UpdateSense(Guid entryId, Sense before, Sense after)
{
await validators.ValidateAndThrow(after);
await Cache.DoUsingNewOrCurrentUOW("Update Sense",
"Revert Sense",
async () =>
Expand Down Expand Up @@ -1067,16 +1078,17 @@
lexExampleSentence.Reference.get_WritingSystem(0));
}

public Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence)
public async Task<ExampleSentence> CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence)
{
if (exampleSentence.Id == default) exampleSentence.Id = Guid.NewGuid();
if (!SenseRepository.TryGetObject(senseId, out var lexSense))
throw new InvalidOperationException("Sense not found");
await validators.ValidateAndThrow(exampleSentence);
UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Example Sentence",
"Remove example sentence",
Cache.ServiceLocator.ActionHandler,
() => CreateExampleSentence(lexSense, exampleSentence));
return Task.FromResult(FromLexExampleSentence(senseId, ExampleSentenceRepository.GetObject(exampleSentence.Id)));
return FromLexExampleSentence(senseId, ExampleSentenceRepository.GetObject(exampleSentence.Id));
}

public Task<ExampleSentence> UpdateExampleSentence(Guid entryId,
Expand All @@ -1102,6 +1114,7 @@
ExampleSentence before,
ExampleSentence after)
{
await validators.ValidateAndThrow(after);
await Cache.DoUsingNewOrCurrentUOW("Update Example Sentence",
"Revert Example Sentence",
async () =>
Expand Down
1 change: 1 addition & 0 deletions backend/FwLite/FwLiteProjectSync.Tests/Sena3SyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace FwLiteProjectSync.Tests;

[Trait("Category", "Integration")]
public class Sena3SyncTests : IClassFixture<Sena3Fixture>, IAsyncLifetime
{
private readonly Sena3Fixture _fixture;
Expand Down
15 changes: 15 additions & 0 deletions backend/FwLite/FwLiteProjectSync.Tests/SyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public SyncTests(SyncFixture fixture)
}

[Fact]
[Trait("Category", "Integration")]
public async Task FirstSyncJustDoesAnImport()
{
var crdtApi = _fixture.CrdtApi;
Expand All @@ -90,6 +91,7 @@ public async Task FirstSyncJustDoesAnImport()
}

[Fact]
[Trait("Category", "Integration")]
public async Task SecondSyncDoesNothing()
{
var crdtApi = _fixture.CrdtApi;
Expand All @@ -101,6 +103,7 @@ public async Task SecondSyncDoesNothing()
}

[Fact]
[Trait("Category", "Integration")]
public static async Task SyncFailsWithMismatchedProjectIds()
{
var fixture = SyncFixture.Create();
Expand All @@ -120,6 +123,7 @@ await fixture.Services.GetRequiredService<LcmCrdtDbContext>().ProjectData.
}

[Fact]
[Trait("Category", "Integration")]
public async Task CreatingAnEntryInEachProjectSyncsAcrossBoth()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -154,6 +158,7 @@ await crdtApi.CreateEntry(new Entry()
}

[Fact]
[Trait("Category", "Integration")]
public async Task SyncDryRun_NoChangesAreSynced()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -189,6 +194,7 @@ await crdtApi.CreateEntry(new Entry()
}

[Fact]
[Trait("Category", "Integration")]
public async Task CreatingAComplexEntryInFwDataSyncsWithoutIssue()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -240,6 +246,7 @@ public async Task CreatingAComplexEntryInFwDataSyncsWithoutIssue()


[Fact]
[Trait("Category", "Integration")]
public async Task PartsOfSpeechSyncBothWays()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -275,6 +282,7 @@ public async Task PartsOfSpeechSyncBothWays()
}

[Fact]
[Trait("Category", "Integration")]
public async Task PartsOfSpeechSyncInEntries()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -318,6 +326,7 @@ await crdtApi.CreateEntry(new Entry()
}

[Fact]
[Trait("Category", "Integration")]
public async Task SemanticDomainsSyncBothWays()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -355,6 +364,7 @@ public async Task SemanticDomainsSyncBothWays()
}

[Fact]
[Trait("Category", "Integration")]
public async Task SemanticDomainsSyncInEntries()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -399,6 +409,7 @@ await crdtApi.CreateEntry(new Entry()
}

[Fact]
[Trait("Category", "Integration")]
public async Task UpdatingAnEntryInEachProjectSyncsAcrossBoth()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -427,6 +438,7 @@ public async Task UpdatingAnEntryInEachProjectSyncsAcrossBoth()
}

[Fact]
[Trait("Category", "Integration")]
public async Task CanSyncAnyEntryWithDeletedComplexForm()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -464,6 +476,7 @@ await fwdataApi.CreateEntry(new Entry()
}

[Fact]
[Trait("Category", "Integration")]
public async Task AddingASenseToAnEntryInEachProjectSyncsAcrossBoth()
{
var crdtApi = _fixture.CrdtApi;
Expand Down Expand Up @@ -493,6 +506,7 @@ public async Task AddingASenseToAnEntryInEachProjectSyncsAcrossBoth()
}

[Fact]
[Trait("Category", "Integration")]
public async Task CanCreateAComplexFormAndItsComponentInOneSync()
{
//ensure they are synced so a real sync will happen when we want it to
Expand All @@ -513,6 +527,7 @@ public async Task CanCreateAComplexFormAndItsComponentInOneSync()
}

[Fact]
[Trait("Category", "Integration")]
public async Task CanCreateAComplexFormTypeAndSyncsIt()
{
//ensure they are synced so a real sync will happen when we want it to
Expand Down
Loading
Loading