From 3bcd2374021da64a1eac881ebe26a3722cfd1dc2 Mon Sep 17 00:00:00 2001 From: Robin Munn Date: Tue, 7 Jan 2025 15:08:45 -0500 Subject: [PATCH] Actually use validators in MiniLCM API --- .../Api/FwDataMiniLcmApi.cs | 16 +++- backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs | 16 +++- .../MiniLcm/Validators/MiniLcmValidators.cs | 83 ++++++++++++++++++- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs index 5599c0b91..1e3b7e6c9 100644 --- a/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs +++ b/backend/FwLite/FwDataMiniLcmBridge/Api/FwDataMiniLcmApi.cs @@ -162,6 +162,7 @@ internal void CompleteExemplars(WritingSystems writingSystems) public Task CreateWritingSystem(WritingSystemType type, WritingSystem writingSystem) { + validators.ValidateAndThrow(writingSystem); var exitingWs = type == WritingSystemType.Analysis ? Cache.ServiceLocator.WritingSystems.AnalysisWritingSystems : Cache.ServiceLocator.WritingSystems.VernacularWritingSystems; if (exitingWs.Any(ws => ws.Id == writingSystem.WsId)) { @@ -220,6 +221,7 @@ await Cache.DoUsingNewOrCurrentUOW("Update WritingSystem", public async Task UpdateWritingSystem(WritingSystem before, WritingSystem after) { + await validators.ValidateAndThrowAsync(after); await Cache.DoUsingNewOrCurrentUOW("Update WritingSystem", "Revert WritingSystem", async () => @@ -248,6 +250,7 @@ public IAsyncEnumerable GetPartsOfSpeech() public Task CreatePartOfSpeech(PartOfSpeech partOfSpeech) { + validators.ValidateAndThrow(partOfSpeech); IPartOfSpeech? lcmPartOfSpeech = null; if (partOfSpeech.Id == default) partOfSpeech.Id = Guid.NewGuid(); UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Part of Speech", @@ -278,6 +281,7 @@ public Task UpdatePartOfSpeech(Guid id, UpdateObjectInput UpdatePartOfSpeech(PartOfSpeech before, PartOfSpeech after) { + await validators.ValidateAndThrowAsync(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}"); } @@ -323,6 +327,7 @@ public IAsyncEnumerable GetSemanticDomains() public async Task CreateSemanticDomain(SemanticDomain semanticDomain) { + await validators.ValidateAndThrowAsync(semanticDomain); if (semanticDomain.Id == Guid.Empty) semanticDomain.Id = Guid.NewGuid(); UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Semantic Domain", "Remove semantic domain", @@ -355,6 +360,7 @@ public Task UpdateSemanticDomain(Guid id, UpdateObjectInput UpdateSemanticDomain(SemanticDomain before, SemanticDomain after) { + await validators.ValidateAndThrowAsync(after); await SemanticDomainSync.Sync(before, after, this); return await GetSemanticDomain(after.Id) ?? throw new NullReferenceException($"unable to find semantic domain with id {after.Id}"); } @@ -396,7 +402,7 @@ private ComplexFormType ToComplexFormType(ILexEntryType t) public async Task CreateComplexFormType(ComplexFormType complexFormType) { - await validators.ValidateAndThrow(complexFormType); + await validators.ValidateAndThrowAsync(complexFormType); if (complexFormType.Id == default) complexFormType.Id = Guid.NewGuid(); UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create complex form type", "Remove complex form type", @@ -429,7 +435,7 @@ public Task UpdateComplexFormType(Guid id, UpdateObjectInput UpdateComplexFormType(ComplexFormType before, ComplexFormType after) { - await validators.ValidateAndThrow(after); + await validators.ValidateAndThrowAsync(after); await ComplexFormTypeSync.Sync(before, after, this); return ToComplexFormType(ComplexFormTypesFlattened.Single(c => c.Guid == after.Id)); } @@ -661,6 +667,7 @@ public IAsyncEnumerable SearchEntries(string query, QueryOptions? options public async Task CreateEntry(Entry entry) { entry.Id = entry.Id == default ? Guid.NewGuid() : entry.Id; + await validators.ValidateAndThrowAsync(entry); try { UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Entry", @@ -853,6 +860,7 @@ public Task UpdateEntry(Guid id, UpdateObjectInput update) public async Task UpdateEntry(Entry before, Entry after) { + await validators.ValidateAndThrowAsync(after); await Cache.DoUsingNewOrCurrentUOW("Update Entry", "Revert entry", async () => @@ -963,6 +971,7 @@ public Task CreateSense(Guid entryId, Sense sense, BetweenPosition? betwe if (sense.Id == default) sense.Id = Guid.NewGuid(); if (!EntriesRepository.TryGetObject(entryId, out var lexEntry)) throw new InvalidOperationException("Entry not found"); + validators.ValidateAndThrow(sense); UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Sense", "Remove sense", Cache.ServiceLocator.ActionHandler, @@ -987,6 +996,7 @@ public Task UpdateSense(Guid entryId, Guid senseId, UpdateObjectInput UpdateSense(Guid entryId, Sense before, Sense after) { + await validators.ValidateAndThrowAsync(after); await Cache.DoUsingNewOrCurrentUOW("Update Sense", "Revert Sense", async () => @@ -1073,6 +1083,7 @@ public Task CreateExampleSentence(Guid entryId, Guid senseId, E if (exampleSentence.Id == default) exampleSentence.Id = Guid.NewGuid(); if (!SenseRepository.TryGetObject(senseId, out var lexSense)) throw new InvalidOperationException("Sense not found"); + validators.ValidateAndThrow(exampleSentence); UndoableUnitOfWorkHelper.DoUsingNewOrCurrentUOW("Create Example Sentence", "Remove example sentence", Cache.ServiceLocator.ActionHandler, @@ -1103,6 +1114,7 @@ public async Task UpdateExampleSentence(Guid entryId, ExampleSentence before, ExampleSentence after) { + await validators.ValidateAndThrowAsync(after); await Cache.DoUsingNewOrCurrentUOW("Update Example Sentence", "Revert Example Sentence", async () => diff --git a/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs b/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs index 3663434ff..3b84e3dd0 100644 --- a/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs +++ b/backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs @@ -45,6 +45,7 @@ public async Task GetWritingSystems() public async Task CreateWritingSystem(WritingSystemType type, WritingSystem writingSystem) { + await validators.ValidateAndThrowAsync(writingSystem); var entityId = Guid.NewGuid(); var wsCount = await WritingSystems.CountAsync(ws => ws.Type == type); try @@ -69,6 +70,7 @@ public async Task UpdateWritingSystem(WritingSystemId id, Writing public async Task UpdateWritingSystem(WritingSystem before, WritingSystem after) { + await validators.ValidateAndThrowAsync(after); await WritingSystemSync.Sync(before, after, this); return await GetWritingSystem(after.WsId, after.Type) ?? throw new NullReferenceException("unable to find writing system with id " + after.WsId); } @@ -101,6 +103,7 @@ public IAsyncEnumerable GetPartsOfSpeech() public async Task CreatePartOfSpeech(PartOfSpeech partOfSpeech) { + await validators.ValidateAndThrowAsync(partOfSpeech); await dataModel.AddChange(ClientId, new CreatePartOfSpeechChange(partOfSpeech.Id, partOfSpeech.Name, partOfSpeech.Predefined)); return await GetPartOfSpeech(partOfSpeech.Id) ?? throw new NullReferenceException(); } @@ -116,6 +119,7 @@ public async Task UpdatePartOfSpeech(Guid id, UpdateObjectInput UpdatePartOfSpeech(PartOfSpeech before, PartOfSpeech after) { + await validators.ValidateAndThrowAsync(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}"); } @@ -137,6 +141,7 @@ public async Task DeletePartOfSpeech(Guid id) public async Task CreateSemanticDomain(MiniLcm.Models.SemanticDomain semanticDomain) { + await validators.ValidateAndThrowAsync(semanticDomain); await dataModel.AddChange(ClientId, new CreateSemanticDomainChange(semanticDomain.Id, semanticDomain.Name, semanticDomain.Code, semanticDomain.Predefined)); return await GetSemanticDomain(semanticDomain.Id) ?? throw new NullReferenceException(); } @@ -152,6 +157,7 @@ public async Task UpdateSemanticDomain(Guid id, UpdateObjectInpu public async Task UpdateSemanticDomain(SemanticDomain before, SemanticDomain after) { + await validators.ValidateAndThrowAsync(after); await SemanticDomainSync.Sync(before, after, this); return await GetSemanticDomain(after.Id) ?? throw new NullReferenceException($"unable to find semantic domain with id {after.Id}"); } @@ -178,7 +184,7 @@ public IAsyncEnumerable GetComplexFormTypes() public async Task CreateComplexFormType(ComplexFormType complexFormType) { - await validators.ValidateAndThrow(complexFormType); + await validators.ValidateAndThrowAsync(complexFormType); if (complexFormType.Id == default) complexFormType.Id = Guid.NewGuid(); await dataModel.AddChange(ClientId, new CreateComplexFormType(complexFormType.Id, complexFormType.Name)); return await ComplexFormTypes.SingleAsync(c => c.Id == complexFormType.Id); @@ -192,7 +198,7 @@ public async Task UpdateComplexFormType(Guid id, UpdateObjectIn public async Task UpdateComplexFormType(ComplexFormType before, ComplexFormType after) { - await validators.ValidateAndThrow(after); + await validators.ValidateAndThrowAsync(after); await ComplexFormTypeSync.Sync(before, after, this); return await GetComplexFormType(after.Id) ?? throw new NullReferenceException($"unable to find complex form type with id {after.Id}"); } @@ -362,6 +368,7 @@ private IEnumerable CreateEntryChanges(Entry entry, Dictionary CreateEntry(Entry entry) { + await validators.ValidateAndThrowAsync(entry); await dataModel.AddChanges(ClientId, [ new CreateEntryChange(entry), @@ -449,6 +456,7 @@ public async Task UpdateEntry(Guid id, public async Task UpdateEntry(Entry before, Entry after) { + await validators.ValidateAndThrowAsync(after); await EntrySync.Sync(before, after, this); return await GetEntry(after.Id) ?? throw new NullReferenceException("unable to find entry with id " + after.Id); } @@ -494,6 +502,7 @@ public async Task CreateSense(Guid entryId, Sense sense, BetweenPosition? throw new InvalidOperationException("Order should not be provided when creating a sense"); sense.Order = await OrderPicker.PickOrder(Senses.Where(s => s.EntryId == entryId), between); + await validators.ValidateAndThrowAsync(sense); await dataModel.AddChanges(ClientId, await CreateSenseChanges(entryId, sense).ToArrayAsync()); return await dataModel.GetLatest(sense.Id) ?? throw new NullReferenceException(); } @@ -510,6 +519,7 @@ public async Task UpdateSense(Guid entryId, public async Task UpdateSense(Guid entryId, Sense before, Sense after) { + await validators.ValidateAndThrowAsync(after); await SenseSync.Sync(entryId, before, after, this); return await GetSense(entryId, after.Id) ?? throw new NullReferenceException("unable to find sense with id " + after.Id); } @@ -539,6 +549,7 @@ public async Task CreateExampleSentence(Guid entryId, Guid senseId, ExampleSentence exampleSentence) { + await validators.ValidateAndThrowAsync(exampleSentence); await dataModel.AddChange(ClientId, new CreateExampleSentenceChange(exampleSentence, senseId)); return await dataModel.GetLatest(exampleSentence.Id) ?? throw new NullReferenceException(); } @@ -567,6 +578,7 @@ public async Task UpdateExampleSentence(Guid entryId, ExampleSentence before, ExampleSentence after) { + await validators.ValidateAndThrowAsync(after); await ExampleSentenceSync.Sync(entryId, senseId, before, after, this); return await GetExampleSentence(entryId, senseId, after.Id) ?? throw new NullReferenceException(); } diff --git a/backend/FwLite/MiniLcm/Validators/MiniLcmValidators.cs b/backend/FwLite/MiniLcm/Validators/MiniLcmValidators.cs index 2da5c773c..e08862ade 100644 --- a/backend/FwLite/MiniLcm/Validators/MiniLcmValidators.cs +++ b/backend/FwLite/MiniLcm/Validators/MiniLcmValidators.cs @@ -4,12 +4,85 @@ namespace MiniLcm.Validators; -public record MiniLcmValidators(IValidator ComplexFormTypeValidator) +public record MiniLcmValidators( + IValidator ComplexFormTypeValidator, + IValidator EntryValidator, + IValidator SenseValidator, + IValidator ExampleSentenceValidator, + IValidator WritingSystemValidator, + IValidator PartOfSpeechValidator, + IValidator SemanticDomainValidator) { - public async Task ValidateAndThrow(ComplexFormType value) + public async Task ValidateAndThrowAsync(ComplexFormType value) { await ComplexFormTypeValidator.ValidateAndThrowAsync(value); } + + public async Task ValidateAndThrowAsync(Entry value) + { + await EntryValidator.ValidateAndThrowAsync(value); + } + + public async Task ValidateAndThrowAsync(Sense value) + { + await SenseValidator.ValidateAndThrowAsync(value); + } + + public async Task ValidateAndThrowAsync(ExampleSentence value) + { + await ExampleSentenceValidator.ValidateAndThrowAsync(value); + } + + public async Task ValidateAndThrowAsync(WritingSystem value) + { + await WritingSystemValidator.ValidateAndThrowAsync(value); + } + + public async Task ValidateAndThrowAsync(PartOfSpeech value) + { + await PartOfSpeechValidator.ValidateAndThrowAsync(value); + } + + public async Task ValidateAndThrowAsync(SemanticDomain value) + { + await SemanticDomainValidator.ValidateAndThrowAsync(value); + } + + public void ValidateAndThrow(ComplexFormType value) + { + ComplexFormTypeValidator.ValidateAndThrow(value); + } + + public void ValidateAndThrow(Entry value) + { + EntryValidator.ValidateAndThrow(value); + } + + public void ValidateAndThrow(Sense value) + { + SenseValidator.ValidateAndThrow(value); + } + + public void ValidateAndThrow(ExampleSentence value) + { + ExampleSentenceValidator.ValidateAndThrow(value); + } + + public void ValidateAndThrow(WritingSystem value) + { + WritingSystemValidator.ValidateAndThrow(value); + } + + public void ValidateAndThrow(PartOfSpeech value) + { + PartOfSpeechValidator.ValidateAndThrow(value); + } + + public void ValidateAndThrow(SemanticDomain value) + { + SemanticDomainValidator.ValidateAndThrow(value); + } + } public static class MiniLcmValidatorsExtensions @@ -18,6 +91,12 @@ public static IServiceCollection AddMiniLcmValidators(this IServiceCollection se { services.AddTransient(); services.AddTransient, ComplexFormTypeValidator>(); + services.AddTransient, EntryValidator>(); + services.AddTransient, SenseValidator>(); + services.AddTransient, ExampleSentenceValidator>(); + services.AddTransient, WritingSystemValidator>(); + services.AddTransient, PartOfSpeechValidator>(); + services.AddTransient, SemanticDomainValidator>(); return services; } }