diff --git a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java index d396b4170..142355c24 100644 --- a/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java +++ b/core/edr-cache-core/src/main/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCache.java @@ -22,7 +22,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.concurrent.locks.ReentrantReadWriteLock; import static java.util.Collections.emptyList; @@ -37,12 +41,16 @@ public class InMemoryEndpointDataReferenceCache implements EndpointDataReference private final LockManager lockManager; private final Map> entriesByAssetId; + + private final Map> entriesByAgreementId; + private final Map entriesByEdrId; private final Map edrsByTransferProcessId; public InMemoryEndpointDataReferenceCache() { lockManager = new LockManager(new ReentrantReadWriteLock()); entriesByAssetId = new HashMap<>(); + entriesByAgreementId = new HashMap<>(); entriesByEdrId = new HashMap<>(); edrsByTransferProcessId = new HashMap<>(); } @@ -68,6 +76,11 @@ public List entriesForAsset(String assetId) { return lockManager.readLock(() -> entriesByAssetId.getOrDefault(assetId, emptyList())); } + @Override + public @NotNull List entriesForAgreement(String agreementId) { + return lockManager.readLock(() -> entriesByAgreementId.getOrDefault(agreementId, emptyList())); + } + @Override public void save(EndpointDataReferenceEntry entry, EndpointDataReference edr) { lockManager.writeLock(() -> { @@ -75,6 +88,9 @@ public void save(EndpointDataReferenceEntry entry, EndpointDataReference edr) { var list = entriesByAssetId.computeIfAbsent(entry.getAssetId(), k -> new ArrayList<>()); list.add(entry); + var agreementList = entriesByAgreementId.computeIfAbsent(entry.getAgreementId(), k -> new ArrayList<>()); + agreementList.add(entry); + edrsByTransferProcessId.put(entry.getTransferProcessId(), edr); return null; }); diff --git a/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java b/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java index c2dab4afc..e151f1b5d 100644 --- a/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java +++ b/core/edr-cache-core/src/test/java/org/eclipse/tractusx/edc/edr/core/defaults/InMemoryEndpointDataReferenceCacheTest.java @@ -18,12 +18,13 @@ import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; import org.junit.jupiter.api.Test; -import static java.util.UUID.randomUUID; import static org.assertj.core.api.Assertions.assertThat; class InMemoryEndpointDataReferenceCacheTest { private static final String TRANSFER_PROCESS_ID = "tp1"; private static final String ASSET_ID = "asset1"; + private static final String AGREEMENT_ID = "agreement1"; + private static final String EDR_ID = "edr1"; private InMemoryEndpointDataReferenceCache cache = new InMemoryEndpointDataReferenceCache(); @@ -39,7 +40,7 @@ void verify_operations() { var entry = EndpointDataReferenceEntry.Builder.newInstance() .assetId(ASSET_ID) - .agreementId(randomUUID().toString()) + .agreementId(AGREEMENT_ID) .transferProcessId(TRANSFER_PROCESS_ID) .build(); @@ -55,6 +56,10 @@ void verify_operations() { assertThat(entries.size()).isEqualTo(1); assertThat(entries.get((0)).getAssetId()).isEqualTo(ASSET_ID); + entries = cache.entriesForAgreement(AGREEMENT_ID); + assertThat(entries.size()).isEqualTo(1); + assertThat(entries.get((0)).getAgreementId()).isEqualTo(AGREEMENT_ID); + assertThat(cache.deleteByTransferProcessId(TRANSFER_PROCESS_ID).succeeded()).isTrue(); assertThat(cache.entriesForAsset(ASSET_ID)).isEmpty(); diff --git a/edc-extensions/business-partner-validation/build.gradle.kts b/edc-extensions/business-partner-validation/build.gradle.kts index b4996ba37..6d5b6059a 100644 --- a/edc-extensions/business-partner-validation/build.gradle.kts +++ b/edc-extensions/business-partner-validation/build.gradle.kts @@ -23,6 +23,7 @@ plugins { } dependencies { + implementation(project(":spi:core-spi")) api(libs.edc.spi.core) implementation(libs.edc.spi.policy) implementation(libs.edc.spi.contract) diff --git a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java index 1786897bd..d88293a72 100644 --- a/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java +++ b/edc-extensions/business-partner-validation/src/main/java/org/eclipse/tractusx/edc/validation/businesspartner/BusinessPartnerValidationExtension.java @@ -35,7 +35,6 @@ import org.eclipse.tractusx.edc.validation.businesspartner.functions.BusinessPartnerProhibitionFunction; import static org.eclipse.edc.policy.engine.spi.PolicyEngine.ALL_SCOPES; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; public class BusinessPartnerValidationExtension implements ServiceExtension { @@ -55,8 +54,7 @@ public class BusinessPartnerValidationExtension implements ServiceExtension { * } * */ - // TODO replace with TX namespace - public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = EDC_NAMESPACE + "BusinessPartnerNumber"; + public static final String BUSINESS_PARTNER_CONSTRAINT_KEY = "BusinessPartnerNumber"; public static final String DEFAULT_LOG_AGREEMENT_EVALUATION = "true"; diff --git a/edc-extensions/control-plane-adapter-api/build.gradle.kts b/edc-extensions/control-plane-adapter-api/build.gradle.kts index 2541d1346..36e1437b9 100644 --- a/edc-extensions/control-plane-adapter-api/build.gradle.kts +++ b/edc-extensions/control-plane-adapter-api/build.gradle.kts @@ -20,6 +20,9 @@ plugins { dependencies { implementation(project(":spi:control-plane-adapter-spi")) + implementation(project(":spi:edr-cache-spi")) + implementation(project(":spi:core-spi")) + implementation(libs.edc.api.management) implementation(libs.edc.spi.aggregateservices) implementation(libs.jakarta.rsApi) diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java index e67f35579..028468fed 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterApiExtension.java @@ -21,10 +21,14 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.web.spi.WebService; +import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; import org.eclipse.tractusx.edc.api.cp.adapter.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer; import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_PREFIX; + public class AdapterApiExtension implements ServiceExtension { @Inject @@ -43,8 +47,10 @@ public class AdapterApiExtension implements ServiceExtension { @Override public void initialize(ServiceExtensionContext context) { + jsonLdService.registerNamespace(TX_PREFIX, TX_NAMESPACE); transformerRegistry.register(new NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer()); transformerRegistry.register(new JsonObjectToNegotiateEdrRequestDtoTransformer()); + transformerRegistry.register(new JsonObjectFromEndpointDataReferenceEntryTransformer()); webService.registerResource(apiConfig.getContextAlias(), new AdapterEdrController(adapterTransferProcessService, jsonLdService, transformerRegistry)); } } diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java index d10d133ba..36d05c073 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApi.java @@ -22,9 +22,13 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.json.JsonObject; +import org.eclipse.edc.api.model.DataAddressDto; import org.eclipse.edc.api.model.IdResponseDto; import org.eclipse.edc.web.spi.ApiErrorDetail; import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; + +import java.util.List; @OpenAPIDefinition @Tag(name = "Control Plane Adapter EDR Api") @@ -39,4 +43,25 @@ public interface AdapterEdrApi { content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), }) JsonObject initiateEdrNegotiation(@Schema(implementation = NegotiateEdrRequestDto.class) JsonObject dto); + + @Operation(description = "Returns all EndpointDataReference entry according to a query", + responses = { + @ApiResponse(responseCode = "200", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = EndpointDataReferenceEntry.class)))), + @ApiResponse(responseCode = "400", description = "Request was malformed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class))))} + ) + List queryEdrs(String assetId, String agreementId); + + @Operation(description = "Gets an EDR with the given transfer process ID)", + responses = { + @ApiResponse(responseCode = "200", description = "The EDR cached", + content = @Content(schema = @Schema(implementation = DataAddressDto.class))), + @ApiResponse(responseCode = "400", description = "Request was malformed, e.g. id was null", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))), + @ApiResponse(responseCode = "404", description = "An EDR with the given ID does not exist", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = ApiErrorDetail.class)))) + } + ) + JsonObject getEdr(String transferProcessId); } diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java index bfbbc483f..55a6b3e44 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrController.java @@ -16,19 +16,30 @@ import jakarta.json.JsonObject; import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import org.eclipse.edc.api.model.IdResponseDto; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataAddressConstants; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.web.spi.exception.InvalidRequestException; import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +import java.util.List; +import java.util.stream.Collectors; + import static org.eclipse.edc.web.spi.exception.ServiceResultHandler.exceptionMapper; @Consumes({MediaType.APPLICATION_JSON}) @@ -40,6 +51,8 @@ public class AdapterEdrController implements AdapterEdrApi { private final TypeTransformerRegistry transformerRegistry; private final JsonLd jsonLdService; + private Monitor monitor; + public AdapterEdrController(AdapterTransferProcessService adapterTransferProcessService, JsonLd jsonLdService, TypeTransformerRegistry transformerRegistry) { this.adapterTransferProcessService = adapterTransferProcessService; this.jsonLdService = jsonLdService; @@ -65,4 +78,36 @@ public JsonObject initiateEdrNegotiation(JsonObject requestObject) { .compose(jsonLdService::compact) .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); } + + @GET + @Path("/{id}") + @Override + public JsonObject getEdr(@PathParam("id") String transferProcessId) { + var edr = adapterTransferProcessService.findByTransferProcessId(transferProcessId).orElseThrow(exceptionMapper(EndpointDataReference.class, transferProcessId)); + + return transformerRegistry.transform(EndpointDataAddressConstants.from(edr), JsonObject.class) + .compose(jsonLdService::compact) + .orElseThrow(f -> new EdcException("Error creating response body: " + f.getFailureDetail())); + } + + @GET + @Override + public List queryEdrs(@QueryParam("assetId") String assetId, @QueryParam("agreementId") String agreementId) { + if (assetId == null && agreementId == null) { + throw new InvalidRequestException("At least one of this query parameter is required [assetId,agreementId]"); + } + return adapterTransferProcessService.findByAssetAndAgreement(assetId, agreementId) + .orElseThrow(exceptionMapper(EndpointDataReferenceEntry.class)) + .stream() + .map(edrCached -> transformerRegistry.transform(edrCached, JsonObject.class) + .compose(jsonLdService::compact)) + .peek(this::logIfError) + .filter(Result::succeeded) + .map(Result::getContent) + .collect(Collectors.toList()); + } + + private void logIfError(Result result) { + result.onFailure(f -> monitor.warning(f.getFailureDetail())); + } } diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java index 074c7017e..d3523a31d 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/dto/NegotiateEdrRequestDto.java @@ -23,16 +23,17 @@ import java.util.List; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; public class NegotiateEdrRequestDto { - - public static final String TYPE = EDC_NAMESPACE + "NegotiateEdrRequestDto"; - public static final String CONNECTOR_ADDRESS = EDC_NAMESPACE + "connectorAddress"; - public static final String PROTOCOL = EDC_NAMESPACE + "protocol"; - public static final String CONNECTOR_ID = EDC_NAMESPACE + "connectorId"; - public static final String PROVIDER_ID = EDC_NAMESPACE + "providerId"; - public static final String OFFER = EDC_NAMESPACE + "offer"; - public static final String CALLBACK_ADDRESSES = EDC_NAMESPACE + "callbackAddresses"; + + public static final String EDR_REQUEST_DTO_TYPE = TX_NAMESPACE + "NegotiateEdrRequestDto"; + public static final String EDR_REQUEST_DTO_CONNECTOR_ADDRESS = EDC_NAMESPACE + "connectorAddress"; + public static final String EDR_REQUEST_DTO_PROTOCOL = EDC_NAMESPACE + "protocol"; + public static final String EDR_REQUEST_DTO_CONNECTOR_ID = EDC_NAMESPACE + "connectorId"; + public static final String EDR_REQUEST_DTO_PROVIDER_ID = EDC_NAMESPACE + "providerId"; + public static final String EDR_REQUEST_DTO_OFFER = EDC_NAMESPACE + "offer"; + public static final String EDR_REQUEST_DTO_CALLBACK_ADDRESSES = EDC_NAMESPACE + "callbackAddresses"; @NotBlank(message = "connectorAddress is mandatory") private String connectorAddress; diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java new file mode 100644 index 000000000..36676d0c3 --- /dev/null +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.api.cp.adapter.transform; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import org.eclipse.edc.jsonld.spi.transformer.AbstractJsonLdTransformer; +import org.eclipse.edc.transform.spi.TransformerContext; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; + + +public class JsonObjectFromEndpointDataReferenceEntryTransformer extends AbstractJsonLdTransformer { + + public JsonObjectFromEndpointDataReferenceEntryTransformer() { + super(EndpointDataReferenceEntry.class, JsonObject.class); + } + + @Override + public @Nullable JsonObject transform(@NotNull EndpointDataReferenceEntry dto, @NotNull TransformerContext context) { + return Json.createObjectBuilder() + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_AGREEMENT_ID, dto.getAgreementId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, dto.getTransferProcessId()) + .add(EDR_ENTRY_ASSET_ID, dto.getAssetId()) + .build(); + } + + +} diff --git a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java index 02a53ab18..234684281 100644 --- a/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java +++ b/edc-extensions/control-plane-adapter-api/src/main/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectToNegotiateEdrRequestDtoTransformer.java @@ -26,12 +26,13 @@ import java.util.ArrayList; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.CALLBACK_ADDRESSES; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.CONNECTOR_ADDRESS; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.CONNECTOR_ID; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.OFFER; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.PROTOCOL; -import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.PROVIDER_ID; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CALLBACK_ADDRESSES; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CONNECTOR_ADDRESS; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_CONNECTOR_ID; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_OFFER; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROTOCOL; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_PROVIDER_ID; +import static org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE; public class JsonObjectToNegotiateEdrRequestDtoTransformer extends AbstractJsonLdTransformer { @@ -50,38 +51,38 @@ public JsonObjectToNegotiateEdrRequestDtoTransformer() { private void setProperties(String key, JsonValue value, NegotiateEdrRequestDto.Builder builder, TransformerContext context) { switch (key) { - case CONNECTOR_ADDRESS: + case EDR_REQUEST_DTO_CONNECTOR_ADDRESS: transformString(value, builder::connectorAddress, context); break; - case PROTOCOL: + case EDR_REQUEST_DTO_PROTOCOL: transformString(value, builder::protocol, context); break; - case CONNECTOR_ID: + case EDR_REQUEST_DTO_CONNECTOR_ID: transformString(value, builder::connectorId, context); break; - case PROVIDER_ID: + case EDR_REQUEST_DTO_PROVIDER_ID: transformString(value, builder::providerId, context); break; - case CALLBACK_ADDRESSES: + case EDR_REQUEST_DTO_CALLBACK_ADDRESSES: var addresses = new ArrayList(); transformArrayOrObject(value, CallbackAddressDto.class, addresses::add, context); builder.callbackAddresses(addresses); break; - case OFFER: + case EDR_REQUEST_DTO_OFFER: transformArrayOrObject(value, ContractOfferDescription.class, builder::offer, context); break; default: context.problem() .unexpectedType() - .type(NegotiateEdrRequestDto.TYPE) + .type(EDR_REQUEST_DTO_TYPE) .property(key) .actual(key) - .expected(CONNECTOR_ADDRESS) - .expected(PROTOCOL) - .expected(CONNECTOR_ID) - .expected(PROVIDER_ID) - .expected(CALLBACK_ADDRESSES) - .expected(OFFER) + .expected(EDR_REQUEST_DTO_CONNECTOR_ADDRESS) + .expected(EDR_REQUEST_DTO_PROTOCOL) + .expected(EDR_REQUEST_DTO_CONNECTOR_ID) + .expected(EDR_REQUEST_DTO_PROVIDER_ID) + .expected(EDR_REQUEST_DTO_CALLBACK_ADDRESSES) + .expected(EDR_REQUEST_DTO_OFFER) .report(); break; } diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java index fbe6c1744..8be15fa5e 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrApiExtensionTest.java @@ -20,6 +20,7 @@ import org.eclipse.edc.spi.system.injection.ObjectFactory; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.web.spi.WebService; +import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectFromEndpointDataReferenceEntryTransformer; import org.eclipse.tractusx.edc.api.cp.adapter.transform.JsonObjectToNegotiateEdrRequestDtoTransformer; import org.eclipse.tractusx.edc.api.cp.adapter.transform.NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer; import org.junit.jupiter.api.BeforeEach; @@ -61,5 +62,7 @@ void initialize_ShouldConfigureTheController(ServiceExtensionContext context) { verify(webService).registerResource(eq(alias), isA(AdapterEdrController.class)); verify(transformerRegistry).register(isA(NegotiateEdrRequestDtoToNegotiatedEdrRequestTransformer.class)); verify(transformerRegistry).register(isA(JsonObjectToNegotiateEdrRequestDtoTransformer.class)); + verify(transformerRegistry).register(isA(JsonObjectFromEndpointDataReferenceEntryTransformer.class)); + } } diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java index 022aa60df..a345406b1 100644 --- a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/AdapterEdrControllerTest.java @@ -25,19 +25,32 @@ import org.eclipse.edc.junit.annotations.ApiTest; import org.eclipse.edc.service.spi.result.ServiceResult; import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.types.domain.DataAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.web.jersey.testfixtures.RestControllerTestBase; import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static io.restassured.RestAssured.given; +import static java.lang.String.format; import static org.eclipse.edc.api.model.IdResponseDto.EDC_ID_RESPONSE_DTO_TYPE; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.ID; import static org.eclipse.edc.jsonld.spi.JsonLdKeywords.TYPE; +import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; import static org.eclipse.tractusx.edc.api.cp.adapter.TestFunctions.openRequest; import static org.eclipse.tractusx.edc.api.cp.adapter.TestFunctions.requestDto; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TYPE; import static org.hamcrest.Matchers.is; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -52,6 +65,12 @@ public class AdapterEdrControllerTest extends RestControllerTestBase { AdapterTransferProcessService adapterTransferProcessService = mock(AdapterTransferProcessService.class); TypeTransformerRegistry transformerRegistry = mock(TypeTransformerRegistry.class); + @BeforeEach + void setup() { + jsonLdService.registerNamespace("edc", EDC_NAMESPACE); + jsonLdService.registerNamespace("tx", TX_NAMESPACE); + } + @Test void initEdrNegotiation_shouldWork_whenValidRequest() { @@ -90,6 +109,119 @@ void initEdrNegotiation_shouldReturnBadRequest_whenValidInvalidRequest() { } + @Test + void initEdrNegotiation_shouldReturnError_whenNotFound() { + var transferProcessId = "id"; + + when(adapterTransferProcessService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.notFound("")); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH + "/" + transferProcessId) + .then() + .statusCode(404); + } + + @Test + void getEdr_shouldReturnDataAddress_whenFound() { + var transferProcessId = "id"; + var edr = EndpointDataReference.Builder.newInstance().endpoint("test").id(transferProcessId).build(); + var response = Json.createObjectBuilder() + .add(DataAddress.TYPE, EndpointDataReference.EDR_SIMPLE_TYPE) + .add(EndpointDataReference.ENDPOINT, edr.getEndpoint()) + .add(EndpointDataReference.ID, edr.getId()) + .build(); + + when(adapterTransferProcessService.findByTransferProcessId(transferProcessId)).thenReturn(ServiceResult.success(edr)); + when(transformerRegistry.transform(any(DataAddress.class), eq(JsonObject.class))).thenReturn(Result.success(response)); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH + "/" + transferProcessId) + .then() + .statusCode(200) + .body("'edc:endpoint'", is(edr.getEndpoint())) + .body("'edc:id'", is(edr.getId())) + .body("'edc:type'", is(EndpointDataReference.EDR_SIMPLE_TYPE)); + + } + + @Test + void queryEdrs_shouldReturnCachedEntries_whenAssetIdIsProvided() { + var assetId = "id"; + var transferProcessId = "id"; + var agreementId = "id"; + + var entry = EndpointDataReferenceEntry.Builder.newInstance() + .transferProcessId(transferProcessId) + .agreementId(agreementId) + .assetId(assetId) + .build(); + + var response = Json.createObjectBuilder() + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) + .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) + .build(); + + when(adapterTransferProcessService.findByAssetAndAgreement(assetId, null)).thenReturn(ServiceResult.success(List.of(entry))); + when(transformerRegistry.transform(any(EndpointDataReferenceEntry.class), eq(JsonObject.class))).thenReturn(Result.success(response)); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH + format("?=assetId=%s", assetId)) + .then() + .statusCode(200) + .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) + .body("[0].'edc:agreementId'", is(entry.getAgreementId())) + .body("[0].'edc:assetId'", is(entry.getAssetId())); + + } + + @Test + void queryEdrs_shouldReturnCachedEntries_whenAgreementIdIsProvided() { + var assetId = "id"; + var transferProcessId = "id"; + var agreementId = "id"; + + var entry = EndpointDataReferenceEntry.Builder.newInstance() + .transferProcessId(transferProcessId) + .agreementId(agreementId) + .assetId(assetId) + .build(); + + + var response = Json.createObjectBuilder() + .add(TYPE, EDR_ENTRY_TYPE) + .add(EDR_ENTRY_ASSET_ID, entry.getAssetId()) + .add(EDR_ENTRY_TRANSFER_PROCESS_ID, entry.getTransferProcessId()) + .add(EDR_ENTRY_AGREEMENT_ID, entry.getAgreementId()) + .build(); + + + when(adapterTransferProcessService.findByAssetAndAgreement(null, agreementId)).thenReturn(ServiceResult.success(List.of(entry))); + when(transformerRegistry.transform(any(EndpointDataReferenceEntry.class), eq(JsonObject.class))).thenReturn(Result.success(response)); + + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH + format("?=agreementId=%s", entry.getAgreementId())) + .then() + .statusCode(200) + .body("[0].'edc:transferProcessId'", is(entry.getTransferProcessId())) + .body("[0].'edc:agreementId'", is(entry.getAgreementId())) + .body("[0].'edc:assetId'", is(entry.getAssetId())); + } + + @Test + void queryEdrs_shouldFail_whenNoQueryParameter() { + baseRequest() + .contentType(MediaType.APPLICATION_JSON) + .get(ADAPTER_EDR_PATH) + .then() + .statusCode(400); + } + @Override protected Object controller() { return new AdapterEdrController(adapterTransferProcessService, jsonLdService, transformerRegistry); diff --git a/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java new file mode 100644 index 000000000..79e0ba5ae --- /dev/null +++ b/edc-extensions/control-plane-adapter-api/src/test/java/org/eclipse/tractusx/edc/api/cp/adapter/transform/JsonObjectFromEndpointDataReferenceEntryTransformerTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.api.cp.adapter.transform; + +import org.eclipse.edc.transform.spi.TransformerContext; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_AGREEMENT_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_ASSET_ID; +import static org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry.EDR_ENTRY_TRANSFER_PROCESS_ID; +import static org.mockito.Mockito.mock; + +class JsonObjectFromEndpointDataReferenceEntryTransformerTest { + + private final TransformerContext context = mock(TransformerContext.class); + private JsonObjectFromEndpointDataReferenceEntryTransformer transformer; + + @BeforeEach + void setUp() { + transformer = new JsonObjectFromEndpointDataReferenceEntryTransformer(); + } + + @Test + void transform() { + + var dto = EndpointDataReferenceEntry.Builder.newInstance() + .assetId("id") + .transferProcessId("tpId") + .agreementId("aId") + .build(); + + var jsonObject = transformer.transform(dto, context); + + assertThat(jsonObject).isNotNull(); + assertThat(jsonObject.getJsonString(EDR_ENTRY_AGREEMENT_ID).getString()).isNotNull().isEqualTo(dto.getAgreementId()); + assertThat(jsonObject.getJsonString(EDR_ENTRY_ASSET_ID).getString()).isNotNull().isEqualTo(dto.getAssetId()); + assertThat(jsonObject.getJsonString(EDR_ENTRY_TRANSFER_PROCESS_ID).getString()).isNotNull().isEqualTo(dto.getTransferProcessId()); + } +} \ No newline at end of file diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java index 0220b1330..a3ed84dc5 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java +++ b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImpl.java @@ -20,13 +20,25 @@ import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.service.spi.result.ServiceResult; import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.lang.String.format; +import static org.eclipse.edc.service.spi.result.ServiceResult.notFound; +import static org.eclipse.edc.service.spi.result.ServiceResult.success; + public class AdapterTransferProcessServiceImpl implements AdapterTransferProcessService { public static final String LOCAL_ADAPTER_URI = "local://adapter"; @@ -38,14 +50,17 @@ public class AdapterTransferProcessServiceImpl implements AdapterTransferProcess .build(); private final ContractNegotiationService contractNegotiationService; - public AdapterTransferProcessServiceImpl(ContractNegotiationService contractNegotiationService) { + private final EndpointDataReferenceCache endpointDataReferenceCache; + + public AdapterTransferProcessServiceImpl(ContractNegotiationService contractNegotiationService, EndpointDataReferenceCache endpointDataReferenceCache) { this.contractNegotiationService = contractNegotiationService; + this.endpointDataReferenceCache = endpointDataReferenceCache; } @Override public ServiceResult initiateEdrNegotiation(NegotiateEdrRequest request) { var contractNegotiation = contractNegotiationService.initiateNegotiation(createContractRequest(request)); - return ServiceResult.success(contractNegotiation); + return success(contractNegotiation); } private ContractRequest createContractRequest(NegotiateEdrRequest request) { @@ -62,4 +77,37 @@ private ContractRequest createContractRequest(NegotiateEdrRequest request) { .requestData(requestData) .callbackAddresses(callbacks).build(); } + + @Override + public ServiceResult findByTransferProcessId(String transferProcessId) { + var edr = endpointDataReferenceCache.resolveReference(transferProcessId); + return Optional.ofNullable(edr) + .map(ServiceResult::success) + .orElse(notFound(format("No Edr found associated to the transfer process with id: %s", transferProcessId))); + } + + @Override + public ServiceResult> findByAssetAndAgreement(String assetId, String agreementId) { + var results = queryEdrs(assetId, agreementId) + .stream() + .filter(fieldFilter(assetId, EndpointDataReferenceEntry::getAssetId)) + .filter(fieldFilter(agreementId, EndpointDataReferenceEntry::getAgreementId)) + .collect(Collectors.toList()); + return success(results); + } + + private Predicate fieldFilter(String value, Function function) { + return entry -> Optional.ofNullable(value) + .map(val -> val.equals(function.apply(entry))) + .orElse(true); + } + + private List queryEdrs(String assetId, String agreementId) { + // Try first for agreementId and then assetId + return Optional.ofNullable(agreementId) + .map(endpointDataReferenceCache::entriesForAgreement) + .or(() -> Optional.ofNullable(assetId).map(endpointDataReferenceCache::entriesForAsset)) + .orElseGet(Collections::emptyList); + } + } diff --git a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java index 4fe11df5d..ba257790e 100644 --- a/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java +++ b/edc-extensions/control-plane-adapter-callback/src/main/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtension.java @@ -20,7 +20,7 @@ import org.eclipse.edc.connector.transfer.spi.store.TransferProcessStore; import org.eclipse.edc.runtime.metamodel.annotation.Extension; import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.runtime.metamodel.annotation.Provides; +import org.eclipse.edc.runtime.metamodel.annotation.Provider; import org.eclipse.edc.spi.message.RemoteMessageDispatcherRegistry; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; @@ -32,7 +32,6 @@ import static org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; -@Provides(AdapterTransferProcessService.class) @Extension(LocalCallbackExtension.NAME) public class LocalCallbackExtension implements ServiceExtension { public static final String NAME = "Local callbacks extension"; @@ -65,6 +64,9 @@ public class LocalCallbackExtension implements ServiceExtension { @Inject private TransactionContext transactionContext; + @Inject + private EndpointDataReferenceCache endpointDataReferenceCache; + @Override public String name() { return NAME; @@ -79,7 +81,11 @@ public void initialize(ServiceExtensionContext context) { resolverRegistry.registerResolver(this::resolveProtocol); registry.register(new InProcessCallbackMessageDispatcher(callbackRegistry)); - context.registerService(AdapterTransferProcessService.class, new AdapterTransferProcessServiceImpl(contractNegotiationService)); + } + + @Provider + public AdapterTransferProcessService adapterTransferProcessService() { + return new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); } private String resolveProtocol(String scheme) { diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java index 1e95f0e1c..888574e37 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java +++ b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/AdapterTransferProcessServiceImplTest.java @@ -19,7 +19,11 @@ import org.eclipse.edc.connector.contract.spi.types.offer.ContractOffer; import org.eclipse.edc.connector.spi.contractnegotiation.ContractNegotiationService; import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.service.spi.result.ServiceFailure; +import org.eclipse.edc.service.spi.result.ServiceResult; import org.eclipse.edc.spi.types.domain.callback.CallbackAddress; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceCache; import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -38,9 +42,11 @@ public class AdapterTransferProcessServiceImplTest { ContractNegotiationService contractNegotiationService = mock(ContractNegotiationService.class); + EndpointDataReferenceCache endpointDataReferenceCache = mock(EndpointDataReferenceCache.class); + @Test void initEdrNegotiation_shouldFireAContractNegotiation_WhenUsingCallbacks() { - var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService); + var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); var captor = ArgumentCaptor.forClass(ContractRequest.class); @@ -65,6 +71,39 @@ void initEdrNegotiation_shouldFireAContractNegotiation_WhenUsingCallbacks() { } + @Test + void findByTransferProcessId_shouldReturnTheEdr_whenFoundInCache() { + + var transferProcessId = "tpId"; + + var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); + when(endpointDataReferenceCache.resolveReference(transferProcessId)).thenReturn(EndpointDataReference.Builder.newInstance().endpoint("test").build()); + + var result = transferService.findByTransferProcessId(transferProcessId); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::getContent) + .isNotNull(); + } + + + @Test + void findByTransferProcessId_shouldNotFound_whenNotPresentInCache() { + var transferProcessId = "tpId"; + + var transferService = new AdapterTransferProcessServiceImpl(contractNegotiationService, endpointDataReferenceCache); + when(endpointDataReferenceCache.resolveReference(transferProcessId)).thenReturn(null); + + var result = transferService.findByTransferProcessId(transferProcessId); + + assertThat(result) + .isNotNull() + .extracting(ServiceResult::getFailure) + .extracting(ServiceFailure::getReason) + .isEqualTo(ServiceFailure.Reason.NOT_FOUND); + } + private NegotiateEdrRequest getNegotiateEdrRequest() { return NegotiateEdrRequest.Builder.newInstance() .protocol("protocol") diff --git a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java index 8e68c0ed1..afff1d6a4 100644 --- a/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java +++ b/edc-extensions/control-plane-adapter-callback/src/test/java/org/eclipse/tractusx/edc/cp/adapter/callback/LocalCallbackExtensionTest.java @@ -22,7 +22,6 @@ import org.eclipse.edc.spi.system.injection.ObjectFactory; import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallback; import org.eclipse.tractusx.edc.spi.cp.adapter.callback.InProcessCallbackRegistry; -import org.eclipse.tractusx.edc.spi.cp.adapter.service.AdapterTransferProcessService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -30,7 +29,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.tractusx.edc.cp.adapter.callback.InProcessCallbackMessageDispatcher.CALLBACK_EVENT_LOCAL; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; @ExtendWith(DependencyInjectionExtension.class) public class LocalCallbackExtensionTest { @@ -64,7 +65,7 @@ void shouldInitializeTheExtension(ServiceExtensionContext context) { assertThat(resolver.resolve("test")).isNull(); - var service = context.getService(AdapterTransferProcessService.class); + var service = extension.adapterTransferProcessService(); assertThat(service).isInstanceOf(AdapterTransferProcessServiceImpl.class); var callbackArgumentCaptor = ArgumentCaptor.forClass(InProcessCallback.class); diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java index 7b0f85d3a..7ccd58f42 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/EdrNegotiationHelperFunctions.java @@ -21,6 +21,7 @@ import org.eclipse.edc.jsonld.TitaniumJsonLd; import org.eclipse.edc.jsonld.spi.JsonLd; import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.tractusx.edc.api.cp.adapter.dto.NegotiateEdrRequestDto; import java.util.Set; @@ -35,7 +36,7 @@ public class EdrNegotiationHelperFunctions { public static JsonObject createEdrNegotiationRequest(String connectorAddress, String providerId, String offerId, String assetId, JsonObject policy, JsonArray callbacks) { return Json.createObjectBuilder() - .add(TYPE, EDC_NAMESPACE + "NegotiateEdrRequestDto") + .add(TYPE, NegotiateEdrRequestDto.EDR_REQUEST_DTO_TYPE) .add(EDC_NAMESPACE + "connectorId", providerId) .add(EDC_NAMESPACE + "providerId", providerId) .add(EDC_NAMESPACE + "connectorAddress", connectorAddress) diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java index 7ff41e964..180900841 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/helpers/PolicyHelperFunctions.java @@ -40,7 +40,7 @@ public class PolicyHelperFunctions { - private static final String BUSINESS_PARTNER_EVALUATION_KEY = EDC_NAMESPACE + "BusinessPartnerNumber"; + private static final String BUSINESS_PARTNER_EVALUATION_KEY = "BusinessPartnerNumber"; /** * Creates a {@link PolicyDefinition} using the given ID, that contains equality constraints for each of the given BusinessPartnerNumbers: diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java index 8eb9c4621..ea6e3054b 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/lifecycle/Participant.java @@ -180,6 +180,7 @@ public String getNegotiationState(String negotiationId) { .extract().body().jsonPath().getString("'edc:state'"); } + public String getContractAgreementId(String negotiationId) { return getContractNegotiationField(negotiationId, "contractAgreementId"); } @@ -194,6 +195,28 @@ private String getContractNegotiationField(String negotiationId, String fieldNam .getString(format("'edc:%s'", fieldName)); } + public JsonObject getEdr(String transferProcessId) { + return baseRequest() + .when() + .get("/adapter/edrs/{id}", transferProcessId) + .then() + .statusCode(200) + .extract() + .body() + .as(JsonObject.class); + } + + public JsonArray getEdrEntries(String assetId) { + return baseRequest() + .when() + .get("/adapter/edrs?assetId={assetId}", assetId) + .then() + .statusCode(200) + .extract() + .body() + .as(JsonArray.class); + } + /** * Returns this participant's BusinessPartnerNumber (=BPN). This is constructed of the runtime name plus "-BPN" diff --git a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java index 06963b571..01bd795a8 100644 --- a/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java +++ b/edc-tests/e2e-tests/src/test/java/org/eclipse/tractusx/edc/tests/edr/AbstractNegotiateEdrTest.java @@ -42,6 +42,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.edc.spi.types.domain.edr.EndpointDataReference.EDR_SIMPLE_TYPE; import static org.eclipse.tractusx.edc.helpers.EdrNegotiationHelperFunctions.createCallback; import static org.eclipse.tractusx.edc.helpers.PolicyHelperFunctions.businessPartnerNumberPolicy; import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.PLATO_BPN; @@ -52,7 +53,7 @@ import static org.eclipse.tractusx.edc.lifecycle.TestRuntimeConfiguration.sokratesConfiguration; public abstract class AbstractNegotiateEdrTest { - + protected static final Participant sokrates = new Participant(SOKRATES_NAME, SOKRATES_BPN, sokratesConfiguration()); protected static final Participant plato = new Participant(PLATO_NAME, PLATO_BPN, platoConfiguration()); @@ -111,6 +112,22 @@ void negotiateEdr_shouldInvokeCallbacks() throws IOException { .collect(Collectors.toList()); assertThat(expectedEvents).usingRecursiveFieldByFieldElementComparator().containsAll(events); + + + var edrCaches = sokrates.getEdrEntries(assetId); + + assertThat(edrCaches).hasSize(1); + + var transferProcessId = edrCaches.get(0).asJsonObject().getString("edc:transferProcessId"); + + var edr = sokrates.getEdr(transferProcessId); + + assertThat(edr.getJsonString("edc:type").getString()).isEqualTo(EDR_SIMPLE_TYPE); + assertThat(edr.getJsonString("edc:authCode").getString()).isNotNull(); + assertThat(edr.getJsonString("edc:authKey").getString()).isNotNull(); + assertThat(edr.getJsonString("edc:endpoint").getString()).isNotNull(); + assertThat(edr.getJsonString("edc:id").getString()).isEqualTo(transferProcessId); + } ReceivedEvent createEvent(Class klass) { diff --git a/resources/openapi/yaml/control-plane-adapter-api.yaml b/resources/openapi/yaml/control-plane-adapter-api.yaml index de9f7fbb0..1d87965e5 100644 --- a/resources/openapi/yaml/control-plane-adapter-api.yaml +++ b/resources/openapi/yaml/control-plane-adapter-api.yaml @@ -1,6 +1,40 @@ openapi: 3.0.1 paths: /adapter/edrs: + get: + description: Returns all EndpointDataReference entry according to a query + operationId: queryEdrs + parameters: + - in: query + name: assetId + schema: + type: string + example: null + - in: query + name: agreementId + schema: + type: string + example: null + responses: + "200": + content: + application/json: + schema: + type: array + example: null + items: + $ref: '#/components/schemas/EndpointDataReferenceEntryDto' + "400": + content: + application/json: + schema: + type: array + example: null + items: + $ref: '#/components/schemas/ApiErrorDetail' + description: Request was malformed + tags: + - Control Plane Adapter EDR Api post: description: Initiates an EDR negotiation by handling a contract negotiation first and then a transfer process for a given offer and with the given counter @@ -48,6 +82,44 @@ paths: description: Request body was malformed tags: - Control Plane Adapter EDR Api + /adapter/edrs/{id}: + get: + description: Gets an EDR with the given transfer process ID) + operationId: getEdr + parameters: + - in: path + name: id + required: true + schema: + type: string + example: null + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/DataAddressDto' + description: The EDR cached + "400": + content: + application/json: + schema: + type: array + example: null + items: + $ref: '#/components/schemas/ApiErrorDetail' + description: "Request was malformed, e.g. id was null" + "404": + content: + application/json: + schema: + type: array + example: null + items: + $ref: '#/components/schemas/ApiErrorDetail' + description: An EDR with the given ID does not exist + tags: + - Control Plane Adapter EDR Api components: schemas: Action: @@ -135,6 +207,18 @@ components: - assetId - offerId - policy + DataAddressDto: + type: object + example: null + properties: + properties: + type: object + additionalProperties: + type: string + example: null + example: null + required: + - properties Duty: type: object example: null @@ -159,6 +243,19 @@ components: target: type: string example: null + EndpointDataReferenceEntryDto: + type: object + example: null + properties: + agreementId: + type: string + example: null + assetId: + type: string + example: null + transferProcessId: + type: string + example: null IdResponseDto: type: object example: null @@ -173,7 +270,7 @@ components: JsonObject: type: object additionalProperties: - $ref: '#/components/schemas/NegotiateEdrRequestDto' + $ref: '#/components/schemas/JsonValue' example: null properties: empty: diff --git a/settings.gradle.kts b/settings.gradle.kts index 484271c1a..ed1a24264 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,6 +22,7 @@ rootProject.name = "tractusx-edc" // spi modules include(":spi:control-plane-adapter-spi") include(":spi:edr-cache-spi") +include(":spi:core-spi") // core modules include(":core:edr-cache-core") diff --git a/spi/control-plane-adapter-spi/build.gradle.kts b/spi/control-plane-adapter-spi/build.gradle.kts index d01290419..80ee806b2 100644 --- a/spi/control-plane-adapter-spi/build.gradle.kts +++ b/spi/control-plane-adapter-spi/build.gradle.kts @@ -19,8 +19,11 @@ plugins { dependencies { + implementation(project(":spi:core-spi")) + implementation(project(":spi:edr-cache-spi")) implementation(libs.edc.spi.core) implementation(libs.edc.spi.contract) implementation(libs.edc.spi.aggregateservices) implementation(libs.edc.spi.controlplane) + implementation(libs.edc.spi.controlplane) } diff --git a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java b/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java index 2ab135030..3ff0b50e9 100644 --- a/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java +++ b/spi/control-plane-adapter-spi/src/main/java/org/eclipse/tractusx/edc/spi/cp/adapter/service/AdapterTransferProcessService.java @@ -16,8 +16,12 @@ import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; import org.eclipse.edc.service.spi.result.ServiceResult; +import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.edc.edr.spi.EndpointDataReferenceEntry; import org.eclipse.tractusx.edc.spi.cp.adapter.model.NegotiateEdrRequest; +import java.util.List; + /** * Service for opening a transfer process. */ @@ -31,4 +35,15 @@ public interface AdapterTransferProcessService { * @return The result containing the contract negotiation id */ ServiceResult initiateEdrNegotiation(NegotiateEdrRequest request); + + /** + * Return a {@link EndpointDataReference} associated with the transferProcessId in input + * + * @param transferProcessId The transferProcessId + * @return The result containing the {@link EndpointDataReference} + */ + ServiceResult findByTransferProcessId(String transferProcessId); + + ServiceResult> findByAssetAndAgreement(String assetId, String agreementId); + } diff --git a/spi/core-spi/build.gradle.kts b/spi/core-spi/build.gradle.kts new file mode 100644 index 000000000..75b96d9ae --- /dev/null +++ b/spi/core-spi/build.gradle.kts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +plugins { + `java-library` +} + +dependencies { + implementation(libs.edc.spi.core) +} diff --git a/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java b/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java new file mode 100644 index 000000000..5d05db6ee --- /dev/null +++ b/spi/core-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/CoreConstants.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * 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 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation + * + */ + +package org.eclipse.tractusx.edc.edr.spi; + +public final class CoreConstants { + + public static final String TX_PREFIX = "tx"; + public static final String TX_NAMESPACE = "https://w3id.org/tractusx/v0.0.1/ns/"; + + private CoreConstants() { + } +} diff --git a/spi/edr-cache-spi/build.gradle.kts b/spi/edr-cache-spi/build.gradle.kts index 9ca2f9437..f60c83b17 100644 --- a/spi/edr-cache-spi/build.gradle.kts +++ b/spi/edr-cache-spi/build.gradle.kts @@ -17,6 +17,7 @@ plugins { } dependencies { + implementation(project(":spi:core-spi")) implementation(libs.edc.spi.core) } diff --git a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java index 67e533bf3..556d97729 100644 --- a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java +++ b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceCache.java @@ -44,6 +44,13 @@ public interface EndpointDataReferenceCache { @NotNull List entriesForAsset(String assetId); + + /** + * Returns the {@link EndpointDataReferenceEntry}s for the agreement. + */ + @NotNull + List entriesForAgreement(String agreementId); + /** * Saves an {@link EndpointDataReference} to the cache using upsert semantics. */ diff --git a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java index 617c856cc..508d8f786 100644 --- a/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java +++ b/spi/edr-cache-spi/src/main/java/org/eclipse/tractusx/edc/edr/spi/EndpointDataReferenceEntry.java @@ -20,12 +20,22 @@ import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import static java.util.Objects.requireNonNull; +import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import static org.eclipse.tractusx.edc.edr.spi.CoreConstants.TX_NAMESPACE; /** * An entry in the cache for an {@link EndpointDataReference}. */ @JsonDeserialize(builder = EndpointDataReferenceEntry.Builder.class) public class EndpointDataReferenceEntry { + + public static final String SIMPLE_TYPE = "EndpointDataReferenceEntry"; + + public static final String EDR_ENTRY_TYPE = TX_NAMESPACE + SIMPLE_TYPE; + public static final String EDR_ENTRY_ASSET_ID = EDC_NAMESPACE + "assetId"; + public static final String EDR_ENTRY_AGREEMENT_ID = EDC_NAMESPACE + "agreementId"; + public static final String EDR_ENTRY_TRANSFER_PROCESS_ID = EDC_NAMESPACE + "transferProcessId"; + private String assetId; private String agreementId; private String transferProcessId;