From 6e2016b07a5abf2adaa7c65e5cf9cb7a5456fff0 Mon Sep 17 00:00:00 2001 From: Hafiz Adewuyi Date: Sat, 2 Nov 2024 02:21:53 +0200 Subject: [PATCH] Extract reusable components from the projects (#115) * Remove unnecessary usings across all projects in the solution. * Replace name with Title. * Create new generalized core library. * Fix Create reusable Name Core and Application layers. * Change local port for API due to restricted port issue. * Remove all references to "Name" in the Dictionary entry service. * Replace all instances of "Name" in the generic entry repository. * Refactor repository logic into reusable class. * Shrink method size. * Create new reusable infrastructure project. * Remove direct reference to MongoDB driver. * Fix broken domain events. * Use one Infrastructure project for all external dependencies. * Fix broken feedback section. * Remove unused using. * Pack YorubaOrganization.Core into a nupkg. * Pack the reusable Application project into to nupkg. * Move the dictionary component projects to a different solution. * Clean up Core project file. * Fix file formatting issue. * Minor cleanups. * Fix namespaces and usings. --- Api/Api.csproj | 9 +- Api/Controllers/AuthController.cs | 4 +- Api/Controllers/EtymologyController.cs | 4 +- Api/Controllers/FeedbacksController.cs | 16 +- Api/Controllers/GeoLocationsController.cs | 6 +- Api/Controllers/NamesController.cs | 35 +- Api/Controllers/SearchController.cs | 38 +-- Api/Controllers/SuggestedNameController.cs | 1 - .../GlobalExceptionHandling.cs | 2 +- Api/Program.cs | 17 +- Api/Utilities/CustomSchemaFilter.cs | 4 +- Application/Application.csproj | 3 +- Application/Cache/ICacheService.cs | 8 +- .../DeletedNameCachingHandler.cs | 38 --- ...Handler.cs => EntryIndexedEventHandler.cs} | 7 +- .../ExactNameSearchedEventHandler.cs | 21 -- .../Events/ExactNameSearchedAdapter.cs | 12 - Application/Events/NameDeletedAdapter.cs | 11 - .../Events/NameEntryNameUpdatedAdapter.cs | 11 - Application/Events/NameIndexedAdapter.cs | 11 - .../NonExistingNameUpdateAttemptedAdapter.cs | 14 - Application/Exceptions/ClientException.cs | 23 -- Application/Exceptions/DuplicateException.cs | 9 - .../Exceptions/RepositoryAccessException.cs | 23 -- .../UnpublishedNameUpdateException.cs | 7 - Application/Mappers/NameEntryMapper.cs | 26 +- Application/Mappers/SuggestedNameMapper.cs | 2 + .../Migrator/MigrationDTOs.cs/etymology.cs | 8 +- .../Migrator/MigrationDTOs.cs/geolocation.cs | 8 +- .../Migrator/MigrationDTOs.cs/nameentry.cs | 9 +- .../MigrationDTOs.cs/nameentryfeedback.cs | 8 +- .../MigrationDTOs.cs/nameentryvideos.cs | 8 +- .../MigrationDTOs.cs/suggested_name.cs | 8 +- .../Migrator/MigrationDTOs.cs/users.cs | 8 +- Application/Migrator/SQLToMongoMigrator.cs | 57 ++-- Application/Services/BasicAuthHandler.cs | 6 +- Application/Services/EventPubService.cs | 16 +- Application/Services/GeoLocationsService.cs | 6 +- .../Services/NameEntryFeedbackService.cs | 6 +- Application/Services/NameEntryService.cs | 188 +---------- Application/Services/SearchService.cs | 27 +- Application/Services/UserService.cs | 8 +- Application/Validation/CreateUserValidator.cs | 6 +- .../Validation/EmbeddedVideoValidator.cs | 9 +- Application/Validation/EtymologyValidator.cs | 9 +- .../Validation/GeoLocationValidator.cs | 6 +- Application/Validation/UpdateUserValidator.cs | 6 +- Core/Cache/IRecentIndexesCache.cs | 6 - Core/Cache/IRecentSearchesCache.cs | 7 - Core/Cache/ISetBasedCache.cs | 15 - Core/Cache/ISimpleCache.cs | 8 - Core/Core.csproj | 3 + Core/Dto/CharacterSeparatedString.cs | 35 -- Core/Dto/CommaSeparatedString.cs | 11 - Core/Dto/HyphenSeparatedString.cs | 14 - Core/Dto/Request/CreateGeoLocationDto.cs | 6 - Core/Dto/Request/CreateNameDto.cs | 4 +- Core/Dto/Request/CreateNameFeedbackDto.cs | 2 +- Core/Dto/Request/CreateSuggestedNameDto.cs | 4 +- Core/Dto/Request/CreateUserDto.cs | 15 - Core/Dto/Request/EmbeddedVideoDto.cs | 7 - Core/Dto/Request/EtymologyDto.cs | 10 - Core/Dto/Request/NameDto.cs | 20 +- Core/Dto/Request/UpdateUserDto.cs | 7 - Core/Dto/Response/FeedbackDto.cs | 6 - Core/Dto/Response/GeoLocationDto.cs | 10 - Core/Dto/Response/NameEntryDto.cs | 6 +- Core/Dto/Response/NameFeedbackDto.cs | 6 + Core/Dto/Response/RecentStats.cs | 23 -- Core/Dto/Response/SearchMetadataDto.cs | 7 - Core/Dto/Response/SuggestedNameDto.cs | 4 +- Core/Dto/Response/UserDto.cs | 9 - Core/Entities/BaseEntity.cs | 20 -- Core/Entities/GeoLocation.cs | 20 -- Core/Entities/NameEntry.cs | 16 + .../NameEntry/Collections/EmbeddedVideo.cs | 14 - .../NameEntry/Collections/Etymology.cs | 13 - .../NameEntry/Collections/Feedback.cs | 18 - Core/Entities/NameEntry/NameEntry.cs | 94 ------ Core/Entities/SuggestedName.cs | 4 +- Core/Entities/User.cs | 15 - Core/Enums/Role.cs | 15 - Core/Enums/State.cs | 9 - Core/Events/ExactNameSearched.cs | 7 - Core/Events/IEventPubService.cs | 7 - Core/Events/NameDeleted.cs | 5 - Core/Events/NameEntryNameUpdated.cs | 6 - Core/Events/NameIndexed.cs | 6 +- Core/Events/NonExistingNameUpdateAttempted.cs | 6 - Core/Repositories/IEtymologyRepository.cs | 7 - Core/Repositories/IGeoLocationsRepository.cs | 14 - .../INameEntryFeedbackRepository.cs | 7 +- Core/Repositories/INameEntryRepository.cs | 53 +-- Core/Repositories/IUserRepository.cs | 16 - .../CharacterSeparatedStringConverter.cs | 2 +- .../CommaSeparatedStringConverter.cs | 2 +- .../HyphenSeparatedStringConverter.cs | 2 +- Core/Utilities/DiacriticsNormalizer.cs | 74 ---- .../Infrastructure.MongoDB.csproj | 14 - .../Repositories/MongoDBRepository.cs | 18 - .../Repositories/NameEntryRepository.cs | 316 ------------------ .../Hangfire/DependencyInjection.cs | 1 - Infrastructure/Hangfire/HangfireAuthFilter.cs | 2 +- Infrastructure/Infrastructure.csproj | 10 +- .../MongoDB}/DependencyInjection.cs | 1 + .../Repositories/EtymologyRepository.cs | 9 +- .../Repositories/GeoLocationsRepository.cs | 7 +- .../NameEntryFeedbackRepository.cs | 43 +-- .../Repositories/NameEntryRepository.cs | 34 ++ .../Repositories/SuggestedNameRepository.cs | 1 - .../MongoDB}/Repositories/UserRepository.cs | 11 +- Infrastructure/Redis/DependencyInjection.cs | 2 +- .../Redis/RedisRecentIndexesCache.cs | 4 +- .../Redis/RedisRecentSearchesCache.cs | 4 +- Infrastructure/Redis/SimpleRedisCache.cs | 2 +- Infrastructure/TweetsV2Poster.cs | 1 - Infrastructure/Twitter/TwitterService.cs | 2 +- README.md | 2 +- .../NameController/Data/NamesAllTestData.cs | 4 +- .../Data/NamesCountAndSubmittedByTestData.cs | 2 +- .../NameController/Data/NamesCountTestData.cs | 6 +- .../Data/NamesStateAndCountTestData.cs | 4 +- .../Data/NamesStateAndSubmittedByTestData.cs | 4 +- .../NamesStateCountAndSubmittedByTestData.cs | 4 +- .../NameController/Data/NamesStateTestData.cs | 4 +- .../Data/NamesSubmittedByTestData.cs | 4 +- .../NameController/NameControllerTest.cs | 20 +- Website/Pages/SearchResults.cshtml.cs | 1 - Website/Pages/Shared/BasePageModel.cshtml.cs | 1 - Website/Pages/SubmitName.cshtml.cs | 3 +- Website/Services/ApiService.cs | 1 + YorubaNameDictionary.sln | 8 +- docker-compose.override.yml | 2 +- 133 files changed, 382 insertions(+), 1636 deletions(-) delete mode 100644 Application/EventHandlers/DeletedNameCachingHandler.cs rename Application/EventHandlers/{NameIndexedEventHandler.cs => EntryIndexedEventHandler.cs} (81%) delete mode 100644 Application/EventHandlers/ExactNameSearchedEventHandler.cs delete mode 100644 Application/Events/ExactNameSearchedAdapter.cs delete mode 100644 Application/Events/NameDeletedAdapter.cs delete mode 100644 Application/Events/NameEntryNameUpdatedAdapter.cs delete mode 100644 Application/Events/NameIndexedAdapter.cs delete mode 100644 Application/Events/NonExistingNameUpdateAttemptedAdapter.cs delete mode 100644 Application/Exceptions/ClientException.cs delete mode 100644 Application/Exceptions/DuplicateException.cs delete mode 100644 Application/Exceptions/RepositoryAccessException.cs delete mode 100644 Application/Exceptions/UnpublishedNameUpdateException.cs delete mode 100644 Core/Cache/IRecentIndexesCache.cs delete mode 100644 Core/Cache/IRecentSearchesCache.cs delete mode 100644 Core/Cache/ISetBasedCache.cs delete mode 100644 Core/Cache/ISimpleCache.cs delete mode 100644 Core/Dto/CharacterSeparatedString.cs delete mode 100644 Core/Dto/CommaSeparatedString.cs delete mode 100644 Core/Dto/HyphenSeparatedString.cs delete mode 100644 Core/Dto/Request/CreateGeoLocationDto.cs delete mode 100644 Core/Dto/Request/CreateUserDto.cs delete mode 100644 Core/Dto/Request/EmbeddedVideoDto.cs delete mode 100644 Core/Dto/Request/EtymologyDto.cs delete mode 100644 Core/Dto/Request/UpdateUserDto.cs delete mode 100644 Core/Dto/Response/FeedbackDto.cs delete mode 100644 Core/Dto/Response/GeoLocationDto.cs create mode 100644 Core/Dto/Response/NameFeedbackDto.cs delete mode 100644 Core/Dto/Response/RecentStats.cs delete mode 100644 Core/Dto/Response/SearchMetadataDto.cs delete mode 100644 Core/Dto/Response/UserDto.cs delete mode 100644 Core/Entities/BaseEntity.cs delete mode 100644 Core/Entities/GeoLocation.cs create mode 100644 Core/Entities/NameEntry.cs delete mode 100644 Core/Entities/NameEntry/Collections/EmbeddedVideo.cs delete mode 100644 Core/Entities/NameEntry/Collections/Etymology.cs delete mode 100644 Core/Entities/NameEntry/Collections/Feedback.cs delete mode 100644 Core/Entities/NameEntry/NameEntry.cs delete mode 100644 Core/Entities/User.cs delete mode 100644 Core/Enums/Role.cs delete mode 100644 Core/Enums/State.cs delete mode 100644 Core/Events/ExactNameSearched.cs delete mode 100644 Core/Events/IEventPubService.cs delete mode 100644 Core/Events/NameDeleted.cs delete mode 100644 Core/Events/NameEntryNameUpdated.cs delete mode 100644 Core/Events/NonExistingNameUpdateAttempted.cs delete mode 100644 Core/Repositories/IEtymologyRepository.cs delete mode 100644 Core/Repositories/IGeoLocationsRepository.cs delete mode 100644 Core/Repositories/IUserRepository.cs delete mode 100644 Core/Utilities/DiacriticsNormalizer.cs delete mode 100644 Infrastructure.MongoDB/Infrastructure.MongoDB.csproj delete mode 100644 Infrastructure.MongoDB/Repositories/MongoDBRepository.cs delete mode 100644 Infrastructure.MongoDB/Repositories/NameEntryRepository.cs rename {Infrastructure.MongoDB => Infrastructure/MongoDB}/DependencyInjection.cs (96%) rename {Infrastructure.MongoDB => Infrastructure/MongoDB}/Repositories/EtymologyRepository.cs (86%) rename {Infrastructure.MongoDB => Infrastructure/MongoDB}/Repositories/GeoLocationsRepository.cs (97%) rename {Infrastructure.MongoDB => Infrastructure/MongoDB}/Repositories/NameEntryFeedbackRepository.cs (63%) create mode 100644 Infrastructure/MongoDB/Repositories/NameEntryRepository.cs rename {Infrastructure.MongoDB => Infrastructure/MongoDB}/Repositories/SuggestedNameRepository.cs (99%) rename {Infrastructure.MongoDB => Infrastructure/MongoDB}/Repositories/UserRepository.cs (93%) diff --git a/Api/Api.csproj b/Api/Api.csproj index 7a53800..74b1ab7 100644 --- a/Api/Api.csproj +++ b/Api/Api.csproj @@ -8,14 +8,13 @@ ..\docker-compose.dcproj - - - - + + + + - \ No newline at end of file diff --git a/Api/Controllers/AuthController.cs b/Api/Controllers/AuthController.cs index f181c34..c465d1b 100644 --- a/Api/Controllers/AuthController.cs +++ b/Api/Controllers/AuthController.cs @@ -1,11 +1,11 @@ using Api.Utilities; using Application.Services; -using Core.Dto.Request; -using Core.Dto.Response; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Net; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Dto.Response; namespace Api.Controllers { diff --git a/Api/Controllers/EtymologyController.cs b/Api/Controllers/EtymologyController.cs index ce72345..e99ed18 100644 --- a/Api/Controllers/EtymologyController.cs +++ b/Api/Controllers/EtymologyController.cs @@ -1,5 +1,5 @@ -using Core.Repositories; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; +using YorubaOrganization.Core.Repositories; namespace Api.Controllers { diff --git a/Api/Controllers/FeedbacksController.cs b/Api/Controllers/FeedbacksController.cs index f69266b..1d22050 100644 --- a/Api/Controllers/FeedbacksController.cs +++ b/Api/Controllers/FeedbacksController.cs @@ -1,8 +1,6 @@ -using Api.Model.Request; -using Application.Domain; -using Application.Services; +using Application.Services; +using Core.Dto.Request; using Core.Dto.Response; -using Core.Entities.NameEntry.Collections; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; @@ -27,7 +25,7 @@ public FeedbacksController(NameEntryFeedbackService nameEntryFeedbackService, Na [HttpGet] [Route("{id}")] - [ProducesResponseType(typeof(FeedbackDto), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(NameFeedbackDto), (int)HttpStatusCode.OK)] public async Task GetById(string id) { var feedback = await _nameEntryFeedbackService.GetFeedbackByIdAsync(id); @@ -46,7 +44,7 @@ public async Task GetById(string id) /// Optional name parameter /// A list of all feedback (or just feedback for a given name) [HttpGet] - [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(List), (int)HttpStatusCode.OK)] public async Task GetFeedback([FromQuery] string? name = null) { var feedbacks = string.IsNullOrWhiteSpace(name) ? await _nameEntryFeedbackService.FindAllAsync() : @@ -64,7 +62,7 @@ public async Task GetFeedback([FromQuery] string? name = null) [ProducesResponseType(typeof(Dictionary), (int)HttpStatusCode.Created)] public async Task Create([FromBody] CreateNameFeedbackDto model) { - var nameEntry = await _nameEntryService.LoadName(model.Name); + var nameEntry = await _nameEntryService.LoadEntry(model.Name); if (nameEntry == null) { @@ -91,7 +89,7 @@ public async Task Delete(string name, string feedbackId) return BadRequest("Name parameter is required."); } - var nameEntry = await _nameEntryService.LoadName(name); + var nameEntry = await _nameEntryService.LoadEntry(name); if (nameEntry == null) { @@ -122,7 +120,7 @@ public async Task DeleteAll([FromQuery][Required] string name) return BadRequest("Name parameter is required."); } - var nameEntry = await _nameEntryService.LoadName(name); + var nameEntry = await _nameEntryService.LoadEntry(name); if (nameEntry == null) { diff --git a/Api/Controllers/GeoLocationsController.cs b/Api/Controllers/GeoLocationsController.cs index 8d4c5a9..ba0cfd8 100644 --- a/Api/Controllers/GeoLocationsController.cs +++ b/Api/Controllers/GeoLocationsController.cs @@ -1,11 +1,11 @@ using Api.Utilities; using Application.Services; -using Core.Dto.Request; -using Core.Dto.Response; -using Core.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Net; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Entities; namespace Api.Controllers { diff --git a/Api/Controllers/NamesController.cs b/Api/Controllers/NamesController.cs index 5ea13a7..ebfd54c 100644 --- a/Api/Controllers/NamesController.cs +++ b/Api/Controllers/NamesController.cs @@ -1,15 +1,15 @@ -using Api.Model.In; -using Api.Utilities; -using Application.Domain; +using Api.Utilities; using Application.Mappers; +using Application.Services; using Core.Dto.Request; using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; using FluentValidation; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using MongoDB.Driver; using System.Net; +using YorubaOrganization.Core.Enums; namespace Api.Controllers { @@ -63,13 +63,18 @@ public async Task Post([FromBody] CreateNameDto request) /// /// [HttpGet("meta")] - [ProducesResponseType(typeof(NamesMetadataDto[]), (int)HttpStatusCode.OK)] + [ProducesResponseType(typeof(NamesMetadataDto), (int)HttpStatusCode.OK)] [AllowAnonymous] public async Task GetMetaData() { var metaData = await _nameEntryService.GetMetadata(); - - return Ok(metaData); + return Ok(new NamesMetadataDto + { + TotalNames = metaData.TotalEntries, + TotalPublishedNames = metaData.TotalPublishedEntries, + TotalModifiedNames = metaData.TotalModifiedEntries, + TotalNewNames = metaData.TotalNewEntries + }); } /// @@ -96,7 +101,7 @@ public async Task GetAllNames( IEnumerable names; if (all.HasValue && all.Value) { - names = await _nameEntryService.GetAllNames(state, submittedBy); + names = await _nameEntryService.GetAllEntries(state, submittedBy); return Ok(names.MapToDtoCollectionMini()); } @@ -116,7 +121,7 @@ public async Task GetAllNames( [AllowAnonymous] public async Task GetName([FromRoute] string name) { - var nameEntry = await _nameEntryService.LoadName(name); + var nameEntry = await _nameEntryService.LoadEntry(name); if (nameEntry == null) { @@ -146,14 +151,14 @@ public async Task UpdateName(string name, [FromBody] UpdateNameDt return BadRequest(ModelState); } - var oldNameEntry = await _nameEntryService.LoadName(name); + var oldNameEntry = await _nameEntryService.LoadEntry(name); if (oldNameEntry == null) { return NotFound($"{name} not in database"); } - _ = await _nameEntryService.UpdateName(oldNameEntry, updated.MapToEntity()); + _ = await _nameEntryService.Update(oldNameEntry, updated.MapToEntity()); return Ok(new { Message = "Name successfully updated" }); } @@ -161,7 +166,7 @@ public async Task UpdateName(string name, [FromBody] UpdateNameDt [Authorize(Policy = "AdminOnly")] public async Task DeleteName(string name) { - var nameEntry = await _nameEntryService.LoadName(name); + var nameEntry = await _nameEntryService.LoadEntry(name); if (nameEntry == null) { return NotFound($"{name} not found in the system so cannot be deleted"); @@ -177,7 +182,7 @@ public async Task DeleteName(string name) public async Task DeleteNamesBatch(string[] names) { - var foundNames = (await _nameEntryService.LoadNames(names))?.Select(f => f.Name)?.ToArray(); + var foundNames = (await _nameEntryService.LoadEntries(names))?.Select(f => f.Title)?.ToArray(); if (foundNames is null || foundNames.Length == 0) { @@ -186,7 +191,7 @@ public async Task DeleteNamesBatch(string[] names) var notFoundNames = names.Where(d => !foundNames.Contains(d)).ToList(); - await _nameEntryService.DeleteNamesBatch(foundNames); + await _nameEntryService.DeleteEntriesBatch(foundNames); string responseMessage = string.Join(", ", foundNames) + " deleted"; if (notFoundNames.Any()) diff --git a/Api/Controllers/SearchController.cs b/Api/Controllers/SearchController.cs index 90714fa..c3b6ce2 100644 --- a/Api/Controllers/SearchController.cs +++ b/Api/Controllers/SearchController.cs @@ -1,16 +1,16 @@ using Api.Utilities; -using Application.Domain; using Application.Mappers; using Application.Services; -using Core.Cache; using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Enums; -using Core.Events; +using Core.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.ComponentModel.DataAnnotations; using System.Net; +using YorubaOrganization.Core.Cache; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Enums; +using YorubaOrganization.Core.Events; namespace Api.Controllers { @@ -55,9 +55,9 @@ public async Task Search([FromQuery(Name = "q"), Required] string var matches = await _searchService.Search(searchTerm); // TODO: Check if the comparison here removes takes diacrits into consideration - if (matches.Count() == 1 && matches.First().Name.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)) + if (matches.Count() == 1 && matches.First().Title.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)) { - await _eventPubService.PublishEvent(new ExactNameSearched(matches.First().Name)); + await _eventPubService.PublishEvent(new ExactEntrySearched(matches.First().Title)); } return Ok(matches.MapToDtoCollection()); @@ -92,11 +92,11 @@ public async Task SearchByStartsWith(string searchTerm) [ProducesResponseType(typeof(NameEntryDto), (int)HttpStatusCode.OK)] public async Task SearchOne(string searchTerm) { - NameEntryDto nameEntry = await _searchService.GetName(searchTerm); + var nameEntry = await _searchService.GetName(searchTerm); if(nameEntry != null) { - await _eventPubService.PublishEvent(new ExactNameSearched(nameEntry.Name)); + await _eventPubService.PublishEvent(new ExactEntrySearched(nameEntry.Name)); } return Ok(nameEntry); @@ -158,7 +158,7 @@ public async Task GetRecentStats() [Authorize(Policy = "AdminAndProLexicographers")] public async Task PublishName([FromRoute] string name) { - var nameEntry = await _nameEntryService.LoadName(name); + var nameEntry = await _nameEntryService.LoadEntry(name); if (nameEntry == null) { return BadRequest(ResponseHelper.GetResponseDict($"{name} not found in the repository so not indexed")); @@ -170,7 +170,7 @@ public async Task PublishName([FromRoute] string name) return BadRequest(ResponseHelper.GetResponseDict($"{name} is already indexed")); } - await _nameEntryService.PublishName(nameEntry, User!.Identity!.Name!); + await _nameEntryService.PublishEntry(nameEntry, User!.Identity!.Name!); return StatusCode((int)HttpStatusCode.Created, ResponseHelper.GetResponseDict($"{name} has been published")); } @@ -189,7 +189,7 @@ public async Task PublishNames([FromBody] string[] names) // TODO Later: Optimize by fetching all names in one database call instead of one-by-one. foreach (var name in names) { - var entry = await _nameEntryService.LoadName(name); + var entry = await _nameEntryService.LoadEntry(name); if (entry != null && entry.State != State.PUBLISHED) { entriesToIndex.Add(entry); @@ -204,10 +204,10 @@ public async Task PublishNames([FromBody] string[] names) foreach (var nameEntry in entriesToIndex) { // TODO Later: The names should be updated in one batch instead of one-by-one. - await _nameEntryService.PublishName(nameEntry, User!.Identity!.Name!); + await _nameEntryService.PublishEntry(nameEntry, User!.Identity!.Name!); } - var successMessage = $"The following names were successfully indexed: {string.Join(',', entriesToIndex.Select(x => x.Name))}"; + var successMessage = $"The following names were successfully indexed: {string.Join(',', entriesToIndex.Select(x => x.Title))}"; return StatusCode((int)HttpStatusCode.Created, ResponseHelper.GetResponseDict(successMessage)); } @@ -220,7 +220,7 @@ public async Task PublishNames([FromBody] string[] names) [Authorize(Policy = "AdminAndProLexicographers")] public async Task UnpublishName([FromRoute] string name) { - var entry = await _nameEntryService.FindByNameAndState(name, State.PUBLISHED); + var entry = await _nameEntryService.FindByTitleAndState(name, State.PUBLISHED); if (entry == null) { @@ -228,7 +228,7 @@ public async Task UnpublishName([FromRoute] string name) } entry.State = State.NEW; - await _nameEntryService.UpdateName(entry); + await _nameEntryService.Update(entry); return Ok(ResponseHelper.GetResponseDict($"{name} removed from index.")); } @@ -245,7 +245,7 @@ public async Task UnpublishNames([FromBody] string[] names) foreach (var name in names) { - var entry = await _nameEntryService.FindByNameAndState(name, State.PUBLISHED); + var entry = await _nameEntryService.FindByTitleAndState(name, State.PUBLISHED); if (entry == null) { @@ -254,8 +254,8 @@ public async Task UnpublishNames([FromBody] string[] names) else { entry.State = State.UNPUBLISHED; - await _nameEntryService.UpdateName(entry); - unpublishedNames.Add(entry.Name); + await _nameEntryService.Update(entry); + unpublishedNames.Add(entry.Title); } } diff --git a/Api/Controllers/SuggestedNameController.cs b/Api/Controllers/SuggestedNameController.cs index ffb56af..c4bf24e 100644 --- a/Api/Controllers/SuggestedNameController.cs +++ b/Api/Controllers/SuggestedNameController.cs @@ -6,7 +6,6 @@ using Microsoft.AspNetCore.Mvc; using System.Net; using Application.Mappers; -using Application.Validation; using FluentValidation; namespace Api.Controllers; diff --git a/Api/ExceptionHandler/GlobalExceptionHandling.cs b/Api/ExceptionHandler/GlobalExceptionHandling.cs index 3f18d1c..f73cc7b 100644 --- a/Api/ExceptionHandler/GlobalExceptionHandling.cs +++ b/Api/ExceptionHandler/GlobalExceptionHandling.cs @@ -1,9 +1,9 @@ namespace Api.ExceptionHandler { using Api.Utilities; - using Application.Exceptions; using System.Net; using System.Text.Json; + using YorubaOrganization.Application.Exceptions; public class GlobalExceptionHandlingMiddleware { diff --git a/Api/Program.cs b/Api/Program.cs index d8f4f4b..da675e0 100644 --- a/Api/Program.cs +++ b/Api/Program.cs @@ -1,16 +1,10 @@ using Api.ExceptionHandler; -using Application.Domain; -using Application.Events; using Application.Migrator; using Application.Services; using Application.Validation; -using Core.Cache; -using Core.Enums; -using Core.Events; using Core.StringObjectConverters; using FluentValidation; using Infrastructure.Twitter; -using Infrastructure.MongoDB; using Microsoft.AspNetCore.Authentication; using Microsoft.OpenApi.Models; using MySqlConnector; @@ -19,6 +13,11 @@ using Infrastructure.Hangfire; using Infrastructure.Redis; using Ardalis.GuardClauses; +using YorubaOrganization.Core.Enums; +using YorubaOrganization.Core.Events; +using YorubaOrganization.Core.Cache; +using Application.EventHandlers; +using Infrastructure.MongoDB; var builder = WebApplication.CreateBuilder(args); var configuration = builder.Configuration; @@ -119,7 +118,11 @@ //Validation services.AddValidatorsFromAssemblyContaining(); -services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(ExactNameSearchedAdapter).Assembly)); +services.AddMediatR(cfg => +{ + cfg.RegisterServicesFromAssembly(typeof(PostPublishedNameCommandHandler).Assembly); + cfg.RegisterServicesFromAssembly(typeof(ExactEntrySearchedEventHandler).Assembly); +}); // Twitter integration configuration services.AddSingleton(); diff --git a/Api/Utilities/CustomSchemaFilter.cs b/Api/Utilities/CustomSchemaFilter.cs index 84153ed..863f8ae 100644 --- a/Api/Utilities/CustomSchemaFilter.cs +++ b/Api/Utilities/CustomSchemaFilter.cs @@ -1,6 +1,6 @@ -using Core.Dto; -using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; +using YorubaOrganization.Core.Dto; public class CustomSchemaFilter : ISchemaFilter { diff --git a/Application/Application.csproj b/Application/Application.csproj index 1310f6a..e4d82e4 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -10,12 +10,13 @@ + - + diff --git a/Application/Cache/ICacheService.cs b/Application/Cache/ICacheService.cs index 8cc97c4..2c38165 100644 --- a/Application/Cache/ICacheService.cs +++ b/Application/Cache/ICacheService.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Cache +namespace Application.Cache { public interface ICacheService { diff --git a/Application/EventHandlers/DeletedNameCachingHandler.cs b/Application/EventHandlers/DeletedNameCachingHandler.cs deleted file mode 100644 index 79df525..0000000 --- a/Application/EventHandlers/DeletedNameCachingHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Application.Events; -using Core.Cache; -using MediatR; -using Microsoft.Extensions.Logging; - -namespace Application.EventHandlers -{ - public class DeletedNameCachingHandler : INotificationHandler - { - private readonly IRecentIndexesCache _recentIndexesCache; - private readonly IRecentSearchesCache _recentSearchesCache; - private readonly ILogger _logger; - - public DeletedNameCachingHandler( - IRecentIndexesCache recentIndexesCache, - IRecentSearchesCache recentSearchesCache, - ILogger logger - ) - { - _recentIndexesCache = recentIndexesCache; - _recentSearchesCache = recentSearchesCache; - _logger = logger; - } - - public async Task Handle(NameDeletedAdapter notification, CancellationToken cancellationToken) - { - try - { - await _recentIndexesCache.Remove(notification.Name); - await _recentSearchesCache.Remove(notification.Name); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error occurred while removing deleted name '{name}' from cache.", notification.Name); - } - } - } -} \ No newline at end of file diff --git a/Application/EventHandlers/NameIndexedEventHandler.cs b/Application/EventHandlers/EntryIndexedEventHandler.cs similarity index 81% rename from Application/EventHandlers/NameIndexedEventHandler.cs rename to Application/EventHandlers/EntryIndexedEventHandler.cs index dc09fc1..fbf9ace 100644 --- a/Application/EventHandlers/NameIndexedEventHandler.cs +++ b/Application/EventHandlers/EntryIndexedEventHandler.cs @@ -1,10 +1,11 @@ using Application.Events; -using Core.Cache; +using Core.Events; using MediatR; +using YorubaOrganization.Core.Cache; namespace Application.EventHandlers { - public class NameIndexedEventHandler : INotificationHandler + public class NameIndexedEventHandler : INotificationHandler { public IRecentIndexesCache _recentIndexesCache; private readonly IMediator _mediator; @@ -17,7 +18,7 @@ public NameIndexedEventHandler( _mediator = mediator; } - public async Task Handle(NameIndexedAdapter notification, CancellationToken cancellationToken) + public async Task Handle(NameIndexed notification, CancellationToken cancellationToken) { await _recentIndexesCache.Stack(notification.Name); await _mediator.Publish(new PostPublishedNameCommand(notification.Name, notification.Meaning), cancellationToken); diff --git a/Application/EventHandlers/ExactNameSearchedEventHandler.cs b/Application/EventHandlers/ExactNameSearchedEventHandler.cs deleted file mode 100644 index a7685b1..0000000 --- a/Application/EventHandlers/ExactNameSearchedEventHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Application.Events; -using Core.Cache; -using MediatR; - -namespace Application.EventHandlers -{ - public class ExactNameSearchedEventHandler : INotificationHandler - { - public IRecentSearchesCache _recentSearchesCache; - - public ExactNameSearchedEventHandler(IRecentSearchesCache recentSearchesCache) - { - _recentSearchesCache = recentSearchesCache; - } - - public async Task Handle(ExactNameSearchedAdapter notification, CancellationToken cancellationToken) - { - await _recentSearchesCache.Stack(notification.SearchTerm); - } - } -} diff --git a/Application/Events/ExactNameSearchedAdapter.cs b/Application/Events/ExactNameSearchedAdapter.cs deleted file mode 100644 index 21df021..0000000 --- a/Application/Events/ExactNameSearchedAdapter.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Core.Events; -using MediatR; - -namespace Application.Events -{ - public record ExactNameSearchedAdapter : ExactNameSearched, INotification - { - public ExactNameSearchedAdapter(ExactNameSearched theEvent) : base(theEvent.SearchTerm) - { - } - } -} \ No newline at end of file diff --git a/Application/Events/NameDeletedAdapter.cs b/Application/Events/NameDeletedAdapter.cs deleted file mode 100644 index a91e513..0000000 --- a/Application/Events/NameDeletedAdapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Core.Events; -using MediatR; - -namespace Application.Events; - -public record NameDeletedAdapter : NameDeleted, INotification -{ - public NameDeletedAdapter(NameDeleted theEvent) : base(theEvent.Name) - { - } -} \ No newline at end of file diff --git a/Application/Events/NameEntryNameUpdatedAdapter.cs b/Application/Events/NameEntryNameUpdatedAdapter.cs deleted file mode 100644 index 74821fd..0000000 --- a/Application/Events/NameEntryNameUpdatedAdapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Core.Events; -using MediatR; - -namespace Application.Events; - -public record NameEntryNameUpdatedAdapter : NameEntryNameUpdated, INotification -{ - public NameEntryNameUpdatedAdapter(NameEntryNameUpdated theEvent) : base(theEvent.OriginalName, theEvent.NewName) - { - } -} \ No newline at end of file diff --git a/Application/Events/NameIndexedAdapter.cs b/Application/Events/NameIndexedAdapter.cs deleted file mode 100644 index 10c9f84..0000000 --- a/Application/Events/NameIndexedAdapter.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Core.Events; -using MediatR; - -namespace Application.Events; - -public record NameIndexedAdapter : NameIndexed, INotification -{ - public NameIndexedAdapter(NameIndexed theEvent) : base(theEvent.Name, theEvent.Meaning) - { - } -} \ No newline at end of file diff --git a/Application/Events/NonExistingNameUpdateAttemptedAdapter.cs b/Application/Events/NonExistingNameUpdateAttemptedAdapter.cs deleted file mode 100644 index 1d043d9..0000000 --- a/Application/Events/NonExistingNameUpdateAttemptedAdapter.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Core.Events; -using MediatR; - -namespace Application.Events -{ - public record NonExistingNameUpdateAttemptedAdapter : NonExistingNameUpdateAttempted, INotification - { - public NonExistingNameUpdateAttemptedAdapter(string name) : base(name) { } - - public NonExistingNameUpdateAttemptedAdapter(NonExistingNameUpdateAttempted theEvent) : base(theEvent.Name) - { - } - } -} \ No newline at end of file diff --git a/Application/Exceptions/ClientException.cs b/Application/Exceptions/ClientException.cs deleted file mode 100644 index 6b08495..0000000 --- a/Application/Exceptions/ClientException.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Application.Exceptions -{ - /// - /// TODO: This should be a 400 Bad Request exception - /// - public class ClientException : Exception - { - public ClientException() - { - } - - public ClientException(string message) - : base(message) - { - } - - public ClientException(string message, Exception inner) - : base(message, inner) - { - } - } - -} diff --git a/Application/Exceptions/DuplicateException.cs b/Application/Exceptions/DuplicateException.cs deleted file mode 100644 index 976451d..0000000 --- a/Application/Exceptions/DuplicateException.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Application.Exceptions -{ - public class DuplicateException : ClientException - { - public DuplicateException() : base() { } - - public DuplicateException(string message) : base(message) { } - } -} diff --git a/Application/Exceptions/RepositoryAccessException.cs b/Application/Exceptions/RepositoryAccessException.cs deleted file mode 100644 index 64abb84..0000000 --- a/Application/Exceptions/RepositoryAccessException.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Application.Exceptions -{ - /// - /// TODO: This should be a 400 Bad Request exception - /// - public class RepositoryAccessException : Exception - { - public RepositoryAccessException() - { - } - - public RepositoryAccessException(string message) - : base(message) - { - } - - public RepositoryAccessException(string message, Exception inner) - : base(message, inner) - { - } - } - -} diff --git a/Application/Exceptions/UnpublishedNameUpdateException.cs b/Application/Exceptions/UnpublishedNameUpdateException.cs deleted file mode 100644 index bcf5def..0000000 --- a/Application/Exceptions/UnpublishedNameUpdateException.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Application.Exceptions -{ - public class UnpublishedNameUpdateException : ClientException - { - public UnpublishedNameUpdateException() : base("There is an unpublished update on this name already") { } - } -} diff --git a/Application/Mappers/NameEntryMapper.cs b/Application/Mappers/NameEntryMapper.cs index 57f9200..990b7f8 100644 --- a/Application/Mappers/NameEntryMapper.cs +++ b/Application/Mappers/NameEntryMapper.cs @@ -1,10 +1,12 @@ -using Core.Dto; -using Core.Dto.Request; +using Core.Dto.Request; using Core.Dto.Response; using Core.Entities; -using Core.Entities.NameEntry; -using Core.Entities.NameEntry.Collections; -using Core.Enums; +using YorubaOrganization.Core.Dto; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Entities.Partials; +using YorubaOrganization.Core.Enums; namespace Application.Mappers { @@ -19,7 +21,7 @@ public static NameEntryMiniDto[] MapToDtoCollectionMini(this IEnumerable new NameEntryMiniDto { - Name = nameEntry.Name, + Name = nameEntry.Title, Meaning = nameEntry.Meaning, SubmittedBy = nameEntry.CreatedBy }).ToArray(); @@ -29,12 +31,12 @@ public static NameEntry MapToEntity(this NameDto request) { return new NameEntry { - Name = request.Name.Trim(), + Title = request.Name.Trim(), Pronunciation = request.Pronunciation?.Trim(), Meaning = request.Meaning.Trim(), ExtendedMeaning = request.ExtendedMeaning?.Trim(), Morphology = request.Morphology ?? new List(), - Media = request.Media ?? new List(), + MediaLinks = request.MediaLinks, State = request.State ?? State.NEW, Etymology = request.Etymology.Select(et => new Etymology(et.Part, et.Meaning)).ToList(), Videos = request.Videos.Select(ev => new EmbeddedVideo(ev.VideoId, ev.Caption)).ToList(), @@ -42,7 +44,7 @@ public static NameEntry MapToEntity(this NameDto request) GeoLocation = request.GeoLocation.Select(ge => new GeoLocation(ge.Place, ge.Region)).ToList(), FamousPeople = request.FamousPeople ?? new List(), Syllables = request.Syllables ?? new List(), - Variants = request.Variants ?? new List(), + VariantsV2 = request.VariantsV2, CreatedBy = request.SubmittedBy, UpdatedBy = request.SubmittedBy }; @@ -54,21 +56,21 @@ public static NameEntryDto MapToDto(this NameEntry nameEntry) { Pronunciation = nameEntry.Pronunciation, IpaNotation = nameEntry.IpaNotation, - Variants = (CommaSeparatedString)nameEntry.Variants, + Variants = (CommaSeparatedString)nameEntry.VariantsV2.Select(v => v.Title).ToList(), Syllables = (HyphenSeparatedString)nameEntry.Syllables, Meaning = nameEntry.Meaning, ExtendedMeaning = nameEntry.ExtendedMeaning, Morphology = (CommaSeparatedString)nameEntry.Morphology, GeoLocation = nameEntry.GeoLocation.Select(ge => new GeoLocationDto(ge.Id, ge.Place, ge.Region)).ToList(), FamousPeople = (CommaSeparatedString)nameEntry.FamousPeople, - Media = (CommaSeparatedString)nameEntry.Media, + Media = (CommaSeparatedString)nameEntry.MediaLinks.Select(m => m.Url).ToList(), SubmittedBy = nameEntry.CreatedBy, Etymology = nameEntry.Etymology.Select(et => new EtymologyDto(et.Part, et.Meaning)).ToList(), Videos = nameEntry.Videos.Select(v => new EmbeddedVideoDto(v.VideoId, v.Caption)).ToList(), State = nameEntry.State, CreatedAt = nameEntry.CreatedAt, UpdatedAt = nameEntry.UpdatedAt, - Name = nameEntry.Name + Name = nameEntry.Title }; } } diff --git a/Application/Mappers/SuggestedNameMapper.cs b/Application/Mappers/SuggestedNameMapper.cs index b26dc52..40f6482 100644 --- a/Application/Mappers/SuggestedNameMapper.cs +++ b/Application/Mappers/SuggestedNameMapper.cs @@ -2,6 +2,8 @@ using Core.Dto.Response; using Core.Entities; using MongoDB.Bson; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Entities; namespace Application.Mappers; diff --git a/Application/Migrator/MigrationDTOs.cs/etymology.cs b/Application/Migrator/MigrationDTOs.cs/etymology.cs index 99119ed..0177c2b 100644 --- a/Application/Migrator/MigrationDTOs.cs/etymology.cs +++ b/Application/Migrator/MigrationDTOs.cs/etymology.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Migrator.MigrationDTOs.cs +namespace Application.Migrator.MigrationDTOs.cs { internal class etymology { diff --git a/Application/Migrator/MigrationDTOs.cs/geolocation.cs b/Application/Migrator/MigrationDTOs.cs/geolocation.cs index 6ac1bee..241c428 100644 --- a/Application/Migrator/MigrationDTOs.cs/geolocation.cs +++ b/Application/Migrator/MigrationDTOs.cs/geolocation.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Migrator.MigrationDTOs.cs +namespace Application.Migrator.MigrationDTOs.cs { internal class geolocation { diff --git a/Application/Migrator/MigrationDTOs.cs/nameentry.cs b/Application/Migrator/MigrationDTOs.cs/nameentry.cs index ead2cc8..0a430e8 100644 --- a/Application/Migrator/MigrationDTOs.cs/nameentry.cs +++ b/Application/Migrator/MigrationDTOs.cs/nameentry.cs @@ -1,10 +1,5 @@ -using Core.Entities; -using Core.Entities.NameEntry.Collections; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Entities.Partials; namespace Application.Migrator.MigrationDTOs.cs { diff --git a/Application/Migrator/MigrationDTOs.cs/nameentryfeedback.cs b/Application/Migrator/MigrationDTOs.cs/nameentryfeedback.cs index a3f304e..44439a7 100644 --- a/Application/Migrator/MigrationDTOs.cs/nameentryfeedback.cs +++ b/Application/Migrator/MigrationDTOs.cs/nameentryfeedback.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Migrator.MigrationDTOs.cs +namespace Application.Migrator.MigrationDTOs.cs { internal class nameentryfeedback { diff --git a/Application/Migrator/MigrationDTOs.cs/nameentryvideos.cs b/Application/Migrator/MigrationDTOs.cs/nameentryvideos.cs index 60c734d..849121a 100644 --- a/Application/Migrator/MigrationDTOs.cs/nameentryvideos.cs +++ b/Application/Migrator/MigrationDTOs.cs/nameentryvideos.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Migrator.MigrationDTOs.cs +namespace Application.Migrator.MigrationDTOs.cs { internal class nameentryvideos { diff --git a/Application/Migrator/MigrationDTOs.cs/suggested_name.cs b/Application/Migrator/MigrationDTOs.cs/suggested_name.cs index d709667..84d6546 100644 --- a/Application/Migrator/MigrationDTOs.cs/suggested_name.cs +++ b/Application/Migrator/MigrationDTOs.cs/suggested_name.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Migrator.MigrationDTOs.cs +namespace Application.Migrator.MigrationDTOs.cs { internal class suggested_name { diff --git a/Application/Migrator/MigrationDTOs.cs/users.cs b/Application/Migrator/MigrationDTOs.cs/users.cs index 6d9e5d0..a30c561 100644 --- a/Application/Migrator/MigrationDTOs.cs/users.cs +++ b/Application/Migrator/MigrationDTOs.cs/users.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Application.Migrator.MigrationDTOs.cs +namespace Application.Migrator.MigrationDTOs.cs { internal class users { diff --git a/Application/Migrator/SQLToMongoMigrator.cs b/Application/Migrator/SQLToMongoMigrator.cs index 7efbcf5..ca19c82 100644 --- a/Application/Migrator/SQLToMongoMigrator.cs +++ b/Application/Migrator/SQLToMongoMigrator.cs @@ -1,14 +1,15 @@ using Application.Migrator.MigrationDTOs.cs; -using Core.Dto; +using Core.Dto.Request; using Core.Entities; -using Core.Entities.NameEntry; -using Core.Entities.NameEntry.Collections; -using Core.Enums; using Dapper; using Microsoft.Extensions.Configuration; using MongoDB.Bson; using MongoDB.Driver; using MySqlConnector; +using YorubaOrganization.Core.Dto; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Entities.Partials; +using YorubaOrganization.Core.Enums; namespace Application.Migrator { @@ -97,26 +98,34 @@ on a.place equals b.geo_location_place Select(s => new EmbeddedVideo(s.url, s.caption)).ToList(); } - List documentsToInsert = name_entry.Select(s => new NameEntry() - { - Id = ObjectId.GenerateNewId().ToString(), - Name = s.name, - ExtendedMeaning = s.extended_meaning, - FamousPeople = new CommaSeparatedString(s.famous_people), - IpaNotation = s.ipa_notation, - Meaning = s.meaning, - Media = new CommaSeparatedString(s.media), - Morphology = new CommaSeparatedString(s.morphology), - Pronunciation = s.pronunciation, - CreatedBy = s.submitted_by, - Syllables = new HyphenSeparatedString(s.syllables), - Variants = new CommaSeparatedString(s.variants), - State = GetPublishState(s.state), - Etymology = s.etymology, - GeoLocation = s.geoLocations, - Feedbacks = s.feedbacks, - Videos = s.embeddedVideo - }).ToList(); + List documentsToInsert = name_entry.Select(s => { + var updateNameDto = new UpdateNameDto + { + Media = new CommaSeparatedString(s.media), + Variants = new CommaSeparatedString(s.variants) + }; + + return new NameEntry() + { + Id = ObjectId.GenerateNewId().ToString(), + Title = s.name, + ExtendedMeaning = s.extended_meaning, + FamousPeople = new CommaSeparatedString(s.famous_people), + IpaNotation = s.ipa_notation, + Meaning = s.meaning, + MediaLinks = updateNameDto.MediaLinks, + Morphology = new CommaSeparatedString(s.morphology), + Pronunciation = s.pronunciation, + CreatedBy = s.submitted_by, + Syllables = new HyphenSeparatedString(s.syllables), + VariantsV2 = updateNameDto.VariantsV2, + State = GetPublishState(s.state), + Etymology = s.etymology, + GeoLocation = s.geoLocations, + Feedbacks = s.feedbacks, + Videos = s.embeddedVideo + }; + }).ToList(); _nameEntryCollection.DeleteMany(FilterDefinition.Empty); _nameEntryCollection.InsertMany(documentsToInsert); diff --git a/Application/Services/BasicAuthHandler.cs b/Application/Services/BasicAuthHandler.cs index f54c1fc..42b785a 100644 --- a/Application/Services/BasicAuthHandler.cs +++ b/Application/Services/BasicAuthHandler.cs @@ -1,6 +1,4 @@ -using Core.Entities; -using Core.Repositories; -using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Data; @@ -8,6 +6,8 @@ using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Repositories; namespace Application.Services { diff --git a/Application/Services/EventPubService.cs b/Application/Services/EventPubService.cs index f97828c..dc2a153 100644 --- a/Application/Services/EventPubService.cs +++ b/Application/Services/EventPubService.cs @@ -1,6 +1,6 @@ -using Core.Events; +using Ardalis.GuardClauses; using MediatR; -using System.Reflection; +using YorubaOrganization.Core.Events; namespace Application.Services { @@ -15,16 +15,8 @@ public EventPubService(IMediator mediator) public async Task PublishEvent(T theEvent) { - var adapterClassName = typeof(T).Name + "Adapter"; - Type? adapterType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == adapterClassName); - - if (adapterType == null) - { - throw new InvalidOperationException("Adapter type not found for " + typeof(T).FullName); - } - - var adapterEvent = Activator.CreateInstance(adapterType, theEvent)!; - await _mediator.Publish(adapterEvent); + Guard.Against.Null(theEvent, nameof(theEvent)); + await _mediator.Publish(theEvent); } } } diff --git a/Application/Services/GeoLocationsService.cs b/Application/Services/GeoLocationsService.cs index e09a01f..488d039 100644 --- a/Application/Services/GeoLocationsService.cs +++ b/Application/Services/GeoLocationsService.cs @@ -1,6 +1,6 @@ -using Application.Exceptions; -using Core.Entities; -using Core.Repositories; +using YorubaOrganization.Application.Exceptions; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Repositories; namespace Application.Services { diff --git a/Application/Services/NameEntryFeedbackService.cs b/Application/Services/NameEntryFeedbackService.cs index 2f5625f..c55d8f3 100644 --- a/Application/Services/NameEntryFeedbackService.cs +++ b/Application/Services/NameEntryFeedbackService.cs @@ -12,12 +12,12 @@ public NameEntryFeedbackService(INameEntryFeedbackRepository nameEntryFeedbackRe _nameEntryFeedbackRepository = nameEntryFeedbackRepository; } - public async Task> FindAllAsync() + public async Task> FindAllAsync() { return await _nameEntryFeedbackRepository.FindAllAsync(); } - public async Task> FindByNameAsync(string name) + public async Task> FindByNameAsync(string name) { return await _nameEntryFeedbackRepository.FindByNameAsync(name); } @@ -32,7 +32,7 @@ public async Task DeleteAllFeedbackForNameAsync(string name) await _nameEntryFeedbackRepository.DeleteAllFeedbackForNameAsync(name); } - public async Task GetFeedbackByIdAsync(string feedbackId) + public async Task GetFeedbackByIdAsync(string feedbackId) { return await _nameEntryFeedbackRepository.GetFeedbackByIdAsync(feedbackId); } diff --git a/Application/Services/NameEntryService.cs b/Application/Services/NameEntryService.cs index a014b2f..79cd358 100644 --- a/Application/Services/NameEntryService.cs +++ b/Application/Services/NameEntryService.cs @@ -1,14 +1,13 @@ -using Application.Exceptions; -using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; using Core.Events; using Core.Repositories; using Microsoft.Extensions.Logging; +using YorubaOrganization.Application.Services; +using YorubaOrganization.Core.Events; -namespace Application.Domain +namespace Application.Services { - public class NameEntryService + public class NameEntryService : DictionaryEntryService { private readonly INameEntryRepository _nameEntryRepository; private readonly IEventPubService _eventPubService; @@ -18,128 +17,13 @@ public NameEntryService( INameEntryRepository nameEntryRepository, IEventPubService eventPubService, ILogger logger - ) + ) : base(nameEntryRepository, eventPubService, logger) { _nameEntryRepository = nameEntryRepository; _eventPubService = eventPubService; _logger = logger; } - public async Task Create(NameEntry entry) - { - var name = entry.Name; - - var existingName = await _nameEntryRepository.FindByName(name); - if (existingName != null) - { - existingName.Duplicates.Add(entry); - await UpdateName(existingName); - _logger.LogWarning("Someone attempted to create a new name over existing name: {name}.", name); - return; - } - - await CreateOrUpdateName(entry); - } - - public async Task BulkCreate(List entries) - { - foreach (var entry in entries) - { - await Create(entry); - // TODO Later: Ensure that removing batched writes to database here will not cause problems - } - } - - public async Task CreateOrUpdateName(NameEntry entry) - { - var updated = await UpdateNameWithUnpublish(entry); - - if (updated == null) - { - await _nameEntryRepository.Create(entry); - } - - return updated ?? entry; - } - - public async Task> SaveNames(List entries) - { - var savedNames = new List(); - foreach (var entry in entries) - { - savedNames.Add(await CreateOrUpdateName(entry)); - // TODO Later: Ensure that removing batched writes to database here will not cause problems - } - return savedNames; - } - - public async Task UpdateName(NameEntry nameEntry) - { - return await _nameEntryRepository.Update(nameEntry.Name, nameEntry); - } - - public async Task PublishName(NameEntry nameEntry, string username) - { - if (string.IsNullOrEmpty(username)) - { - throw new ClientException("Unexpected. User must be logged in to publish a name!"); - } - - NameEntry? updates = nameEntry.Modified; - string originalName = nameEntry.Name; - - if (updates != null) - { - // Copy latest updates to the main object as part of the publish operation. - nameEntry.Name = updates.Name; - nameEntry.Pronunciation = updates.Pronunciation; - nameEntry.IpaNotation = updates.IpaNotation; - nameEntry.Meaning = updates.Meaning; - nameEntry.ExtendedMeaning = updates.ExtendedMeaning; - nameEntry.Morphology = updates.Morphology; - nameEntry.Media = updates.Media; - nameEntry.State = updates.State; - nameEntry.Etymology = updates.Etymology; - nameEntry.Videos = updates.Videos; - nameEntry.GeoLocation = updates.GeoLocation; - nameEntry.FamousPeople = updates.FamousPeople; - nameEntry.Syllables = updates.Syllables; - nameEntry.Variants = updates.Variants; - nameEntry.UpdatedBy = username; - - nameEntry.Modified = null; - } - - nameEntry.State = State.PUBLISHED; - await _nameEntryRepository.Update(originalName, nameEntry); - - // TODO Later: Use the outbox pattern to enforce event publishing after the DB update (https://www.youtube.com/watch?v=032SfEBFIJs&t=913s). - await _eventPubService.PublishEvent(new NameIndexed(nameEntry.Name, nameEntry.Meaning)); - } - - public async Task UpdateNameWithUnpublish(NameEntry nameEntry) - { - if (nameEntry.State == State.PUBLISHED) - { - // Unpublish name if it is currently published since it is awaiting some changes. - nameEntry.State = State.MODIFIED; - } - - return await UpdateName(nameEntry); - } - - /// - /// Update an existing NameEntry with a new version. - /// - /// - /// - /// - public async Task UpdateName(NameEntry originalEntry, NameEntry newEntry) - { - originalEntry.Modified = newEntry; - return await UpdateNameWithUnpublish(originalEntry); - } - public async Task> BulkUpdateNames(List nameEntries) { var updatedNames = new List(); @@ -147,7 +31,7 @@ public async Task> BulkUpdateNames(List nameEntries) // TODO Later: Update all names in one batch foreach (var nameEntry in nameEntries) { - var updated = await UpdateNameWithUnpublish(nameEntry); + var updated = await UpdateEntryWithUnpublish(nameEntry); if (updated != null) { @@ -155,65 +39,17 @@ public async Task> BulkUpdateNames(List nameEntries) } else { - await _eventPubService.PublishEvent(new NonExistingNameUpdateAttempted(nameEntry.Name)); + await _eventPubService.PublishEvent(new NonExistingEntryUpdateAttempted(nameEntry.Title)); } } return updatedNames; } - public async Task> ListNames() + public async override Task PublishEntry(NameEntry nameEntry, string username) { - var result = await _nameEntryRepository.ListAll(); - return result.ToList(); - } - - public async Task GetMetadata() - { - return await _nameEntryRepository.GetMetadata(); - } - - public async Task> FindBy(State state) - { - return await _nameEntryRepository.FindByState(state); - } - - public async Task> GetAllNames(State? state, string? submittedBy) - { - return await _nameEntryRepository.GetAllNames(state, submittedBy); - } - - public async Task> List(State? state, string? submittedBy, int pageNumber, int pageSize) - { - return await _nameEntryRepository.List(pageNumber, pageSize, state, submittedBy); - } - - public async Task LoadName(string name) - { - return await _nameEntryRepository.FindByName(name); - } - - public async Task> LoadNames(string[] names) - { - return await _nameEntryRepository.FindByNames(names); - } - - public async Task Delete(string name) - { - await _nameEntryRepository.Delete(name); - await PublishNameDeletedEvent(name); - } - - private async Task PublishNameDeletedEvent(string name) - { - await _eventPubService.PublishEvent(new NameDeleted(name)); - } - - public async Task FindByNameAndState(string name, State state) => - await _nameEntryRepository.FindByNameAndState(name, state); - - public async Task DeleteNamesBatch(string[] names) - { - await _nameEntryRepository.DeleteMany(names); + await base.PublishEntry(nameEntry, username, n => n.Meaning, n => n.ExtendedMeaning, n => n.FamousPeople); + // TODO Later: Use the outbox pattern to enforce event publishing after the DB update (https://www.youtube.com/watch?v=032SfEBFIJs&t=913s). + await _eventPubService.PublishEvent(new NameIndexed(nameEntry.Title, nameEntry.Meaning)); } } } \ No newline at end of file diff --git a/Application/Services/SearchService.cs b/Application/Services/SearchService.cs index 4c38f93..600f94d 100644 --- a/Application/Services/SearchService.cs +++ b/Application/Services/SearchService.cs @@ -1,8 +1,9 @@ using Application.Mappers; using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; using Core.Repositories; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Enums; namespace Application.Services { @@ -21,18 +22,18 @@ public async Task> AutoComplete(string query) if(query.Length > 1) { - namesResult = await _namesRepository.FindByNameStartingWithAndState(query, State.PUBLISHED); + namesResult = await _namesRepository.FindByTitleStartingWithAndState(query, State.PUBLISHED); } - var namesContainingQuery = await _namesRepository.FindNameEntryByNameContainingAndState(query, State.PUBLISHED); + var namesContainingQuery = await _namesRepository.FindEntryByTitleContainingAndState(query, State.PUBLISHED); namesResult.UnionWith(namesContainingQuery); - return new HashSet(namesResult.Select(n => n.Name)); + return new HashSet(namesResult.Select(n => n.Title)); } public async Task GetName(string searchTerm) { - var result = await _namesRepository.FindByNameAndState(searchTerm, State.PUBLISHED); + var result = await _namesRepository.FindByTitleAndState(searchTerm, State.PUBLISHED); return result?.MapToDto(); } @@ -48,23 +49,23 @@ public async Task GetNamesMetadata() public async Task> Search(string searchTerm) { - var exactFound = await _namesRepository.FindByNameAndState(searchTerm, State.PUBLISHED); + var exactFound = await _namesRepository.FindByTitleAndState(searchTerm, State.PUBLISHED); if (exactFound != null) { return new NameEntry[] { exactFound }; } - var startingWithSearchTerm = await _namesRepository.FindByNameStartingWithAndState(searchTerm, State.PUBLISHED); + var startingWithSearchTerm = await _namesRepository.FindByTitleStartingWithAndState(searchTerm, State.PUBLISHED); if (startingWithSearchTerm.Any()) { return startingWithSearchTerm; } var possibleFound = new HashSet(); - possibleFound.UnionWith(await _namesRepository.FindNameEntryByNameContainingAndState(searchTerm, State.PUBLISHED)); - possibleFound.UnionWith(await _namesRepository.FindNameEntryByVariantsContainingAndState(searchTerm, State.PUBLISHED)); - possibleFound.UnionWith(await _namesRepository.FindNameEntryByMeaningContainingAndState(searchTerm, State.PUBLISHED)); - possibleFound.UnionWith(await _namesRepository.FindNameEntryByExtendedMeaningContainingAndState(searchTerm, State.PUBLISHED)); + possibleFound.UnionWith(await _namesRepository.FindEntryByTitleContainingAndState(searchTerm, State.PUBLISHED)); + possibleFound.UnionWith(await _namesRepository.FindEntryByVariantsContainingAndState(searchTerm, State.PUBLISHED)); + possibleFound.UnionWith(await _namesRepository.FindEntryByMeaningContainingAndState(searchTerm, State.PUBLISHED)); + possibleFound.UnionWith(await _namesRepository.FindEntryByExtendedMeaningContainingAndState(searchTerm, State.PUBLISHED)); return possibleFound; @@ -72,7 +73,7 @@ public async Task> Search(string searchTerm) public async Task> SearchByStartsWith(string searchTerm) { - return await _namesRepository.FindByNameStartingWithAndState(searchTerm, State.PUBLISHED); + return await _namesRepository.FindByTitleStartingWithAndState(searchTerm, State.PUBLISHED); } } } diff --git a/Application/Services/UserService.cs b/Application/Services/UserService.cs index 3cad646..b6e6529 100644 --- a/Application/Services/UserService.cs +++ b/Application/Services/UserService.cs @@ -1,7 +1,7 @@ -using Core.Dto.Request; -using Core.Dto.Response; -using Core.Entities; -using Core.Repositories; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Repositories; using BCrypt_ = BCrypt.Net.BCrypt; namespace Application.Services diff --git a/Application/Validation/CreateUserValidator.cs b/Application/Validation/CreateUserValidator.cs index e5df7ee..6c307cf 100644 --- a/Application/Validation/CreateUserValidator.cs +++ b/Application/Validation/CreateUserValidator.cs @@ -1,6 +1,6 @@ -using Core.Dto.Response; -using Core.Enums; -using FluentValidation; +using FluentValidation; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Enums; namespace Application.Validation { diff --git a/Application/Validation/EmbeddedVideoValidator.cs b/Application/Validation/EmbeddedVideoValidator.cs index aa2ca23..2fd3972 100644 --- a/Application/Validation/EmbeddedVideoValidator.cs +++ b/Application/Validation/EmbeddedVideoValidator.cs @@ -1,10 +1,5 @@ -using Core.Dto.Request; -using FluentValidation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using FluentValidation; +using YorubaOrganization.Core.Dto.Request; namespace Application.Validation { diff --git a/Application/Validation/EtymologyValidator.cs b/Application/Validation/EtymologyValidator.cs index 02371e0..5f42349 100644 --- a/Application/Validation/EtymologyValidator.cs +++ b/Application/Validation/EtymologyValidator.cs @@ -1,10 +1,5 @@ -using Core.Dto.Request; -using FluentValidation; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using FluentValidation; +using YorubaOrganization.Core.Dto.Request; namespace Application.Validation { diff --git a/Application/Validation/GeoLocationValidator.cs b/Application/Validation/GeoLocationValidator.cs index 233e46e..19aa950 100644 --- a/Application/Validation/GeoLocationValidator.cs +++ b/Application/Validation/GeoLocationValidator.cs @@ -1,6 +1,6 @@ -using Core.Dto.Request; -using Core.Repositories; -using FluentValidation; +using FluentValidation; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Repositories; namespace Application.Validation { diff --git a/Application/Validation/UpdateUserValidator.cs b/Application/Validation/UpdateUserValidator.cs index a6e3648..d2599fe 100644 --- a/Application/Validation/UpdateUserValidator.cs +++ b/Application/Validation/UpdateUserValidator.cs @@ -1,6 +1,6 @@ -using Core.Dto.Request; -using Core.Enums; -using FluentValidation; +using FluentValidation; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Enums; namespace Application.Validation { diff --git a/Core/Cache/IRecentIndexesCache.cs b/Core/Cache/IRecentIndexesCache.cs deleted file mode 100644 index f533cd7..0000000 --- a/Core/Cache/IRecentIndexesCache.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Core.Cache -{ - public interface IRecentIndexesCache : ISetBasedCache - { - } -} \ No newline at end of file diff --git a/Core/Cache/IRecentSearchesCache.cs b/Core/Cache/IRecentSearchesCache.cs deleted file mode 100644 index 73d9bd3..0000000 --- a/Core/Cache/IRecentSearchesCache.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Cache -{ - public interface IRecentSearchesCache : ISetBasedCache - { - Task> GetMostPopular(); - } -} \ No newline at end of file diff --git a/Core/Cache/ISetBasedCache.cs b/Core/Cache/ISetBasedCache.cs deleted file mode 100644 index 581739e..0000000 --- a/Core/Cache/ISetBasedCache.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Core.Cache -{ - public interface ISetBasedCache - { - Task> Get(); - Task Stack(T item); - Task Remove(T item); - } -} diff --git a/Core/Cache/ISimpleCache.cs b/Core/Cache/ISimpleCache.cs deleted file mode 100644 index d943fb8..0000000 --- a/Core/Cache/ISimpleCache.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Core.Cache -{ - public interface ISimpleCache - { - Task SetAsync(string key, T value, TimeSpan? expiry = null); - Task GetAsync(string key); - } -} diff --git a/Core/Core.csproj b/Core/Core.csproj index a6b9113..f754c70 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -4,4 +4,7 @@ enable enable + + + \ No newline at end of file diff --git a/Core/Dto/CharacterSeparatedString.cs b/Core/Dto/CharacterSeparatedString.cs deleted file mode 100644 index 57eb98b..0000000 --- a/Core/Dto/CharacterSeparatedString.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace Core.Dto -{ - public abstract class CharacterSeparatedString where T : CharacterSeparatedString - { - protected string SeparatorOut { get; init; } - protected char SeparatorIn { get; init; } - protected readonly string value; - - public CharacterSeparatedString(string? value) - { - this.value = value?.Trim() ?? string.Empty; - } - - public override string ToString() - { - return value; - } - - public static implicit operator CharacterSeparatedString(List list) - { - var anObject = (CharacterSeparatedString) Activator.CreateInstance(typeof(T), string.Empty); - return (CharacterSeparatedString) Activator.CreateInstance(typeof(T), string.Join(anObject.SeparatorOut, list)); - - } - - public static implicit operator List(CharacterSeparatedString charSeparatedString) - { - return charSeparatedString.value - .Split(charSeparatedString.SeparatorIn) - .Select(item => item.Trim()) - .Where(item => !string.IsNullOrEmpty(item)) - .ToList(); - } - } -} diff --git a/Core/Dto/CommaSeparatedString.cs b/Core/Dto/CommaSeparatedString.cs deleted file mode 100644 index a272f4e..0000000 --- a/Core/Dto/CommaSeparatedString.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Core.Dto -{ - public class CommaSeparatedString : CharacterSeparatedString - { - public CommaSeparatedString(string? value) : base(value) - { - SeparatorIn = ','; - SeparatorOut = ", "; - } - } -} diff --git a/Core/Dto/HyphenSeparatedString.cs b/Core/Dto/HyphenSeparatedString.cs deleted file mode 100644 index c3e5426..0000000 --- a/Core/Dto/HyphenSeparatedString.cs +++ /dev/null @@ -1,14 +0,0 @@ - -namespace Core.Dto -{ - public class HyphenSeparatedString : CharacterSeparatedString - { - public HyphenSeparatedString(string? value) : base(value) - { - SeparatorIn = '-'; - SeparatorOut = "-"; - } - - - } -} diff --git a/Core/Dto/Request/CreateGeoLocationDto.cs b/Core/Dto/Request/CreateGeoLocationDto.cs deleted file mode 100644 index e334b9c..0000000 --- a/Core/Dto/Request/CreateGeoLocationDto.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Core.Dto.Request -{ - public record CreateGeoLocationDto(string Place, string Region) - { - } -} diff --git a/Core/Dto/Request/CreateNameDto.cs b/Core/Dto/Request/CreateNameDto.cs index 59cefb8..6bfcecb 100644 --- a/Core/Dto/Request/CreateNameDto.cs +++ b/Core/Dto/Request/CreateNameDto.cs @@ -1,6 +1,4 @@ -using Core.Dto.Request; - -namespace Api.Model.In +namespace Core.Dto.Request { public class CreateNameDto : NameDto { diff --git a/Core/Dto/Request/CreateNameFeedbackDto.cs b/Core/Dto/Request/CreateNameFeedbackDto.cs index 9ee0f0f..d068866 100644 --- a/Core/Dto/Request/CreateNameFeedbackDto.cs +++ b/Core/Dto/Request/CreateNameFeedbackDto.cs @@ -1,6 +1,6 @@ using System.ComponentModel.DataAnnotations; -namespace Api.Model.Request +namespace Core.Dto.Request { public record CreateNameFeedbackDto([Required] string Name, [Required(ErrorMessage = "Cannot give an empty feedback")] string Feedback); } diff --git a/Core/Dto/Request/CreateSuggestedNameDto.cs b/Core/Dto/Request/CreateSuggestedNameDto.cs index de0cb9c..3b72c55 100644 --- a/Core/Dto/Request/CreateSuggestedNameDto.cs +++ b/Core/Dto/Request/CreateSuggestedNameDto.cs @@ -1,4 +1,6 @@ -namespace Core.Dto.Request; +using YorubaOrganization.Core.Dto.Request; + +namespace Core.Dto.Request; public record CreateSuggestedNameDto { diff --git a/Core/Dto/Request/CreateUserDto.cs b/Core/Dto/Request/CreateUserDto.cs deleted file mode 100644 index 93afbab..0000000 --- a/Core/Dto/Request/CreateUserDto.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Core.Dto.Response -{ - public record CreateUserDto - { - public string? Username { get; set; } - public string? Password { get; set; } - public string? Email { get; set; } - - public string[] Roles { get; set; } - - public string? CreatedBy { get; set; } - } -} diff --git a/Core/Dto/Request/EmbeddedVideoDto.cs b/Core/Dto/Request/EmbeddedVideoDto.cs deleted file mode 100644 index 036fbbf..0000000 --- a/Core/Dto/Request/EmbeddedVideoDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Dto.Request -{ - public record EmbeddedVideoDto(string VideoId, string Caption) - { - } -} - diff --git a/Core/Dto/Request/EtymologyDto.cs b/Core/Dto/Request/EtymologyDto.cs deleted file mode 100644 index cb1a815..0000000 --- a/Core/Dto/Request/EtymologyDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Core.Dto.Request -{ - /// - /// TODO: Add field validations - /// - public record EtymologyDto(string Part, string Meaning) - { - } - -} diff --git a/Core/Dto/Request/NameDto.cs b/Core/Dto/Request/NameDto.cs index beb674e..43d8698 100644 --- a/Core/Dto/Request/NameDto.cs +++ b/Core/Dto/Request/NameDto.cs @@ -1,4 +1,7 @@ -using Core.Enums; +using YorubaOrganization.Core.Dto; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Entities.Partials; +using YorubaOrganization.Core.Enums; namespace Core.Dto.Request { @@ -20,6 +23,21 @@ public abstract class NameDto public CommaSeparatedString? Variants { get; set; } + public List VariantsV2 { + get + { + return Variants == null ? [] : ((List)Variants).Select(v => new Variant(v)).ToList(); + } + } + + public List MediaLinks + { + get + { + return Media == null ? [] : ((List)Media).Select(m => new MediaLink(m)).ToList(); + } + } + public CommaSeparatedString? Morphology { get; set; } public CommaSeparatedString? Media { get; set; } diff --git a/Core/Dto/Request/UpdateUserDto.cs b/Core/Dto/Request/UpdateUserDto.cs deleted file mode 100644 index ee762b5..0000000 --- a/Core/Dto/Request/UpdateUserDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Dto.Request -{ - public record UpdateUserDto (string? Username, string? Password, string[]? Roles, string? UpdatedBy) - { - - } -} diff --git a/Core/Dto/Response/FeedbackDto.cs b/Core/Dto/Response/FeedbackDto.cs deleted file mode 100644 index 5aae22a..0000000 --- a/Core/Dto/Response/FeedbackDto.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Core.Dto.Response -{ - public record FeedbackDto(string Id, string Name, string Feedback, DateTime SubmittedAt) - { - } -} diff --git a/Core/Dto/Response/GeoLocationDto.cs b/Core/Dto/Response/GeoLocationDto.cs deleted file mode 100644 index 99441ae..0000000 --- a/Core/Dto/Response/GeoLocationDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Core.Dto.Response -{ - public record GeoLocationDto(string Id, string Place, string Region) - { - public override string ToString() - { - return Place; - } - } -} diff --git a/Core/Dto/Response/NameEntryDto.cs b/Core/Dto/Response/NameEntryDto.cs index 44325d6..b0000e2 100644 --- a/Core/Dto/Response/NameEntryDto.cs +++ b/Core/Dto/Response/NameEntryDto.cs @@ -1,5 +1,7 @@ -using Core.Dto.Request; -using Core.Enums; +using YorubaOrganization.Core.Dto; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Enums; namespace Core.Dto.Response { diff --git a/Core/Dto/Response/NameFeedbackDto.cs b/Core/Dto/Response/NameFeedbackDto.cs new file mode 100644 index 0000000..7e2f0db --- /dev/null +++ b/Core/Dto/Response/NameFeedbackDto.cs @@ -0,0 +1,6 @@ +namespace Core.Dto.Response +{ + public record NameFeedbackDto(string Id, string Name, string Feedback, DateTime SubmittedAt) + { + } +} diff --git a/Core/Dto/Response/RecentStats.cs b/Core/Dto/Response/RecentStats.cs deleted file mode 100644 index 3904200..0000000 --- a/Core/Dto/Response/RecentStats.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Core.Dto.Response -{ - public record RecentStats - { - [JsonPropertyName("search")] - public string[] LatestSearches { get; init; } - - [JsonPropertyName("index")] - public string[] LatestAdditions { get; init; } - - [JsonPropertyName("popular")] - public string[] MostPopular { get; init; } - - public RecentStats() - { - LatestSearches = new string[0]; - LatestAdditions = new string[0]; - MostPopular = new string[0]; - } - } -} diff --git a/Core/Dto/Response/SearchMetadataDto.cs b/Core/Dto/Response/SearchMetadataDto.cs deleted file mode 100644 index 7bb5a51..0000000 --- a/Core/Dto/Response/SearchMetadataDto.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Dto.Response -{ - public class SearchMetadataDto - { - public int TotalPublishedNames { get; set; } - } -} diff --git a/Core/Dto/Response/SuggestedNameDto.cs b/Core/Dto/Response/SuggestedNameDto.cs index c1d1cc8..17d356b 100644 --- a/Core/Dto/Response/SuggestedNameDto.cs +++ b/Core/Dto/Response/SuggestedNameDto.cs @@ -1,4 +1,6 @@ -namespace Core.Dto.Response; +using YorubaOrganization.Core.Dto.Response; + +namespace Core.Dto.Response; public record SuggestedNameDto { diff --git a/Core/Dto/Response/UserDto.cs b/Core/Dto/Response/UserDto.cs deleted file mode 100644 index 6a9c157..0000000 --- a/Core/Dto/Response/UserDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Core.Dto.Response -{ - public record UserDto - { - public string Email { get; set; } - public string? Username { get; set; } - public string[] Roles { get; set; } - } -} diff --git a/Core/Entities/BaseEntity.cs b/Core/Entities/BaseEntity.cs deleted file mode 100644 index 1fd9f47..0000000 --- a/Core/Entities/BaseEntity.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Core.Entities -{ - public abstract class BaseEntity - { - public BaseEntity() - { - CreatedAt = DateTime.UtcNow; - UpdatedAt = CreatedAt; - } - - public string Id { get; set; } - public DateTime CreatedAt { get; set; } - - public string? CreatedBy { get; set; } - public DateTime UpdatedAt { get; set; } - public string? UpdatedBy { get; set; } - } -} diff --git a/Core/Entities/GeoLocation.cs b/Core/Entities/GeoLocation.cs deleted file mode 100644 index f0465f4..0000000 --- a/Core/Entities/GeoLocation.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Core.Entities -{ - public class GeoLocation : BaseEntity - { - public string Place { get; set; } - - // TODO Minor: Get Kola's brief about why this is separated into Place and Region. - // TODO Minor: Is the Region which is not currently used anywhere of any use? - public string Region { get; set; } - - - public GeoLocation() { } - - public GeoLocation(string place, string region) - { - Place = place; - Region = region; - } - } -} \ No newline at end of file diff --git a/Core/Entities/NameEntry.cs b/Core/Entities/NameEntry.cs new file mode 100644 index 0000000..7438491 --- /dev/null +++ b/Core/Entities/NameEntry.cs @@ -0,0 +1,16 @@ +using YorubaOrganization.Core.Entities; + +namespace Core.Entities; + +public class NameEntry : DictionaryEntry +{ + public string Meaning { get; set; } + public string? ExtendedMeaning { get; set; } + public List FamousPeople { get; set; } + + protected override void InitializeLists() + { + base.InitializeLists(); + FamousPeople = []; + } +} \ No newline at end of file diff --git a/Core/Entities/NameEntry/Collections/EmbeddedVideo.cs b/Core/Entities/NameEntry/Collections/EmbeddedVideo.cs deleted file mode 100644 index 98e1183..0000000 --- a/Core/Entities/NameEntry/Collections/EmbeddedVideo.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Core.Entities.NameEntry.Collections; - -public class EmbeddedVideo : BaseEntity -{ - public EmbeddedVideo(string videoId, string caption) - { - VideoId = videoId; - Caption = caption; - } - - public string VideoId { get; set; } - public string Caption { get; set; } - -} diff --git a/Core/Entities/NameEntry/Collections/Etymology.cs b/Core/Entities/NameEntry/Collections/Etymology.cs deleted file mode 100644 index 48e01e8..0000000 --- a/Core/Entities/NameEntry/Collections/Etymology.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Core.Entities.NameEntry.Collections; - -public class Etymology : BaseEntity -{ - public string Part { get; set; } - public string Meaning { get; set; } - - public Etymology(string part, string meaning) - { - Part = part; - Meaning = meaning; - } -} \ No newline at end of file diff --git a/Core/Entities/NameEntry/Collections/Feedback.cs b/Core/Entities/NameEntry/Collections/Feedback.cs deleted file mode 100644 index 65232bf..0000000 --- a/Core/Entities/NameEntry/Collections/Feedback.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Core.Entities.NameEntry.Collections -{ - /// - /// Feedback from anonymous website users. - /// TODO Later: We can capture their name or email address going forward (in the universal SubmittedBy field) - /// - public class Feedback : BaseEntity - { - public string? Content { get; set; } - - public Feedback() { } - - public Feedback(string content) - { - Content = content; - } - } -} diff --git a/Core/Entities/NameEntry/NameEntry.cs b/Core/Entities/NameEntry/NameEntry.cs deleted file mode 100644 index a4f5142..0000000 --- a/Core/Entities/NameEntry/NameEntry.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Core.Entities.NameEntry.Collections; -using Core.Enums; - -namespace Core.Entities.NameEntry; - -public class NameEntry : BaseEntity, IComparable -{ - public string Name { get; set; } - - public string? Pronunciation { get; set; } - // Only 3 values exist in the DB for this field at the moment. However, Kola would like to retain it. - public string? IpaNotation { get; set; } - public string Meaning { get; set; } - public string? ExtendedMeaning { get; set; } - public List Morphology { get; set; } - public List Media { get; set; } - public State State { get; set; } - - // Note: Did not migrate TonalMark, Tags, InOtherLanguages intentionally since all values are null in the database (no admin boxes for them) - public List Etymology { get; set; } - public List Videos { get; set; } - public List GeoLocation { get; set; } - - public List FamousPeople { get; set; } - - public List Syllables { get; set; } - - public List Variants { get; set; } - - /// - /// When the current entry is edited, the edited version will be stored here until it is published - /// - public NameEntry? Modified { get; set; } - - /// - /// An item is added in here if an attempt is made to create a name which already exists. - /// Only adding in this feature because it exists in the Java version. - /// There is no functional application of these objects at the moment. - /// - public List Duplicates { get; set; } - public List Feedbacks { get; set; } - - - private void InitializeLists() - { - Etymology = new List(); - Videos = new List(); - Duplicates = new List(); - Feedbacks = new List(); - - Syllables = new List(); - FamousPeople = new List(); - Variants = new List(); - Morphology = new List(); - Media = new List(); - - GeoLocation = new List(); - } - - public NameEntry(string name, string meaning) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - Meaning = meaning ?? throw new ArgumentNullException(nameof(meaning)); - - InitializeLists(); - } - public NameEntry() - { - InitializeLists(); - } - - public int CompareTo(NameEntry? other) - { - return Name.CompareTo(other?.Name); - } - - public override int GetHashCode() - { - return HashCode.Combine(Name); - } - - public override bool Equals(object? obj) - { - // Standard equality checks - if (obj is not NameEntry) - { - return false; - } - - NameEntry other = (NameEntry)obj; - - return Name == other.Name; - } -} \ No newline at end of file diff --git a/Core/Entities/SuggestedName.cs b/Core/Entities/SuggestedName.cs index 69347d7..cf5d6ed 100644 --- a/Core/Entities/SuggestedName.cs +++ b/Core/Entities/SuggestedName.cs @@ -1,4 +1,6 @@ -namespace Core.Entities +using YorubaOrganization.Core.Entities; + +namespace Core.Entities { public class SuggestedName : BaseEntity { diff --git a/Core/Entities/User.cs b/Core/Entities/User.cs deleted file mode 100644 index ea41ead..0000000 --- a/Core/Entities/User.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Core.Entities -{ - public class User : BaseEntity - { - public string? Email { get; set; } - public string? Username { get; set; } - public string? Password { get; set; } - public List Roles { get; set; } - - public User() - { - Roles = new List(); - } - } -} diff --git a/Core/Enums/Role.cs b/Core/Enums/Role.cs deleted file mode 100644 index b39a49b..0000000 --- a/Core/Enums/Role.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Core.Enums -{ - public enum Role - { - ADMIN, - PRO_LEXICOGRAPHER, - BASIC_LEXICOGRAPHER - } -} diff --git a/Core/Enums/State.cs b/Core/Enums/State.cs deleted file mode 100644 index 580c08e..0000000 --- a/Core/Enums/State.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Core.Enums; - -public enum State -{ - NEW, - UNPUBLISHED, - PUBLISHED, - MODIFIED -} \ No newline at end of file diff --git a/Core/Events/ExactNameSearched.cs b/Core/Events/ExactNameSearched.cs deleted file mode 100644 index 0de0ffb..0000000 --- a/Core/Events/ExactNameSearched.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Events -{ - public record ExactNameSearched(string SearchTerm) - { - - } -} \ No newline at end of file diff --git a/Core/Events/IEventPubService.cs b/Core/Events/IEventPubService.cs deleted file mode 100644 index c323f80..0000000 --- a/Core/Events/IEventPubService.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Events -{ - public interface IEventPubService - { - Task PublishEvent(T theEvent); - } -} diff --git a/Core/Events/NameDeleted.cs b/Core/Events/NameDeleted.cs deleted file mode 100644 index 4816490..0000000 --- a/Core/Events/NameDeleted.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Core.Events; - -public record class NameDeleted(string Name) -{ -} \ No newline at end of file diff --git a/Core/Events/NameEntryNameUpdated.cs b/Core/Events/NameEntryNameUpdated.cs deleted file mode 100644 index 0588536..0000000 --- a/Core/Events/NameEntryNameUpdated.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Core.Events -{ - public record NameEntryNameUpdated(string OriginalName, string NewName) - { - } -} \ No newline at end of file diff --git a/Core/Events/NameIndexed.cs b/Core/Events/NameIndexed.cs index 52f6a65..30cd03c 100644 --- a/Core/Events/NameIndexed.cs +++ b/Core/Events/NameIndexed.cs @@ -1,6 +1,8 @@ -namespace Core.Events +using MediatR; + +namespace Core.Events { - public record NameIndexed(string Name, string Meaning) + public record NameIndexed(string Name, string Meaning) : INotification { } } diff --git a/Core/Events/NonExistingNameUpdateAttempted.cs b/Core/Events/NonExistingNameUpdateAttempted.cs deleted file mode 100644 index a9ae1df..0000000 --- a/Core/Events/NonExistingNameUpdateAttempted.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Core.Events -{ - public record NonExistingNameUpdateAttempted(string Name) - { - } -} \ No newline at end of file diff --git a/Core/Repositories/IEtymologyRepository.cs b/Core/Repositories/IEtymologyRepository.cs deleted file mode 100644 index 16fd445..0000000 --- a/Core/Repositories/IEtymologyRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Repositories -{ - public interface IEtymologyRepository - { - Task> GetLatestMeaningOf(IEnumerable parts); - } -} diff --git a/Core/Repositories/IGeoLocationsRepository.cs b/Core/Repositories/IGeoLocationsRepository.cs deleted file mode 100644 index 3f826ec..0000000 --- a/Core/Repositories/IGeoLocationsRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Core.Entities; - -namespace Core.Repositories -{ - public interface IGeoLocationsRepository - { - Task> GetAll(); - - Task FindByPlace(string place); - Task FindByPlaceAndRegion(string region, string place); - Task Create(GeoLocation geoLocation); - Task Delete(string id, string place); - } -} diff --git a/Core/Repositories/INameEntryFeedbackRepository.cs b/Core/Repositories/INameEntryFeedbackRepository.cs index f2f9876..b66224f 100644 --- a/Core/Repositories/INameEntryFeedbackRepository.cs +++ b/Core/Repositories/INameEntryFeedbackRepository.cs @@ -1,13 +1,14 @@ using Core.Dto.Response; + namespace Core.Repositories { public interface INameEntryFeedbackRepository { - Task> FindAllAsync(); - Task> FindByNameAsync(string name); + Task> FindAllAsync(); + Task> FindByNameAsync(string name); Task AddFeedbackByNameAsync(string name, string feedbackContent); Task DeleteAllFeedbackForNameAsync(string name); - Task GetFeedbackByIdAsync(string feedbackId); + Task GetFeedbackByIdAsync(string feedbackId); Task DeleteFeedbackAsync(string name, string feedbackId); } } diff --git a/Core/Repositories/INameEntryRepository.cs b/Core/Repositories/INameEntryRepository.cs index 29b9346..fbec837 100644 --- a/Core/Repositories/INameEntryRepository.cs +++ b/Core/Repositories/INameEntryRepository.cs @@ -1,54 +1,11 @@ -using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Enums; -using System.Linq.Expressions; +using Core.Entities; +using YorubaOrganization.Core.Enums; +using YorubaOrganization.Core.Repositories; namespace Core.Repositories { - public interface INameEntryRepository + public interface INameEntryRepository : IDictionaryEntryRepository { - Task FindById(string id); - Task Create(NameEntry newName); - Task Create(List newName); - Task DeleteAll(); - - // TODO Later: This method should not be accessible. Too heavy on the DB - Task> ListAll(); - - Task FindByName(string name); - Task> FindByNames(string[] names); - - Task> FindByState(State state); - - Task> FindByNameStartingWithAndState(string alphabet, State state); - - Task> FindByNameStartingWithAnyAndState(IEnumerable searchTerms, State state); - - Task> FindNameEntryByNameContainingAndState(string name, State state); - - Task> FindNameEntryByVariantsContainingAndState(string name, State state); - - Task> FindNameEntryByMeaningContainingAndState(string name, State state); - - Task> FindNameEntryByExtendedMeaningContainingAndState(string name, State state); - - Task FindByNameAndState(string name, State state); - - Task CountByState(State state); - - Task Delete(string name); - - Task DeleteMany(string[] name); - - Task DeleteByNameAndState(string name, State state); - - Task Update(string originalName, NameEntry newEntry); - - Task CountWhere(Expression> filter); - - Task> List(int pageNumber, int pageSize, State? state, string? submittedBy); - - Task GetMetadata(); - Task> GetAllNames(State? state, string? submittedBy); + Task> FindEntryByExtendedMeaningContainingAndState(string title, State state); } } diff --git a/Core/Repositories/IUserRepository.cs b/Core/Repositories/IUserRepository.cs deleted file mode 100644 index 4864243..0000000 --- a/Core/Repositories/IUserRepository.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Core.Dto.Request; -using Core.Dto.Response; -using Core.Entities; - -namespace Core.Repositories -{ - public interface IUserRepository - { - Task GetUserByEmail(string email); - Task Create(User newUser); - Task DeleteBy(string email); - Task Update(string email, UpdateUserDto update); - Task> List(); - Task CountUsers(); - } -} diff --git a/Core/StringObjectConverters/CharacterSeparatedStringConverter.cs b/Core/StringObjectConverters/CharacterSeparatedStringConverter.cs index 93a57c1..972aa88 100644 --- a/Core/StringObjectConverters/CharacterSeparatedStringConverter.cs +++ b/Core/StringObjectConverters/CharacterSeparatedStringConverter.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; using System.Text.Json; -using Core.Dto; +using YorubaOrganization.Core.Dto; namespace Core.StringObjectConverters { diff --git a/Core/StringObjectConverters/CommaSeparatedStringConverter.cs b/Core/StringObjectConverters/CommaSeparatedStringConverter.cs index 6f5db6a..be6bb02 100644 --- a/Core/StringObjectConverters/CommaSeparatedStringConverter.cs +++ b/Core/StringObjectConverters/CommaSeparatedStringConverter.cs @@ -1,4 +1,4 @@ -using Core.Dto; +using YorubaOrganization.Core.Dto; namespace Core.StringObjectConverters { diff --git a/Core/StringObjectConverters/HyphenSeparatedStringConverter.cs b/Core/StringObjectConverters/HyphenSeparatedStringConverter.cs index 27d0405..cb64d5f 100644 --- a/Core/StringObjectConverters/HyphenSeparatedStringConverter.cs +++ b/Core/StringObjectConverters/HyphenSeparatedStringConverter.cs @@ -1,4 +1,4 @@ -using Core.Dto; +using YorubaOrganization.Core.Dto; namespace Core.StringObjectConverters { diff --git a/Core/Utilities/DiacriticsNormalizer.cs b/Core/Utilities/DiacriticsNormalizer.cs deleted file mode 100644 index 5fb35ed..0000000 --- a/Core/Utilities/DiacriticsNormalizer.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Globalization; -using System.Text; -using System.Text.RegularExpressions; - -namespace Core.Utilities -{ - public static class DiacriticsNormalizer - { - const char a = 'a', e = 'e', i = 'i', o = 'o', u = 'u'; - static readonly Dictionary vowelMap = new() - { - { 'á', a }, - { 'à', a }, - { 'a', a }, - { 'é', e }, - { 'è', e }, - { 'e', e }, - { 'ẹ', e }, - { 'í', i }, - { 'ì', i }, - { 'i', i }, - { 'ó', o }, - { 'ò', o }, - { 'o', o }, - { 'ọ', o }, - { 'ú', u }, - { 'ù', u }, - { 'u', u } - }; - - - static readonly Dictionary vowelPatterns = new() - { - // "\\p{Mn}?" means zero or one nonspacing unicode mark. It could occur after any vowel. - { 'a', "[aáà]\\p{Mn}?" }, - { 'e', "[eéèẹ]\\p{Mn}?" }, - { 'i', "[iíì]\\p{Mn}?" }, - { 'o', "[oóòọ]\\p{Mn}?" }, - { 'u', "[uúù]\\p{Mn}?" } - }; - - private static string RemoveDiacriticsAndSimplify(this string text) - { - var decomposed = text.Normalize(NormalizationForm.FormD); - var stringBuilder = new StringBuilder(); - - foreach (var c in decomposed) - { - if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark) - { - if (vowelMap.TryGetValue(c, out var simpleVowel)) - { - stringBuilder.Append(simpleVowel); - } - else - { - stringBuilder.Append(c); - } - } - } - - return stringBuilder.ToString().Normalize(NormalizationForm.FormC); - } - - public static string ReplaceYorubaVowelsWithPattern(this string term) - { - term = RemoveDiacriticsAndSimplify(term); - - return string.Concat(term.Select(c => - vowelPatterns.TryGetValue(c, out var pattern) ? pattern : Regex.Escape(c.ToString()) - )); - } - } -} diff --git a/Infrastructure.MongoDB/Infrastructure.MongoDB.csproj b/Infrastructure.MongoDB/Infrastructure.MongoDB.csproj deleted file mode 100644 index fedebeb..0000000 --- a/Infrastructure.MongoDB/Infrastructure.MongoDB.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - net8.0 - enable - enable - - - - - - - - - \ No newline at end of file diff --git a/Infrastructure.MongoDB/Repositories/MongoDBRepository.cs b/Infrastructure.MongoDB/Repositories/MongoDBRepository.cs deleted file mode 100644 index 6fae029..0000000 --- a/Infrastructure.MongoDB/Repositories/MongoDBRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MongoDB.Driver; - -namespace Infrastructure.MongoDB.Repositories -{ - public abstract class MongoDBRepository - { - protected static T SetCollationPrimary(dynamic dbCommandOption) - { - dbCommandOption.Collation = new Collation("en", strength: CollationStrength.Primary); - return (T)dbCommandOption; - } - protected static T SetCollationSecondary(dynamic dbCommandOption) - { - dbCommandOption.Collation = new Collation("en", strength: CollationStrength.Secondary); - return (T)dbCommandOption; - } - } -} \ No newline at end of file diff --git a/Infrastructure.MongoDB/Repositories/NameEntryRepository.cs b/Infrastructure.MongoDB/Repositories/NameEntryRepository.cs deleted file mode 100644 index 73b5ee1..0000000 --- a/Infrastructure.MongoDB/Repositories/NameEntryRepository.cs +++ /dev/null @@ -1,316 +0,0 @@ -using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Enums; -using Core.Events; -using Core.Repositories; -using Core.Utilities; -using MongoDB.Bson; -using MongoDB.Driver; -using System.Linq.Expressions; - -namespace Infrastructure.MongoDB.Repositories; - -public class NameEntryRepository : MongoDBRepository, INameEntryRepository -{ - private readonly IMongoCollection _nameEntryCollection; - private readonly IEventPubService _eventPubService; - - public NameEntryRepository( - IMongoDatabase database, - IEventPubService eventPubService) - { - _nameEntryCollection = database.GetCollection("NameEntries"); - _eventPubService = eventPubService; - - CreateIndexes(); - } - - private void CreateIndexes() - { - var indexKeys = Builders.IndexKeys.Ascending(x => x.Name); - var indexOptions = new CreateIndexOptions - { - Unique = true, - Name = "IX_NameEntries_Name_Unique", - Background = true - }; - _nameEntryCollection.Indexes.CreateOne(new CreateIndexModel(indexKeys, indexOptions)); - } - - public async Task FindById(string id) - { - return await _nameEntryCollection.Find(x => x.Id == id).SingleOrDefaultAsync(); - } - - public async Task DeleteAll() - { - var deleteResult = await _nameEntryCollection.DeleteManyAsync(FilterDefinition.Empty); - return deleteResult.DeletedCount > 0; - } - - - public async Task Create(NameEntry entry) - { - entry.Id = ObjectId.GenerateNewId().ToString(); - await _nameEntryCollection.InsertOneAsync(entry); - } - - public async Task Create(List entries) - { - entries.ForEach(entry => entry.Id = ObjectId.GenerateNewId().ToString()!); - await _nameEntryCollection.InsertManyAsync(entries); - } - - public async Task CountByState(State state) - { - return await CountWhere(ne => ne.State == state); - } - - public async Task Delete(string name) - { - var filter = Builders.Filter.Eq("Name", name); - var options = SetCollationPrimary(new DeleteOptions()); - - await _nameEntryCollection.DeleteOneAsync(filter, options); - } - public async Task DeleteMany(string[] names) - { - var filter = Builders.Filter.In("Name", names); - var options = SetCollationPrimary(new DeleteOptions()); - - await _nameEntryCollection.DeleteManyAsync(filter, options); - } - - public async Task DeleteByNameAndState(string name, State state) - { - var filter = Builders - .Filter - .And(Builders.Filter.Eq("Name", name), Builders.Filter.Eq("State", state)); - - var options = SetCollationPrimary(new DeleteOptions()); - - var deleteResult = await _nameEntryCollection.DeleteOneAsync(filter, options); - - return deleteResult.DeletedCount > 0; - } - - // TODO Hafiz (Later): This is pulling too much data. We should eventually get rid of it. - public async Task> ListAll() - { - var allEntries = await _nameEntryCollection.Find(_ => true).ToListAsync(); - return new HashSet(allEntries); - } - - public async Task FindByName(string name) - { - var filter = Builders.Filter.Eq("Name", name); - var options = SetCollationPrimary(new FindOptions()); - - return await _nameEntryCollection.Find(filter, options).SingleOrDefaultAsync(); - } - - public async Task> FindByNames(string[] names) - { - var filter = Builders.Filter.In("Name", names); - var options = SetCollationPrimary(new FindOptions()); - - return await _nameEntryCollection.Find(filter, options).ToListAsync(); - } - - public async Task FindByNameAndState(string name, State state) - { - var options = SetCollationPrimary(new FindOptions()); - return await _nameEntryCollection - .Find(ne => ne.Name == name && ne.State == state, options) - .SingleOrDefaultAsync(); - } - - public async Task> FindByNameStartingWithAndState(string searchTerm, State state) - { - return await FindByNameStartingWithAnyAndState(new string[] { searchTerm }, state); - } - - public async Task> FindByNameStartingWithAnyAndState(IEnumerable searchTerms, State state) - { - var regexFilters = searchTerms.Select(term => - { - return Builders.Filter.Regex(ne => ne.Name, - new BsonRegularExpression($"^{term.ReplaceYorubaVowelsWithPattern()}", "i")); - }); - - var namesFilter = Builders.Filter.Or(regexFilters); - var stateFilter = Builders.Filter.Eq(ne => ne.State, state); - var combinedFilter = Builders.Filter.And(namesFilter, stateFilter); - - var result = await _nameEntryCollection.Find(combinedFilter).ToListAsync(); - return new HashSet(result); - } - - - public async Task> FindByState(State state) - { - return await _nameEntryCollection.Find(ne => ne.State == state).ToListAsync(); - } - - public async Task> FindNameEntryByExtendedMeaningContainingAndState(string name, State state) - { - var filter = Builders.Filter.Regex(ne => ne.ExtendedMeaning, - new BsonRegularExpression(name.ReplaceYorubaVowelsWithPattern(), "i")) & - Builders.Filter.Eq(ne => ne.State, state); - var result = await _nameEntryCollection.Find(filter).ToListAsync(); - return new HashSet(result); - } - - public async Task> FindNameEntryByMeaningContainingAndState(string name, State state) - { - var filter = Builders.Filter.Regex(ne => ne.Name, - new BsonRegularExpression(name.ReplaceYorubaVowelsWithPattern(), "i")) - & Builders.Filter.Eq(ne => ne.State, state); - var result = await _nameEntryCollection.Find(filter).ToListAsync(); - return new HashSet(result); - } - - public async Task> FindNameEntryByNameContainingAndState(string name, State state) - { - var filter = Builders.Filter.Regex(ne => ne.Name, - new BsonRegularExpression(name.ReplaceYorubaVowelsWithPattern(), "i")) & - Builders.Filter.Eq(ne => ne.State, state); - var result = await _nameEntryCollection.Find(filter).ToListAsync(); - return new HashSet(result); - } - - public async Task> FindNameEntryByVariantsContainingAndState(string name, State state) - { - var regex = new BsonRegularExpression(name.ReplaceYorubaVowelsWithPattern(), "i"); - var filter = Builders.Filter.Regex(ne => ne.Variants, regex) & Builders.Filter.Eq(ne => ne.State, state); - var result = await _nameEntryCollection.Find(filter).ToListAsync(); - return new HashSet(result); - } - - public async Task Update(string originalName, NameEntry newEntry) - { - var filter = Builders.Filter.Eq(ne => ne.Name, originalName); - var updateStatement = GenerateUpdateStatement(newEntry); - - var options = new FindOneAndUpdateOptions - { - ReturnDocument = ReturnDocument.After - }; - - var updated = await _nameEntryCollection.FindOneAndUpdateAsync(filter, updateStatement, options); - - if (updated == null) - { - await _eventPubService.PublishEvent(new NonExistingNameUpdateAttempted(originalName)); - } - else if (originalName != newEntry.Name) - { - await _eventPubService.PublishEvent(new NameEntryNameUpdated(originalName, newEntry.Name)); - } - - return updated; - } - - public async Task CountWhere(Expression> filter) - { - var count = await _nameEntryCollection.CountDocumentsAsync(filter); - return (int)count; - } - - public async Task> GetAllNames(State? state, string? submittedBy) - { - var filterBuilder = Builders.Filter; - var filter = filterBuilder.Empty; - - if (state.HasValue) - { - filter &= filterBuilder.Eq(ne => ne.State, state.Value); - } - - if (!string.IsNullOrWhiteSpace(submittedBy)) - { - filter &= filterBuilder.Eq(ne => ne.CreatedBy, submittedBy.Trim()); - } - - var projection = Builders.Projection.Include(ne => ne.Name).Exclude(ne => ne.Id); - - var names = await _nameEntryCollection - .Find(filter) - .Project(projection) - .Sort(Builders.Sort.Ascending(ne => ne.CreatedAt)) - .ToListAsync(); - - return names; - } - - public async Task> List(int pageNumber, int pageSize, State? state, string? submittedBy) - { - var filterBuilder = Builders.Filter; - var filter = filterBuilder.Empty; - - if (state.HasValue) - { - filter &= filterBuilder.Eq(ne => ne.State, state.Value); - } - - if (!string.IsNullOrWhiteSpace(submittedBy)) - { - filter &= filterBuilder.Eq(ne => ne.CreatedBy, submittedBy.Trim()); - } - - int skip = (pageNumber - 1) * pageSize; - var names = await _nameEntryCollection - .Find(filter) - .Skip(skip) - .Limit(pageSize) - .Sort(Builders.Sort.Ascending(ne => ne.CreatedAt)) - .ToListAsync(); - return names; - } - - public async Task GetMetadata() - { - return new NamesMetadataDto - { - TotalNames = await _nameEntryCollection.CountDocumentsAsync(FilterDefinition.Empty), - TotalNewNames = await _nameEntryCollection.CountDocumentsAsync(Builders.Filter.Eq(x => x.State, State.NEW)), - TotalModifiedNames = await _nameEntryCollection.CountDocumentsAsync(Builders.Filter.Eq(x => x.State, State.MODIFIED)), - TotalPublishedNames = await _nameEntryCollection.CountDocumentsAsync(Builders.Filter.Eq(x => x.State, State.PUBLISHED)) - }; - } - - private static UpdateDefinition GenerateUpdateStatement(NameEntry newEntry) - { - var statement = Builders.Update - .Set(ne => ne.Name, newEntry.Name) - .Set(ne => ne.State, newEntry.State) - .Set(ne => ne.Pronunciation, newEntry.Pronunciation) - .Set(ne => ne.IpaNotation, newEntry.IpaNotation) - .Set(ne => ne.Meaning, newEntry.Meaning) - .Set(ne => ne.ExtendedMeaning, newEntry.ExtendedMeaning) - .Set(ne => ne.Morphology, newEntry.Morphology) - .Set(ne => ne.Media, newEntry.Media) - .Set(ne => ne.State, newEntry.State) - .Set(ne => ne.Etymology, newEntry.Etymology) - .Set(ne => ne.Videos, newEntry.Videos) - .Set(ne => ne.GeoLocation, newEntry.GeoLocation) - .Set(ne => ne.FamousPeople, newEntry.FamousPeople) - .Set(ne => ne.Syllables, newEntry.Syllables) - .Set(ne => ne.Variants, newEntry.Variants) - .Set(ne => ne.Modified, newEntry.Modified) - .Set(ne => ne.Duplicates, newEntry.Duplicates) - .CurrentDate(ne => ne.UpdatedAt); - - if (!string.IsNullOrWhiteSpace(newEntry.UpdatedBy)) - { - statement = statement.Set(ne => ne.UpdatedBy, newEntry.UpdatedBy); - } - - if (newEntry.Duplicates.Any()) - { - statement = statement.Set(ne => ne.Duplicates, newEntry.Duplicates); - } - - return statement; - } -} \ No newline at end of file diff --git a/Infrastructure/Hangfire/DependencyInjection.cs b/Infrastructure/Hangfire/DependencyInjection.cs index 499775c..5f9bbfe 100644 --- a/Infrastructure/Hangfire/DependencyInjection.cs +++ b/Infrastructure/Hangfire/DependencyInjection.cs @@ -5,7 +5,6 @@ using Hangfire.Mongo.Migration.Strategies.Backup; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Builder; -using Api.Utilities; using Ardalis.GuardClauses; namespace Infrastructure.Hangfire diff --git a/Infrastructure/Hangfire/HangfireAuthFilter.cs b/Infrastructure/Hangfire/HangfireAuthFilter.cs index ad7fd43..3d99aeb 100644 --- a/Infrastructure/Hangfire/HangfireAuthFilter.cs +++ b/Infrastructure/Hangfire/HangfireAuthFilter.cs @@ -1,6 +1,6 @@ using Hangfire.Dashboard; -namespace Api.Utilities +namespace Infrastructure.Hangfire { public class HangfireAuthFilter : IDashboardAuthorizationFilter { diff --git a/Infrastructure/Infrastructure.csproj b/Infrastructure/Infrastructure.csproj index 6b0bce6..57f13f0 100644 --- a/Infrastructure/Infrastructure.csproj +++ b/Infrastructure/Infrastructure.csproj @@ -7,14 +7,16 @@ - - - + + + - + + + diff --git a/Infrastructure.MongoDB/DependencyInjection.cs b/Infrastructure/MongoDB/DependencyInjection.cs similarity index 96% rename from Infrastructure.MongoDB/DependencyInjection.cs rename to Infrastructure/MongoDB/DependencyInjection.cs index 58b8204..b64da57 100644 --- a/Infrastructure.MongoDB/DependencyInjection.cs +++ b/Infrastructure/MongoDB/DependencyInjection.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Core.Repositories; using Infrastructure.MongoDB.Repositories; +using YorubaOrganization.Core.Repositories; namespace Infrastructure.MongoDB { diff --git a/Infrastructure.MongoDB/Repositories/EtymologyRepository.cs b/Infrastructure/MongoDB/Repositories/EtymologyRepository.cs similarity index 86% rename from Infrastructure.MongoDB/Repositories/EtymologyRepository.cs rename to Infrastructure/MongoDB/Repositories/EtymologyRepository.cs index d47a1df..579e725 100644 --- a/Infrastructure.MongoDB/Repositories/EtymologyRepository.cs +++ b/Infrastructure/MongoDB/Repositories/EtymologyRepository.cs @@ -1,8 +1,9 @@ -using Core.Entities.NameEntry; -using Core.Entities.NameEntry.Collections; -using Core.Events; -using Core.Repositories; +using Core.Entities; using MongoDB.Driver; +using YorubaOrganization.Core.Entities.Partials; +using YorubaOrganization.Core.Events; +using YorubaOrganization.Core.Repositories; +using YorubaOrganization.Infrastructure.Repositories; namespace Infrastructure.MongoDB.Repositories; diff --git a/Infrastructure.MongoDB/Repositories/GeoLocationsRepository.cs b/Infrastructure/MongoDB/Repositories/GeoLocationsRepository.cs similarity index 97% rename from Infrastructure.MongoDB/Repositories/GeoLocationsRepository.cs rename to Infrastructure/MongoDB/Repositories/GeoLocationsRepository.cs index c50ccc5..ef5aa04 100644 --- a/Infrastructure.MongoDB/Repositories/GeoLocationsRepository.cs +++ b/Infrastructure/MongoDB/Repositories/GeoLocationsRepository.cs @@ -1,7 +1,8 @@ -using Core.Entities; -using Core.Repositories; -using MongoDB.Bson; +using MongoDB.Bson; using MongoDB.Driver; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Repositories; +using YorubaOrganization.Infrastructure.Repositories; namespace Infrastructure.MongoDB.Repositories { diff --git a/Infrastructure.MongoDB/Repositories/NameEntryFeedbackRepository.cs b/Infrastructure/MongoDB/Repositories/NameEntryFeedbackRepository.cs similarity index 63% rename from Infrastructure.MongoDB/Repositories/NameEntryFeedbackRepository.cs rename to Infrastructure/MongoDB/Repositories/NameEntryFeedbackRepository.cs index 169710f..17c0618 100644 --- a/Infrastructure.MongoDB/Repositories/NameEntryFeedbackRepository.cs +++ b/Infrastructure/MongoDB/Repositories/NameEntryFeedbackRepository.cs @@ -1,9 +1,9 @@ using Core.Dto.Response; -using Core.Entities.NameEntry; -using Core.Entities.NameEntry.Collections; +using Core.Entities; using Core.Repositories; using MongoDB.Bson; using MongoDB.Driver; +using YorubaOrganization.Core.Entities.Partials; namespace Infrastructure.MongoDB.Repositories; @@ -16,44 +16,45 @@ public NameEntryFeedbackRepository(IMongoDatabase database) _nameEntryCollection = database.GetCollection("NameEntries"); } - public async Task> FindAllAsync() + public async Task> FindAllAsync() { + var feedbacksFieldName = nameof(NameEntry.Feedbacks); var pipeline = new BsonDocument[] { - new BsonDocument("$unwind", "$Feedbacks"), - new BsonDocument("$project", new BsonDocument + new("$unwind", $"${feedbacksFieldName}"), + new("$project", new BsonDocument { - { "Name", "$Name" }, - { "Feedback", "$Feedbacks.Content" }, - { "SubmittedAt", "$Feedbacks.CreatedAt" }, - { "_id", "$Feedbacks._id" } + { "Name", $"${nameof(NameEntry.Title)}" }, + { "Feedback", $"${feedbacksFieldName}.{nameof(Feedback.Content)}" }, + { $"{nameof(NameFeedbackDto.SubmittedAt)}", $"${feedbacksFieldName}.{nameof(Feedback.CreatedAt)}" }, + { "_id", $"${feedbacksFieldName}._id" } }), - new BsonDocument("$sort", new BsonDocument("SubmittedAt", -1)) + new("$sort", new BsonDocument($"{nameof(NameFeedbackDto.SubmittedAt)}", -1)) }; var feedbacks = await _nameEntryCollection - .Aggregate(pipeline) + .Aggregate(pipeline) .ToListAsync(); return feedbacks; } - public async Task> FindByNameAsync(string name) + public async Task> FindByNameAsync(string name) { var theName = await _nameEntryCollection - .Find(entry => entry.Name.ToLower() == name.ToLower()) + .Find(entry => entry.Title.ToLower() == name.ToLower()) .SingleOrDefaultAsync(); - var feedbacksForName = theName?.Feedbacks?.Select(f => new FeedbackDto(f.Id, theName.Name, f.Content!, f.CreatedAt)) + var feedbacksForName = theName?.Feedbacks?.Select(f => new NameFeedbackDto(f.Id, theName.Title, f.Content!, f.CreatedAt)) .OrderByDescending(x => x.SubmittedAt) .ToList(); - return feedbacksForName ?? new List(); + return feedbacksForName ?? new List(); } public async Task AddFeedbackByNameAsync(string name, string feedbackContent) { - var filter = Builders.Filter.Where(x => x.Name.ToLower() == name.ToLower()); + var filter = Builders.Filter.Where(x => x.Title.ToLower() == name.ToLower()); var nameFeedback = new Feedback { @@ -69,7 +70,7 @@ public async Task AddFeedbackByNameAsync(string name, string feedbackContent) public async Task DeleteAllFeedbackForNameAsync(string name) { - var filter = Builders.Filter.Where(x => x.Name.ToLower() == name.ToLower()); + var filter = Builders.Filter.Where(x => x.Title.ToLower() == name.ToLower()); var update = Builders.Update.Set(entry => entry.Feedbacks, new List()); await _nameEntryCollection.UpdateOneAsync(filter, update); @@ -77,7 +78,7 @@ public async Task DeleteAllFeedbackForNameAsync(string name) public async Task DeleteFeedbackAsync(string name, string feedbackId) { - var filter = Builders.Filter.Where(x => x.Name.ToLower() == name.ToLower()); + var filter = Builders.Filter.Where(x => x.Title.ToLower() == name.ToLower()); var entry = await _nameEntryCollection.Find(filter).FirstOrDefaultAsync(); if (entry != null && entry.Feedbacks.Any(feedback => feedback.Id == feedbackId)) @@ -91,12 +92,12 @@ public async Task DeleteFeedbackAsync(string name, string feedbackId) return false; } - public async Task GetFeedbackByIdAsync(string feedbackId) + public async Task GetFeedbackByIdAsync(string feedbackId) { var filter = Builders.Filter.ElemMatch(entry => entry.Feedbacks, feedback => feedback.Id == feedbackId); var projectionDefinition = Builders .Projection - .Include(entry => entry.Name) + .Include(entry => entry.Title) .ElemMatch(entry => entry.Feedbacks, feedback => feedback.Id == feedbackId); var nameEntry = await _nameEntryCollection.Find(filter) @@ -104,7 +105,7 @@ public async Task DeleteFeedbackAsync(string name, string feedbackId) .FirstOrDefaultAsync(); var theMatch = nameEntry?.Feedbacks?.FirstOrDefault(feedback => feedback.Id == feedbackId); - return theMatch == null ? null : new FeedbackDto(theMatch.Id, nameEntry!.Name, theMatch!.Content!, theMatch.CreatedAt); + return theMatch == null ? null : new NameFeedbackDto(theMatch.Id, nameEntry!.Title, theMatch!.Content!, theMatch.CreatedAt); } } diff --git a/Infrastructure/MongoDB/Repositories/NameEntryRepository.cs b/Infrastructure/MongoDB/Repositories/NameEntryRepository.cs new file mode 100644 index 0000000..7d29146 --- /dev/null +++ b/Infrastructure/MongoDB/Repositories/NameEntryRepository.cs @@ -0,0 +1,34 @@ +using Core.Entities; +using Core.Repositories; +using MongoDB.Bson; +using MongoDB.Driver; +using YorubaOrganization.Core.Enums; +using YorubaOrganization.Core.Events; +using YorubaOrganization.Core.Utilities; +using YorubaOrganization.Infrastructure.Repositories; + +namespace Infrastructure.MongoDB.Repositories; + +public sealed class NameEntryRepository : DictionaryEntryRepository, INameEntryRepository +{ + private const string CollectionName = "NameEntries"; + public NameEntryRepository( + IMongoDatabase database, + IEventPubService eventPubService) : base(CollectionName, database, eventPubService) + { + } + + public async Task> FindEntryByExtendedMeaningContainingAndState(string name, State state) + { + var filter = Builders.Filter.Regex(ne => ne.ExtendedMeaning, + new BsonRegularExpression(name.ReplaceYorubaVowelsWithPattern(), "i")) & + Builders.Filter.Eq(ne => ne.State, state); + var result = await EntryCollection.Find(filter).ToListAsync(); + return new HashSet(result); + } + + protected override UpdateDefinition GenerateCustomUpdateStatement(NameEntry newEntry) => Builders.Update + .Set(ne => ne.Meaning, newEntry.Meaning) + .Set(ne => ne.ExtendedMeaning, newEntry.ExtendedMeaning) + .Set(ne => ne.FamousPeople, newEntry.FamousPeople); +} \ No newline at end of file diff --git a/Infrastructure.MongoDB/Repositories/SuggestedNameRepository.cs b/Infrastructure/MongoDB/Repositories/SuggestedNameRepository.cs similarity index 99% rename from Infrastructure.MongoDB/Repositories/SuggestedNameRepository.cs rename to Infrastructure/MongoDB/Repositories/SuggestedNameRepository.cs index b616a2a..664efa9 100644 --- a/Infrastructure.MongoDB/Repositories/SuggestedNameRepository.cs +++ b/Infrastructure/MongoDB/Repositories/SuggestedNameRepository.cs @@ -1,6 +1,5 @@ using Core.Entities; using Core.Repositories; -using MongoDB.Bson; using MongoDB.Driver; namespace Infrastructure.MongoDB.Repositories; diff --git a/Infrastructure.MongoDB/Repositories/UserRepository.cs b/Infrastructure/MongoDB/Repositories/UserRepository.cs similarity index 93% rename from Infrastructure.MongoDB/Repositories/UserRepository.cs rename to Infrastructure/MongoDB/Repositories/UserRepository.cs index 0fc0409..2f174d7 100644 --- a/Infrastructure.MongoDB/Repositories/UserRepository.cs +++ b/Infrastructure/MongoDB/Repositories/UserRepository.cs @@ -1,9 +1,10 @@ -using Core.Dto.Request; -using Core.Dto.Response; -using Core.Entities; -using Core.Repositories; -using MongoDB.Bson; +using MongoDB.Bson; using MongoDB.Driver; +using YorubaOrganization.Core.Dto.Request; +using YorubaOrganization.Core.Dto.Response; +using YorubaOrganization.Core.Entities; +using YorubaOrganization.Core.Repositories; +using YorubaOrganization.Infrastructure.Repositories; namespace Infrastructure.MongoDB.Repositories { diff --git a/Infrastructure/Redis/DependencyInjection.cs b/Infrastructure/Redis/DependencyInjection.cs index 2e7c906..d67c2dd 100644 --- a/Infrastructure/Redis/DependencyInjection.cs +++ b/Infrastructure/Redis/DependencyInjection.cs @@ -1,9 +1,9 @@ using Ardalis.GuardClauses; -using Core.Cache; using Infrastructure.Configuration; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using StackExchange.Redis; +using YorubaOrganization.Core.Cache; namespace Infrastructure.Redis { diff --git a/Infrastructure/Redis/RedisRecentIndexesCache.cs b/Infrastructure/Redis/RedisRecentIndexesCache.cs index 755fee1..0f502c1 100644 --- a/Infrastructure/Redis/RedisRecentIndexesCache.cs +++ b/Infrastructure/Redis/RedisRecentIndexesCache.cs @@ -1,7 +1,7 @@ -using Core.Cache; -using Infrastructure.Configuration; +using Infrastructure.Configuration; using Microsoft.Extensions.Options; using StackExchange.Redis; +using YorubaOrganization.Core.Cache; namespace Infrastructure.Redis { diff --git a/Infrastructure/Redis/RedisRecentSearchesCache.cs b/Infrastructure/Redis/RedisRecentSearchesCache.cs index e34ad2b..f22a510 100644 --- a/Infrastructure/Redis/RedisRecentSearchesCache.cs +++ b/Infrastructure/Redis/RedisRecentSearchesCache.cs @@ -1,7 +1,7 @@ -using Core.Cache; -using Infrastructure.Configuration; +using Infrastructure.Configuration; using Microsoft.Extensions.Options; using StackExchange.Redis; +using YorubaOrganization.Core.Cache; namespace Infrastructure.Redis { diff --git a/Infrastructure/Redis/SimpleRedisCache.cs b/Infrastructure/Redis/SimpleRedisCache.cs index e1e3cf1..7e86230 100644 --- a/Infrastructure/Redis/SimpleRedisCache.cs +++ b/Infrastructure/Redis/SimpleRedisCache.cs @@ -1,8 +1,8 @@ using Ardalis.GuardClauses; -using Core.Cache; using Infrastructure.Configuration; using Microsoft.Extensions.Options; using StackExchange.Redis; +using YorubaOrganization.Core.Cache; namespace Infrastructure.Redis { public class SimpleRedisCache(IConnectionMultiplexer connectionMultiplexer, IOptions redisConfig) : diff --git a/Infrastructure/TweetsV2Poster.cs b/Infrastructure/TweetsV2Poster.cs index 7d6637a..01b2de6 100644 --- a/Infrastructure/TweetsV2Poster.cs +++ b/Infrastructure/TweetsV2Poster.cs @@ -1,5 +1,4 @@ using System.Text; -using Tweetinvi.Core.Web; using Tweetinvi.Models; using Tweetinvi; using Newtonsoft.Json; diff --git a/Infrastructure/Twitter/TwitterService.cs b/Infrastructure/Twitter/TwitterService.cs index 0677d41..6159e0d 100644 --- a/Infrastructure/Twitter/TwitterService.cs +++ b/Infrastructure/Twitter/TwitterService.cs @@ -4,7 +4,7 @@ using System.Diagnostics; using Hangfire; using Infrastructure.Configuration; -using Core.Cache; +using YorubaOrganization.Core.Cache; namespace Infrastructure.Twitter { diff --git a/README.md b/README.md index f528172..364ba9d 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ To run the Website and API locally, follow these steps: 5. **Access the Application:** - Once the Docker containers are up and running, you can access the Website in your browser. The website should launch automatically as soon as all the containers are ready. - If the Website does not launch automatically, it will be running at `http://localhost:{port}` (the actual port will be displayed in the output window of Visual Studio: "Container Tools"). - - The API will also be running locally and accessible via a different URL. You can see its documentation and test the endpoints at `http://localhost:51515/swagger`. + - The API will also be running locally and accessible via a different URL. You can see its documentation and test the endpoints at `http://localhost:9001/swagger`. - You can login to the API with any username from [the database initialization script](./mongo-init.js) and password: **Password@135**. ## Contributing diff --git a/Test/Integration/NameController/Data/NamesAllTestData.cs b/Test/Integration/NameController/Data/NamesAllTestData.cs index 0e87dbf..7cd2bf8 100644 --- a/Test/Integration/NameController/Data/NamesAllTestData.cs +++ b/Test/Integration/NameController/Data/NamesAllTestData.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; diff --git a/Test/Integration/NameController/Data/NamesCountAndSubmittedByTestData.cs b/Test/Integration/NameController/Data/NamesCountAndSubmittedByTestData.cs index e3ea1b4..f918fcf 100644 --- a/Test/Integration/NameController/Data/NamesCountAndSubmittedByTestData.cs +++ b/Test/Integration/NameController/Data/NamesCountAndSubmittedByTestData.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; using AutoFixture; -using Core.Entities.NameEntry; +using Core.Entities; namespace Test.Integration.NameController.Data { diff --git a/Test/Integration/NameController/Data/NamesCountTestData.cs b/Test/Integration/NameController/Data/NamesCountTestData.cs index 77b1d3b..61da9d7 100644 --- a/Test/Integration/NameController/Data/NamesCountTestData.cs +++ b/Test/Integration/NameController/Data/NamesCountTestData.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; public class NamesCountTestData : IEnumerable @@ -20,7 +20,7 @@ public NamesCountTestData() .With(ne => ne.State, State.PUBLISHED) .With(ne => ne.Modified, (NameEntry?)default) .With(ne => ne.Duplicates, []) - .Do(ne => ne.Name = GetNextName())); + .Do(ne => ne.Title = GetNextName())); } public IEnumerator GetEnumerator() diff --git a/Test/Integration/NameController/Data/NamesStateAndCountTestData.cs b/Test/Integration/NameController/Data/NamesStateAndCountTestData.cs index d350431..ae90518 100644 --- a/Test/Integration/NameController/Data/NamesStateAndCountTestData.cs +++ b/Test/Integration/NameController/Data/NamesStateAndCountTestData.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; diff --git a/Test/Integration/NameController/Data/NamesStateAndSubmittedByTestData.cs b/Test/Integration/NameController/Data/NamesStateAndSubmittedByTestData.cs index f033572..e4939b5 100644 --- a/Test/Integration/NameController/Data/NamesStateAndSubmittedByTestData.cs +++ b/Test/Integration/NameController/Data/NamesStateAndSubmittedByTestData.cs @@ -1,8 +1,8 @@ using System.Collections; using System.Collections.Generic; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; diff --git a/Test/Integration/NameController/Data/NamesStateCountAndSubmittedByTestData.cs b/Test/Integration/NameController/Data/NamesStateCountAndSubmittedByTestData.cs index 39f769c..e85ac0c 100644 --- a/Test/Integration/NameController/Data/NamesStateCountAndSubmittedByTestData.cs +++ b/Test/Integration/NameController/Data/NamesStateCountAndSubmittedByTestData.cs @@ -1,8 +1,8 @@ using System.Collections; using System.Collections.Generic; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; diff --git a/Test/Integration/NameController/Data/NamesStateTestData.cs b/Test/Integration/NameController/Data/NamesStateTestData.cs index ce0c61b..6e96808 100644 --- a/Test/Integration/NameController/Data/NamesStateTestData.cs +++ b/Test/Integration/NameController/Data/NamesStateTestData.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; diff --git a/Test/Integration/NameController/Data/NamesSubmittedByTestData.cs b/Test/Integration/NameController/Data/NamesSubmittedByTestData.cs index 12b9acb..a1faaea 100644 --- a/Test/Integration/NameController/Data/NamesSubmittedByTestData.cs +++ b/Test/Integration/NameController/Data/NamesSubmittedByTestData.cs @@ -1,8 +1,8 @@ using System.Collections; using System.Collections.Generic; using AutoFixture; -using Core.Entities.NameEntry; -using Core.Enums; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController.Data; diff --git a/Test/Integration/NameController/NameControllerTest.cs b/Test/Integration/NameController/NameControllerTest.cs index 7d511ac..bce0075 100644 --- a/Test/Integration/NameController/NameControllerTest.cs +++ b/Test/Integration/NameController/NameControllerTest.cs @@ -1,5 +1,4 @@ using Core.Dto.Response; -using Core.Entities.NameEntry; using Core.Repositories; using System.Collections.Generic; using System.Net.Http; @@ -10,8 +9,9 @@ using Xunit; using System.Linq; using Microsoft.Extensions.DependencyInjection; -using Core.Enums; using FluentAssertions; +using Core.Entities; +using YorubaOrganization.Core.Enums; namespace Test.Integration.NameController { @@ -53,7 +53,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyAllParameter(List dto.Name) .Should() - .BeEquivalentTo(seed.Select(s => s.Name)); + .BeEquivalentTo(seed.Select(s => s.Title)); } [Theory] @@ -76,7 +76,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyState(List seed nameEntryDtos .Select(dto => dto.Name) .Should() - .BeEquivalentTo(filteredSeed.Select(s => s.Name)); + .BeEquivalentTo(filteredSeed.Select(s => s.Title)); } [Theory] @@ -98,7 +98,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyCount(List seed namesResponse .Select(dto => dto.Name) .Should() - .BeSubsetOf(seed.Select(s => s.Name)); + .BeSubsetOf(seed.Select(s => s.Title)); } [Theory] @@ -121,7 +121,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlySubmittedBy(List dto.Name) .Should() - .Equal(expectedData.Select(ed => ed.Name)); + .Equal(expectedData.Select(ed => ed.Title)); } [Theory] @@ -145,7 +145,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyStateAndCount(List dto.Name) .Should() - .BeEquivalentTo(filteredSeed.Select(s => s.Name)); + .BeEquivalentTo(filteredSeed.Select(s => s.Title)); } [Theory] @@ -168,7 +168,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyStateAndSubmittedBy(List dto.Name) .Should() - .BeEquivalentTo(filteredSeed.Select(s => s.Name)); + .BeEquivalentTo(filteredSeed.Select(s => s.Title)); } [Theory] @@ -192,7 +192,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyCountAndSubmittedBy(List dto.Name) .Should() - .BeEquivalentTo(filteredSeed.Select(s => s.Name)); + .BeEquivalentTo(filteredSeed.Select(s => s.Title)); } [Theory] @@ -216,7 +216,7 @@ public async Task TestGetAllNamesEndpointProvidingOnlyState_CountAndSubmittedBy( namesResponse .Select(dto => dto.Name) .Should() - .BeEquivalentTo(filteredSeed.Select(s => s.Name)); + .BeEquivalentTo(filteredSeed.Select(s => s.Title)); } public Task InitializeAsync() diff --git a/Website/Pages/SearchResults.cshtml.cs b/Website/Pages/SearchResults.cshtml.cs index e6d2e21..75f715c 100644 --- a/Website/Pages/SearchResults.cshtml.cs +++ b/Website/Pages/SearchResults.cshtml.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; using System.Globalization; -using System.Text.Json; using Website.Pages.Shared; using Website.Resources; using Website.Services; diff --git a/Website/Pages/Shared/BasePageModel.cshtml.cs b/Website/Pages/Shared/BasePageModel.cshtml.cs index f6df708..dc4d877 100644 --- a/Website/Pages/Shared/BasePageModel.cshtml.cs +++ b/Website/Pages/Shared/BasePageModel.cshtml.cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Localization; -using System.Web; using Website.Resources; namespace Website.Pages.Shared diff --git a/Website/Pages/SubmitName.cshtml.cs b/Website/Pages/SubmitName.cshtml.cs index 886918d..26920b4 100644 --- a/Website/Pages/SubmitName.cshtml.cs +++ b/Website/Pages/SubmitName.cshtml.cs @@ -1,10 +1,9 @@ -using Application.Services; -using Core.Dto.Response; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; using Website.Pages.Shared; using Website.Resources; using Website.Services; +using YorubaOrganization.Core.Dto.Response; namespace Website.Pages { diff --git a/Website/Services/ApiService.cs b/Website/Services/ApiService.cs index e924411..61ef1b9 100644 --- a/Website/Services/ApiService.cs +++ b/Website/Services/ApiService.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Options; using System.Text.Json; using Website.Config; +using YorubaOrganization.Core.Dto.Response; namespace Website.Services { diff --git a/YorubaNameDictionary.sln b/YorubaNameDictionary.sln index 6bbaa85..b254de3 100644 --- a/YorubaNameDictionary.sln +++ b/YorubaNameDictionary.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 17.7.34221.43 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{12B35F2A-85F6-45C5-B1E9-84089F529ED6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure.MongoDB", "Infrastructure.MongoDB\Infrastructure.MongoDB.csproj", "{2C8E50D5-BE74-4370-B0CA-156C16D00AA4}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Application", "Application\Application.csproj", "{6FBF4BDD-D321-4B30-BA9C-70B1B0320361}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "Api\Api.csproj", "{6DF0AC0B-335E-44A3-AD11-5DC7A12602D4}" @@ -27,7 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{78930A71-3759-46B3-9429-80C4D4933D12}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "Infrastructure\Infrastructure.csproj", "{78930A71-3759-46B3-9429-80C4D4933D12}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,10 +37,6 @@ Global {12B35F2A-85F6-45C5-B1E9-84089F529ED6}.Debug|Any CPU.Build.0 = Debug|Any CPU {12B35F2A-85F6-45C5-B1E9-84089F529ED6}.Release|Any CPU.ActiveCfg = Release|Any CPU {12B35F2A-85F6-45C5-B1E9-84089F529ED6}.Release|Any CPU.Build.0 = Release|Any CPU - {2C8E50D5-BE74-4370-B0CA-156C16D00AA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2C8E50D5-BE74-4370-B0CA-156C16D00AA4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2C8E50D5-BE74-4370-B0CA-156C16D00AA4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2C8E50D5-BE74-4370-B0CA-156C16D00AA4}.Release|Any CPU.Build.0 = Release|Any CPU {6FBF4BDD-D321-4B30-BA9C-70B1B0320361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6FBF4BDD-D321-4B30-BA9C-70B1B0320361}.Debug|Any CPU.Build.0 = Debug|Any CPU {6FBF4BDD-D321-4B30-BA9C-70B1B0320361}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/docker-compose.override.yml b/docker-compose.override.yml index b594ccd..857958e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -19,7 +19,7 @@ services: - Twitter__AccessToken=${YND_Twitter__AccessToken} - Twitter__AccessTokenSecret=${YND_Twitter__AccessTokenSecret} ports: - - "51515:80" + - "9001:80" volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro