Skip to content

Commit

Permalink
Merge pull request #769 from eclipse-tractusx/fix/538-propagate-excep…
Browse files Browse the repository at this point in the history
…tions-via-Either-and-addSuppressed

Fix/538 propagate exceptions via either and add suppressed
  • Loading branch information
ds-mwesener authored Jul 11, 2024
2 parents 03d1145 + caea0b0 commit b5badde
Show file tree
Hide file tree
Showing 25 changed files with 625 additions and 207 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ _**For better traceability add the corresponding GitHub issue number in each cha
or `edc:type`: `data.core.digitalTwinRegistry`. #616
- Fix missing and malformed properties for EDC policy transformation. #648

## Fixed

- Propagates exceptions to have more detail in tombstone. #538


## [5.1.2] - 2024-05-13

### Fixed
Expand Down Expand Up @@ -99,11 +104,11 @@ _**For better traceability add the corresponding GitHub issue number in each cha
## [5.0.0] - 2024-04-16

### Added

- SAMM models can now be added locally #488
- Introduced new Cucumber Tests to cover Industry Core 2.0.0 compatibility #488



### Fixed

- Policy store API fixes. #199, #505
Expand All @@ -119,6 +124,7 @@ _**For better traceability add the corresponding GitHub issue number in each cha
- RestClientExceptions are handled correctly in BpdmFacade now. #405
- Fixed Base64 encoding and decoding for locally provided Semantic Models #488


## [4.9.0] - 2024-04-03
### Added
- Extended EdcPolicyDefinitionService to check if a policy in the edc exists
Expand Down
113 changes: 57 additions & 56 deletions DEPENDENCIES

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions docs/src/api/irs-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2572,6 +2572,10 @@ components:
format: int32
maximum: 2147483647
minimum: 0
rootCauses:
type: array
items:
type: string
ProtocolInformation:
type: object
additionalProperties: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@
********************************************************************************/
package org.eclipse.tractusx.irs.aaswrapper.job.delegate;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

