Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/538 propagate exceptions via either and add suppressed #769

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1cb33ca
fix(impl): [#538] propagate exceptions to tombstones using Either and…
dsmf Apr 11, 2024
db48632
Update owasp.yml
dsmf Jun 27, 2024
9b01f6c
Merge branch 'main' into 538-propagate-exceptions-via-Either-and-addS…
dsmf Jun 27, 2024
989244d
Merge branch 'main' into 538-propagate-exceptions-via-Either-and-addS…
dsmf Jun 27, 2024
e3c272a
Update irs-common/src/main/java/org/eclipse/tractusx/irs/common/util/…
dsmf Jun 28, 2024
e4d9216
Update irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/clie…
dsmf Jun 28, 2024
26eab7a
Merge pull request #737 from dsmf/538-propagate-exceptions-via-Either…
ds-jhartmann Jul 8, 2024
12677d7
fix(registry-client):[#538] add rootCauses field in ErrorDetails to d…
ds-jhartmann Jul 8, 2024
aa23c8f
fix(registry-client):[#538] add exception for case that no edc urls c…
ds-jhartmann Jul 9, 2024
a33e2c3
Merge branch 'refs/heads/main' into fix/538-propagate-exceptions-via-…
ds-jhartmann Jul 9, 2024
4b752d5
fix(registry-client):[#538] adjust openapi definition
ds-jhartmann Jul 9, 2024
9b0d66b
Merge branch 'refs/heads/main' into fix/538-propagate-exceptions-via-…
ds-jhartmann Jul 9, 2024
78854e6
fix(irs-models):[#538] add tombstone tests for suppressed exceptions
ds-jhartmann Jul 10, 2024
8f918af
fix(irs-models):[#538] refactoring
ds-jhartmann Jul 10, 2024
dbc501a
fix(irs-api):[#538] fix code-smells
ds-jhartmann Jul 10, 2024
8bd2911
fix(irs-api):[#538] exclude irs-testing from coverage
ds-jhartmann Jul 10, 2024
0467380
chore(depedencies):[#xxx] update spring boot to 3.1.12
dsmf Jul 10, 2024
20ce541
Merge pull request #785 from dsmf/chore/update-spring-boot-to-3_1_12
ds-jhartmann Jul 10, 2024
4d80fab
fix(deps):[#538] update DEPENDENCIES
ds-jhartmann Jul 10, 2024
e2021e7
fix(deps):[#538] update DEPENDENCIES
ds-jhartmann Jul 10, 2024
d7ec6ac
Merge branch 'refs/heads/main' into fix/538-propagate-exceptions-via-…
ds-jhartmann Jul 11, 2024
caea0b0
fix(deps):[#538] update DEPENDENCIES
ds-jhartmann Jul 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading