Skip to content

Commit

Permalink
Merge branch 'main' into xedkn/send-requests-to-process-manager
Browse files Browse the repository at this point in the history
  • Loading branch information
ebbeknudsen committed Dec 16, 2024
2 parents ce6614c + 2bb71be commit 313b2d0
Show file tree
Hide file tree
Showing 33 changed files with 1,166 additions and 188 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci-dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,13 @@ jobs:
tests_filter_expression:
- name: Architecture Tests
paths: \source\ArchitectureTests\bin\Release\net8.0\Energinet.DataHub.EDI.ArchitectureTests.dll

- name: Tests
paths: \source\Tests\bin\Release\net8.0\Energinet.DataHub.EDI.Tests.dll

- name: OutgoingMessages Unit Tests
paths: \source\OutgoingMessages.UnitTests\bin\Release\net8.0\Energinet.DataHub.EDI.OutgoingMessages.UnitTests.dll

uses: Energinet-DataHub/.github/.github/workflows/dotnet-postbuild-test.yml@v14
with:
download_attempt_limit: 30
Expand Down
4 changes: 3 additions & 1 deletion source/B2BApi/IncomingMessages/IncomingMessageReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ public async Task<HttpResponseData> RunAsync(
var stopwatch = Stopwatch.StartNew();
var cancellationToken = request.GetCancellationToken(hostCancellationToken);

if (!await _featureFlagManager.ReceiveMeteredDataForMeasurementPointsAsync().ConfigureAwait(false))
if (incomingDocumentTypeName != null &&
incomingDocumentTypeName.Equals(IncomingDocumentType.NotifyValidatedMeasureData.Name, StringComparison.OrdinalIgnoreCase)
&& !await _featureFlagManager.ReceiveMeteredDataForMeasurementPointsAsync().ConfigureAwait(false))
{
/*
* The HTTP 403 Forbidden client error response status code indicates that the server understood the request
Expand Down
1 change: 1 addition & 0 deletions source/Edi.Filtered.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"OutgoingMessages.Infrastructure\\OutgoingMessages.Infrastructure.csproj",
"OutgoingMessages.IntegrationTests\\OutgoingMessages.IntegrationTests.csproj",
"OutgoingMessages.Interfaces\\OutgoingMessages.Interfaces.csproj",
"OutgoingMessages.UnitTests\\OutgoingMessages.UnitTests.csproj",
"Process.Application\\Process.Application.csproj",
"Process.Domain\\Process.Domain.csproj",
"Process.Infrastructure\\Process.Infrastructure.csproj",
Expand Down
7 changes: 7 additions & 0 deletions source/Edi.Repository.sln
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MasterData.Domain", "Master
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArchivedMessages.Domain", "ArchivedMessages.Domain\ArchivedMessages.Domain.csproj", "{5DBB75FB-38DC-47ED-987B-255A2D48186C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutgoingMessages.UnitTests", "OutgoingMessages.UnitTests\OutgoingMessages.UnitTests.csproj", "{2A087DB2-8A23-489C-979D-AE8D1783B52A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -452,6 +454,10 @@ Global
{5DBB75FB-38DC-47ED-987B-255A2D48186C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5DBB75FB-38DC-47ED-987B-255A2D48186C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5DBB75FB-38DC-47ED-987B-255A2D48186C}.Release|Any CPU.Build.0 = Release|Any CPU
{2A087DB2-8A23-489C-979D-AE8D1783B52A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2A087DB2-8A23-489C-979D-AE8D1783B52A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A087DB2-8A23-489C-979D-AE8D1783B52A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A087DB2-8A23-489C-979D-AE8D1783B52A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -548,6 +554,7 @@ Global
{0E787394-5AD2-4920-900F-E802EDB726B6} = {ED4BC8EC-5C20-4250-B2DD-9FEAEF726D8E}
{4A238A51-ED28-4039-8046-ED7B1336C614} = {ED4BC8EC-5C20-4250-B2DD-9FEAEF726D8E}
{5DBB75FB-38DC-47ED-987B-255A2D48186C} = {0B7C34CA-1AC7-4D94-B44B-5357EFE13F88}
{2A087DB2-8A23-489C-979D-AE8D1783B52A} = {75A9EB8E-EEE3-443B-B497-F624E454DC22}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8C390B97-896A-4AAD-8609-3C20E80966D2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,9 @@ private async Task<IReadOnlyCollection<ValidationError>> CheckTransactionIdsAsyn
{
var transactionId = series.TransactionId;

var errorsForSeries = await CheckTransactionIdAsync(
var errorsForSeries = CheckTransactionId(
transactionId,
message.SenderNumber,
transactionIdsToBeStored,
cancellationToken)
.ConfigureAwait(false);
transactionIdsToBeStored);

if (errorsForSeries is null)
{
Expand All @@ -188,34 +185,28 @@ private async Task<IReadOnlyCollection<ValidationError>> CheckTransactionIdsAsyn
}
}

var duplicatedTransactionIds = await transactionIdRepository
.GetDuplicatedTransactionIdsAsync(message.SenderNumber, transactionIdsToBeStored, cancellationToken)
.ConfigureAwait(false);
foreach (var duplicatedTransactionId in duplicatedTransactionIds)
{
errors.Add(new DuplicateTransactionIdDetected(duplicatedTransactionId));
}

return errors;
}

private async Task<ValidationError?> CheckTransactionIdAsync(
private ValidationError? CheckTransactionId(
string transactionId,
string senderNumber,
IReadOnlyCollection<string> transactionIdsToBeStored,
CancellationToken cancellationToken)
IReadOnlyCollection<string> transactionIdsToBeStored)
{
return transactionId switch
{
_ when string.IsNullOrEmpty(transactionId) => new EmptyTransactionId(),
_ when transactionId.Length > MaxTransactionIdLength => new InvalidTransactionIdSize(transactionId),
_ when await TransactionIdIsDuplicatedAsync(senderNumber, transactionId, cancellationToken)
.ConfigureAwait(false) => new DuplicateTransactionIdDetected(transactionId),
_ when transactionIdsToBeStored.Contains(transactionId) =>
new DuplicateTransactionIdDetected(transactionId),
_ => null,
};
}

private async Task<bool> TransactionIdIsDuplicatedAsync(
string senderNumber,
string transactionId,
CancellationToken cancellationToken)
{
return await transactionIdRepository
.TransactionIdExistsAsync(senderNumber, transactionId, cancellationToken)
.ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ namespace Energinet.DataHub.EDI.IncomingMessages.Infrastructure.Repositories.Tra
public interface ITransactionIdRepository
{
/// <summary>
/// Checks if <paramref name="transactionId"/> is already registered by the sender <paramref name="senderId"/>
/// Returns a list of existing <paramref name="transactionIds"/> if they already is registered by the sender <paramref name="senderId"/>
/// </summary>
/// <param name="senderId"></param>
/// <param name="transactionId"></param>
/// <param name="transactionIds"></param>
/// <param name="cancellationToken"></param>
Task<bool> TransactionIdExistsAsync(string senderId, string transactionId, CancellationToken cancellationToken);
Task<IReadOnlyList<string>> GetDuplicatedTransactionIdsAsync(string senderId, IReadOnlyCollection<string> transactionIds, CancellationToken cancellationToken);

/// <summary>
/// Store transaction ids for the specified sender
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ public TransactionIdRepository(IncomingMessagesContext incomingMessagesContext)
_incomingMessagesContext = incomingMessagesContext;
}

public async Task<bool> TransactionIdExistsAsync(
public async Task<IReadOnlyList<string>> GetDuplicatedTransactionIdsAsync(
string senderId,
string transactionId,
IReadOnlyCollection<string> transactionIds,
CancellationToken cancellationToken)
{
var transaction = await GetTransactionFromDbAsync(senderId, transactionId, cancellationToken).ConfigureAwait(false)
?? GetTransactionFromInMemoryCollection(senderId, transactionId);
var duplicatedTransactionIdsForSender = await GetDuplicatedTransactionsFromDbAsync(senderId, transactionIds, cancellationToken)
.ConfigureAwait(false);
if (!transactionIds.Any())
duplicatedTransactionIdsForSender = GetDuplicatedTransactionsFromInMemoryCollection(senderId, transactionIds);

return transaction != null;
return duplicatedTransactionIdsForSender.Select(x => x.TransactionId).ToList();
}

public async Task AddAsync(
Expand All @@ -50,19 +52,26 @@ public async Task AddAsync(
}
}

private TransactionIdForSender? GetTransactionFromInMemoryCollection(string senderId, string transactionId)
private IReadOnlyList<TransactionIdForSender> GetDuplicatedTransactionsFromInMemoryCollection(
string senderId,
IReadOnlyCollection<string> transactionIds)
{
return _incomingMessagesContext.TransactionIdForSenders.Local
.FirstOrDefault(x => x.TransactionId == transactionId && x.SenderId == senderId);
.Where(
transactionIdForSender => transactionIds.Contains(transactionIdForSender.TransactionId)
&& transactionIdForSender.SenderId == senderId)
.ToList();
}

private async Task<TransactionIdForSender?> GetTransactionFromDbAsync(string senderId, string transactionId, CancellationToken cancellationToken)
private async Task<IReadOnlyList<TransactionIdForSender>> GetDuplicatedTransactionsFromDbAsync(
string senderId,
IReadOnlyCollection<string> transactionIds,
CancellationToken cancellationToken)
{
return await _incomingMessagesContext.TransactionIdForSenders
.FirstOrDefaultAsync(
transactionIdForSender => transactionIdForSender.TransactionId == transactionId
&& transactionIdForSender.SenderId == senderId,
cancellationToken)
.Where(transactionIdForSender => transactionIds.Contains(transactionIdForSender.TransactionId)
&& transactionIdForSender.SenderId == senderId)
.ToListAsync(cancellationToken)
.ConfigureAwait(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,37 @@ public async Task When_MultipleTransactionsWithSameId_Then_ResultContainExcepted
result.Errors.Should().Contain(error => error is DuplicateTransactionIdDetected);
}

[Fact]
public async Task When_MultipleTransactionsWithSameIdAsExisting_Then_ResultContainExceptedValidationError()
{
var documentFormat = DocumentFormat.Ebix;
var existingTransactionIdForSender = "123456";
var newTransactionIdForSender = "654321";
await StoreTransactionIdForActorAsync(existingTransactionIdForSender, _actorIdentity.ActorNumber.Value);
var message = MeteredDataForMeasurementPointBuilder.CreateIncomingMessage(
documentFormat,
_actorIdentity.ActorNumber,
[
(existingTransactionIdForSender,
Instant.FromUtc(2024, 1, 1, 0, 0),
Instant.FromUtc(2024, 1, 2, 0, 0),
Resolution.QuarterHourly),
(newTransactionIdForSender,
Instant.FromUtc(2024, 1, 1, 0, 0),
Instant.FromUtc(2024, 1, 2, 0, 0),
Resolution.QuarterHourly),
]);

var (incomingMessage, _) = await ParseMessageAsync(message.Stream, documentFormat);
var result = await _validateIncomingMessage.ValidateAsync(
incomingMessage!,
documentFormat,
CancellationToken.None);

result.Success.Should().BeFalse();
result.Errors.Should().Contain(error => error is DuplicateTransactionIdDetected);
}

[Fact]
public async Task When_TransactionIdIsEmpty_Then_ResultContainExceptedValidationError()
{
Expand Down Expand Up @@ -622,6 +653,17 @@ public async Task When_BusinessTypeIsNotAllowed_Then_ExpectedValidationError()
throw new NotSupportedException($"No message parser found for message format '{documentFormat}' and document type '{IncomingDocumentType.NotifyValidatedMeasureData}'");
}

private async Task StoreTransactionIdForActorAsync(string existingTransactionIdForSender, string senderActorNumber)
{
var databaseConnectionFactory = GetService<IDatabaseConnectionFactory>();
using var dbConnection = await databaseConnectionFactory.GetConnectionAndOpenAsync(CancellationToken.None).ConfigureAwait(false);

await dbConnection.ExecuteAsync(
"INSERT INTO [dbo].[TransactionRegistry] ([TransactionId], [SenderId]) VALUES (@TransactionId, @SenderId)",
new { TransactionId = existingTransactionIdForSender, SenderId = senderActorNumber })
.ConfigureAwait(false);
}

private async Task StoreMessageIdForActorAsync(string messageId, string senderActorNumber)
{
var databaseConnectionFactory = GetService<IDatabaseConnectionFactory>();
Expand Down
Loading

0 comments on commit 313b2d0

Please sign in to comment.