diff --git a/CHANGELOG.md b/CHANGELOG.md index 19dabeee3d..cc94ccbe43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 @@ -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 diff --git a/DEPENDENCIES b/DEPENDENCIES index c37433cca7..79befad7bb 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -134,33 +134,33 @@ maven/mavencentral/io.github.resilience4j/resilience4j-retry/2.1.0, Apache-2.0, maven/mavencentral/io.github.resilience4j/resilience4j-spring-boot3/2.1.0, Apache-2.0, approved, #10913 maven/mavencentral/io.github.resilience4j/resilience4j-spring6/2.1.0, Apache-2.0, approved, #10915 maven/mavencentral/io.github.resilience4j/resilience4j-timelimiter/2.1.0, Apache-2.0, approved, #10166 -maven/mavencentral/io.micrometer/micrometer-commons/1.11.11, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 -maven/mavencentral/io.micrometer/micrometer-core/1.11.11, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 -maven/mavencentral/io.micrometer/micrometer-observation/1.11.11, Apache-2.0, approved, #9242 +maven/mavencentral/io.micrometer/micrometer-commons/1.11.12, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 +maven/mavencentral/io.micrometer/micrometer-core/1.11.12, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 +maven/mavencentral/io.micrometer/micrometer-observation/1.11.12, Apache-2.0, approved, #9242 maven/mavencentral/io.micrometer/micrometer-registry-prometheus/1.11.4, Apache-2.0, approved, #9805 maven/mavencentral/io.minio/minio/8.5.9, Apache-2.0, approved, #9097 maven/mavencentral/io.netty.incubator/netty-incubator-transport-classes-io_uring/0.0.21.Final, Apache-2.0, approved, #9622 maven/mavencentral/io.netty.incubator/netty-incubator-transport-native-io_uring/0.0.21.Final, GPL-2.0-only WITH Linux-syscall-note OR MIT AND Apache-2.0 AND MIT, approved, #9649 -maven/mavencentral/io.netty/netty-buffer/4.1.109.Final, Apache-2.0, approved, CQ21842 -maven/mavencentral/io.netty/netty-codec-dns/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-http/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-http2/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec-mqtt/4.1.109.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 -maven/mavencentral/io.netty/netty-codec-socks/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-codec/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-common/4.1.109.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 -maven/mavencentral/io.netty/netty-handler-proxy/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-handler/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.109.Final, Apache-2.0, approved, #6367 -maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.109.Final, Apache-2.0, approved, #7004 -maven/mavencentral/io.netty/netty-resolver-dns/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-resolver/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-buffer/4.1.110.Final, Apache-2.0, approved, CQ21842 +maven/mavencentral/io.netty/netty-codec-dns/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-http2/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec-mqtt/4.1.110.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 +maven/mavencentral/io.netty/netty-codec-socks/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-codec/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-common/4.1.110.Final, Apache-2.0 AND MIT AND CC0-1.0, approved, CQ21843 +maven/mavencentral/io.netty/netty-handler-proxy/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-handler/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver-dns-classes-macos/4.1.110.Final, Apache-2.0, approved, #6367 +maven/mavencentral/io.netty/netty-resolver-dns-native-macos/4.1.110.Final, Apache-2.0, approved, #7004 +maven/mavencentral/io.netty/netty-resolver-dns/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-resolver/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.netty/netty-tcnative-boringssl-static/2.0.65.Final, Apache-2.0 OR LicenseRef-Public-Domain OR BSD-2-Clause OR MIT, approved, CQ15280 maven/mavencentral/io.netty/netty-tcnative-classes/2.0.65.Final, Apache-2.0, approved, clearlydefined -maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.109.Final, Apache-2.0, approved, #6366 -maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 -maven/mavencentral/io.netty/netty-transport/4.1.109.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-classes-epoll/4.1.110.Final, Apache-2.0, approved, #6366 +maven/mavencentral/io.netty/netty-transport-native-epoll/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport-native-unix-common/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 +maven/mavencentral/io.netty/netty-transport/4.1.110.Final, Apache-2.0 AND BSD-3-Clause AND MIT, approved, CQ20926 maven/mavencentral/io.opentelemetry/opentelemetry-api/1.25.0, Apache-2.0, approved, clearlydefined maven/mavencentral/io.opentelemetry/opentelemetry-api/1.32.0, Apache-2.0, approved, #11682 maven/mavencentral/io.opentelemetry/opentelemetry-context/1.25.0, Apache-2.0, approved, clearlydefined @@ -192,10 +192,10 @@ maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-onl maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.2, BSD-3-Clause, approved, ee4j.jaxb maven/mavencentral/javax.jms/javax.jms-api/2.0.1, CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0, approved, #1516 maven/mavencentral/junit/junit/4.13.2, EPL-2.0, approved, CQ23636 -maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.13, Apache-2.0, approved, #7164 +maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.16, Apache-2.0, approved, #7164 maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.14.4, Apache-2.0, approved, #7164 maven/mavencentral/net.bytebuddy/byte-buddy/1.12.21, Apache-2.0 AND BSD-3-Clause, approved, #1811 -maven/mavencentral/net.bytebuddy/byte-buddy/1.14.13, Apache-2.0 AND BSD-3-Clause, approved, #7163 +maven/mavencentral/net.bytebuddy/byte-buddy/1.14.16, Apache-2.0 AND BSD-3-Clause, approved, #7163 maven/mavencentral/net.datafaker/datafaker/1.9.0, Apache-2.0, approved, #8797 maven/mavencentral/net.debasishg/redisclient_2.13/3.42, Apache-2.0, approved, clearlydefined maven/mavencentral/net.java.dev.jna/jna/5.12.1, Apache-2.0 OR LGPL-2.1-or-later, approved, #3217 @@ -225,10 +225,10 @@ maven/mavencentral/org.apache.logging.log4j/log4j-core/2.20.0, Apache-2.0 AND (A maven/mavencentral/org.apache.logging.log4j/log4j-jul/2.20.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.logging.log4j/log4j-slf4j2-impl/2.20.0, Apache-2.0, approved, #8801 maven/mavencentral/org.apache.logging.log4j/log4j-to-slf4j/2.20.0, Apache-2.0, approved, #8799 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.20, Apache-2.0 AND (EPL-2.0 OR (GPL-2.0 WITH Classpath-exception-2.0)) AND CDDL-1.0 AND (CDDL-1.1 OR (GPL-2.0-only WITH Classpath-exception-2.0)) AND EPL-2.0, approved, #15195 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.24, Apache-2.0 AND (EPL-2.0 OR (GPL-2.0 WITH Classpath-exception-2.0)) AND CDDL-1.0 AND (CDDL-1.1 OR (GPL-2.0-only WITH Classpath-exception-2.0)) AND EPL-2.0, approved, #15195 maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.25, Apache-2.0 AND (EPL-2.0 OR (GPL-2.0 WITH Classpath-exception-2.0)) AND CDDL-1.0 AND (CDDL-1.1 OR (GPL-2.0-only WITH Classpath-exception-2.0)) AND EPL-2.0, approved, #15195 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.20, Apache-2.0, approved, #6997 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.20, Apache-2.0, approved, #7920 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.24, Apache-2.0, approved, #6997 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.24, Apache-2.0, approved, #7920 maven/mavencentral/org.apiguardian/apiguardian-api/1.1.2, Apache-2.0, approved, clearlydefined maven/mavencentral/org.aspectj/aspectjweaver/1.9.22, Apache-2.0 AND BSD-3-Clause AND EPL-1.0 AND BSD-3-Clause AND Apache-1.1, approved, #15252 maven/mavencentral/org.assertj/assertj-core/3.24.2, Apache-2.0, approved, #6161 @@ -373,27 +373,27 @@ maven/mavencentral/org.slf4j/slf4j-api/2.0.13, MIT, approved, #5915 maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.2.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.2.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/3.1.11, Apache-2.0, approved, #9348 -maven/mavencentral/org.springframework.boot/spring-boot-actuator/3.1.11, Apache-2.0, approved, #9342 -maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.11, Apache-2.0, approved, #9341 -maven/mavencentral/org.springframework.boot/spring-boot-configuration-metadata/3.1.11, Apache-2.0, approved, #11032 -maven/mavencentral/org.springframework.boot/spring-boot-properties-migrator/3.1.11, Apache-2.0, approved, #10675 -maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/3.1.11, Apache-2.0, approved, #9344 -maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.11, Apache-2.0, approved, #9338 -maven/mavencentral/org.springframework.boot/spring-boot-starter-json/3.1.11, Apache-2.0, approved, #9336 -maven/mavencentral/org.springframework.boot/spring-boot-starter-log4j2/3.1.11, Apache-2.0, approved, #8800 -maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/3.1.11, Apache-2.0, approved, #9343 -maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-client/3.1.11, Apache-2.0, approved, #8806 -maven/mavencentral/org.springframework.boot/spring-boot-starter-security/3.1.11, Apache-2.0, approved, #9337 -maven/mavencentral/org.springframework.boot/spring-boot-starter-test/3.1.11, Apache-2.0, approved, #9353 -maven/mavencentral/org.springframework.boot/spring-boot-starter-tomcat/3.1.11, Apache-2.0, approved, #9351 -maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/3.1.11, Apache-2.0, approved, #9335 -maven/mavencentral/org.springframework.boot/spring-boot-starter-web/3.1.11, Apache-2.0, approved, #9347 -maven/mavencentral/org.springframework.boot/spring-boot-starter/3.1.11, Apache-2.0, approved, #9349 -maven/mavencentral/org.springframework.boot/spring-boot-test-autoconfigure/3.1.11, Apache-2.0, approved, #9339 -maven/mavencentral/org.springframework.boot/spring-boot-test/3.1.11, Apache-2.0, approved, #9346 -maven/mavencentral/org.springframework.boot/spring-boot/3.1.11, Apache-2.0, approved, #9352 -maven/mavencentral/org.springframework.data/spring-data-commons/3.1.11, Apache-2.0, approved, #8805 +maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/3.1.12, Apache-2.0, approved, #9348 +maven/mavencentral/org.springframework.boot/spring-boot-actuator/3.1.12, Apache-2.0, approved, #9342 +maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.12, Apache-2.0, approved, #9341 +maven/mavencentral/org.springframework.boot/spring-boot-configuration-metadata/3.1.12, Apache-2.0, approved, #11032 +maven/mavencentral/org.springframework.boot/spring-boot-properties-migrator/3.1.12, Apache-2.0, approved, #10675 +maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/3.1.12, Apache-2.0, approved, #9344 +maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.12, Apache-2.0, approved, #9338 +maven/mavencentral/org.springframework.boot/spring-boot-starter-json/3.1.12, Apache-2.0, approved, #9336 +maven/mavencentral/org.springframework.boot/spring-boot-starter-log4j2/3.1.12, Apache-2.0, approved, #8800 +maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/3.1.12, Apache-2.0, approved, #9343 +maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-client/3.1.12, Apache-2.0, approved, #8806 +maven/mavencentral/org.springframework.boot/spring-boot-starter-security/3.1.12, Apache-2.0, approved, #9337 +maven/mavencentral/org.springframework.boot/spring-boot-starter-test/3.1.12, Apache-2.0, approved, #9353 +maven/mavencentral/org.springframework.boot/spring-boot-starter-tomcat/3.1.12, Apache-2.0, approved, #9351 +maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/3.1.12, Apache-2.0, approved, #9335 +maven/mavencentral/org.springframework.boot/spring-boot-starter-web/3.1.12, Apache-2.0, approved, #9347 +maven/mavencentral/org.springframework.boot/spring-boot-starter/3.1.12, Apache-2.0, approved, #9349 +maven/mavencentral/org.springframework.boot/spring-boot-test-autoconfigure/3.1.12, Apache-2.0, approved, #9339 +maven/mavencentral/org.springframework.boot/spring-boot-test/3.1.12, Apache-2.0, approved, #9346 +maven/mavencentral/org.springframework.boot/spring-boot/3.1.12, Apache-2.0, approved, #9352 +maven/mavencentral/org.springframework.data/spring-data-commons/3.1.12, Apache-2.0, approved, #8805 maven/mavencentral/org.springframework.security/spring-security-config/6.1.9, Apache-2.0, approved, #9736 maven/mavencentral/org.springframework.security/spring-security-core/6.1.9, Apache-2.0, approved, #9801 maven/mavencentral/org.springframework.security/spring-security-crypto/6.1.9, Apache-2.0 AND ISC, approved, #9735 @@ -402,15 +402,16 @@ maven/mavencentral/org.springframework.security/spring-security-oauth2-core/6.1. maven/mavencentral/org.springframework.security/spring-security-oauth2-jose/6.1.9, Apache-2.0, approved, #9345 maven/mavencentral/org.springframework.security/spring-security-test/6.1.9, Apache-2.0, approved, #10674 maven/mavencentral/org.springframework.security/spring-security-web/6.1.9, Apache-2.0, approved, #9800 -maven/mavencentral/org.springframework/spring-aop/6.0.19, Apache-2.0, approved, #5940 -maven/mavencentral/org.springframework/spring-beans/6.0.19, Apache-2.0, approved, #5937 -maven/mavencentral/org.springframework/spring-context/6.0.19, Apache-2.0, approved, #5936 -maven/mavencentral/org.springframework/spring-core/6.0.19, Apache-2.0 AND BSD-3-Clause, approved, #5948 -maven/mavencentral/org.springframework/spring-expression/6.0.19, Apache-2.0, approved, #3284 -maven/mavencentral/org.springframework/spring-jcl/6.0.19, Apache-2.0, approved, #3283 -maven/mavencentral/org.springframework/spring-test/6.0.19, Apache-2.0, approved, #7003 -maven/mavencentral/org.springframework/spring-web/6.0.19, Apache-2.0, approved, #5942 -maven/mavencentral/org.springframework/spring-webmvc/6.0.19, Apache-2.0, approved, #5944 +maven/mavencentral/org.springframework/spring-aop/6.0.21, Apache-2.0, approved, #5940 +maven/mavencentral/org.springframework/spring-beans/6.0.20, Apache-2.0, approved, #5937 +maven/mavencentral/org.springframework/spring-beans/6.0.21, Apache-2.0, approved, #5937 +maven/mavencentral/org.springframework/spring-context/6.0.21, Apache-2.0, approved, #5936 +maven/mavencentral/org.springframework/spring-core/6.0.21, Apache-2.0 AND BSD-3-Clause, approved, #5948 +maven/mavencentral/org.springframework/spring-expression/6.0.21, Apache-2.0, approved, #3284 +maven/mavencentral/org.springframework/spring-jcl/6.0.21, Apache-2.0, approved, #3283 +maven/mavencentral/org.springframework/spring-test/6.0.21, Apache-2.0, approved, #7003 +maven/mavencentral/org.springframework/spring-web/6.0.21, Apache-2.0, approved, #5942 +maven/mavencentral/org.springframework/spring-webmvc/6.0.21, Apache-2.0, approved, #5944 maven/mavencentral/org.testcontainers/junit-jupiter/1.18.3, MIT, approved, #7941 maven/mavencentral/org.testcontainers/junit-jupiter/1.19.7, MIT, approved, #10344 maven/mavencentral/org.testcontainers/testcontainers/1.18.3, MIT, approved, #7938 diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index ba68dad725..b51cc259b3 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -2572,6 +2572,10 @@ components: format: int32 maximum: 2147483647 minimum: 0 + rootCauses: + type: array + items: + type: string ProtocolInformation: type: object additionalProperties: false diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegate.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegate.java index dbec718442..5a50fd5887 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegate.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegate.java @@ -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; @@ -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()); @@ -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())) { @@ -92,6 +101,12 @@ public ItemContainer process(final ItemContainer.ItemContainerBuilder itemContai return itemContainerBuilder.build(); } + private static RegistryServiceException shellNotFound(final Collection> 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; diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java index 6a3ae93082..477f072e5b 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsWireMockIntegrationTest.java @@ -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; @@ -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) { @@ -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)) diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/WiremockSupport.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/WiremockSupport.java index ae50633ce3..5c73b9dc13 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/WiremockSupport.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/WiremockSupport.java @@ -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)); } diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegateTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegateTest.java index 25b9a78cc9..c5347581b3 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegateTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/aaswrapper/job/delegate/DigitalTwinDelegateTest.java @@ -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; @@ -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(), @@ -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(); @@ -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 @@ -132,7 +134,8 @@ 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); } @@ -140,6 +143,7 @@ void shouldCreateTombstoneIfBPNEmpty() { private static PartChainIdentificationKey createKey() { return PartChainIdentificationKey.builder().globalAssetId("itemId").bpn("bpn123").build(); } + private static PartChainIdentificationKey createKeyWithoutBpn() { return PartChainIdentificationKey.builder().globalAssetId("itemId").build(); } diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/component/TombstoneTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/component/TombstoneTest.java new file mode 100644 index 0000000000..bded98daed --- /dev/null +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/component/TombstoneTest.java @@ -0,0 +1,144 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.component; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import io.github.resilience4j.retry.RetryRegistry; +import org.eclipse.tractusx.irs.component.enums.ProcessStep; +import org.junit.jupiter.api.Test; + +class TombstoneTest { + + @Test + void fromTombstoneTest() { + // arrange + final String catenaXId = "5e3e9060-ba73-4d5d-a6c8-dfd5123f4d99"; + final IllegalArgumentException illegalArgumentException = new IllegalArgumentException( + "Some funny error occur"); + final String endPointUrl = "http://localhost/dummy/interfaceinformation/urn:uuid:8a61c8db-561e-4db0-84ec-a693fc5ffdf6"; + + final ProcessingError processingError = ProcessingError.builder() + .withProcessStep(ProcessStep.SUBMODEL_REQUEST) + .withRetryCounter(RetryRegistry.ofDefaults() + .getDefaultConfig() + .getMaxAttempts()) + .withLastAttempt(ZonedDateTime.now(ZoneOffset.UTC)) + .withErrorDetail("Some funny error occur") + .build(); + + final Tombstone expectedTombstone = Tombstone.builder() + .catenaXId(catenaXId) + .endpointURL(endPointUrl) + .processingError(processingError) + .build(); + + //act + final Tombstone tombstone = Tombstone.from(catenaXId, endPointUrl, illegalArgumentException, + RetryRegistry.ofDefaults().getDefaultConfig().getMaxAttempts(), ProcessStep.SUBMODEL_REQUEST); + + // assert + assertThat(tombstone).isNotNull(); + assertThat(tombstone.getProcessingError().getErrorDetail()).isEqualTo(processingError.getErrorDetail()); + assertThat(tombstone.getProcessingError().getRetryCounter()).isEqualTo(processingError.getRetryCounter()); + assertThat(zonedDateTimeExcerpt(tombstone.getProcessingError().getLastAttempt())).isEqualTo( + zonedDateTimeExcerpt(processingError.getLastAttempt())); + assertThat(tombstone.getCatenaXId()).isEqualTo(expectedTombstone.getCatenaXId()); + assertThat(tombstone.getEndpointURL()).isEqualTo(expectedTombstone.getEndpointURL()); + assertThat(tombstone.getProcessingError().getRetryCounter()).isEqualTo( + expectedTombstone.getProcessingError().getRetryCounter()); + } + + @Test + void shouldUseSuppressedExceptionWhenPresent() { + // arrange + final String mainExceptionMessage = "Exception occurred."; + final Exception exception = new Exception(mainExceptionMessage); + final String suppressedExceptionMessage = "Suppressed Exception which occurred deeper."; + exception.addSuppressed(new Exception(suppressedExceptionMessage)); + final Throwable[] suppressed = exception.getSuppressed(); + + // act + final Tombstone from = Tombstone.from("testId", "testUrl", exception, suppressed, 1, + ProcessStep.DIGITAL_TWIN_REQUEST); + + // assert + assertThat(from.getProcessingError().getErrorDetail()).isEqualTo(exception.getMessage()); + assertThat(from.getProcessingError().getRootCauses()).contains(suppressedExceptionMessage); + } + + @Test + void shouldUseDeepSuppressedExceptionWhenPresent() { + // arrange + final Exception exception = new Exception("Exception occurred."); + + final Exception rootCause = new Exception("Wrapper exception to the root cause"); + rootCause.addSuppressed(new Exception("Root cause of the exception")); + + final Exception suppressedWrapperException = new Exception( + "Suppressed Exception which was added through Futures.", rootCause); + exception.addSuppressed(suppressedWrapperException); + + final Throwable[] suppressed = exception.getSuppressed(); + + // act + final Tombstone from = Tombstone.from("testId", "testUrl", exception, suppressed, 1, + ProcessStep.DIGITAL_TWIN_REQUEST); + + // assert + assertThat(from.getProcessingError().getErrorDetail()).isEqualTo(exception.getMessage()); + assertThat(from.getProcessingError().getRootCauses()).contains("Root cause of the exception"); + } + + @Test + void shouldUseExceptionMessageWhenSuppressedExceptionNotPresent() { + // arrange + final String mainExceptionMessage = "Exception occurred."; + final Exception exception = new Exception(mainExceptionMessage); + final Throwable[] suppressed = exception.getSuppressed(); + + // act + final Tombstone from = Tombstone.from("testId", "testUrl", exception, suppressed, 1, + ProcessStep.DIGITAL_TWIN_REQUEST); + + // assert + assertThat(from.getProcessingError().getErrorDetail()).isEqualTo(exception.getMessage()); + assertThat(from.getProcessingError().getRootCauses()).isEmpty(); + } + + private String zonedDateTimeExcerpt(ZonedDateTime dateTime) { + return new StringBuilder().append(dateTime.getYear()) + .append("-") + .append(dateTime.getMonth()) + .append("-") + .append(dateTime.getDayOfMonth()) + .append("T") + .append(dateTime.getHour()) + .append(":") + .append(dateTime.getMinute()) + .append(":") + .append(dateTime.getSecond()) + .toString(); + } + +} diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/component/tombstone/TombStoneTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/component/tombstone/TombStoneTest.java deleted file mode 100644 index bd8a0b97c6..0000000000 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/component/tombstone/TombStoneTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2022,2024 - * 2022: ZF Friedrichshafen AG - * 2022: ISTOS GmbH - * 2022,2024: Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * 2022,2023: BOSCH AG - * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Apache License, Version 2.0 which is available at - * https://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. - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ -package org.eclipse.tractusx.irs.component.tombstone; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.ZoneOffset; -import java.time.ZonedDateTime; - -import io.github.resilience4j.retry.RetryRegistry; -import org.eclipse.tractusx.irs.component.ProcessingError; -import org.eclipse.tractusx.irs.component.Tombstone; -import org.eclipse.tractusx.irs.component.enums.ProcessStep; -import org.junit.jupiter.api.Test; - -class TombStoneTest { - - Tombstone tombstone; - - @Test - void fromTombstoneTest() { - // arrange - String catenaXId = "5e3e9060-ba73-4d5d-a6c8-dfd5123f4d99"; - IllegalArgumentException illegalArgumentException = new IllegalArgumentException("Some funny error occur"); - String endPointUrl = "http://localhost/dummy/interfaceinformation/urn:uuid:8a61c8db-561e-4db0-84ec-a693fc5ffdf6"; - - ProcessingError processingError = ProcessingError.builder() - .withProcessStep(ProcessStep.SUBMODEL_REQUEST) - .withRetryCounter(RetryRegistry.ofDefaults() - .getDefaultConfig() - .getMaxAttempts()) - .withLastAttempt(ZonedDateTime.now(ZoneOffset.UTC)) - .withErrorDetail("Some funny error occur") - .build(); - - Tombstone expectedTombstone = Tombstone.builder() - .catenaXId(catenaXId) - .endpointURL(endPointUrl) - .processingError(processingError) - .build(); - - //act - tombstone = Tombstone.from(catenaXId, endPointUrl, illegalArgumentException, - RetryRegistry.ofDefaults().getDefaultConfig().getMaxAttempts(), ProcessStep.SUBMODEL_REQUEST); - - // assert - assertThat(tombstone).isNotNull(); - assertThat(tombstone.getProcessingError().getErrorDetail()).isEqualTo(processingError.getErrorDetail()); - assertThat(tombstone.getProcessingError().getRetryCounter()).isEqualTo(processingError.getRetryCounter()); - assertThat(zonedDateTimeExcerpt(tombstone.getProcessingError().getLastAttempt())).isEqualTo( - zonedDateTimeExcerpt(processingError.getLastAttempt())); - assertThat(tombstone.getCatenaXId()).isEqualTo(expectedTombstone.getCatenaXId()); - assertThat(tombstone.getEndpointURL()).isEqualTo(expectedTombstone.getEndpointURL()); - assertThat(tombstone.getProcessingError().getRetryCounter()).isEqualTo( - expectedTombstone.getProcessingError().getRetryCounter()); - } - - private String zonedDateTimeExcerpt(ZonedDateTime dateTime) { - return new StringBuilder().append(dateTime.getYear()) - .append("-") - .append(dateTime.getMonth()) - .append("-") - .append(dateTime.getDayOfMonth()) - .append("T") - .append(dateTime.getHour()) - .append(":") - .append(dateTime.getMinute()) - .append(":") - .append(dateTime.getSecond()) - .toString(); - } - -} diff --git a/irs-common/src/main/java/org/eclipse/tractusx/irs/common/ExceptionUtils.java b/irs-common/src/main/java/org/eclipse/tractusx/irs/common/ExceptionUtils.java new file mode 100644 index 0000000000..59541693e3 --- /dev/null +++ b/irs-common/src/main/java/org/eclipse/tractusx/irs/common/ExceptionUtils.java @@ -0,0 +1,51 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.common; + +import java.util.Collection; + +import io.github.resilience4j.core.functions.Either; + +/** + * Utilities for exception handling + */ +public final class ExceptionUtils { + + private ExceptionUtils() { + // private constructor, utility class + } + + /** + * Adds all exceptions from left side of the given Eithers to the exception. + * + * @param eithers the {@link Either}s + * @param exception the exception + * @param the exception type (left-hand side of {@link Either}) + * @param the object type (right-hand side of {@link Either}) + */ + public static void addSuppressedExceptions(final Collection> eithers, + final Exception exception) { + for (final Either either : eithers) { + if (either.isLeft() && either.getLeft() != null) { + exception.addSuppressed(either.getLeft()); + } + } + } +} diff --git a/irs-common/src/main/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinder.java b/irs-common/src/main/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinder.java index 7895c9f8c1..4a69271811 100644 --- a/irs-common/src/main/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinder.java +++ b/irs-common/src/main/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinder.java @@ -78,12 +78,19 @@ public CompletableFuture getFastestResult(final List log.debug("All of the futures completed"); if (ex != null) { - log.warn("All failed: " + System.lineSeparator() // + log.warn("All failed: " + System.lineSeparator() + // TODO (#538) can we remove logging here and log only up in call hierarchy + exceptions.stream() .map(ExceptionUtils::getStackTrace) .collect(Collectors.joining(System.lineSeparator())), ex); - overallFuture.completeExceptionally(new CompletionExceptions("None successful", exceptions)); + final CompletionExceptions noneSuccessful = new CompletionExceptions("None successful"); + for (final Throwable exception : exceptions) { + noneSuccessful.addSuppressed(exception); + } + + overallFuture.completeExceptionally(noneSuccessful); + } else { overallFuture.complete(null); } @@ -139,11 +146,8 @@ private static Function collectingExceptionsAndThrow(final Lis @ToString public static class CompletionExceptions extends CompletionException { - private final List causes; - - public CompletionExceptions(final String msg, final List causes) { + public CompletionExceptions(final String msg) { super(msg); - this.causes = causes; } } diff --git a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/ExceptionUtilsTest.java b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/ExceptionUtilsTest.java new file mode 100644 index 0000000000..69fa88d06a --- /dev/null +++ b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/ExceptionUtilsTest.java @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2022,2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021,2024 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License, Version 2.0 which is available at + * https://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. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +package org.eclipse.tractusx.irs.common; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import io.github.resilience4j.core.functions.Either; +import org.junit.jupiter.api.Test; + +/** + * Test for class {@link ExceptionUtils} + */ +class ExceptionUtilsTest { + + @Test + void addSuppressedExceptionsTest() { + + // ARRANGE + final List> eithers = new ArrayList<>(); + eithers.add(Either.left(new RuntimeException("Some runtime exception"))); + eithers.add(Either.right("Some string")); + eithers.add(Either.left(new IllegalArgumentException("Another exception"))); + eithers.add(Either.left(null)); + + final Exception mainException = new Exception("Main exception"); + + // ACT + ExceptionUtils.addSuppressedExceptions(eithers, mainException); + + // ASSERT + final Throwable[] suppressedExceptions = mainException.getSuppressed(); + assertEquals(2, suppressedExceptions.length); // Expecting two suppressed exceptions + assertEquals("Some runtime exception", suppressedExceptions[0].getMessage()); + assertEquals("Another exception", suppressedExceptions[1].getMessage()); + } +} \ No newline at end of file diff --git a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinderTest.java b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinderTest.java index 06e51406ad..c13692265c 100644 --- a/irs-common/src/test/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinderTest.java +++ b/irs-common/src/test/java/org/eclipse/tractusx/irs/common/util/concurrent/ResultFinderTest.java @@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -106,10 +107,11 @@ void withAllCompletableFuturesFailing_itShouldThrow() { .extracting(Throwable::getCause) .isInstanceOf(ResultFinder.CompletionExceptions.class) .extracting(collectedFailures -> (ResultFinder.CompletionExceptions) collectedFailures) - .extracting(ResultFinder.CompletionExceptions::getCauses) + .extracting(ResultFinder.CompletionExceptions::getSuppressed) .describedAs("should have collected all exceptions") - .satisfies(causes -> assertThat( - causes.stream().map(Throwable::getMessage).toList()).containsExactlyInAnyOrder( + .satisfies(causes -> assertThat(Arrays.stream(causes) + .map(Throwable::getMessage) + .toList()).containsExactlyInAnyOrder( "java.lang.RuntimeException: failing 1", "java.lang.RuntimeException: failing 2", "java.lang.RuntimeException: failing 3")); diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java index 9a0ed6bec3..acb0039045 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java @@ -319,12 +319,13 @@ private NegotiationResponse negotiateContract(final EndpointDataReferenceStatus try { response = contractNegotiationService.negotiate(providerWithSuffix, catalogItem, endpointDataReferenceStatus, bpn); - } catch (TransferProcessException | UsagePolicyPermissionException | UsagePolicyExpiredException - | ContractNegotiationException e) { + } catch (TransferProcessException | ContractNegotiationException e) { throw new EdcClientException(("Negotiation failed for endpoint '%s', " + "tokenStatus '%s', " + "providerWithSuffix '%s', catalogItem '%s'").formatted( endpointDataReferenceStatus.endpointDataReference(), endpointDataReferenceStatus.tokenStatus(), - providerWithSuffix, endpointDataReferenceStatus), e); + providerWithSuffix, catalogItem), e); + } catch (UsagePolicyExpiredException | UsagePolicyPermissionException e) { + throw new EdcClientException("Asset could not be negotiated for providerWithSuffix '%s', BPN '%s', catalogItem '%s'".formatted(providerWithSuffix, bpn, catalogItem), e); } return response; } diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/ProcessingError.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/ProcessingError.java index 16dac4e96b..74878c299e 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/ProcessingError.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/ProcessingError.java @@ -24,6 +24,7 @@ package org.eclipse.tractusx.irs.component; import java.time.ZonedDateTime; +import java.util.List; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; @@ -44,6 +45,7 @@ public class ProcessingError { private ProcessStep processStep; private String errorDetail; private ZonedDateTime lastAttempt; + private List rootCauses; @Schema(implementation = Integer.class) @Min(0) diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Tombstone.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Tombstone.java index d16b22a5b3..632485c3e0 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Tombstone.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Tombstone.java @@ -25,7 +25,10 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import java.util.stream.Stream; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; @@ -83,6 +86,38 @@ public static Tombstone from(final String catenaXId, final String endpointURL, f .build(); } + public static Tombstone from(final String globalAssetId, final String endpointURL, final Throwable exception, + final Throwable[] suppressed, final int retryCount, final ProcessStep processStep) { + final ProcessingError processingError = + withProcessingError(processStep, retryCount, exception.getMessage(), suppressed); + return Tombstone.builder() + .endpointURL(endpointURL) + .catenaXId(globalAssetId) + .processingError(processingError) + .build(); + } + + private static ProcessingError withProcessingError(final ProcessStep processStep, final int retryCount, + final String message, final Throwable... suppressed) { + final List rootCauses = Arrays.stream(suppressed).flatMap(Tombstone::getErrorMessages).toList(); + + return ProcessingError.builder() + .withProcessStep(processStep) + .withRetryCounter(retryCount) + .withLastAttempt(ZonedDateTime.now(ZoneOffset.UTC)) + .withErrorDetail(message) + .withRootCauses(rootCauses) + .build(); + } + + private static Stream getErrorMessages(final Throwable throwable) { + final Throwable cause = throwable.getCause(); + if (cause != null && hasSuppressedExceptions(cause)) { + return Arrays.stream(throwable.getCause().getSuppressed()).map(Throwable::getMessage); + } + return Stream.of(throwable.getMessage()); + } + private static ProcessingError withProcessingError(final ProcessStep processStep, final int retryCount, final String exception) { return ProcessingError.builder() @@ -93,4 +128,7 @@ private static ProcessingError withProcessingError(final ProcessStep processStep .build(); } + private static boolean hasSuppressedExceptions(final Throwable exception) { + return exception.getSuppressed().length > 0; + } } diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DigitalTwinRegistryService.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DigitalTwinRegistryService.java index 153acd3140..bd8894babb 100644 --- a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DigitalTwinRegistryService.java +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DigitalTwinRegistryService.java @@ -25,6 +25,7 @@ import java.util.Collection; +import io.github.resilience4j.core.functions.Either; import org.eclipse.tractusx.irs.component.Shell; import org.eclipse.tractusx.irs.registryclient.exceptions.RegistryServiceException; @@ -39,7 +40,7 @@ public interface DigitalTwinRegistryService { * @param bpn the BPN to retrieve the shells for * @return the collection of asset administration shells */ - default Collection lookupShellsByBPN(final String bpn) throws RegistryServiceException { + default Collection> lookupShellsByBPN(final String bpn) throws RegistryServiceException { return fetchShells(lookupShellIdentifiers(bpn)).stream().toList(); } @@ -69,6 +70,6 @@ default Collection lookupShells(final String bpn) throws * @param identifiers the shell identifiers * @return the shell descriptors */ - Collection fetchShells(Collection identifiers) + Collection> fetchShells(Collection identifiers) throws RegistryServiceException; } diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryService.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryService.java index 538e8cfce6..92efd31b35 100644 --- a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryService.java +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryService.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; +import io.github.resilience4j.core.functions.Either; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.irs.component.Shell; @@ -44,12 +45,13 @@ public class CentralDigitalTwinRegistryService implements DigitalTwinRegistrySer private final DigitalTwinRegistryClient digitalTwinRegistryClient; @Override - public Collection fetchShells(final Collection keys) { + public Collection> fetchShells(final Collection keys) { return keys.stream().map(key -> { final String aaShellIdentification = getAAShellIdentificationOrGlobalAssetId(key.shellId()); log.info("Retrieved AAS Identification {} for globalAssetId {}", aaShellIdentification, key.shellId()); - return new Shell("", digitalTwinRegistryClient.getAssetAdministrationShellDescriptor(aaShellIdentification)); + return Either.right(new Shell("", + digitalTwinRegistryClient.getAssetAdministrationShellDescriptor(aaShellIdentification))); }).toList(); } diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java index ea3ec0833d..8706884183 100644 --- a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java @@ -35,9 +35,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import io.github.resilience4j.core.functions.Either; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.irs.common.ExceptionUtils; import org.eclipse.tractusx.irs.common.util.concurrent.ResultFinder; import org.eclipse.tractusx.irs.component.Shell; import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; @@ -56,7 +58,9 @@ */ @RequiredArgsConstructor @Slf4j -@SuppressWarnings("PMD.TooManyMethods") +@SuppressWarnings({ "PMD.TooManyMethods", + "PMD.ExcessiveImports" +}) public class DecentralDigitalTwinRegistryService implements DigitalTwinRegistryService { private static final String TOOK_MS = "{} took {} ms"; @@ -85,7 +89,7 @@ private static Stream>> groupKeys @Override @SuppressWarnings("PMD.AvoidCatchingGenericException") - public Collection fetchShells(final Collection keys) + public Collection> fetchShells(final Collection keys) throws RegistryServiceException { final var watch = new StopWatch(); @@ -96,7 +100,7 @@ public Collection fetchShells(final Collection ke try { final var calledEndpoints = new HashSet(); - final var collectedShells = groupKeysByBpn(keys).flatMap(entry -> { + final List> collectedShells = groupKeysByBpn(keys).flatMap(entry -> { try { return fetchShellDescriptors(entry, calledEndpoints); @@ -104,14 +108,18 @@ public Collection fetchShells(final Collection ke // catching generic exception is intended here, // otherwise Jobs stay in state RUNNING forever log.warn(e.getMessage(), e); - return Stream.empty(); + return Stream.of(Either.left(e)); } }).toList(); - if (collectedShells.isEmpty()) { + if (collectedShells.stream().noneMatch(Either::isRight)) { log.info("No shells found"); - throw new ShellNotFoundException("Unable to find any of the requested shells", calledEndpoints); + + final ShellNotFoundException shellNotFoundException = new ShellNotFoundException( + "Unable to find any of the requested shells", calledEndpoints); + ExceptionUtils.addSuppressedExceptions(collectedShells, shellNotFoundException); + throw shellNotFoundException; } else { log.info("Found {} shell(s) for {} key(s)", collectedShells.size(), keys.size()); return collectedShells; @@ -123,8 +131,9 @@ public Collection fetchShells(final Collection ke } } - private Stream fetchShellDescriptors(final Map.Entry> entry, - final Set calledEndpoints) throws TimeoutException { + private Stream> fetchShellDescriptors( + final Map.Entry> entry, final Set calledEndpoints) + throws TimeoutException { try { @@ -135,15 +144,15 @@ private Stream fetchShellDescriptors(final Map.Entry> fetchShellDescriptors(final Set calledEndpoints, final String bpn, - final List keys) { + private CompletableFuture>> fetchShellDescriptors(final Set calledEndpoints, + final String bpn, final List keys) throws RegistryServiceException { final var watch = new StopWatch(); final String msg = "Fetching %s shells for bpn '%s'".formatted(keys.size(), bpn); @@ -153,6 +162,9 @@ private CompletableFuture> fetchShellDescriptors(final Set c try { final var edcUrls = connectorEndpointsService.fetchConnectorEndpoints(bpn); + if (edcUrls.isEmpty()) { + throw new RegistryServiceException("No EDC Endpoints could be discovered for BPN '%s'".formatted(bpn)); + } log.info("Found {} connector endpoints for bpn '{}'", edcUrls.size(), bpn); calledEndpoints.addAll(edcUrls); @@ -165,7 +177,7 @@ private CompletableFuture> fetchShellDescriptors(final Set c } } - private CompletableFuture> fetchShellDescriptorsForConnectorEndpoints( + private CompletableFuture>> fetchShellDescriptorsForConnectorEndpoints( final List keys, final List edcUrls, final String bpn) { final var service = endpointDataForConnectorsService; @@ -180,7 +192,7 @@ private CompletableFuture> fetchShellDescriptorsForConnectorEndpoint return resultFinder.getFastestResult(shellsFuture); } - private List fetchShellDescriptorsForKey(final List keys, + private List> fetchShellDescriptorsForKey(final List keys, final EndpointDataReference endpointDataReference) { final var watch = new StopWatch(); @@ -191,8 +203,9 @@ private List fetchShellDescriptorsForKey(final List new Shell(endpointDataReference.getContractId(), - fetchShellDescriptor(endpointDataReference, key))) + .map(key -> Either.right( + new Shell(endpointDataReference.getContractId(), + fetchShellDescriptor(endpointDataReference, key)))) .toList(); } finally { watch.stop(); @@ -333,8 +346,8 @@ private Collection lookupShellIds(final String bpn, final EndpointDataRe try { return decentralDigitalTwinRegistryClient.getAllAssetAdministrationShellIdsByAssetLink( - endpointDataReference, - IdentifierKeyValuePair.builder().name("manufacturerId").value(bpn).build()).getResult(); + endpointDataReference, IdentifierKeyValuePair.builder().name("manufacturerId").value(bpn).build()) + .getResult(); } finally { watch.stop(); log.info(TOOK_MS, watch.getLastTaskName(), watch.getLastTaskTimeMillis()); diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryServiceTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryServiceTest.java index 4964d32587..8b148026c4 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryServiceTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/central/CentralDigitalTwinRegistryServiceTest.java @@ -37,6 +37,7 @@ import java.util.Collections; import java.util.List; +import io.github.resilience4j.core.functions.Either; import org.eclipse.tractusx.irs.SemanticModelNames; import org.eclipse.tractusx.irs.component.Shell; import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; @@ -78,11 +79,12 @@ void setUp() throws IOException { void shouldReturnSubmodelEndpointsWhenRequestingWithCatenaXId() throws RegistryServiceException { final String existingCatenaXId = "urn:uuid:5e1908ed-e176-4f57-9616-1415097d0fdf"; - final Collection aasShellDescriptor = digitalTwinRegistryService.fetchShells( + final Collection> aasShellDescriptor = digitalTwinRegistryService.fetchShells( List.of(new DigitalTwinRegistryKey(existingCatenaXId, ""))); final List shellEndpoints = aasShellDescriptor.stream() .findFirst() .get() + .getOrNull() .payload() .getSubmodelDescriptors(); @@ -121,7 +123,13 @@ void shouldReturnTombstoneWhenClientReturnsEmptyDescriptor() { LookupShellsResponse.builder().result(Collections.emptyList()).build()); final List submodelEndpoints = dtRegistryFacadeWithMock.fetchShells( - List.of(new DigitalTwinRegistryKey(catenaXId, ""))).stream().findFirst().get().payload().getSubmodelDescriptors(); + List.of(new DigitalTwinRegistryKey(catenaXId, ""))) + .stream() + .findFirst() + .get() + .getOrNull() + .payload() + .getSubmodelDescriptors(); assertThat(submodelEndpoints).isEmpty(); } @@ -157,6 +165,7 @@ void shouldReturnAssetAdministrationShellDescriptorForFoundIdentification() { .stream() .findFirst() .get() + .getOrNull() .payload() .getSubmodelDescriptors(); assertThat(submodelEndpoints).isEmpty(); @@ -181,6 +190,7 @@ void shouldReturnSubmodelEndpointsWhenFilteringByAspectType() throws RegistrySer .stream() .findFirst() .get() + .getOrNull() .payload() .getSubmodelDescriptors(); diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java index 8a64391986..d340c8e31b 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java @@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import io.github.resilience4j.core.functions.Either; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.eclipse.tractusx.irs.common.util.concurrent.ResultFinder; import org.eclipse.tractusx.irs.component.Shell; @@ -108,7 +109,10 @@ void shouldReturnExpectedShell() throws RegistryServiceException { expectedShell); // when - final var actualShell = sut.fetchShells(List.of(digitalTwinRegistryKey)).stream().map(Shell::payload); + final var actualShell = sut.fetchShells(List.of(digitalTwinRegistryKey)) + .stream() + .map(Either::getOrNull) + .map(Shell::payload); // then assertThat(actualShell).containsExactly(expectedShell); @@ -143,6 +147,7 @@ void whenInterruptedExceptionOccurs() throws ExecutionException, InterruptedExce // then assertThatThrownBy(call).isInstanceOf(ShellNotFoundException.class) .hasMessage("Unable to find any of the requested shells") + .hasSuppressedException(new InterruptedException("interrupted")) .satisfies(e -> assertThat( ((ShellNotFoundException) e).getCalledEndpoints()).containsExactlyInAnyOrder( "address1", "address2")); @@ -236,6 +241,7 @@ void shouldReturnTheExpectedGlobalAssetId() throws RegistryServiceException { String actualGlobalAssetId = assetAdministrationShellDescriptors.stream() .findFirst() + .map(Either::getOrNull) .map(Shell::payload) .map(AssetAdministrationShellDescriptor::getGlobalAssetId) .get(); diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java index fb3164c045..915a384f8b 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java @@ -60,6 +60,7 @@ import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo; import com.github.tomakehurst.wiremock.junit5.WireMockTest; +import io.github.resilience4j.core.functions.Either; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.irs.component.Shell; import org.eclipse.tractusx.irs.edc.client.EdcConfiguration; @@ -115,12 +116,12 @@ void shouldDiscoverEDCAndRequestRegistry() throws RegistryServiceException, EdcR when(edcEndpointReferenceRetrieverMock.getEndpointReferencesForAsset(any(), any())).thenReturn(List.of(CompletableFuture.completedFuture(endpointDataReference))); // Act - final Collection shells = decentralDigitalTwinRegistryService.fetchShells( + final Collection> shells = decentralDigitalTwinRegistryService.fetchShells( List.of(new DigitalTwinRegistryKey("testId", TEST_BPN))); // Assert assertThat(shells).hasSize(1); - assertThat(shells.stream().findFirst().get().payload().getSubmodelDescriptors()).hasSize(3); + assertThat(shells.stream().findFirst().get().getOrNull().payload().getSubmodelDescriptors()).hasSize(3); verify(exactly(1), postRequestedFor(urlPathEqualTo(DISCOVERY_FINDER_PATH))); verify(exactly(1), postRequestedFor(urlPathEqualTo(EDC_DISCOVERY_PATH))); verify(exactly(1), getRequestedFor(urlPathEqualTo(LOOKUP_SHELLS_PATH))); diff --git a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockSupport.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockSupport.java index 0d11ffb12e..fdfa23ff5c 100644 --- a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockSupport.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/DiscoveryServiceWiremockSupport.java @@ -48,6 +48,10 @@ public static MappingBuilder postEdcDiscovery200() { return postEdcDiscovery200(TEST_BPN, List.of(CONTROLPLANE_PUBLIC_URL)); } + public static MappingBuilder postEdcDiscovery200Empty() { + return post(urlPathEqualTo(EDC_DISCOVERY_PATH)).willReturn(responseWithStatus(STATUS_CODE_OK).withBody("[]")); + } + public static MappingBuilder postEdcDiscoveryEmpty200() { return postEdcDiscovery200(TEST_BPN, List.of()); } diff --git a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java index 17d1573002..a245cb29af 100644 --- a/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java +++ b/irs-testing/src/main/java/org/eclipse/tractusx/irs/testing/wiremock/SubmodelFacadeWiremockSupport.java @@ -57,6 +57,7 @@ public final class SubmodelFacadeWiremockSupport { public static final String CX_POLICY_USAGE_PURPOSE = "cx-policy:UsagePurpose"; public static final String CX_CORE_INDUSTRYCORE_1 = "cx.core.industrycore:1"; public static final String PERMISSION_TYPE = "use"; + public static final int STATUS_CODE_BAD_GATEWAY = 502; private SubmodelFacadeWiremockSupport() { } @@ -76,7 +77,8 @@ public static void prepareNegotiation(final String negotiationId, final String t .withBody(getCatalogResponse(edcAssetId, contractAgreementId, PERMISSION_TYPE, - EDC_PROVIDER_BPN)))); + EDC_PROVIDER_BPN, + createConstraints())))); stubFor(post(urlPathEqualTo(PATH_NEGOTIATE)).willReturn( WireMockConfig.responseWithStatus(STATUS_CODE_OK).withBody(startNegotiationResponse(negotiationId)))); @@ -107,6 +109,20 @@ public static void prepareNegotiation(final String negotiationId, final String t contractAgreementId)))); } + public static void prepareMissmatchPolicyCatalog(final String edcAssetId, final String contractAgreementId) { + stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn(WireMockConfig.responseWithStatus(STATUS_CODE_OK) + .withBody(getCatalogResponse(edcAssetId, + contractAgreementId, + PERMISSION_TYPE, + EDC_PROVIDER_BPN, + createNotAcceptedConstraints())))); + } + + public static void prepareFailingCatalog() { + stubFor(post(urlPathEqualTo(PATH_CATALOG)).willReturn(WireMockConfig.responseWithStatus(STATUS_CODE_BAD_GATEWAY) + .withBody(""))); + } + private static String startTransferProcessResponse(final String transferProcessId) { return startNegotiationResponse(transferProcessId); } @@ -190,7 +206,7 @@ private static String getTransferConfirmedResponse(final String transferProcessI @SuppressWarnings("PMD.UseObjectForClearerAPI") // used only for testing public static String getCatalogResponse(final String edcAssetId, final String offerId, final String permissionType, - final String edcProviderBpn) { + final String edcProviderBpn, final String constraints) { return """ { "@id": "78ff625c-0c05-4014-965c-bd3d0a6a0de0", @@ -237,7 +253,7 @@ public static String getCatalogResponse(final String edcAssetId, final String of "dspace:participantId": "%s", "@context": %s } - """.formatted(edcAssetId, offerId, permissionType, createConstraints(), EDC_PROVIDER_DUMMY_URL, + """.formatted(edcAssetId, offerId, permissionType, constraints, EDC_PROVIDER_DUMMY_URL, edcAssetId, EDC_PROVIDER_DUMMY_URL, edcProviderBpn, edcProviderBpn, CONTEXT); } @@ -253,6 +269,17 @@ private static String createConstraints() { }""".formatted(String.join(",\n", atomitConstraints)); } + private static String createNotAcceptedConstraints() { + final List atomitConstraints = List.of( + createAtomicConstraint("test", "test")); + return """ + { + "odrl:and": [ + %s + ] + }""".formatted(String.join(",\n", atomitConstraints)); + } + private static String createAtomicConstraint(final String leftOperand, final String rightOperand) { return """ { diff --git a/pom.xml b/pom.xml index d24b28ee50..b79bc1fa23 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 2.1.5-SNAPSHOT - 3.1.11 + 3.1.12 2.2.0 1.11.4 1.9.0