Skip to content

Commit

Permalink
Added service and handler to check for existing consolidations
Browse files Browse the repository at this point in the history
  • Loading branch information
FirestarJes committed Dec 27, 2024
1 parent d3cbfb4 commit 62b59e3
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using Energinet.DataHub.MarketParticipant.Domain.Model;
using Energinet.DataHub.MarketParticipant.Domain.Model.Events;
using Energinet.DataHub.MarketParticipant.Domain.Repositories;
using Energinet.DataHub.MarketParticipant.Domain.Services.Rules;
using MediatR;
using NodaTime.Extensions;

Expand All @@ -36,21 +37,24 @@ public sealed class ScheduleConsolidateActorsHandler : IRequestHandler<ScheduleC
private readonly IDomainEventRepository _domainEventRepository;
private readonly IActorRepository _actorRepository;
private readonly IUnitOfWorkProvider _unitOfWorkProvider;
private readonly IExistingActorConsolidationService _existingActorConsolidationService;

public ScheduleConsolidateActorsHandler(
IAuditIdentityProvider auditIdentityProvider,
IActorConsolidationAuditLogRepository actorConsolidationAuditLogRepository,
IActorConsolidationRepository actorConsolidationRepository,
IDomainEventRepository domainEventRepository,
IUnitOfWorkProvider unitOfWorkProvider,
IActorRepository actorRepository)
IActorRepository actorRepository,
IExistingActorConsolidationService existingActorConsolidationService)
{
_auditIdentityProvider = auditIdentityProvider;
_actorConsolidationAuditLogRepository = actorConsolidationAuditLogRepository;
_actorConsolidationRepository = actorConsolidationRepository;
_domainEventRepository = domainEventRepository;
_unitOfWorkProvider = unitOfWorkProvider;
_actorRepository = actorRepository;
_existingActorConsolidationService = existingActorConsolidationService;
}