import io.github.resilience4j.core.functions.Either;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.tractusx.irs.aaswrapper.job.AASTransferProcess;
import org.eclipse.tractusx.irs.aaswrapper.job.ItemContainer;
import org.eclipse.tractusx.irs.common.ExceptionUtils;
import org.eclipse.tractusx.irs.component.JobParameter;
import org.eclipse.tractusx.irs.component.PartChainIdentificationKey;
import org.eclipse.tractusx.irs.component.Shell;
Expand Down Expand Up @@ -69,10 +73,14 @@ public ItemContainer process(final ItemContainer.ItemContainerBuilder itemContai

try {
final var dtrKeys = List.of(new DigitalTwinRegistryKey(itemId.getGlobalAssetId(), itemId.getBpn()));
final Shell shell = digitalTwinRegistryService.fetchShells(dtrKeys).stream()
// we use findFirst here, because we query only for one
// DigitalTwinRegistryKey here
.findFirst().orElseThrow();
final var eithers = digitalTwinRegistryService.fetchShells(dtrKeys);
final var shell = eithers.stream()
// we use findFirst here, because we query only for one
// DigitalTwinRegistryKey here
.map(Either::getOrNull)
.filter(Objects::nonNull)
.findFirst()
.orElseThrow(() -> shellNotFound(eithers));

itemContainerBuilder.shell(
jobData.isAuditContractNegotiation() ? shell : shell.withoutContractAgreementId());
Expand All @@ -81,7 +89,8 @@ public ItemContainer process(final ItemContainer.ItemContainerBuilder itemContai
// otherwise Jobs stay in state RUNNING forever
log.info("Shell Endpoint could not be retrieved for Item: {}. Creating Tombstone.", itemId);
itemContainerBuilder.tombstone(
Tombstone.from(itemId.getGlobalAssetId(), null, e, retryCount, ProcessStep.DIGITAL_TWIN_REQUEST));
Tombstone.from(itemId.getGlobalAssetId(), null, e, e.getSuppressed(), retryCount,
ProcessStep.DIGITAL_TWIN_REQUEST));
}

if (expectedDepthOfTreeIsNotReached(jobData.getDepth(), aasTransferProcess.getDepth())) {
Expand All @@ -92,6 +101,12 @@ public ItemContainer process(final ItemContainer.ItemContainerBuilder itemContai
return itemContainerBuilder.build();
}

private static RegistryServiceException shellNotFound(final Collection<Either<Exception, Shell>> eithers) {
final RegistryServiceException shellNotFound = new RegistryServiceException("Shell not found");
ExceptionUtils.addSuppressedExceptions(eithers, shellNotFound);
return shellNotFound;
}

private boolean expectedDepthOfTreeIsNotReached(final int expectedDepth, final int currentDepth) {
log.info("Expected tree depth is {}, current depth is {}", expectedDepth, currentDepth);
return currentDepth < expectedDepth;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.eclipse.tractusx.irs.component.JobHandle;
import org.eclipse.tractusx.irs.component.Jobs;
import org.eclipse.tractusx.irs.component.RegisterJob;
import org.eclipse.tractusx.irs.component.Tombstone;
import org.eclipse.tractusx.irs.component.enums.JobState;
import org.eclipse.tractusx.irs.edc.client.EndpointDataReferenceStorage;
import org.eclipse.tractusx.irs.semanticshub.AspectModels;
Expand Down Expand Up @@ -276,6 +277,102 @@ void shouldStartRecursiveProcesses() {
WiremockSupport.verifyNegotiationCalls(6);
}

@Test
void shouldCreateDetailedTombstoneForMissmatchPolicy() {
// Arrange
final String globalAssetId = "urn:uuid:334cce52-1f52-4bc9-9dd1-410bbe497bbc";

WiremockSupport.successfulSemanticModelRequest();
WiremockSupport.successfulSemanticHubRequests();
WiremockSupport.successfulDiscovery();

failedRegistryRequestMissmatchPolicy();

final RegisterJob request = WiremockSupport.jobRequest(globalAssetId, TEST_BPN, 4);

// Act
final JobHandle jobHandle = irsService.registerItemJob(request);

// Assert
assertThat(jobHandle.getId()).isNotNull();
waitForCompletion(jobHandle);
final Jobs jobForJobId = irsService.getJobForJobId(jobHandle.getId(), false);

assertThat(jobForJobId.getJob().getState()).isEqualTo(JobState.COMPLETED);
assertThat(jobForJobId.getShells()).isEmpty();
assertThat(jobForJobId.getRelationships()).isEmpty();
assertThat(jobForJobId.getSubmodels()).isEmpty();
assertThat(jobForJobId.getTombstones()).hasSize(1);
final Tombstone actualTombstone = jobForJobId.getTombstones().get(0);
assertThat(actualTombstone.getProcessingError().getRootCauses()).hasSize(1);
assertThat(actualTombstone.getProcessingError().getRootCauses().get(0)).contains(
"Asset could not be negotiated for providerWithSuffix 'https://test.edc.io/api/v1/dsp', BPN 'BPNL00000000TEST', catalogItem");
}

@Test
void shouldCreateDetailedTombstoneForEdcErrors() {
// Arrange
final String globalAssetId = "urn:uuid:334cce52-1f52-4bc9-9dd1-410bbe497bbc";

WiremockSupport.successfulSemanticModelRequest();
WiremockSupport.successfulSemanticHubRequests();
WiremockSupport.successfulDiscovery();

failedRegistryRequestEdcError();

final RegisterJob request = WiremockSupport.jobRequest(globalAssetId, TEST_BPN, 4);

// Act
final JobHandle jobHandle = irsService.registerItemJob(request);

// Assert
assertThat(jobHandle.getId()).isNotNull();
waitForCompletion(jobHandle);
final Jobs jobForJobId = irsService.getJobForJobId(jobHandle.getId(), false);

assertThat(jobForJobId.getJob().getState()).isEqualTo(JobState.COMPLETED);
assertThat(jobForJobId.getShells()).isEmpty();
assertThat(jobForJobId.getRelationships()).isEmpty();
assertThat(jobForJobId.getSubmodels()).isEmpty();
assertThat(jobForJobId.getTombstones()).hasSize(1);
final Tombstone actualTombstone = jobForJobId.getTombstones().get(0);
assertThat(actualTombstone.getProcessingError().getRootCauses()).hasSize(1);
assertThat(actualTombstone.getProcessingError().getRootCauses().get(0)).contains(
"502 Bad Gateway");
}

@Test
void shouldCreateDetailedTombstoneForDiscoveryErrors() {
// Arrange
final String globalAssetId = "urn:uuid:334cce52-1f52-4bc9-9dd1-410bbe497bbc";

WiremockSupport.successfulSemanticModelRequest();
WiremockSupport.successfulSemanticHubRequests();
WiremockSupport.failedEdcDiscovery();

failedRegistryRequestEdcError();

final RegisterJob request = WiremockSupport.jobRequest(globalAssetId, TEST_BPN, 4);

// Act
final JobHandle jobHandle = irsService.registerItemJob(request);

// Assert
assertThat(jobHandle.getId()).isNotNull();
waitForCompletion(jobHandle);
final Jobs jobForJobId = irsService.getJobForJobId(jobHandle.getId(), false);

assertThat(jobForJobId.getJob().getState()).isEqualTo(JobState.COMPLETED);
assertThat(jobForJobId.getShells()).isEmpty();
assertThat(jobForJobId.getRelationships()).isEmpty();
assertThat(jobForJobId.getSubmodels()).isEmpty();
assertThat(jobForJobId.getTombstones()).hasSize(1);
final Tombstone actualTombstone = jobForJobId.getTombstones().get(0);
assertThat(actualTombstone.getProcessingError().getRootCauses()).hasSize(1);
assertThat(actualTombstone.getProcessingError().getRootCauses().get(0)).contains(
"No EDC Endpoints could be discovered for BPN '%s'".formatted(TEST_BPN));
}

private void successfulRegistryAndDataRequest(final String globalAssetId, final String idShort, final String bpn,
final String batchFileName, final String sbomFileName) {

Expand Down Expand Up @@ -306,6 +403,24 @@ private void successfulNegotiation(final String edcAssetId) {
endpointDataReferenceStorage.put(contractAgreementId, createEndpointDataReference(contractAgreementId));
}

private void failedRegistryRequestMissmatchPolicy() {
final String registryEdcAssetId = "registry-asset";
failedPolicyMissmatchNegotiation(registryEdcAssetId);
}

private void failedRegistryRequestEdcError() {
failedNegotiation();
}

private void failedPolicyMissmatchNegotiation(final String edcAssetId) {
final String contractAgreementId = "%s:%s:%s".formatted(randomUUID(), edcAssetId, randomUUID());
SubmodelFacadeWiremockSupport.prepareMissmatchPolicyCatalog(edcAssetId, contractAgreementId);
}

private void failedNegotiation() {
SubmodelFacadeWiremockSupport.prepareFailingCatalog();
}

private void waitForCompletion(final JobHandle jobHandle) {
Awaitility.await()
.timeout(Duration.ofSeconds(35))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ static void successfulDiscovery() {
stubFor(DiscoveryServiceWiremockSupport.postEdcDiscovery200());
}

static void failedEdcDiscovery() {
stubFor(DiscoveryServiceWiremockSupport.postDiscoveryFinder200());
stubFor(DiscoveryServiceWiremockSupport.postEdcDiscovery200Empty());
}

static String encodedId(final String shellId) {
return encodeBase64String(shellId.getBytes(StandardCharsets.UTF_8));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import java.util.List;

import io.github.resilience4j.core.functions.Either;
import io.github.resilience4j.retry.RetryRegistry;
import org.eclipse.tractusx.irs.aaswrapper.job.AASTransferProcess;
import org.eclipse.tractusx.irs.aaswrapper.job.ItemContainer;
Expand All @@ -55,7 +56,7 @@ class DigitalTwinDelegateTest {
void shouldFillItemContainerWithShell() throws RegistryServiceException {
// given
when(digitalTwinRegistryService.fetchShells(any())).thenReturn(
List.of(shell("", shellDescriptor(List.of(submodelDescriptorWithoutHref("any"))))));
List.of(Either.right(shell("", shellDescriptor(List.of(submodelDescriptorWithoutHref("any")))))));

// when
final ItemContainer result = digitalTwinDelegate.process(ItemContainer.builder(), jobParameter(),
Expand All @@ -72,11 +73,11 @@ void shouldFillItemContainerWithShell() throws RegistryServiceException {
void shouldFillItemContainerWithShellAndContractAgreementIdWhenAuditFlag() throws RegistryServiceException {
// given
when(digitalTwinRegistryService.fetchShells(any())).thenReturn(
List.of(shell("", shellDescriptor(List.of(submodelDescriptorWithoutHref("any"))))));
List.of(Either.right(shell("", shellDescriptor(List.of(submodelDescriptorWithoutHref("any")))))));

// when
final ItemContainer result = digitalTwinDelegate.process(ItemContainer.builder(), jobParameterAuditContractNegotiation(),
new AASTransferProcess("id", 0), createKey());
final ItemContainer result = digitalTwinDelegate.process(ItemContainer.builder(),
jobParameterAuditContractNegotiation(), new AASTransferProcess("id", 0), createKey());

// then
assertThat(result).isNotNull();
Expand All @@ -86,10 +87,11 @@ void shouldFillItemContainerWithShellAndContractAgreementIdWhenAuditFlag() throw
}

@Test
void shouldFillItemContainerWithShellAndSubmodelDescriptorsWhenDepthReached() throws RegistryServiceException {
void shouldFillItemContainerWithShellAndSubmodelDescriptorsWhenDepthReached()
throws RegistryServiceException {
// given
when(digitalTwinRegistryService.fetchShells(any())).thenReturn(
List.of(shell("", shellDescriptor(List.of(submodelDescriptorWithoutHref("any"))))));
List.of(Either.right(shell("", shellDescriptor(List.of(submodelDescriptorWithoutHref("any")))))));
final JobParameter jobParameter = JobParameter.builder().depth(1).aspects(List.of()).build();

// when
Expand Down Expand Up @@ -132,14 +134,16 @@ void shouldCreateTombstoneIfBPNEmpty() {
assertThat(result).isNotNull();
assertThat(result.getTombstones()).hasSize(1);
assertThat(result.getTombstones().get(0).getCatenaXId()).isEqualTo("itemId");
assertThat(result.getTombstones().get(0).getProcessingError().getErrorDetail()).isEqualTo("Can't get relationship without a BPN");
assertThat(result.getTombstones().get(0).getProcessingError().getErrorDetail()).isEqualTo(
"Can't get relationship without a BPN");
assertThat(result.getTombstones().get(0).getProcessingError().getProcessStep()).isEqualTo(
ProcessStep.DIGITAL_TWIN_REQUEST);
}

private static PartChainIdentificationKey createKey() {
return PartChainIdentificationKey.builder().globalAssetId("itemId").bpn("bpn123").build();
}

private static PartChainIdentificationKey createKeyWithoutBpn() {
return PartChainIdentificationKey.builder().globalAssetId("itemId").build();
}
Expand Down
Loading

0 comments on commit b5badde

Please sign in to comment.