From 7332314feb3e56547d6f9728e9524641bc69dba3 Mon Sep 17 00:00:00 2001 From: Francis Pion Date: Mon, 7 Oct 2024 12:41:14 -0400 Subject: [PATCH] SetDefaultLanguageCommand --- .../Commands/SetDefaultLanguageCommand.cs | 42 +++++++++++ .../Languages/ILanguageQuerier.cs | 1 + .../Languages/ILanguageRepository.cs | 2 + .../Logitar.Cms.Core/Languages/Language.cs | 23 ++++++ .../SetDefaultLanguageCommandHandlerTests.cs | 72 +++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 backend/src/Logitar.Cms.Core/Languages/Commands/SetDefaultLanguageCommand.cs create mode 100644 backend/tests/Logitar.Cms.Core.UnitTests/Languages/Commands/SetDefaultLanguageCommandHandlerTests.cs diff --git a/backend/src/Logitar.Cms.Core/Languages/Commands/SetDefaultLanguageCommand.cs b/backend/src/Logitar.Cms.Core/Languages/Commands/SetDefaultLanguageCommand.cs new file mode 100644 index 0000000..7ce9a0b --- /dev/null +++ b/backend/src/Logitar.Cms.Core/Languages/Commands/SetDefaultLanguageCommand.cs @@ -0,0 +1,42 @@ +using Logitar.Cms.Contracts.Languages; +using Logitar.EventSourcing; +using MediatR; + +namespace Logitar.Cms.Core.Languages.Commands; + +public record SetDefaultLanguageCommand(Guid Id) : Activity, IRequest; + +internal class SetDefaultLanguageCommandHandler : IRequestHandler +{ + private readonly ILanguageQuerier _languageQuerier; + private readonly ILanguageRepository _languageRepository; + + public SetDefaultLanguageCommandHandler(ILanguageQuerier languageQuerier, ILanguageRepository languageRepository) + { + _languageQuerier = languageQuerier; + _languageRepository = languageRepository; + } + + public async Task Handle(SetDefaultLanguageCommand command, CancellationToken cancellationToken) + { + LanguageId languageId = new(command.Id); + Language? language = await _languageRepository.LoadAsync(languageId, cancellationToken); + if (language == null) + { + return null; + } + else if (!language.IsDefault) + { + ActorId actorId = command.GetActorId(); + + Language @default = await _languageRepository.LoadDefaultAsync(cancellationToken); + @default.SetDefault(isDefault: false, actorId); + + language.SetDefault(actorId); + + await _languageRepository.SaveAsync([@default, language], cancellationToken); + } + + return await _languageQuerier.ReadAsync(language, cancellationToken); + } +} diff --git a/backend/src/Logitar.Cms.Core/Languages/ILanguageQuerier.cs b/backend/src/Logitar.Cms.Core/Languages/ILanguageQuerier.cs index 83ec441..30099d6 100644 --- a/backend/src/Logitar.Cms.Core/Languages/ILanguageQuerier.cs +++ b/backend/src/Logitar.Cms.Core/Languages/ILanguageQuerier.cs @@ -10,4 +10,5 @@ public interface ILanguageQuerier Task ReadAsync(LanguageId id, CancellationToken cancellationToken = default); Task ReadAsync(Guid id, CancellationToken cancellationToken = default); Task ReadAsync(string locale, CancellationToken cancellationToken = default); + Task ReadDefaultAsync(CancellationToken cancellationToken = default); } diff --git a/backend/src/Logitar.Cms.Core/Languages/ILanguageRepository.cs b/backend/src/Logitar.Cms.Core/Languages/ILanguageRepository.cs index f402fc8..da8f364 100644 --- a/backend/src/Logitar.Cms.Core/Languages/ILanguageRepository.cs +++ b/backend/src/Logitar.Cms.Core/Languages/ILanguageRepository.cs @@ -5,6 +5,8 @@ public interface ILanguageRepository Task LoadAsync(LanguageId id, CancellationToken cancellationToken = default); Task LoadAsync(LanguageId id, long? version, CancellationToken cancellationToken = default); + Task LoadDefaultAsync(CancellationToken cancellationToken = default); + Task SaveAsync(Language language, CancellationToken cancellationToken = default); Task SaveAsync(IEnumerable languages, CancellationToken cancellationToken = default); } diff --git a/backend/src/Logitar.Cms.Core/Languages/Language.cs b/backend/src/Logitar.Cms.Core/Languages/Language.cs index 6c15596..1abf900 100644 --- a/backend/src/Logitar.Cms.Core/Languages/Language.cs +++ b/backend/src/Logitar.Cms.Core/Languages/Language.cs @@ -35,6 +35,19 @@ protected virtual void Apply(CreatedEvent @event) _locale = @event.Locale; } + public void SetDefault(ActorId actorId = default) => SetDefault(isDefault: true, actorId); + public void SetDefault(bool isDefault, ActorId actorId = default) + { + if (IsDefault != isDefault) + { + Raise(new SetDefaultEvent(isDefault), actorId); + } + } + protected virtual void Apply(SetDefaultEvent @event) + { + IsDefault = @event.IsDefault; + } + public void Update(ActorId actorId = default) { if (_updatedEvent.HasChanges) @@ -65,6 +78,16 @@ public CreatedEvent(bool isDefault, Locale locale) } } + public class SetDefaultEvent : DomainEvent, INotification + { + public bool IsDefault { get; } + + public SetDefaultEvent(bool isDefault) + { + IsDefault = isDefault; + } + } + public class UpdatedEvent : DomainEvent, INotification { public Locale? Locale { get; set; } diff --git a/backend/tests/Logitar.Cms.Core.UnitTests/Languages/Commands/SetDefaultLanguageCommandHandlerTests.cs b/backend/tests/Logitar.Cms.Core.UnitTests/Languages/Commands/SetDefaultLanguageCommandHandlerTests.cs new file mode 100644 index 0000000..4be09fb --- /dev/null +++ b/backend/tests/Logitar.Cms.Core.UnitTests/Languages/Commands/SetDefaultLanguageCommandHandlerTests.cs @@ -0,0 +1,72 @@ +using Logitar.Cms.Contracts.Languages; +using Moq; + +namespace Logitar.Cms.Core.Languages.Commands; + +[Trait(Traits.Category, Categories.Unit)] +public class SetDefaultLanguageCommandHandlerTests +{ + private readonly CancellationToken _cancellationToken = default; + + private readonly Mock _languageQuerier = new(); + private readonly Mock _languageRepository = new(); + + private readonly SetDefaultLanguageCommandHandler _handler; + + private readonly Language _default = new(new Locale("en"), isDefault: true); + private readonly Language _language = new(new Locale("fr")); + + public SetDefaultLanguageCommandHandlerTests() + { + _handler = new(_languageQuerier.Object, _languageRepository.Object); + + _languageRepository.Setup(x => x.LoadAsync(_default.Id, _cancellationToken)).ReturnsAsync(_default); + _languageRepository.Setup(x => x.LoadAsync(_language.Id, _cancellationToken)).ReturnsAsync(_language); + _languageRepository.Setup(x => x.LoadDefaultAsync(_cancellationToken)).ReturnsAsync(_default); + } + + [Fact(DisplayName = "It should do nothing when the language is already default.")] + public async Task It_should_do_nothing_when_the_language_is_already_default() + { + LanguageModel model = new(); + _languageQuerier.Setup(x => x.ReadAsync(_default, _cancellationToken)).ReturnsAsync(model); + + SetDefaultLanguageCommand command = new(_default.Id.ToGuid()); + + LanguageModel? result = await _handler.Handle(command, _cancellationToken); + Assert.NotNull(result); + Assert.Same(model, result); + + _languageRepository.Verify(x => x.LoadDefaultAsync(It.IsAny()), Times.Never); + _languageRepository.Verify(x => x.SaveAsync(It.IsAny>(), It.IsAny()), Times.Never); + } + + [Fact(DisplayName = "It should return null when the language could not be found.")] + public async Task It_should_return_null_when_the_language_could_not_be_found() + { + SetDefaultLanguageCommand command = new(Guid.NewGuid()); + + Assert.Null(await _handler.Handle(command, _cancellationToken)); + } + + [Fact(DisplayName = "It should set the default language.")] + public async Task It_should_set_the_default_language() + { + LanguageModel model = new(); + _languageQuerier.Setup(x => x.ReadAsync(_language, _cancellationToken)).ReturnsAsync(model); + + SetDefaultLanguageCommand command = new(_language.Id.ToGuid()); + + LanguageModel? result = await _handler.Handle(command, _cancellationToken); + Assert.NotNull(result); + Assert.Same(model, result); + + Assert.False(_default.IsDefault); + Assert.True(_language.IsDefault); + + _languageRepository.Verify(x => x.LoadDefaultAsync(_cancellationToken), Times.Once); + _languageRepository.Verify(x => x.SaveAsync( + It.Is>(y => y.SequenceEqual(new Language[] { _default, _language })), + _cancellationToken), Times.Once); + } +}