public async Task Handle(ScheduleConsolidateActorsCommand request, CancellationToken cancellationToken)
Expand All @@ -65,6 +69,11 @@ public async Task Handle(ScheduleConsolidateActorsCommand request, CancellationT
var toActor = await _actorRepository.GetAsync(toActorId).ConfigureAwait(false);
NotFoundValidationException.ThrowIfNull(toActor, request.Consolidation.ToActorId);

// Ensure that the actors are not already consolidated
await _existingActorConsolidationService
.CheckExistingConsolidationAsync(fromActorId, toActorId)
.ConfigureAwait(false);

var uow = await _unitOfWorkProvider
.NewUnitOfWorkAsync()
.ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Energinet.DataHub.MarketParticipant.Domain.Exception;
using Energinet.DataHub.MarketParticipant.Domain.Model;
using Energinet.DataHub.MarketParticipant.Domain.Repositories;

namespace Energinet.DataHub.MarketParticipant.Domain.Services.Rules;

public sealed class ExistingActorConsolidationService : IExistingActorConsolidationService
{
private readonly IActorConsolidationRepository _actorConsolidationRepository;

public ExistingActorConsolidationService(IActorConsolidationRepository organizationRepository)
{
_actorConsolidationRepository = organizationRepository;
}

public async Task CheckExistingConsolidationAsync(ActorId fromActorId, ActorId toActorId)
{
var existingConsolidations = (await _actorConsolidationRepository
.GetAsync()
.ConfigureAwait(false)).ToList();

if (existingConsolidations.Any(consolidation => consolidation.ActorFromId == fromActorId || consolidation.ActorToId == fromActorId))
{
throw new ValidationException("The specified From actor has already been consolidated before.")
.WithErrorCode("actor.consolidation.fromexists");
}

if (existingConsolidations.Any(consolidation => consolidation.ActorToId == fromActorId || consolidation.ActorFromId == toActorId))
{
throw new ValidationException("The specified From actor has an existing consolidation scheduled already.")
.WithErrorCode("actor.consolidation.toexists");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Threading.Tasks;
using Energinet.DataHub.MarketParticipant.Domain.Model;

namespace Energinet.DataHub.MarketParticipant.Domain.Services.Rules;

/// <summary>
/// Ensures that there are no existing Consolidation for any of the actors chosen for a consolidation.
/// </summary>
public interface IExistingActorConsolidationService
{
/// <summary>
/// Checks whether either the from or to actor is already part of an existing consolidation, will throw an exception if they are.
/// </summary>
/// <param name="fromActorId">The <see cref="ActorId"/> of the discontinued actor.</param>
/// <param name="toActorId">The <see cref="ActorId"/> of the surviving actor.</param>
Task CheckExistingConsolidationAsync(ActorId fromActorId, ActorId toActorId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Energinet.DataHub.MarketParticipant.Application.Commands.Actors;
using Energinet.DataHub.MarketParticipant.Application.Handlers.Actors;
using Energinet.DataHub.MarketParticipant.Application.Services;
using Energinet.DataHub.MarketParticipant.Domain;
using Energinet.DataHub.MarketParticipant.Domain.Repositories;
using Energinet.DataHub.MarketParticipant.Domain.Services.Rules;
using Energinet.DataHub.MarketParticipant.Tests.Common;
using Moq;
using Xunit;
using Xunit.Categories;

namespace Energinet.DataHub.MarketParticipant.Tests.Handlers;

[UnitTest]
public sealed class ScheduleActorConsolidationsHandlerTests
{
[Fact]
public async Task Handle_InvalidFromActorId_ThrowsException()
{
// Arrange
var actorRepositoryMock = new Mock<IActorRepository>();
var fromActorId = Guid.NewGuid();
var target = new ScheduleConsolidateActorsHandler(
Mock.Of<IAuditIdentityProvider>(),
Mock.Of<IActorConsolidationAuditLogRepository>(),
Mock.Of<IActorConsolidationRepository>(),
Mock.Of<IDomainEventRepository>(),
Mock.Of<IUnitOfWorkProvider>(),
actorRepositoryMock.Object,
Mock.Of<IExistingActorConsolidationService>());
var command = new ScheduleConsolidateActorsCommand(fromActorId, new ConsolidationRequestDto(Guid.NewGuid(), DateTimeOffset.UtcNow.AddDays(65)));

// Act + Assert
var exception = await Record.ExceptionAsync(() => target.Handle(command, CancellationToken.None));
Assert.NotNull(exception);
Assert.Equal(typeof(ValidationException), exception.GetType());
Assert.Contains(exception.Message, $"Entity '{fromActorId}' does not exist.", StringComparison.OrdinalIgnoreCase);
}

[Fact]
public async Task Handle_InvalidToActorId_ThrowsException()
{
// Arrange
var actorRepositoryMock = new Mock<IActorRepository>();
var validFromActor = TestPreparationModels.MockedActor();
var toActorId = Guid.NewGuid();
var target = new ScheduleConsolidateActorsHandler(
Mock.Of<IAuditIdentityProvider>(),
Mock.Of<IActorConsolidationAuditLogRepository>(),
Mock.Of<IActorConsolidationRepository>(),
Mock.Of<IDomainEventRepository>(),
Mock.Of<IUnitOfWorkProvider>(),
actorRepositoryMock.Object,
Mock.Of<IExistingActorConsolidationService>());
actorRepositoryMock
.Setup(actorRepository => actorRepository.GetAsync(validFromActor.Id))
.ReturnsAsync(validFromActor);

var command = new ScheduleConsolidateActorsCommand(validFromActor.Id.Value, new ConsolidationRequestDto(toActorId, DateTimeOffset.UtcNow.AddDays(65)));

// Act + Assert
var exception = await Record.ExceptionAsync(() => target.Handle(command, CancellationToken.None));
Assert.NotNull(exception);
Assert.Equal(typeof(ValidationException), exception.GetType());
Assert.Contains(exception.Message, $"Entity '{toActorId}' does not exist.", StringComparison.OrdinalIgnoreCase);
}
}

0 comments on commit 62b59e3

Please sign in to comment.