Skip to content

Commit

Permalink
chore: move incoming message validation requirement into domain models
Browse files Browse the repository at this point in the history
  • Loading branch information
MadsDue committed Dec 3, 2024
1 parent 5dad335 commit 083d828
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 183 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,37 +21,18 @@

namespace Energinet.DataHub.EDI.IncomingMessages.Application.UseCases;

public class ValidateIncomingMessage
public class ValidateIncomingMessage(
ISenderAuthorizer senderAuthorizer,
IReceiverValidator receiverValidator,
IMessageIdRepository messageIdRepository,
IMessageTypeValidator messageTypeValidator,
IProcessTypeValidator processTypeValidator,
IBusinessTypeValidator businessTypeValidator,
ITransactionIdRepository transactionIdRepository)
{
private const int MaxMessageIdLength = 36;
private const int MaxTransactionIdLength = 36;

private readonly ISenderAuthorizer _senderAuthorizer;
private readonly IReceiverValidator _receiverValidator;
private readonly IMessageIdRepository _messageIdRepository;
private readonly IMessageTypeValidator _messageTypeValidator;
private readonly IProcessTypeValidator _processTypeValidator;
private readonly IBusinessTypeValidator _businessTypeValidator;
private readonly ITransactionIdRepository _transactionIdRepository;

public ValidateIncomingMessage(
ISenderAuthorizer senderAuthorizer,
IReceiverValidator receiverValidator,
IMessageIdRepository messageIdRepository,
IMessageTypeValidator messageTypeValidator,
IProcessTypeValidator processTypeValidator,
IBusinessTypeValidator businessTypeValidator,
ITransactionIdRepository transactionIdRepository)
{
_senderAuthorizer = senderAuthorizer;
_receiverValidator = receiverValidator;
_messageIdRepository = messageIdRepository;
_messageTypeValidator = messageTypeValidator;
_processTypeValidator = processTypeValidator;
_businessTypeValidator = businessTypeValidator;
_transactionIdRepository = transactionIdRepository;
}

public async Task<Result> ValidateAsync(
IIncomingMessage incomingMessage,
DocumentFormat documentFormat,
Expand Down Expand Up @@ -85,7 +66,7 @@ private async Task<IReadOnlyCollection<ValidationError>> AuthorizeSenderAsync(II
{
var allSeriesAreDelegated = message.Series.Count > 0 && message.Series.All(s => s.IsDelegated);

var result = await _senderAuthorizer
var result = await senderAuthorizer
.AuthorizeAsync(message, allSeriesAreDelegated)
.ConfigureAwait(false);

Expand All @@ -94,7 +75,7 @@ private async Task<IReadOnlyCollection<ValidationError>> AuthorizeSenderAsync(II

private async Task<IReadOnlyCollection<ValidationError>> VerifyReceiverAsync(IIncomingMessage message)
{
var receiverVerification = await _receiverValidator
var receiverVerification = await receiverValidator
.VerifyAsync(message.ReceiverNumber, message.ReceiverRoleCode)
.ConfigureAwait(false);

Expand All @@ -116,7 +97,7 @@ private async Task<IReadOnlyCollection<ValidationError>> CheckMessageIdAsync(
{
errors.Add(new InvalidMessageIdSize(message.MessageId));
}
else if (await _messageIdRepository
else if (await messageIdRepository
.MessageIdExistsAsync(message.SenderNumber, message.MessageId, cancellationToken)
.ConfigureAwait(false))
{
Expand All @@ -130,7 +111,7 @@ private async Task<IReadOnlyCollection<ValidationError>> CheckMessageTypeAsync(
IIncomingMessage message,
CancellationToken cancellationToken)
{
var result = await _messageTypeValidator.ValidateAsync(message, cancellationToken)
var result = await messageTypeValidator.ValidateAsync(message, cancellationToken)
.ConfigureAwait(false);
return result.Errors;
}
Expand All @@ -140,7 +121,7 @@ private async Task<IReadOnlyCollection<ValidationError>> CheckBusinessReasonAsyn
DocumentFormat documentFormat,
CancellationToken cancellationToken)
{
var result = await _processTypeValidator.ValidateAsync(message, documentFormat, cancellationToken)
var result = await processTypeValidator.ValidateAsync(message, documentFormat, cancellationToken)
.ConfigureAwait(false);
return result.Errors;
}
Expand All @@ -149,7 +130,7 @@ private async Task<IReadOnlyCollection<ValidationError>> CheckBusinessTypeAsync(
IIncomingMessage message,
CancellationToken cancellationToken)
{
var result = await _businessTypeValidator.ValidateAsync(message.BusinessType, cancellationToken)
var result = await businessTypeValidator.ValidateAsync(message.BusinessType, cancellationToken)
.ConfigureAwait(false);
return result.Errors;
}
Expand Down Expand Up @@ -208,7 +189,7 @@ private async Task<bool> TransactionIdIsDuplicatedAsync(
string transactionId,
CancellationToken cancellationToken)
{
return await _transactionIdRepository
return await transactionIdRepository
.TransactionIdExistsAsync(senderNumber, transactionId, cancellationToken)
.ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using Energinet.DataHub.EDI.BuildingBlocks.Domain.DataHub;
using Energinet.DataHub.EDI.BuildingBlocks.Domain.Models;

namespace Energinet.DataHub.EDI.IncomingMessages.Domain.Abstractions;

/// <summary>
Expand Down Expand Up @@ -68,4 +71,10 @@ public interface IIncomingMessage
/// Series of the incoming message
/// </summary>
IReadOnlyCollection<IIncomingMessageSeries> Series { get; }

public IReadOnlyCollection<string> AllowedMessageTypes { get; }

public IReadOnlyCollection<BusinessReason> AllowedBusinessReasons { get; }

public IReadOnlyCollection<ActorRole> AllowedSenderRoles { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected override IReadOnlyCollection<IIncomingMessageSeries> ParseTransactions

protected override IncomingMarketMessageParserResult CreateResult(MessageHeader header, IReadOnlyCollection<IIncomingMessageSeries> transactions)
{
return new IncomingMarketMessageParserResult(new MeteredDataForMeasurementPointMessage(
return new IncomingMarketMessageParserResult(new MeteredDataForMeasurementPointMessageBase(
header.MessageId,
header.MessageType,
header.CreatedAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ protected override IIncomingMessageSeries ParseTransaction(JsonElement transacti

protected override IncomingMarketMessageParserResult CreateResult(MessageHeader header, IReadOnlyCollection<IIncomingMessageSeries> transactions)
{
return new IncomingMarketMessageParserResult(new MeteredDataForMeasurementPointMessage(
return new IncomingMarketMessageParserResult(new MeteredDataForMeasurementPointMessageBase(
header.MessageId,
header.MessageType,
header.CreatedAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ protected override IncomingMarketMessageParserResult CreateResult(
IReadOnlyCollection<IIncomingMessageSeries> transactions)
{
return new IncomingMarketMessageParserResult(
new MeteredDataForMeasurementPointMessage(
new MeteredDataForMeasurementPointMessageBase(
header.MessageId,
header.MessageType,
header.CreatedAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,44 @@ namespace Energinet.DataHub.EDI.IncomingMessages.Domain;
/// <summary>
/// Represents the message for metered data for measurement point known as RSM-012.
/// </summary>
public record MeteredDataForMeasurementPointMessage(
string MessageId,
string MessageType,
string CreatedAt,
string SenderNumber,
string ReceiverNumber,
string SenderRoleCode,
string BusinessReason,
string ReceiverRoleCode,
string? BusinessType,
IReadOnlyCollection<IIncomingMessageSeries> Series) : IIncomingMessage;
public class MeteredDataForMeasurementPointMessageBase(
string messageId,
string messageType,
string createdAt,
string senderNumber,
string receiverNumber,
string senderRoleCode,
string businessReason,
string receiverRoleCode,
string? businessType,
IReadOnlyCollection<IIncomingMessageSeries> series) : IIncomingMessage
{
public string MessageId { get; } = messageId;

public string ReceiverNumber { get; } = receiverNumber;

public string ReceiverRoleCode { get; } = receiverRoleCode;

public string SenderNumber { get; } = senderNumber;

public string SenderRoleCode { get; } = senderRoleCode;

public string BusinessReason { get; } = businessReason;

public string MessageType { get; } = messageType;

public string CreatedAt { get; } = createdAt;

public string? BusinessType { get; } = businessType;

public IReadOnlyCollection<IIncomingMessageSeries> Series { get; } = series;

public IReadOnlyCollection<string> AllowedMessageTypes => ["E66"];

public IReadOnlyCollection<BusinessReason> AllowedBusinessReasons => [Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.PeriodicMetering];

public IReadOnlyCollection<ActorRole> AllowedSenderRoles => [ActorRole.MeteredDataResponsible];
}

public record MeteredDataForMeasurementPointSeries(
string TransactionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,24 @@ public record RequestAggregatedMeasureDataMessage(
string MessageId,
string CreatedAt,
string? BusinessType,
IReadOnlyCollection<IIncomingMessageSeries> Series) : IIncomingMessage;
IReadOnlyCollection<IIncomingMessageSeries> Series) : IIncomingMessage
{
public IReadOnlyCollection<string> AllowedMessageTypes => ["E74"];

public IReadOnlyCollection<BusinessReason> AllowedBusinessReasons =>
[
Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.PreliminaryAggregation,
Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.BalanceFixing,
Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.WholesaleFixing,
Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.Correction,
];

public IReadOnlyCollection<ActorRole> AllowedSenderRoles => [
ActorRole.EnergySupplier,
ActorRole.MeteredDataResponsible,
ActorRole.BalanceResponsibleParty,
];
}

public record RequestAggregatedMeasureDataMessageSeries(
string TransactionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,22 @@ public record RequestWholesaleServicesMessage(
string MessageId,
string CreatedAt,
string? BusinessType,
IReadOnlyCollection<IIncomingMessageSeries> Series) : IIncomingMessage;
IReadOnlyCollection<IIncomingMessageSeries> Series) : IIncomingMessage
{
public IReadOnlyCollection<string> AllowedMessageTypes => ["D21"];

public IReadOnlyCollection<BusinessReason> AllowedBusinessReasons =>
[
Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.WholesaleFixing,
Energinet.DataHub.EDI.BuildingBlocks.Domain.Models.BusinessReason.Correction,
];

public IReadOnlyCollection<ActorRole> AllowedSenderRoles => [
ActorRole.EnergySupplier,
ActorRole.GridAccessProvider,
ActorRole.SystemOperator,
];
}

public record RequestWholesaleServicesSeries(
string TransactionId,
Expand Down
28 changes: 4 additions & 24 deletions source/IncomingMessages.Domain/Validation/MessageTypeValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,12 @@ namespace Energinet.DataHub.EDI.IncomingMessages.Domain.Validation;

public class MessageTypeValidator : IMessageTypeValidator
{
private static readonly IReadOnlyCollection<string> _aggregatedMeasureDataWhiteList = new[] { "E74" };
private static readonly IReadOnlyCollection<string> _wholesaleServicesWhiteList = new[] { "D21" };
private static readonly IReadOnlyCollection<string> _meteredDataForMeasurementPointWhiteList = new[] { "E66" };

public async Task<Result> ValidateAsync(IIncomingMessage message, CancellationToken cancellationToken)
public Task<Result> ValidateAsync(IIncomingMessage message, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(message);

return await Task.FromResult(
message switch
{
RequestAggregatedMeasureDataMessage ramdm =>
_aggregatedMeasureDataWhiteList.Contains(ramdm.MessageType)
? Result.Succeeded()
: Result.Failure(
new NotSupportedMessageType(ramdm.MessageType)),
RequestWholesaleServicesMessage rwsm =>
_wholesaleServicesWhiteList.Contains(rwsm.MessageType)
? Result.Succeeded()
: Result.Failure(new NotSupportedMessageType(rwsm.MessageType)),
MeteredDataForMeasurementPointMessage mdfmpm =>
_meteredDataForMeasurementPointWhiteList.Contains(mdfmpm.MessageType)
? Result.Succeeded()
: Result.Failure(new NotSupportedMessageType(mdfmpm.MessageType)),
_ => throw new InvalidOperationException($"The baw's on the slates! {message.GetType().Name}"),
})
.ConfigureAwait(false);
return Task.FromResult(message.AllowedMessageTypes.Contains(message.MessageType)
? Result.Succeeded()
: Result.Failure(new NotSupportedMessageType(message.MessageType)));
}
}
61 changes: 18 additions & 43 deletions source/IncomingMessages.Domain/Validation/ProcessTypeValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,58 +20,33 @@ namespace Energinet.DataHub.EDI.IncomingMessages.Domain.Validation;

public class ProcessTypeValidator : IProcessTypeValidator
{
private static readonly IReadOnlyCollection<string> _aggregatedMeasureDataWhitelist =
[
BusinessReason.PreliminaryAggregation.Code,
BusinessReason.BalanceFixing.Code,
BusinessReason.WholesaleFixing.Code,
BusinessReason.Correction.Code,
];

private static readonly IReadOnlyCollection<string> _wholesaleServicesWhitelist =
[
BusinessReason.WholesaleFixing.Code,
BusinessReason.Correction.Code,
];

private static readonly IReadOnlyCollection<string> _meteredDataForMeasurementPointEbixWhiteList =
[
BusinessReason.PeriodicMetering.Code,
BusinessReason.PeriodicFlexMetering
.Code, // Flex metering is only supported for Ebix and should be rejected when used for CIM
];

private static readonly IReadOnlyCollection<string> _meteredDataForMeasurementPointWhiteList =
[
BusinessReason.PeriodicMetering.Code,
// Flex metering is only supported for Ebix and should be rejected when used for CIM
BusinessReason.PeriodicFlexMetering.Code,
];

public async Task<Result> ValidateAsync(
IIncomingMessage message,
DocumentFormat documentFormat,
CancellationToken cancellationToken)
{
return await Task.FromResult(
message switch
{
RequestAggregatedMeasureDataMessage ramdm =>
_aggregatedMeasureDataWhitelist.Contains(ramdm.BusinessReason)
? Result.Succeeded()
: Result.Failure(new NotSupportedProcessType(ramdm.BusinessReason)),
RequestWholesaleServicesMessage rwsm =>
_wholesaleServicesWhitelist.Contains(rwsm.BusinessReason)
? Result.Succeeded()
: Result.Failure(new NotSupportedProcessType(rwsm.BusinessReason)),
MeteredDataForMeasurementPointMessage mdfmpm =>
documentFormat == DocumentFormat.Ebix
? _meteredDataForMeasurementPointEbixWhiteList.Contains(mdfmpm.BusinessReason)
? Result.Succeeded()
: Result.Failure(new NotSupportedProcessType(mdfmpm.BusinessReason))
: _meteredDataForMeasurementPointWhiteList.Contains(mdfmpm.BusinessReason)
? Result.Succeeded()
: Result.Failure(new NotSupportedProcessType(mdfmpm.BusinessReason)),
_ => throw new InvalidOperationException($"The baw's on the slates! {message.GetType().Name}"),
})
.ConfigureAwait(false);
ArgumentNullException.ThrowIfNull(message);

if (documentFormat == DocumentFormat.Ebix
&& message is MeteredDataForMeasurementPointMessageBase meteredDataForMeasurementPointMessage
&& _meteredDataForMeasurementPointEbixWhiteList.Contains(
meteredDataForMeasurementPointMessage.BusinessReason))
{
return await Task.FromResult(Result.Succeeded()).ConfigureAwait(false);
}

if (message.AllowedBusinessReasons.Select(x => x.Code).Contains(message.BusinessReason))
{
return await Task.FromResult(Result.Succeeded()).ConfigureAwait(false);
}

return await Task.FromResult(Result.Failure(new NotSupportedProcessType(message.BusinessReason))).ConfigureAwait(false);
}
}
Loading

0 comments on commit 083d828

Please sign in to comment.