diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 398d9da2..669779d4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -29,6 +29,6 @@ jobs: java-version: '17' distribution: 'temurin' - name: Build with Gradle - uses: gradle/gradle-build-action@585b565652cefbba63202a7f927df0ff99f34001 + uses: gradle/gradle-build-action@0706ab3a3c20483a3f37c3d9de1b0d95297e3743 with: arguments: clean build diff --git a/README.md b/README.md index 089a10be..5c812d11 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,15 @@ model via the EDC. ## Version compatibility -| Specification | Version | -|:-----------------------|-------------------------| -| Eclipse Dataspace Connector | v0.4.1 | +| Specification | Version | +|:-----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| +| Eclipse Dataspace Connector | v0.4.1 | | AAS - Details of the Asset Administration Shell - Part 1
The exchange of information between partners in the value chain of Industrie 4.0 | Version 3.0RC01
(based on [admin-shell-io/java-model](https://github.com/admin-shell-io/java-model)) | ## Repo Structure The repository contains several material: + - `client`: Source code for the client extension (automated contract negotiation) - `config`: Checkstyle files for code formatting - `edc-extension4aas`: Source code for the AAS extension @@ -36,7 +37,8 @@ Assets. Furthermore, this extension can also start AAS by reading an AAS model. applied for all elements. For critical elements, additional contracts can be placed. External changes to the model of an AAS are automatically synchronized by the extension. -Additionally, a client extension providing API calls for aggregations of processes such as contract negotiation and data transfer +Additionally, a client extension providing API calls for aggregations of processes such as contract negotiation and data +transfer is available. ### Use Cases @@ -49,90 +51,88 @@ Provide digital twin (AAS) data to business partners in Data Spaces like Catena- #### **Provider Interfaces** -| HTTP Method | Interface (edc:1234/api/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | -|:-|:-|:-|:-| -| GET | config (a) | - | Get current extension configuration values. | -| PUT | config (a) | Body: Updated config values (JSON) (r) | Update config values. | -| POST | client (a) | Query Parameter "url" (r) | Register a standalone AAS service (e.g., FA³ST) to this extension. | -| DELETE | client (a) | Query Parameter "url" (r) | Unregister an AAS service (e.g., FA³ST) from this extension, possibly shutting down the service if it has been started internally. | -| POST | environment (a) | Query Parameter "environment": Path to new AAS environment (r), Query Parameter "port": Port of service to be created , Query Parameter "config": Path of AAS service configuration file | Create a new AAS service. Either (http) "port" or "config" must be given to ensure communication with the AAS service via an HTTP endpoint on the service's side. This command returns the URL of the newly created AAS service on success, which can be used to remove the service using the interface "DELETE /client" | -| POST | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward POST request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | -| DELETE | aas (a) | Query Parameter requestUrl: URL of AAS service to be updated (r) | Forward DELETE request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | -| PUT | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward PUT request to provided host in requestUrl. | -| GET | selfDescription | - | Return self description of extension. | +| HTTP Method | Interface (edc:1234/api/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | +|:------------|:------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| GET | config (a) | - | Get current extension configuration values. | +| PUT | config (a) | Body: Updated config values (JSON) (r) | Update config values. | +| POST | client (a) | Query Parameter "url" (r) | Register a standalone AAS service (e.g., FA³ST) to this extension. | +| DELETE | client (a) | Query Parameter "url" (r) | Unregister an AAS service (e.g., FA³ST) from this extension, possibly shutting down the service if it has been started internally. | +| POST | environment (a) | Query Parameter "environment": Path to new AAS environment (r), Query Parameter "port": Port of service to be created , Query Parameter "config": Path of AAS service configuration file | Create a new AAS service. Either (http) "port" or "config" must be given to ensure communication with the AAS service via an HTTP endpoint on the service's side. This command returns the URL of the newly created AAS service on success, which can be used to remove the service using the interface "DELETE /client" | +| POST | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward POST request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | +| DELETE | aas (a) | Query Parameter requestUrl: URL of AAS service to be updated (r) | Forward DELETE request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | +| PUT | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward PUT request to provided host in requestUrl. | +| GET | selfDescription | - | Return self description of extension. | #### **Client Interfaces** -| HTTP Method | Interface (edc:1234/api/automated/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | -|:-|:-|:-|:-| -| POST | negotiate (a) | Query Parameter "providerUrl": URL (r),Query Parameter "providerId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl": URL | Perform an automated contract negotiation with a provider (given provider URL and ID) and get the data stored for the specified asset. Optionally, a data destination URL can be specified where the data is sent to instead of the extension's log. | -| GET | dataset (a) | Query Parameter "providerUrl": URL (r), Query Parameter "assetId": String (r) | Get dataset from the specified provider's catalog that contains the specified asset's policies. | -| POST | negotiateContract (a) | request body: org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest (r) | Using a contractRequest (JSON in http request body), negotiate a contract. Returns the corresponding agreementId on success. | -| GET | transfer (a) | Query Parameter "providerUrl": URL (r), Query Parameter "agreementId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl" | Submits a data transfer request to the providerUrl. On success, returns the data behind the specified asset. Optionally, a data destination URL can be specified where the data is sent to instead of the extension's log. | - -| POST | acceptedPolicies (a) | request body: List of PolicyDefinitions (JSON) (r) | Adds the given PolicyDefinitions to the accepted PolicyDefinitions list (Explanation: On fully automated negotiation, the provider's PolicyDefinition is matched against the consumer's accepted PolicyDefinitions list. If any PolicyDefinition fits the provider's, the negotiation continues.) Returns "OK"-Response if requestBody is valid. | -| GET | acceptedPolicies (a) | - | Returns the client extension's accepted policy definitions for fully automated negotiation. | -| DELETE | acceptedPolicies (a) | request body: PolicyDefinition: PolicyDefinition (JSON) (r) | Updates the client extension's accepted policy definition with the same policyDefinitionId as the request. | -| PUT | acceptedPolicies (a) | request body: PolicyDefinitionId: String (JSON) (r) | Deletes a client extension's accepted policy definition with the same policyDefinitionId as the request. | - +| HTTP Method | Interface (edc:1234/api/automated/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | +|:------------|:----------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| POST | negotiate (a) | Query Parameter "providerUrl": URL (r), Query Parameter "providerId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl": URL | Perform an automated contract negotiation with a provider (given provider URL and ID) and get the data stored for the specified asset. Optionally, a data destination URL can be specified where the data is sent to instead of the extension's log. | +| GET | dataset (a) | Query Parameter "providerUrl": URL (r), Query Parameter "assetId": String (r), Query Parameter "providerId": String (r) | Get dataset from the specified provider's catalog that contains the specified asset's policies. | +| POST | negotiateContract (a) | request body: org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest (r) | Using a contractRequest (JSON in http request body), negotiate a contract. Returns the corresponding agreementId on success. | +| GET | transfer (a) | Query Parameter "providerUrl": URL (r), Query Parameter "agreementId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl" | Submits a data transfer request to the providerUrl. On success, returns the data behind the specified asset. Optionally, a data destination URL can be specified where the data is sent to instead of the extension's log. | +| POST | acceptedPolicies (a) | request body: List of PolicyDefinitions (JSON) (r) | Adds the given PolicyDefinitions to the accepted PolicyDefinitions list (Explanation: On fully automated negotiation, the provider's PolicyDefinition is matched against the consumer's accepted PolicyDefinitions list. If any PolicyDefinition fits the provider's, the negotiation continues.) Returns "OK"-Response if requestBody is valid. | +| GET | acceptedPolicies (a) | - | Returns the client extension's accepted policy definitions for fully automated negotiation. | +| DELETE | acceptedPolicies (a) | request body: PolicyDefinition: PolicyDefinition (JSON) (r) | Updates the client extension's accepted policy definition with the same policyDefinitionId as the request. | +| PUT | acceptedPolicies (a) | request body: PolicyDefinitionId: String (JSON) (r) | Deletes a client extension's accepted policy definition with the same policyDefinitionId as the request. | ### Dependencies #### EDC-Extension-for-AAS -| Name | Description | -|:-|:-| -| de.fraunhofer.iosb.ilt.faaast.service:starter | [FA³ST Service](https://github.com/FraunhoferIOSB/FAAAST-Service) to start AAS services internally. | -| io.admin-shell.aas:dataformat-json | [admin-shell-io java serializer](https://github.com/admin-shell-io/java-serializer) (de-)serialize AAS models | -| io.admin-shell.aas:model | [admin-shell-io java model](https://github.com/admin-shell-io/java-model) (de-)serialize AAS models | -| com.squareup.okhttp3:okhttp | Send HTTP requests to AAS services | -| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | -| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | -| org.eclipse.edc:management-api | EDC asset/contract management | -| org.eclipse.edc:runtime-metamodel | EDC metamodel | -| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | +| Name | Description | +|:----------------------------------------------|:--------------------------------------------------------------------------------------------------------------| +| de.fraunhofer.iosb.ilt.faaast.service:starter | [FA³ST Service](https://github.com/FraunhoferIOSB/FAAAST-Service) to start AAS services internally. | +| io.admin-shell.aas:dataformat-json | [admin-shell-io java serializer](https://github.com/admin-shell-io/java-serializer) (de-)serialize AAS models | +| io.admin-shell.aas:model | [admin-shell-io java model](https://github.com/admin-shell-io/java-model) (de-)serialize AAS models | +| com.squareup.okhttp3:okhttp | Send HTTP requests to AAS services | +| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | +| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | +| org.eclipse.edc:management-api | EDC asset/contract management | +| org.eclipse.edc:runtime-metamodel | EDC metamodel | +| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | #### Client Extension -| Name | Description | -|:-|:-| -| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | -| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | -| org.eclipse.edc:management-api | EDC asset/contract management | -| org.eclipse.edc:runtime-metamodel | EDC metamodel | -| org.eclipse.edc:data-plane-http-spi | HttpDataAddress | -| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | +| Name | Description | +|:--------------------------------------------|:-------------------------------------------| +| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | +| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | +| org.eclipse.edc:management-api | EDC asset/contract management | +| org.eclipse.edc:runtime-metamodel | EDC metamodel | +| org.eclipse.edc:data-plane-http-spi | HttpDataAddress | +| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | ### Configurations #### **EDC-Extension-for-AAS Configurations** -| Key| Value Type| Description| -|:-|:-|:-| -| edc.aas.remoteAasLocation| URL| A URL of an AAS service (such as FA³ST) that is already running and is conformant with official AAS API specification| -| edc.aas.localAASModelPath| path| A path to a serialized AAS environment compatible to specification version 3.0RC01 (see: https://github.com/FraunhoferIOSB/FAAAST-Service/blob/main/README.md) | -| edc.aas.localAASServicePort| Open port from 1 to 65535 | Port to locally created AAS service. Required, if localAASModelPath is defined and localAASServiceConfigPath is not defined.| -| edc.aas.localAASServiceConfigPath | path| Path to AAS config for locally started AAS service. Required, if localAASServicePort is not defined, but localAASModelPath is defined.| -| edc.aas.syncPeriod | whole number in seconds | Time period in which AAS services should be polled for structural changes (added/deleted elements etc.). Default value is 5 (seconds). Note: This configuration value is only read on startup, the synchronization period cannot be changed at runtime. | -| edc.aas.exposeSelfDescription| true/false| Whether the Self Description should be exposed on {edc}/api/selfDescription. When set to False, the selfDescription is still available for authenticated requests.| -| edc.aas.defaultAccessPolicyPath| path| Path to an access policy file (JSON). This policy will be used as the default access policy for all assets created after the configuration value has been set.| -| edc.aas.defaultContractPolicyPath | path| Path to a contract policy file (JSON). This policy will be used as the default contract policy for all assets created after the configuration value has been set.| +| Key | Value Type | Description | +|:----------------------------------|:--------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| edc.aas.remoteAasLocation | URL | A URL of an AAS service (such as FA³ST) that is already running and is conformant with official AAS API specification | +| edc.aas.localAASModelPath | path | A path to a serialized AAS environment compatible to specification version 3.0RC01 (see: https://github.com/FraunhoferIOSB/FAAAST-Service/blob/main/README.md) | +| edc.aas.localAASServicePort | Open port from 1 to 65535 | Port to locally created AAS service. Required, if localAASModelPath is defined and localAASServiceConfigPath is not defined. | +| edc.aas.localAASServiceConfigPath | path | Path to AAS config for locally started AAS service. Required, if localAASServicePort is not defined, but localAASModelPath is defined. | +| edc.aas.syncPeriod | whole number in seconds | Time period in which AAS services should be polled for structural changes (added/deleted elements etc.). Default value is 5 (seconds). Note: This configuration value is only read on startup, the synchronization period cannot be changed at runtime. | +| edc.aas.exposeSelfDescription | true/false | Whether the Self Description should be exposed on {edc}/api/selfDescription. When set to False, the selfDescription is still available for authenticated requests. | +| edc.aas.defaultAccessPolicyPath | path | Path to an access policy file (JSON). This policy will be used as the default access policy for all assets created after the configuration value has been set. | +| edc.aas.defaultContractPolicyPath | path | Path to a contract policy file (JSON). This policy will be used as the default contract policy for all assets created after the configuration value has been set. | #### **Client Extension Configurations** -| Key | Value Type | Description | -|:-|:-|:-| -| edc.client.waitForAgreementTimeout | whole number in seconds | How long should the extension wait for an agreement when automatically negotiating a contract? Default value is 10(s). | -| edc.client.waitForTransferTimeout | whole number in seconds | How long should the extension wait for a data transfer when automatically negotiating a contract? Default value is 10(s). | -| edc.client.acceptAllProviderOffers | boolean | If true, the client accepts any contractOffer offered by a provider connector on automated contract negotiation (e.g., trusted provider). Default value: false | -| edc.client.acceptedPolicyDefinitionsPath | path | Path pointing to a JSON-file containing acceptable PolicyDefinitions for automated contract negotiation in a list (only policies must match in a provider's PolicyDefinition) | +| Key | Value Type | Description | +|:-----------------------------------------|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| edc.client.waitForAgreementTimeout | whole number in seconds | How long should the extension wait for an agreement when automatically negotiating a contract? Default value is 10(s). | +| edc.client.waitForTransferTimeout | whole number in seconds | How long should the extension wait for a data transfer when automatically negotiating a contract? Default value is 10(s). | +| edc.client.acceptAllProviderOffers | boolean | If true, the client accepts any contractOffer offered by a provider connector on automated contract negotiation (e.g., trusted provider). Default value: false | +| edc.client.acceptedPolicyDefinitionsPath | path | Path pointing to a JSON-file containing acceptable PolicyDefinitions for automated contract negotiation in a list (only policies must match in a provider's PolicyDefinition) | ## Terminology -| Term | Description | -|:-|:-| -| AAS | Asset Administration Shell (see [AAS reading guide](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Asset_Administration_Shell_Reading_Guide.html) or [AAS specification part 1](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Details_of_the_Asset_Administration_Shell_Part1_V3.html)) | -| FA³ST Service | Open Source java implementation of the AAS part 2 [see on GitHub](https://github.com/FraunhoferIOSB/FAAAST-Service) | +| Term | Description | +|:--------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| AAS | Asset Administration Shell (see [AAS reading guide](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Asset_Administration_Shell_Reading_Guide.html) or [AAS specification part 1](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Details_of_the_Asset_Administration_Shell_Part1_V3.html)) | +| FA³ST Service | Open Source java implementation of the AAS part 2 [see on GitHub](https://github.com/FraunhoferIOSB/FAAAST-Service) | ## Roadmap diff --git a/changelog.md b/changelog.md index a04229ed..0b06e90a 100644 --- a/changelog.md +++ b/changelog.md @@ -2,10 +2,13 @@ ## Current development version -Compatibility: **Eclipse Dataspace Connector v0.4.1** +Compatibility: **Eclipse Dataspace Connector v0.5.1** **New Features** +- counterPartyId now needed when using client extension + + **Bugfixes** ## V1.0.0-alpha5 diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java index 7ea7eb45..e5eaca6c 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java @@ -15,24 +15,13 @@ */ package de.fraunhofer.iosb.client; -import static java.lang.String.format; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; - -import java.net.URL; -import java.util.Objects; -import java.util.concurrent.ExecutionException; - -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; -import org.eclipse.edc.connector.policy.spi.PolicyDefinition; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; - -import de.fraunhofer.iosb.client.dataTransfer.DataTransferController; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.fraunhofer.iosb.client.datatransfer.DataTransferController; import de.fraunhofer.iosb.client.negotiation.NegotiationController; import de.fraunhofer.iosb.client.policy.PolicyController; import de.fraunhofer.iosb.client.util.Pair; +import jakarta.json.*; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -43,6 +32,24 @@ import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.eclipse.edc.catalog.spi.Dataset; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; +import org.eclipse.edc.connector.policy.spi.PolicyDefinition; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; +import org.eclipse.edc.spi.types.domain.offer.ContractOffer; + +import java.io.StringReader; +import java.net.URL; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import static java.lang.String.format; +import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; /** * Automated contract negotiation @@ -56,7 +63,7 @@ public class ClientEndpoint { */ public static final String AUTOMATED_PATH = "automated"; private static final String ACCEPTED_POLICIES_PATH = "acceptedPolicies"; - private static final String DATASET_PATH = "dataset"; + private static final String OFFER_PATH = "offer"; private static final String NEGOTIATE_CONTRACT_PATH = "negotiateContract"; private static final String NEGOTIATE_PATH = "negotiate"; private static final String TRANSFER_PATH = "transfer"; @@ -67,12 +74,14 @@ public class ClientEndpoint { private final PolicyController policyController; private final DataTransferController transferController; + private final ObjectMapper objectMapper; + /** * Initialize a client endpoint. * * @param monitor Logging functionality + * @param policyController Finds out policy for a given asset id and provider EDC url. * @param negotiationController Send contract offer, negotiation status watch. - * @param policyController Provides API for accepted policy management and provider dataset retrieval. * @param transferController Initiate transfer requests. */ public ClientEndpoint(Monitor monitor, @@ -84,64 +93,90 @@ public ClientEndpoint(Monitor monitor, this.policyController = policyController; this.negotiationController = negotiationController; this.transferController = transferController; + this.objectMapper = new ObjectMapper(); } /** - * Return dataset for assetId that match any policyDefinitions' policy + * Return dataset for assetId that match any policyDefinition's policy * of the services' policyDefinitionStore instance containing user added * policyDefinitions. If more than one policyDefinitions are provided by the * provider connector, an AmbiguousOrNullException will be thrown. * * @param providerUrl Provider of the asset. * @param assetId Asset ID of the asset whose contract should be fetched. - * @return One policyDefinition offered by the provider for the given assetId. + * @return A dataset offered by the provider for the given assetId. */ @GET - @Path(DATASET_PATH) - public Response getDataset(@QueryParam("providerUrl") URL providerUrl, @QueryParam("assetId") String assetId) { - monitor.debug(format("[Client] Received a %s GET request", DATASET_PATH)); + @Path(OFFER_PATH) + public Response getOffer(@QueryParam("providerUrl") URL providerUrl, @QueryParam("assetId") String assetId, @QueryParam("providerId") String counterPartyId) { + monitor.debug(format("[Client] Received a %s GET request", OFFER_PATH)); if (Objects.isNull(providerUrl)) { return Response.status(Response.Status.BAD_REQUEST).entity("Provider URL must not be null").build(); } try { - var dataset = policyController.getDataset(providerUrl, assetId); - return Response.ok(dataset).build(); + var dataset = policyController.getDataset(counterPartyId, providerUrl, assetId); + + var parsedResponse = buildResponseFrom(dataset); + return Response.ok(parsedResponse).build(); + } catch (InterruptedException interruptedException) { - monitor.severe(format("[Client] Getting dataset failed for provider %s and asset %s", providerUrl, + monitor.severe(format("[Client] Getting offer failed for provider %s and asset %s", providerUrl, assetId), interruptedException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(interruptedException.getMessage()) .build(); + + } catch (JsonProcessingException policyWriteException) { + monitor.severe(format("[Client] Parsing policy failed for provider %s and asset %s", providerUrl, + assetId), policyWriteException); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(policyWriteException.getMessage()) + .build(); + } } + private String buildResponseFrom(Dataset dataset) throws JsonProcessingException { + var offer = dataset.getOffers().entrySet().stream().findFirst().orElseThrow(); + + // Build negotiation request body for the user + var policyString = objectMapper.writeValueAsString(offer.getValue()); + var policyJson = Json.createReader(new StringReader(policyString)).read(); + + return Json.createObjectBuilder() + .add("id", offer.getKey()) + .add("policy", policyJson) + .add("assetId", offer.getValue().getTarget()) + .build() + .toString(); + } + /** * Negotiate a contract agreement using the given contract offer if no agreement * exists for this constellation. * - * @param providerUrl Provider EDCs URL (DSP endpoint) - * @param providerId Provider EDCs ID + * @param counterPartyUrl Provider EDCs URL (DSP endpoint) + * @param counterPartyId Provider EDCs ID * @param assetId ID of the asset to be retrieved * @param dataDestinationUrl URL of destination data sink. * @return Asset data */ @POST @Path(NEGOTIATE_PATH) - public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, - @QueryParam("providerId") String providerId, + public Response negotiateContract(@QueryParam("providerUrl") URL counterPartyUrl, + @QueryParam("providerId") String counterPartyId, @QueryParam("assetId") String assetId, @QueryParam("dataDestinationUrl") URL dataDestinationUrl) { monitor.debug(format("[Client] Received a %s POST request", NEGOTIATE_PATH)); - Objects.requireNonNull(providerUrl, "Provider URL must not be null"); - Objects.requireNonNull(providerId, "Provider ID must not be null"); + Objects.requireNonNull(counterPartyUrl, "Provider URL must not be null"); + Objects.requireNonNull(counterPartyId, "Provider ID must not be null"); Objects.requireNonNull(assetId, "Asset ID must not be null"); Pair idPolicyPair; // id means contractOfferId try { - idPolicyPair = policyController.getAcceptablePolicyForAssetId(providerUrl, assetId); + idPolicyPair = policyController.getAcceptablePolicyForAssetId(counterPartyId, counterPartyUrl, assetId); } catch (InterruptedException negotiationException) { - monitor.severe(format("[Client] Getting policies failed for provider %s and asset %s", providerUrl, + monitor.severe(format("[Client] Getting policies failed for provider %s and asset %s", counterPartyUrl, assetId), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) .build(); @@ -155,8 +190,8 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, var contractRequest = ContractRequest.Builder.newInstance() .contractOffer(offer) - .counterPartyAddress(providerUrl.toString()) - .providerId(providerId) + .counterPartyAddress(counterPartyUrl.toString()) + .providerId(counterPartyId) .protocol(DATASPACE_PROTOCOL_HTTP) .build(); ContractAgreement agreement; @@ -164,13 +199,13 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, try { agreement = negotiationController.negotiateContract(contractRequest); } catch (InterruptedException | ExecutionException negotiationException) { - monitor.severe(format("[Client] Negotiation failed for provider %s and contractOffer %s", providerUrl, + monitor.severe(format("[Client] Negotiation failed for provider %s and contractOffer %s", counterPartyUrl, offer.getId()), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) .build(); } - return getData(providerUrl, agreement.getId(), assetId, dataDestinationUrl); + return getData(counterPartyUrl, agreement.getId(), assetId, dataDestinationUrl); } /** @@ -187,7 +222,12 @@ public Response negotiateContract(ContractRequest contractRequest) { Objects.requireNonNull(contractRequest, "ContractRequest must not be null"); try { var agreement = negotiationController.negotiateContract(contractRequest); - return Response.ok(agreement).build(); + // Sanitize response (only ID is relevant here) + var agreementResponse = Json.createObjectBuilder() + .add("agreement-id", agreement.getId()) + .build() + .toString(); + return Response.ok(agreementResponse).build(); } catch (InterruptedException | ExecutionException negotiationException) { monitor.severe( format("[Client] Negotiation failed for provider %s and contractRequest %s", @@ -211,7 +251,8 @@ public Response negotiateContract(ContractRequest contractRequest) { @GET @Path(TRANSFER_PATH) public Response getData(@QueryParam("providerUrl") URL providerUrl, - @QueryParam("agreementId") String agreementId, @QueryParam("assetId") String assetId, + @QueryParam("agreementId") String agreementId, + @QueryParam("assetId") String assetId, @QueryParam("dataDestinationUrl") URL dataDestinationUrl) { monitor.debug(format("[Client] Received a %s GET request", TRANSFER_PATH)); Objects.requireNonNull(providerUrl, "providerUrl must not be null"); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java index 80bc3f94..d9d547b0 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -16,7 +16,7 @@ package de.fraunhofer.iosb.client; import de.fraunhofer.iosb.api.PublicApiManagementService; -import de.fraunhofer.iosb.client.dataTransfer.DataTransferController; +import de.fraunhofer.iosb.client.datatransfer.DataTransferController; import de.fraunhofer.iosb.client.negotiation.NegotiationController; import de.fraunhofer.iosb.client.policy.PolicyController; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; @@ -61,8 +61,9 @@ public void initialize(ServiceExtensionContext context) { var negotiationController = new NegotiationController(consumerNegotiationManager, contractNegotiationObservable, contractNegotiationStore, config); + // This controller needs base config to read EDC's hostname + specific ports var dataTransferController = new DataTransferController(monitor, context.getConfig(), webService, - publicApiManagementService, transferProcessManager, context.getConnectorId()); + publicApiManagementService, transferProcessManager); webService.registerResource(new ClientEndpoint(monitor, negotiationController, policyController, dataTransferController)); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/authentication/DataTransferEndpointManager.java b/client/src/main/java/de/fraunhofer/iosb/client/authentication/DataTransferEndpointManager.java index 4872ebc1..6ad4efcc 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/authentication/DataTransferEndpointManager.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/authentication/DataTransferEndpointManager.java @@ -19,18 +19,9 @@ import de.fraunhofer.iosb.api.model.Endpoint; import de.fraunhofer.iosb.api.model.HttpMethod; import de.fraunhofer.iosb.client.ClientEndpoint; -import de.fraunhofer.iosb.client.dataTransfer.DataTransferEndpoint; -import jakarta.ws.rs.container.ContainerRequestContext; -import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; -import org.eclipse.edc.api.auth.spi.AuthenticationService; -import org.eclipse.edc.spi.monitor.Monitor; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -import static java.lang.String.format; /** * Custom AuthenticationRequestFilter filtering requests that go directly to an @@ -56,4 +47,4 @@ public void addTemporaryEndpoint(String agreementId, String key, String value) { var endpointSuffix = ClientEndpoint.AUTOMATED_PATH + "/receiveData/" + agreementId; publicApiManagementService.addTemporaryEndpoint(new Endpoint(endpointSuffix, HttpMethod.POST, Map.of(key, List.of(value)))); } -} \ No newline at end of file +} diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferController.java similarity index 94% rename from client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java rename to client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferController.java index 1d5721cb..995d66a3 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferController.java @@ -13,9 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.client.dataTransfer; +package de.fraunhofer.iosb.client.datatransfer; -import static java.lang.String.format; +import de.fraunhofer.iosb.api.PublicApiManagementService; +import de.fraunhofer.iosb.client.authentication.DataTransferEndpointManager; +import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; +import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.web.spi.WebService; import java.net.URL; import java.util.Objects; @@ -25,14 +32,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import de.fraunhofer.iosb.api.PublicApiManagementService; -import de.fraunhofer.iosb.client.authentication.DataTransferEndpointManager; -import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; -import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.web.spi.WebService; +import static java.lang.String.format; public class DataTransferController { @@ -58,12 +58,11 @@ public class DataTransferController { * keys for each data transfer. * @param transferProcessManager Initiating a transfer process as a * consumer. - * @param connectorId Connector ID for the provider to learn */ public DataTransferController(Monitor monitor, Config config, WebService webService, - PublicApiManagementService publicApiManagementService, TransferProcessManager transferProcessManager, String connectorId) { + PublicApiManagementService publicApiManagementService, TransferProcessManager transferProcessManager) { + this.transferInitiator = new TransferInitiator(config, monitor, transferProcessManager); this.config = config.getConfig("edc.client"); - this.transferInitiator = new TransferInitiator(config, monitor, transferProcessManager, connectorId); this.dataTransferEndpointManager = new DataTransferEndpointManager(publicApiManagementService); this.dataTransferObservable = new DataTransferObservable(monitor); var dataTransferEndpoint = new DataTransferEndpoint(monitor, dataTransferObservable); @@ -86,8 +85,7 @@ public DataTransferController(Monitor monitor, Config config, WebService webServ public String initiateTransferProcess(URL providerUrl, String agreementId, String assetId, URL dataDestinationUrl) throws InterruptedException, ExecutionException { // Prepare for incoming data - var dataFuture = new CompletableFuture(); - dataTransferObservable.register(dataFuture, agreementId); + var dataFuture = dataTransferObservable.register(agreementId); if (Objects.isNull(dataDestinationUrl)) { var apiKey = UUID.randomUUID().toString(); @@ -96,11 +94,13 @@ public String initiateTransferProcess(URL providerUrl, String agreementId, Strin this.transferInitiator.initiateTransferProcess(providerUrl, agreementId, assetId, apiKey); return waitForData(dataFuture, agreementId); } else { + // Send data to custom target url var dataSinkAddress = HttpDataAddress.Builder.newInstance() .baseUrl(dataDestinationUrl.toString()) .build(); this.transferInitiator.initiateTransferProcess(providerUrl, agreementId, assetId, dataSinkAddress); + // Don't have to wait for data return null; } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferEndpoint.java similarity index 79% rename from client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java rename to client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferEndpoint.java index 8b0cc590..441ca47d 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferEndpoint.java @@ -13,38 +13,41 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.client.dataTransfer; +package de.fraunhofer.iosb.client.datatransfer; import de.fraunhofer.iosb.client.ClientEndpoint; -import jakarta.ws.rs.*; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import org.eclipse.edc.spi.monitor.Monitor; import java.util.Objects; -import org.eclipse.edc.spi.monitor.Monitor; - import static java.lang.String.format; /** * Endpoint for automated data transfer */ -@Consumes({ MediaType.APPLICATION_JSON, MediaType.WILDCARD }) -@Produces({ MediaType.APPLICATION_JSON }) +@Consumes({MediaType.APPLICATION_JSON, MediaType.WILDCARD}) +@Produces({MediaType.APPLICATION_JSON}) @Path(ClientEndpoint.AUTOMATED_PATH) public class DataTransferEndpoint { /* * Path for providers to send data to. */ - public static final String RECEIVE_DATA_PATH = "receiveData"; + static final String RECEIVE_DATA_PATH = "receiveData"; private final Monitor monitor; private final DataTransferObservable observable; - public DataTransferEndpoint(Monitor monitor, DataTransferObservable observable) { + DataTransferEndpoint(Monitor monitor, DataTransferObservable dataTransferObservable) { this.monitor = monitor; - this.observable = observable; + this.observable = dataTransferObservable; } /** diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferObservable.java similarity index 89% rename from client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java rename to client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferObservable.java index fcf6b1fe..1423f57b 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/DataTransferObservable.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.client.dataTransfer; +package de.fraunhofer.iosb.client.datatransfer; + +import org.eclipse.edc.spi.monitor.Monitor; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import org.eclipse.edc.spi.monitor.Monitor; - import static java.lang.String.format; /** @@ -40,11 +40,12 @@ class DataTransferObservable { /** * Register a future that should complete if a data transfer is finished. * - * @param observer The future to complete if data transfer is finished. * @param agreementId The agreement ID this future is dependent on. + * @return Object containing data in case of transfer. */ - void register(CompletableFuture observer, String agreementId) { - observers.put(agreementId, observer); + CompletableFuture register(String agreementId) { + observers.put(agreementId, new CompletableFuture()); + return observers.get(agreementId); } /** diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/TransferInitiator.java similarity index 92% rename from client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java rename to client/src/main/java/de/fraunhofer/iosb/client/datatransfer/TransferInitiator.java index 1129d26f..3ad3ecd0 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/datatransfer/TransferInitiator.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.client.dataTransfer; +package de.fraunhofer.iosb.client.datatransfer; import de.fraunhofer.iosb.client.ClientEndpoint; import jakarta.ws.rs.core.UriBuilder; @@ -30,12 +30,11 @@ import java.util.Objects; import java.util.UUID; -import static de.fraunhofer.iosb.client.dataTransfer.DataTransferController.DATA_TRANSFER_API_KEY; +import static de.fraunhofer.iosb.client.datatransfer.DataTransferController.DATA_TRANSFER_API_KEY; import static java.lang.String.format; import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; - /** * Initiate transfer requests */ @@ -44,14 +43,12 @@ class TransferInitiator { private final TransferProcessManager transferProcessManager; private final Monitor monitor; private final URI ownUri; - private final String connectorId; TransferInitiator(Config config, Monitor monitor, - TransferProcessManager transferProcessManager, String connectorId) { + TransferProcessManager transferProcessManager) { this.monitor = monitor; this.ownUri = createOwnUriFromConfigurationValues(config); this.transferProcessManager = transferProcessManager; - this.connectorId = connectorId; } void initiateTransferProcess(URL providerUrl, String agreementId, String assetId, String apiKey) { @@ -74,10 +71,8 @@ void initiateTransferProcess(URL providerUrl, String agreementId, String assetId var transferRequest = TransferRequest.Builder.newInstance() .id(UUID.randomUUID().toString()) // this is not relevant, thus can be random - .connectorId(providerUrl.toString()) // the address of the provider connector - .counterPartyAddress(providerUrl.toString()) + .counterPartyAddress(providerUrl.toString()) // the address of the provider connector .protocol(DATASPACE_PROTOCOL_HTTP) - .connectorId(this.connectorId) .assetId(assetId) .dataDestination(dataSinkAddress) .contractId(agreementId) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java index 795094e6..3d34c93b 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java @@ -15,13 +15,6 @@ */ package de.fraunhofer.iosb.client.negotiation; -import static java.lang.String.format; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; @@ -31,6 +24,14 @@ import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static java.lang.String.format; + /** * Provides API for contract negotiation by * {@link de.fraunhofer.iosb.client.negotiation.Negotiator the Negotiator @@ -48,10 +49,10 @@ public class NegotiationController { private final ClientContractNegotiationListener listener; public NegotiationController(ConsumerContractNegotiationManager consumerNegotiationManager, - ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, - Config config) { + ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, + Config config) { this.config = config; - this.negotiator = new Negotiator(consumerNegotiationManager, contractNegotiationStore, config); + this.negotiator = new Negotiator(consumerNegotiationManager, contractNegotiationStore); this.listener = new ClientContractNegotiationListener(); observable.registerListener(listener); } @@ -60,14 +61,18 @@ public ContractAgreement negotiateContract(ContractRequest contractRequest) throws InterruptedException, ExecutionException { var negotiationStatusResult = negotiator.negotiate(contractRequest); - - if (negotiationStatusResult.succeeded()) { - return waitForAgreement(negotiationStatusResult.getContent().getId()); - } else { + if (!negotiationStatusResult.succeeded()) { throw new EdcException(negotiationStatusResult.getFailureDetail()); } + var negotiation = negotiationStatusResult.getContent(); + if (Objects.nonNull(negotiation.getContractAgreement())) { + return negotiationStatusResult.getContent().getContractAgreement(); + } else { + return waitForAgreement(negotiation.getId()); + } } + private ContractAgreement waitForAgreement(String negotiationId) throws InterruptedException, ExecutionException { var agreementFuture = new CompletableFuture(); var timeout = config.getInteger("waitForAgreementTimeout", diff --git a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java index dcc165bd..822d10c6 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java @@ -15,15 +15,12 @@ */ package de.fraunhofer.iosb.client.negotiation; -import java.util.concurrent.ExecutionException; - import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.system.configuration.Config; /** * Send contractrequest, negotiation status watch @@ -41,7 +38,7 @@ public class Negotiator { * negotiating */ public Negotiator(ConsumerContractNegotiationManager consumerNegotiationManager, - ContractNegotiationStore contractNegotiationStore, Config config) { + ContractNegotiationStore contractNegotiationStore) { this.consumerNegotiationManager = consumerNegotiationManager; this.contractNegotiationStore = contractNegotiationStore; } @@ -49,7 +46,7 @@ public Negotiator(ConsumerContractNegotiationManager consumerNegotiationManager, /* * InterruptedException: Thread for agreementId was waiting, sleeping, or * otherwise occupied, and was interrupted. - * + * * ExecutionException: Attempted to retrieve the agreementId but the task * aborted by throwing an exception. This exception can be inspected using the * getCause() method. @@ -64,7 +61,12 @@ StatusResult negotiate(ContractRequest contractRequest) { if (!relevantAgreements.isEmpty()) { // assuming contractNegotiationStore removes invalid agreements return StatusResult.success( - ContractNegotiation.Builder.newInstance().contractAgreement(relevantAgreements.get(0)).build()); + ContractNegotiation.Builder.newInstance() + .contractAgreement(relevantAgreements.get(0)) + .counterPartyAddress(contractRequest.getCounterPartyAddress()) + .counterPartyId(contractRequest.getProviderId()) + .protocol(contractRequest.getProtocol()) + .build()); } return consumerNegotiationManager.initiate(contractRequest); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java index dccf0cd1..ce6c6acd 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java @@ -15,10 +15,7 @@ */ package de.fraunhofer.iosb.client.policy; -import java.net.URL; -import java.util.List; -import java.util.Optional; - +import de.fraunhofer.iosb.client.util.Pair; import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.connector.spi.catalog.CatalogService; @@ -27,7 +24,9 @@ import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import de.fraunhofer.iosb.client.util.Pair; +import java.net.URL; +import java.util.List; +import java.util.Optional; /** * Provides API for accepted policy management and provider dataset retrieval. @@ -40,7 +39,7 @@ public class PolicyController { private final PolicyService policyService; public PolicyController(Monitor monitor, CatalogService catalogService, - TypeTransformerRegistry typeTransformerRegistry, Config systemConfig) { + TypeTransformerRegistry typeTransformerRegistry, Config systemConfig) { var config = new PolicyServiceConfig(systemConfig); this.policyDefinitionStore = new PolicyDefinitionStore(monitor, config.getAcceptedPolicyDefinitionsPath()); @@ -48,8 +47,8 @@ public PolicyController(Monitor monitor, CatalogService catalogService, this.policyDefinitionStore, monitor); } - public Dataset getDataset(URL providerUrl, String assetId) throws InterruptedException { - return policyService.getDatasetForAssetId(providerUrl, assetId); + public Dataset getDataset(String counterPartyId, URL counterPartyUrl, String assetId) throws InterruptedException { + return policyService.getDatasetForAssetId(counterPartyId, counterPartyUrl, assetId); } /** @@ -60,16 +59,17 @@ public Dataset getDataset(URL providerUrl, String assetId) throws InterruptedExc * If more than one policyDefinitions are provided by the provider * connector, an AmbiguousOrNullException will be thrown. * - * @param providerUrl Provider of the asset. - * @param assetId Asset ID of the asset whose contract should be fetched. + * @param counterPartyId Provider of the asset. (id) + * @param counterPartyUrl Provider of the asset. (url) + * @param assetId Asset ID of the asset whose contract should be fetched. * @return One policyDefinition offered by the provider for the given assetId. * @throws InterruptedException Thread for agreementId was waiting, sleeping, or * otherwise occupied, and was * interrupted. */ - public Pair getAcceptablePolicyForAssetId(URL providerUrl, String assetId) + public Pair getAcceptablePolicyForAssetId(String counterPartyId, URL counterPartyUrl, String assetId) throws InterruptedException { - return policyService.getAcceptablePolicyForAssetId(providerUrl, assetId); + return policyService.getAcceptablePolicyForAssetId(counterPartyId, counterPartyUrl, assetId); } public void addAcceptedPolicyDefinitions(PolicyDefinition[] policyDefinitions) { diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java index 82de55ee..6e8f8b21 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java @@ -19,13 +19,17 @@ import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.spi.monitor.Monitor; -import static java.lang.String.format; - import java.io.IOException; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import static java.lang.String.format; + /** * Contains user added PolicyDefinitions. */ @@ -77,7 +81,6 @@ Optional removePolicyDefinition(String policyDefinitionId) { /** * Update a policyDefinition * - * @param policyDefinitionId PolicyDefinition ID (non null) * @param policyDefinition The updated policyDefinition * @return Optional containing updated policy definition or null */ diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java index 6064bbbf..5f3ebef3 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java @@ -15,22 +15,9 @@ */ package de.fraunhofer.iosb.client.policy; -import static java.lang.String.format; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; -import static org.eclipse.edc.spi.query.Criterion.criterion; - -import java.io.ByteArrayInputStream; -import java.net.URL; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; -import java.util.stream.Stream; - +import de.fraunhofer.iosb.client.exception.AmbiguousOrNullException; +import de.fraunhofer.iosb.client.util.Pair; +import jakarta.json.Json; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.connector.spi.catalog.CatalogService; @@ -44,10 +31,23 @@ import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.jetbrains.annotations.NotNull; -import de.fraunhofer.iosb.client.exception.AmbiguousOrNullException; -import de.fraunhofer.iosb.client.util.Pair; -import jakarta.json.Json; +import java.io.ByteArrayInputStream; +import java.net.URL; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.lang.String.format; +import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; +import static org.eclipse.edc.spi.query.Criterion.criterion; /** * Finds out policy for a given asset id and provider EDC url @@ -69,8 +69,8 @@ class PolicyService { * @param catalogService Fetching the catalog of a provider. * @param transformer Transform json-ld byte-array catalog to catalog class */ - public PolicyService(CatalogService catalogService, TypeTransformerRegistry transformer, - PolicyServiceConfig config, PolicyDefinitionStore policyDefinitionStore, Monitor monitor) { + PolicyService(CatalogService catalogService, TypeTransformerRegistry transformer, + PolicyServiceConfig config, PolicyDefinitionStore policyDefinitionStore, Monitor monitor) { this.catalogService = catalogService; this.transformer = transformer; @@ -81,27 +81,29 @@ public PolicyService(CatalogService catalogService, TypeTransformerRegistry tran } - Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws InterruptedException { + Dataset getDatasetForAssetId(@NotNull String counterPartyId, @NotNull URL counterPartyUrl, @NotNull String assetId) throws InterruptedException { + var catalogFuture = catalogService.requestCatalog( - providerUrl.toString(), + counterPartyId, // why do we even need a provider id when we have the url... + counterPartyUrl.toString(), DATASPACE_PROTOCOL_HTTP, QuerySpec.Builder.newInstance() - .filter(List.of(criterion(Asset.PROPERTY_ID, "=", assetId))) + .filter(criterion(Asset.PROPERTY_ID, "=", assetId)) .build()); StatusResult catalogResponse; try { catalogResponse = catalogFuture.get(config.getWaitForCatalogTimeout(), TimeUnit.SECONDS); } catch (ExecutionException futureExecutionException) { - throw new EdcException(format("Failed fetching a catalog by provider %s.", providerUrl), + throw new EdcException(format("Failed fetching a catalog by provider %s.", counterPartyUrl), futureExecutionException); } catch (TimeoutException timeoutCatalogFutureGetException) { - throw new EdcException(format("Timeout while waiting for catalog by provider %s.", providerUrl), + throw new EdcException(format("Timeout while waiting for catalog by provider %s.", counterPartyUrl), timeoutCatalogFutureGetException); } if (catalogResponse.failed()) { - throw new EdcException(format(CATALOG_RETRIEVAL_FAILURE_MSG, providerUrl, + throw new EdcException(format(CATALOG_RETRIEVAL_FAILURE_MSG, counterPartyUrl, catalogResponse.getFailureMessages())); } @@ -111,14 +113,14 @@ Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws Interrupted var catalogJsonExpansionResult = jsonLdExpander.expand(catalogJson); if (catalogJsonExpansionResult.failed()) { - throw new EdcException(format(CATALOG_RETRIEVAL_FAILURE_MSG, providerUrl, + throw new EdcException(format(CATALOG_RETRIEVAL_FAILURE_MSG, counterPartyUrl, catalogJsonExpansionResult.getFailureMessages())); } var catalogResult = transformer.transform(catalogJsonExpansionResult.getContent(), Catalog.class); if (catalogResult.failed()) { - throw new EdcException(format(CATALOG_RETRIEVAL_FAILURE_MSG, providerUrl, + throw new EdcException(format(CATALOG_RETRIEVAL_FAILURE_MSG, counterPartyUrl, catalogResult.getFailureMessages())); } @@ -132,9 +134,9 @@ Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws Interrupted return datasets.get(0); } - Pair getAcceptablePolicyForAssetId(URL providerUrl, String assetId) + Pair getAcceptablePolicyForAssetId(String counterPartyId, URL providerUrl, String assetId) throws InterruptedException { - var dataset = getDatasetForAssetId(providerUrl, assetId); + var dataset = getDatasetForAssetId(counterPartyId, providerUrl, assetId); Map.Entry acceptablePolicy; if (config.isAcceptAllProviderOffers()) { @@ -165,16 +167,16 @@ private boolean matchesOwnPolicyDefinitions(Policy policy) { private boolean policyDefinitionRulesEquality(Policy first, Policy second) { List firstRules = Stream.of( - first.getPermissions(), - first.getProhibitions(), - first.getObligations()) + first.getPermissions(), + first.getProhibitions(), + first.getObligations()) .flatMap(Collection::stream) .collect(Collectors.toList()); List secondRules = Stream.of( - second.getPermissions(), - second.getProhibitions(), - second.getObligations()) + second.getPermissions(), + second.getProhibitions(), + second.getObligations()) .flatMap(Collection::stream) .collect(Collectors.toList()); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java index 29fc5c89..993f9cd6 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java @@ -22,7 +22,7 @@ */ public class PolicyServiceConfig { - private static final boolean ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT = false; + private static final boolean ACCEPT_ALL_POLICY_DEFINITIONS_DEFAULT = false; private static final int WAIT_FOR_CATALOG_TIMEOUT_DEFAULT = 10; private static final String ACCEPTED_POLICY_DEFINITIONS_PATH_DEFAULT = null; @@ -33,7 +33,7 @@ public PolicyServiceConfig(Config config) { } boolean isAcceptAllProviderOffers() { - return config.getBoolean("acceptAllProviderOffers", ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT); + return config.getBoolean("acceptAllProviderOffers", ACCEPT_ALL_POLICY_DEFINITIONS_DEFAULT); } int getWaitForCatalogTimeout() { diff --git a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java index e877a5ea..1bf6780d 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java @@ -15,25 +15,13 @@ */ package de.fraunhofer.iosb.client; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; - -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeoutException; - +import com.fasterxml.jackson.databind.ObjectMapper; import de.fraunhofer.iosb.api.PublicApiManagementService; -import org.eclipse.edc.api.auth.spi.AuthenticationService; +import de.fraunhofer.iosb.client.datatransfer.DataTransferController; +import de.fraunhofer.iosb.client.negotiation.NegotiationController; +import de.fraunhofer.iosb.client.policy.PolicyController; +import jakarta.ws.rs.core.Response; + import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; @@ -62,12 +50,22 @@ import org.junit.jupiter.api.Test; import org.mockserver.integration.ClientAndServer; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; -import de.fraunhofer.iosb.client.dataTransfer.DataTransferController; -import de.fraunhofer.iosb.client.negotiation.NegotiationController; -import de.fraunhofer.iosb.client.policy.PolicyController; -import jakarta.ws.rs.core.Response; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; public class ClientEndpointTest { @@ -118,7 +116,7 @@ public void setup() throws IOException { mockConfig(), mock(WebService.class), mock(PublicApiManagementService.class), - mockTransferProcessManager(), "")); + mockTransferProcessManager())); } private Config mockConfig() { @@ -148,7 +146,7 @@ private CatalogService mockCatalogService() throws IOException { var completableFuture = new CompletableFuture>(); completableFuture.complete(StatusResult.success(new ObjectMapper().writeValueAsBytes(mockCatalog))); - when(catalogService.requestCatalog(any(), any(), any())).thenReturn(completableFuture); + when(catalogService.requestCatalog(any(), any(), any(), any())).thenReturn(completableFuture); return catalogService; } @@ -187,8 +185,8 @@ public void negotiateContractTest() { .build())) { fail(); } catch (EdcException expected) { - if (!(expected.getCause().getClass().equals(TimeoutException.class) - && expected.getMessage().contains("Agreement"))) { + if (!(expected.getCause().getClass().equals(TimeoutException.class) && + expected.getMessage().contains("Agreement"))) { fail(); // This must fail because of agreement timeout. } } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java index 0893a4c6..11b3922d 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java @@ -15,7 +15,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; @ExtendWith(DependencyInjectionExtension.class) public class ClientExtensionTest { @@ -29,14 +30,13 @@ void setup(ServiceExtensionContext context, ObjectFactory factory) { context.registerService(CatalogService.class, mock(CatalogService.class)); context.registerService(ConsumerContractNegotiationManager.class, mock(ConsumerContractNegotiationManager.class)); - context.registerService(ContractNegotiationStore.class, mock(ContractNegotiationStore.class)); context.registerService(ContractNegotiationObservable.class, mock(ContractNegotiationObservable.class)); + context.registerService(ContractNegotiationStore.class, mock(ContractNegotiationStore.class)); context.registerService(TransferProcessManager.class, mock(TransferProcessManager.class)); context.registerService(WebService.class, mock(WebService.class)); context.registerService(Monitor.class, mock(Monitor.class)); this.context = spy(context); - clientExtension = factory.constructInstance(ClientExtension.class); } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/datatransfer/TransferInitiatorTest.java similarity index 93% rename from client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java rename to client/src/test/java/de/fraunhofer/iosb/client/datatransfer/TransferInitiatorTest.java index ea5253aa..8d3ca270 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/datatransfer/TransferInitiatorTest.java @@ -13,20 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.client.dataTransfer; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Map; -import java.util.UUID; +package de.fraunhofer.iosb.client.datatransfer; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; @@ -34,11 +21,22 @@ import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Map; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + public class TransferInitiatorTest { private final TransferProcessManager mockTransferProcessManager = mock(TransferProcessManager.class); @@ -52,7 +50,7 @@ void initializeContractOfferService() { var configMock = ConfigFactory.fromMap(Map.of("edc.dsp.callback.address", "http://localhost:4321/dsp", "web.http.port", "8080", "web.http.path", "/api")); - transferInitiator = new TransferInitiator(configMock, mock(Monitor.class), mockTransferProcessManager, "http://localhost"); + transferInitiator = new TransferInitiator(configMock, mock(Monitor.class), mockTransferProcessManager); mockStatusResult = (StatusResult) mock(StatusResult.class); diff --git a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java index b99e093f..d4a8d417 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java @@ -15,20 +15,6 @@ */ package de.fraunhofer.iosb.client.negotiation; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.stream.Stream; - import org.eclipse.edc.connector.contract.observe.ContractNegotiationObservableImpl; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; @@ -39,31 +25,42 @@ import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.eclipse.edc.spi.types.domain.offer.ContractOffer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + public class NegotiatorTest { - private final ConsumerContractNegotiationManager ccnmMock = mock(ConsumerContractNegotiationManager.class); - private final ContractNegotiationStore cnsMock = mock(ContractNegotiationStore.class); - private final ContractNegotiationObservable contractNegotiationObservable = new ContractNegotiationObservableImpl(); - private final Config configMock = ConfigFactory.empty(); + private final ConsumerContractNegotiationManager ccnmMock = mock(ConsumerContractNegotiationManager.class); + private final ContractNegotiationStore cnsMock = mock(ContractNegotiationStore.class); + private final ContractNegotiationObservable contractNegotiationObservable = new ContractNegotiationObservableImpl(); - private final String assetId = "test-asset-id"; - private final Policy mockPolicy = buildPolicy(); - private final ContractNegotiation negotiation = getContractNegotiation(); + private final String assetId = "test-asset-id"; + private final Policy mockPolicy = buildPolicy(); + private final ContractNegotiation negotiation = getContractNegotiation(); - private Negotiator clientNegotiator; + private Negotiator clientNegotiator; - @BeforeEach - void initializeClientNegotiator() { - defineMockBehaviour(); - clientNegotiator = new Negotiator(ccnmMock, cnsMock, configMock); - } + @BeforeEach + void initializeClientNegotiator() { + defineMockBehaviour(); + clientNegotiator = new Negotiator(ccnmMock, cnsMock); + } void defineMockBehaviour() { when(cnsMock.queryAgreements(any())).thenReturn(Stream.of()); @@ -71,74 +68,74 @@ void defineMockBehaviour() { .thenReturn(StatusResult.success(negotiation)); } - @Test - void testNegotiate() throws MalformedURLException, ExecutionException, InterruptedException { - // Mocked EDC negotiation manager returns a future which completes to a - // successful negotiation (agreement) - // Input is providerUrl (unimportant), contractOffer. The resulting - // contractAgreement should have the same - // policy as our contractOffer (not the same object reference) and the same - // asset ID - var fakeUrl = new URL("https://example.com/fakeurl"); - var contractOffer = ContractOffer.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .assetId(assetId) - .policy(mockPolicy) - .build(); - - var contractRequest = ContractRequest.Builder.newInstance() - .contractOffer(contractOffer) - .counterPartyAddress(fakeUrl.toString()) - .protocol("dataspace-protocol-http") - .build(); - - var future = Executors.newSingleThreadExecutor() - .submit(() -> clientNegotiator.negotiate(contractRequest)); - - // Let the negotiator add a listener to this negotiation. - // If we don't, the "confirmed" signal will be sent too soon, and the negotiator - // will never see it - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - fail(); - } - contractNegotiationObservable.invokeForEach(listener -> listener.finalized(negotiation)); - - assertNotNull(future); - var contractNegotiation = future.get(); - assertNotNull(contractNegotiation); - assertEquals(mockPolicy, contractNegotiation.getContent().getContractAgreement().getPolicy()); - assertEquals(assetId, contractNegotiation.getContent().getContractAgreement().getAssetId()); + @Test + void testNegotiate() throws MalformedURLException, ExecutionException, InterruptedException { + // Mocked EDC negotiation manager returns a future which completes to a + // successful negotiation (agreement) + // Input is providerUrl (unimportant), contractOffer. The resulting + // contractAgreement should have the same + // policy as our contractOffer (not the same object reference) and the same + // asset ID + var fakeUrl = new URL("https://example.com/fakeurl"); + var contractOffer = ContractOffer.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .assetId(assetId) + .policy(mockPolicy) + .build(); + + var contractRequest = ContractRequest.Builder.newInstance() + .contractOffer(contractOffer) + .counterPartyAddress(fakeUrl.toString()) + .protocol("dataspace-protocol-http") + .build(); + + var future = Executors.newSingleThreadExecutor() + .submit(() -> clientNegotiator.negotiate(contractRequest)); + + // Let the negotiator add a listener to this negotiation. + // If we don't, the "confirmed" signal will be sent too soon, and the negotiator + // will never see it + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + fail(); } + contractNegotiationObservable.invokeForEach(listener -> listener.finalized(negotiation)); - /* - * Policy containing MOCK as permitted action - */ - private Policy buildPolicy() { - return Policy.Builder.newInstance() - .permission(Permission.Builder.newInstance() - .action(Action.Builder.newInstance() - .type("MOCK") - .build()) - .build()) - .build(); - } + assertNotNull(future); + var contractNegotiation = future.get(); + assertNotNull(contractNegotiation); + assertEquals(mockPolicy, contractNegotiation.getContent().getContractAgreement().getPolicy()); + assertEquals(assetId, contractNegotiation.getContent().getContractAgreement().getAssetId()); + } - private ContractNegotiation getContractNegotiation() { - return ContractNegotiation.Builder.newInstance() - .counterPartyId("mock-counter-party-id") - .counterPartyAddress("mock-counter-party-address") - .protocol("mock-protocol") - .id("mocked-negotiation-id") - .contractAgreement(ContractAgreement.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .providerId("provider") - .consumerId("consumer") - .assetId(assetId) - .policy(mockPolicy) - .build()) - .build(); - } + /* + * Policy containing MOCK as permitted action + */ + private Policy buildPolicy() { + return Policy.Builder.newInstance() + .permission(Permission.Builder.newInstance() + .action(Action.Builder.newInstance() + .type("MOCK") + .build()) + .build()) + .build(); + } + + private ContractNegotiation getContractNegotiation() { + return ContractNegotiation.Builder.newInstance() + .counterPartyId("mock-counter-party-id") + .counterPartyAddress("mock-counter-party-address") + .protocol("mock-protocol") + .id("mocked-negotiation-id") + .contractAgreement(ContractAgreement.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .providerId("provider") + .consumerId("consumer") + .assetId(assetId) + .policy(mockPolicy) + .build()) + .build(); + } } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfigTest.java b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfigTest.java new file mode 100644 index 00000000..aa8e4556 --- /dev/null +++ b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfigTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://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. + */ +package de.fraunhofer.iosb.client.policy; + +import org.eclipse.edc.spi.system.configuration.Config; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class PolicyServiceConfigTest { + private PolicyServiceConfig policyServiceConfig; + private Config config; + + @BeforeEach + public void initialize() { + config = mock(Config.class); + policyServiceConfig = new PolicyServiceConfig(config); + } + + @Test + public void getWaitForCatalogTimeoutTest() { + var expected = 42; + when(config.getInteger("waitForCatalogTimeout", 10)).thenReturn(expected); + + assertEquals(expected, policyServiceConfig.getWaitForCatalogTimeout()); + } + + @Test + public void getAcceptedPolicyDefinitionsPathTest() { + var expected = "/tmp/test/policy-definitions/accepted/"; + when(config.getString("acceptedPolicyDefinitionsPath", null)).thenReturn(expected); + + assertEquals(expected, policyServiceConfig.getAcceptedPolicyDefinitionsPath()); + } + + @Test + public void isAcceptAllProviderOffersTest() { + var expected = true; + when(config.getBoolean("acceptAllProviderOffers", false)).thenReturn(expected); + + assertTrue(policyServiceConfig.isAcceptAllProviderOffers()); + } +} diff --git a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java index 7c762ef8..40694f4a 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java @@ -15,19 +15,8 @@ */ package de.fraunhofer.iosb.client.policy; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - +import de.fraunhofer.iosb.client.exception.AmbiguousOrNullException; +import de.fraunhofer.iosb.client.testutils.FileManager; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.DataService; import org.eclipse.edc.catalog.spi.Dataset; @@ -36,75 +25,193 @@ import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.response.ResponseStatus; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.system.configuration.ConfigFactory; +import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; -import de.fraunhofer.iosb.client.testUtils.FileManager; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; -public class PolicyServiceTest { +import static java.lang.String.format; +import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; +import static org.eclipse.edc.spi.query.Criterion.criterion; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; - private final int providerPort = 54321; - private final CatalogService mockCatalogService = mock(CatalogService.class); - private final TypeTransformerRegistry mockTransformer = mock(TypeTransformerRegistry.class); +/** + * We assume here that catalogService does not return null objects as well as null catalogs inside their return value. + * Also, we assume that catalogs are valid JSON and expandable by the connector's JSON LD expander TitaniumJsonLd.class. + * Finally, we assume that catalogs can be transformed with the TypeTransformerRegistry. + */ +public class PolicyServiceTest { + private CatalogService catalogService; + private TypeTransformerRegistry typeTransformerRegistry; + private PolicyServiceConfig config; private PolicyService policyService; - private final URL testUrl = new URL("http://localhost:" + providerPort); + private final URL testUrl; public PolicyServiceTest() throws MalformedURLException { + int providerPort = 54321; + testUrl = new URL("http://localhost:" + providerPort); } + @BeforeEach - void initializeContractOfferService() { - policyService = new PolicyService(mockCatalogService, mockTransformer, mockConfig(), - mock(PolicyDefinitionStore.class), mock(Monitor.class)); + void initializePolicyService() { + catalogService = mock(CatalogService.class); + typeTransformerRegistry = mock(TypeTransformerRegistry.class); + var policyDefinitionStore = mock(PolicyDefinitionStore.class); + config = mock(PolicyServiceConfig.class); + + policyService = new PolicyService(catalogService, typeTransformerRegistry, config, policyDefinitionStore, mock(Monitor.class)); + } + + @Test + void getDatasetCatalogResponseFailureTest() throws InterruptedException { + var querySpec = QuerySpec.Builder.newInstance().filter(List.of(criterion(Asset.PROPERTY_ID, "=", "test-asset-id"))).build(); + var future = new CompletableFuture>(); + future.complete(StatusResult.failure(ResponseStatus.FATAL_ERROR, "This is a test")); + + when(catalogService.requestCatalog("test-counter-party-id", testUrl.toString(), DATASPACE_PROTOCOL_HTTP, querySpec)).thenReturn(future); + + try { + policyService.getDatasetForAssetId("test-counter-party-id", testUrl, "test-asset-id"); + fail(); // Should throw exception + } catch (EdcException expected) { + assertEquals(format("Catalog by provider %s couldn't be retrieved: %s", testUrl, "[This is a test]"), expected.getMessage()); + } } @Test - void getPolicyForAssetIdTest() throws InterruptedException { + void getDatasetCatalogFutureTimeoutTest() throws InterruptedException { + var querySpec = QuerySpec.Builder.newInstance().filter(criterion(Asset.PROPERTY_ID, "=", "test-asset-id")).build(); + var future = new CompletableFuture>(); + + when(catalogService.requestCatalog("test-counter-party-id", testUrl.toString(), DATASPACE_PROTOCOL_HTTP, querySpec)).thenReturn(future); + + try { + policyService.getDatasetForAssetId("test-counter-party-id", testUrl, "test-asset-id"); + fail(); // Should throw exception + } catch (EdcException expected) { + assertEquals(format("Timeout while waiting for catalog by provider %s.", testUrl), expected.getMessage()); + } + } + + @Test + void getDatasetNoDatasetsTest() throws InterruptedException { + var querySpec = QuerySpec.Builder.newInstance().filter(criterion(Asset.PROPERTY_ID, "=", "test-asset-id")).build(); + var future = new CompletableFuture>(); + var catalogString = FileManager.loadResource("catalog.json"); + assert catalogString != null; + future.complete(StatusResult.success(catalogString.getBytes(StandardCharsets.UTF_8))); + + when(catalogService.requestCatalog("test-counter-party-id", testUrl.toString(), DATASPACE_PROTOCOL_HTTP, querySpec)).thenReturn(future); + when(typeTransformerRegistry.transform(any(), any())) + .thenReturn(Result.success(Catalog.Builder.newInstance().build())); + + try { + policyService.getDatasetForAssetId("test-counter-party-id", testUrl, "test-asset-id"); + fail(); // Should throw exception + } catch (AmbiguousOrNullException expected) { + assertEquals(format("Multiple or no policyDefinitions were found for assetId %s!", "test-asset-id"), expected.getMessage()); + } + } + + @Test + void getDatasetTest() throws InterruptedException { var mockedFuture = new CompletableFuture>(); var datasetId = "ef4d028f-70d7-404a-b22e-c5b0ffa3aa0b"; var catalogString = FileManager.loadResource("catalog.json"); assert catalogString != null; mockedFuture.complete(StatusResult.success(catalogString.getBytes(StandardCharsets.UTF_8))); - when(mockCatalogService.requestCatalog(any(), any(), any())).thenReturn(mockedFuture); - - when(mockTransformer.transform(any(), any())).thenReturn(Result.success(Catalog.Builder.newInstance() - .dataset(Dataset.Builder.newInstance() - .id(datasetId) - .offer(UUID.randomUUID().toString(), - Policy.Builder.newInstance().build()) - .distribution(Distribution.Builder.newInstance() - .dataService(DataService.Builder.newInstance().build()) - .format("") - .build()) - .build()) - .build())); - - assertEquals(datasetId, policyService.getDatasetForAssetId(testUrl, "test-asset-id").getId()); + when(catalogService.requestCatalog(any(), any(), any(), any())).thenReturn(mockedFuture); + + when(typeTransformerRegistry.transform(any(), any())) + .thenReturn(Result.success( + Catalog.Builder.newInstance() + .dataset(Dataset.Builder.newInstance() + .id(datasetId) + .offer(UUID.randomUUID().toString(), Policy.Builder.newInstance().build()) + .distribution( + Distribution.Builder.newInstance() + .dataService(DataService.Builder.newInstance().build()) + .format("") + .build()) + .build()) + .build())); + + assertEquals(datasetId, policyService.getDatasetForAssetId("provider", testUrl, "test-asset-id").getId()); } @Test - void getContractUnreachableProviderTest() throws MalformedURLException, InterruptedException { - var mockedFuture = new CompletableFuture>(); - when(mockCatalogService.requestCatalog(any(), any(), any())).thenReturn(mockedFuture); + void getAcceptablePolicyForAssetIdTest() throws InterruptedException { + var shouldPolicy = Policy.Builder.newInstance().build(); + var dataset = Dataset.Builder.newInstance() + .offer("test-offer-id", shouldPolicy).build(); + // mock getDatasetMethod + var policyServiceSpy = spy(policyService); + Mockito.doReturn(dataset) + .when(policyServiceSpy) + .getDatasetForAssetId("test-counter-party-id", testUrl, "test-asset-id"); + when(config.isAcceptAllProviderOffers()).thenReturn(true); + + var resultPolicy = policyServiceSpy.getAcceptablePolicyForAssetId("test-counter-party-id", testUrl, "test-asset-id"); + assertEquals(shouldPolicy, resultPolicy.getSecond()); + assertEquals("test-offer-id", resultPolicy.getFirst()); + } + + @Test + void getAcceptablePolicyForAssetIdEmptyPolicyListTest() throws InterruptedException { + // mock getDatasetMethod + var policyServiceSpy = spy(policyService); + Mockito.doReturn(Dataset.Builder.newInstance().build()).when(policyServiceSpy).getDatasetForAssetId("test-counter-party-id", testUrl, "test-asset-id"); try { - policyService.getDatasetForAssetId(new URL("http://fakeUrl:4321/not/working"), "test-asset-id"); - fail("This should not complete without throwing an exception"); + policyServiceSpy.getAcceptablePolicyForAssetId("test-counter-party-id", testUrl, "test-asset-id"); } catch (EdcException expected) { + assertEquals("Could not find any acceptable policyDefinition", expected.getMessage()); } + } - private PolicyServiceConfig mockConfig() { - return new PolicyServiceConfig(ConfigFactory.fromMap(Map.of( - "edc.dsp.callback.address", "http://localhost:4321/dsp", - "web.http.port", "8080", - "web.http.path", "/api"))); + @Test + void getAcceptablePolicyForAssetIdAcceptAllOffersTest() { + //TODO + } + @Test + void getAcceptablePolicyForAssetIdAcceptFromAcceptedListTest() { + //TODO + } + + @Test + void getAcceptablePolicyForAssetIdNoAcceptablePolicyTest() { + //TODO + } + + @Test + void getAcceptablePolicyForAssetIdTimeoutTest() { + //TODO + } + + @Test + void getAcceptablePolicyForAssetIdExceptionbyGetDatasetTest() { + //TODO } } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java b/client/src/test/java/de/fraunhofer/iosb/client/testutils/FileManager.java similarity index 86% rename from client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java rename to client/src/test/java/de/fraunhofer/iosb/client/testutils/FileManager.java index 3e7c70ec..997f03af 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/testutils/FileManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.client.testUtils; +package de.fraunhofer.iosb.client.testutils; import org.apache.commons.io.IOUtils; @@ -30,10 +30,10 @@ public class FileManager { private FileManager() { } - private static final File resourcesDirectory = new File("src/test/resources"); + private static final File RESOURCES_DIRECTORY = new File("src/test/resources"); public static String loadResource(String fileName) { - try (FileInputStream x = new FileInputStream(new File(resourcesDirectory, fileName))) { + try (FileInputStream x = new FileInputStream(new File(RESOURCES_DIRECTORY, fileName))) { return IOUtils.toString(x, StandardCharsets.UTF_8); } catch (FileNotFoundException e) { fail("File not found exception on file " + fileName); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java index 15672b0e..58adc0d8 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java @@ -19,7 +19,14 @@ import de.fraunhofer.iosb.app.controller.AasController; import de.fraunhofer.iosb.app.controller.ConfigurationController; import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; -import jakarta.ws.rs.*; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response.Status; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java index f1745232..b52f3087 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java @@ -15,11 +15,11 @@ */ package de.fraunhofer.iosb.app; +import org.eclipse.edc.spi.monitor.ConsoleMonitor; + import java.util.Objects; import java.util.function.Supplier; -import org.eclipse.edc.spi.monitor.ConsoleMonitor; - /** * Singleton class. * Wrapper for logging with prefix diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java index f1fcb4ce..f9fcc99e 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java @@ -15,25 +15,11 @@ */ package de.fraunhofer.iosb.app.aas; -import static java.lang.String.format; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -import org.eclipse.edc.spi.EdcException; - import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; - import de.fraunhofer.iosb.app.Logger; import de.fraunhofer.iosb.app.model.aas.CustomAssetAdministrationShell; import de.fraunhofer.iosb.app.model.aas.CustomAssetAdministrationShellEnvironment; @@ -57,6 +43,18 @@ import io.adminshell.aas.v3.model.impl.DefaultSubmodel; import jakarta.ws.rs.core.Response; import okhttp3.OkHttpClient; +import org.eclipse.edc.spi.EdcException; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static java.lang.String.format; /** * Communicating with AAS service @@ -149,8 +147,7 @@ public Response deleteModel(URL aasServiceUrl, String element) { * sourceUrl field. * * @param aasServiceUrl AAS service to be updated - * @return AAS model enriched with each elements access URL as string in assetId - * field. + * @return AAS model enriched with each elements access URL as string in assetId field. */ public CustomAssetAdministrationShellEnvironment getAasEnvWithUrls(URL aasServiceUrl, boolean onlySubmodels) throws IOException, DeserializationException { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java index 13071166..e06f51b5 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java @@ -139,8 +139,7 @@ public URL startService(Path aasModelPath, Path configPath, int port) throws IOE faaastServiceRepository.put(new URL(LOCALHOST_URL + localFaaastServicePort), service); - } catch ( - Exception faaastServiceException) { + } catch (Exception faaastServiceException) { throw new EdcException(FAAAST_SERVICE_EXCEPTION_MESSAGE, faaastServiceException); } return new URL(LOCALHOST_URL + localFaaastServicePort); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java new file mode 100644 index 00000000..e69de29b diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java index ce5af23f..736526f6 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java @@ -15,23 +15,20 @@ */ package de.fraunhofer.iosb.app.controller; -import java.net.URL; -import java.util.Map; - -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.spi.system.configuration.ConfigFactory; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.json.JsonMapper; - import de.fraunhofer.iosb.app.Logger; import de.fraunhofer.iosb.app.RequestType; import de.fraunhofer.iosb.app.model.configuration.Configuration; import jakarta.ws.rs.core.Response; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; + +import java.net.URL; /** * Handles requests regarding the application's configuration. @@ -88,7 +85,7 @@ private Response updateConfiguration(String newConfigValues) { // Read config values as map -> edc Config -> merge with old // -> set as AAS extension config Config newConfig = ConfigFactory.fromMap(objectMapper.readValue(newConfigValues, - new TypeReference>() { + new TypeReference<>() { })); Config mergedConfig = sysConfig.merge(newConfig); configuration = objectReader.readValue(objectMapper.writeValueAsString(mergedConfig.getEntries())); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ResourceHandler.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ResourceHandler.java index 61342eae..2e9ffa57 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ResourceHandler.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ResourceHandler.java @@ -15,8 +15,8 @@ */ package de.fraunhofer.iosb.app.edc; -import org.eclipse.edc.spi.asset.AssetIndex; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; +import org.eclipse.edc.spi.asset.AssetIndex; import org.eclipse.edc.spi.types.domain.asset.Asset; /** diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/AASElement.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/AASElement.java index 5cdc52ae..10c03d64 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/AASElement.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/AASElement.java @@ -15,12 +15,11 @@ */ package de.fraunhofer.iosb.app.model.aas; -import java.util.List; - import com.fasterxml.jackson.annotation.JsonProperty; - import io.adminshell.aas.v3.model.EmbeddedDataSpecification; +import java.util.List; + /* * Collect common attributes of every AAS element. */ diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShell.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShell.java index 50f52888..207d9350 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShell.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShell.java @@ -18,10 +18,11 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; - import io.adminshell.aas.v3.model.AssetInformation; import io.adminshell.aas.v3.model.impl.DefaultAssetInformation; +import java.util.Objects; + /** * AAS Model for the self description of the edc */ @@ -58,6 +59,11 @@ public void setAssetInformation(DefaultAssetInformation assetInformation) { this.assetInformation = assetInformation; } + @Override + public int hashCode() { + return Objects.hash(identification, idShort); + } + @Override public boolean equals(Object obj) { if (obj == null) { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShellEnvironment.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShellEnvironment.java index dacd3f7a..6f9e3d9c 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShellEnvironment.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomAssetAdministrationShellEnvironment.java @@ -27,9 +27,9 @@ public class CustomAssetAdministrationShellEnvironment { protected List assetAdministrationShells = new ArrayList<>(); - protected List submodels= new ArrayList<>(); + protected List submodels = new ArrayList<>(); - protected List conceptDescriptions= new ArrayList<>(); + protected List conceptDescriptions = new ArrayList<>(); public List getAssetAdministrationShells() { return assetAdministrationShells; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomConceptDescription.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomConceptDescription.java index aeab45ba..4270e5fa 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomConceptDescription.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomConceptDescription.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.Objects; + @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonAutoDetect @@ -43,6 +45,11 @@ public void setIdShort(String idShort) { this.idShort = idShort; } + @Override + public int hashCode() { + return Objects.hash(identification, idShort); + } + @Override public boolean equals(Object obj) { if (obj == null) { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticId.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticId.java index 4459e986..174c3e58 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticId.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticId.java @@ -1,16 +1,29 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://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. + */ package de.fraunhofer.iosb.app.model.aas; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; - +import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.adminshell.aas.v3.model.Key; -import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; @JsonAutoDetect @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) @@ -39,7 +52,7 @@ public CustomSemanticId(List keys) { customSemanticIdKey.setIdType(key.getIdType()); customSemanticIdKey.setType(key.getType()); customSemanticIdKey.setValue(key.getValue()); - + this.keys.add(customSemanticIdKey); } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticIdKey.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticIdKey.java index cfaa1652..22a588a0 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticIdKey.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSemanticIdKey.java @@ -1,15 +1,28 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://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. + */ package de.fraunhofer.iosb.app.model.aas; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; - +import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.adminshell.aas.v3.model.KeyElements; import io.adminshell.aas.v3.model.KeyType; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - @JsonAutoDetect @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) @JsonInclude(Include.NON_NULL) diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodel.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodel.java index 89d7017d..839f0461 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodel.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodel.java @@ -18,11 +18,11 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; - import io.adminshell.aas.v3.model.Constraint; import java.util.ArrayList; import java.util.List; +import java.util.Objects; @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown = true) @@ -66,6 +66,11 @@ public void setSubmodelElements(List submodelElements) { this.submodelElements = submodelElements; } + @Override + public int hashCode() { + return Objects.hash(identification, idShort); + } + @Override public boolean equals(Object obj) { if (obj == null) { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodelElement.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodelElement.java index d1f91edb..359e9478 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodelElement.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/CustomSubmodelElement.java @@ -15,15 +15,16 @@ */ package de.fraunhofer.iosb.app.model.aas; -import java.util.List; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; - import io.adminshell.aas.v3.model.Constraint; +import java.util.List; +import java.util.Objects; + @JsonAutoDetect @JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION) @JsonInclude(Include.NON_NULL) @@ -49,6 +50,11 @@ public void setIdShort(String idShort) { this.idShort = idShort; } + @Override + public int hashCode() { + return Objects.hash(idShort); + } + @Override public boolean equals(Object obj) { if (obj == null) { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/Identifier.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/Identifier.java index 9cfe2c21..8e16e6a5 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/Identifier.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/aas/Identifier.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Objects; + @JsonIgnoreProperties(ignoreUnknown = true) @JsonAutoDetect public class Identifier { @@ -45,6 +47,11 @@ public void setId(String id) { this.id = id; } + @Override + public int hashCode() { + return Objects.hash(idType, id); + } + @Override public boolean equals(Object obj) { if (obj == null) { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java index f98e5d12..ff5bd4a7 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java @@ -15,11 +15,11 @@ */ package de.fraunhofer.iosb.app.model.configuration; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.net.URL; import java.util.Objects; -import com.fasterxml.jackson.annotation.JsonProperty; - /** * Singleton class. * The configuration of the application. diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/sync/Synchronizer.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/sync/Synchronizer.java index a4c06c2a..a14a1397 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/sync/Synchronizer.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/sync/Synchronizer.java @@ -17,7 +17,11 @@ import de.fraunhofer.iosb.app.controller.AasController; import de.fraunhofer.iosb.app.controller.ResourceController; -import de.fraunhofer.iosb.app.model.aas.*; +import de.fraunhofer.iosb.app.model.aas.AASElement; +import de.fraunhofer.iosb.app.model.aas.CustomAssetAdministrationShellEnvironment; +import de.fraunhofer.iosb.app.model.aas.CustomSubmodel; +import de.fraunhofer.iosb.app.model.aas.CustomSubmodelElement; +import de.fraunhofer.iosb.app.model.aas.IdsAssetElement; import de.fraunhofer.iosb.app.model.configuration.Configuration; import de.fraunhofer.iosb.app.model.ids.SelfDescription; import de.fraunhofer.iosb.app.model.ids.SelfDescriptionChangeListener; @@ -153,7 +157,7 @@ private void syncSubmodel(CustomAssetAdministrationShellEnvironment newEnvironme oldSubmodel = oldSubmodels.get(oldSubmodels.indexOf(submodel)); } else { oldSubmodel = oldSubmodels.stream().filter( - oldSubmodelTest -> submodel.equals(oldSubmodelTest)) + submodel::equals) .findFirst().orElse(null); if (Objects.isNull(oldSubmodel)) { return; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/AASUtil.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/AASUtil.java index 89a6d67a..b70b6f26 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/AASUtil.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/AASUtil.java @@ -15,7 +15,12 @@ */ package de.fraunhofer.iosb.app.util; -import de.fraunhofer.iosb.app.model.aas.*; +import de.fraunhofer.iosb.app.model.aas.CustomAssetAdministrationShellEnvironment; +import de.fraunhofer.iosb.app.model.aas.CustomSemanticId; +import de.fraunhofer.iosb.app.model.aas.CustomSubmodel; +import de.fraunhofer.iosb.app.model.aas.CustomSubmodelElement; +import de.fraunhofer.iosb.app.model.aas.CustomSubmodelElementCollection; +import de.fraunhofer.iosb.app.model.aas.IdsAssetElement; import io.adminshell.aas.v3.model.Submodel; import io.adminshell.aas.v3.model.SubmodelElement; import io.adminshell.aas.v3.model.SubmodelElementCollection; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/HttpRestClient.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/HttpRestClient.java index 689a32b1..28bffc35 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/HttpRestClient.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/HttpRestClient.java @@ -16,7 +16,12 @@ package de.fraunhofer.iosb.app.util; import de.fraunhofer.iosb.app.Logger; -import okhttp3.*; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import java.io.IOException; import java.net.URL; diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/AasExtensionTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/AasExtensionTest.java index 7e8e5076..a8217576 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/AasExtensionTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/AasExtensionTest.java @@ -35,7 +35,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; @ExtendWith(DependencyInjectionExtension.class) public class AasExtensionTest { diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java index 0621f1fc..f2a22720 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java @@ -15,29 +15,28 @@ */ package de.fraunhofer.iosb.app; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; - -import org.eclipse.edc.spi.system.configuration.ConfigFactory; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import de.fraunhofer.iosb.app.controller.AasController; import de.fraunhofer.iosb.app.controller.ConfigurationController; import de.fraunhofer.iosb.app.model.configuration.Configuration; import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; -import de.fraunhofer.iosb.app.testUtils.FileManager; +import de.fraunhofer.iosb.app.testutils.FileManager; import de.fraunhofer.iosb.app.util.Encoder; import jakarta.ws.rs.core.Response; import okhttp3.OkHttpClient; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Not mocking the controllers this endpoint uses, as the mocking/validation diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java index 862409d3..2468f59b 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java @@ -15,35 +15,30 @@ */ package de.fraunhofer.iosb.app.aas; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; - +import com.fasterxml.jackson.databind.ObjectMapper; +import de.fraunhofer.iosb.app.testutils.FileManager; +import io.adminshell.aas.v3.dataformat.DeserializationException; +import okhttp3.OkHttpClient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockserver.integration.ClientAndServer; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; -import de.fraunhofer.iosb.app.testUtils.FileManager; -import io.adminshell.aas.v3.dataformat.DeserializationException; -import okhttp3.OkHttpClient; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; /** * Testing AAS Agent. Using mocked AAS service (HTTP endpoints) */ public class AasAgentTest { - /** - * - */ private static final String HTTP_LOCALHOST_8080 = "http://localhost:8080"; private AasAgent aasAgent; diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java index 926639ec..00b8569f 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java @@ -15,22 +15,21 @@ */ package de.fraunhofer.iosb.app.aas; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import de.fraunhofer.iosb.app.util.HttpRestClient; +import jakarta.ws.rs.core.Response; +import okhttp3.OkHttpClient; +import org.eclipse.edc.spi.EdcException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; -import org.eclipse.edc.spi.EdcException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import de.fraunhofer.iosb.app.util.HttpRestClient; -import jakarta.ws.rs.core.Response; -import okhttp3.OkHttpClient; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; public class FaaastServiceManagerTest { @@ -89,7 +88,7 @@ public void stopServiceEmptyRepositoryTest() { try { faaastServiceManager.stopService(new URL("http://does-not-exist.com:1234/aas")); fail("This operation should fail"); - } catch (IllegalArgumentException ignored) { + } catch (IllegalArgumentException expected) { } catch (Exception unexpectedException) { fail(); } diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java new file mode 100644 index 00000000..e69de29b diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java index 177652aa..6f72fb86 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java @@ -15,6 +15,11 @@ */ package de.fraunhofer.iosb.app.edc; +import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; +import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -22,11 +27,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; -import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - public class ContractHandlerTest { private static final String DEFAULT_CONTRACT_NAME = "DEFAULT_CONTRACT"; diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java index 162f62dc..325756e8 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java @@ -15,19 +15,11 @@ */ package de.fraunhofer.iosb.app.sync; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Objects; - +import de.fraunhofer.iosb.app.controller.AasController; +import de.fraunhofer.iosb.app.controller.ResourceController; +import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; +import de.fraunhofer.iosb.app.testutils.FileManager; +import okhttp3.OkHttpClient; import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; import org.eclipse.edc.spi.EdcException; @@ -39,11 +31,18 @@ import org.mockserver.integration.ClientAndServer; import org.mockserver.matchers.Times; -import de.fraunhofer.iosb.app.controller.AasController; -import de.fraunhofer.iosb.app.controller.ResourceController; -import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; -import de.fraunhofer.iosb.app.testUtils.FileManager; -import okhttp3.OkHttpClient; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Objects; + +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; public class SynchronizerTest { diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/testUtils/FileManager.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/testutils/FileManager.java similarity index 86% rename from edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/testUtils/FileManager.java rename to edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/testutils/FileManager.java index f1a5a139..f7f0b2bc 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/testUtils/FileManager.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/testutils/FileManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.testUtils; +package de.fraunhofer.iosb.app.testutils; import org.apache.commons.io.IOUtils; @@ -30,10 +30,10 @@ public class FileManager { private FileManager() { } - private static final File resourcesDirectory = new File("src/test/resources"); + private static final File RESOURCES_DIRECTORY = new File("src/test/resources"); public static String loadResource(String fileName) { - try (FileInputStream x = new FileInputStream(new File(resourcesDirectory, fileName))) { + try (FileInputStream x = new FileInputStream(new File(RESOURCES_DIRECTORY, fileName))) { return IOUtils.toString(x, StandardCharsets.UTF_8); } catch (FileNotFoundException e) { fail("File not found exception on file " + fileName); diff --git a/example/README.md b/example/README.md index 77b93181..83a7124a 100644 --- a/example/README.md +++ b/example/README.md @@ -113,10 +113,10 @@ java -Dedc.fs.config=./example/configurations/consumer.properties -jar ./example ``` Starting the data transfer from provider to consumer. There is a `postman collection` containing the necessary http -request located in `/examples/resources`. Do the following steps: +requests located in `/examples/resources`. Complete the following steps: -1. Call the provider's self description on `http://localhost:8181/api/selfDescription`, and choose an element you want - to fetch. Put its `asset id` as a variable in the postman collection's variables section. +1. Call the provider's self-description on `http://localhost:8181/api/selfDescription` and choose an element you want + to fetch. Put its `asset id` as a variable in the postman collection's variables section ("Current value"). ### Fully automated @@ -142,15 +142,16 @@ __Important__: ### Separate requests 1. Execute the request `Client/1. Get dataset for asset` - Choose a policy of the response body. + This will return the provider offer for this asset. Copy the full response for the next step. -2. Put the policy inside of request `Client/2. Initiate negotiation with contractOffer`'s body (policy field) and +2. Paste the response inside of request `Client/2. Initiate negotiation with contractOffer`'s body ("contractOffer" field, as can be seen in the screenshot) and execute said request. + -3. If everything went right, request `2` returns an agreementID. Update the postman collection's agreementID variable - with the response value. +3. If everything succeeded, request `2` returns an agreementID. Update the postman collection's "agreement-id" variable + using the response value. -4. Execute request `3. Get data for agreement id and asset id`. If everything went right, the response should be the +4. Execute request `3. Get data for agreement id and asset id`. If again everything went right, the response should be the data behind the previously selected asset. ## Running the Example (manual) @@ -190,18 +191,17 @@ requests for data transfer in this extensions repository located in `/examples/r 3. With this ``, query the consumer connector about the state of the negotiation. Execute request 2 of the data transfer folder. It should return: - -```json -{ - "contractAgreementId": "", - "counterPartyAddress": "http://localhost:8282/api/v1/ids/data", - "errorDetail": null, - "id": "ac6e1c97-13d6-41ff-8b79-1029d7f094bb", - "protocol": "ids-multipart", - "state": "CONFIRMED", - "type": "CONSUMER" -} -``` + ```json + { + "contractAgreementId": "", + "counterPartyAddress": "http://localhost:8282/api/v1/ids/data", + "errorDetail": null, + "id": "ac6e1c97-13d6-41ff-8b79-1029d7f094bb", + "protocol": "ids-multipart", + "state": "CONFIRMED", + "type": "CONSUMER" + } + ``` 4. Put the `` in the postman collection's agreement-id variable. Execute request 3 of the Data Transfer folder. The provider connector should now send the data, and in the consumer diff --git a/example/build.gradle.kts b/example/build.gradle.kts index 9c20a1a8..4153357d 100644 --- a/example/build.gradle.kts +++ b/example/build.gradle.kts @@ -38,10 +38,8 @@ dependencies { implementation("$group:data-plane-core:$edcVersion") implementation("$group:data-plane-http:$edcVersion") implementation("$group:data-plane-client:$edcVersion") - implementation("$group:data-plane-selector-client:$edcVersion") implementation("$group:data-plane-selector-core:$edcVersion") implementation("$group:transfer-data-plane:$edcVersion") - } diff --git a/example/resources/aas_edc_extension.postman_collection.json b/example/resources/aas_edc_extension.postman_collection.json index 33420325..25e6c969 100644 --- a/example/resources/aas_edc_extension.postman_collection.json +++ b/example/resources/aas_edc_extension.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "8953f55e-787b-4b76-9c0b-7c1b15731185", + "_postman_id": "14677191-e263-4d83-ba13-d126c064b460", "name": "EDC Extension", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "20511334" @@ -207,19 +207,19 @@ "response": [] }, { - "name": "1. Get dataset for asset", + "name": "1. Get offer for asset", "request": { "method": "GET", "header": [], "url": { - "raw": "{{consumer}}/api/automated/dataset?providerUrl={{provider-dsp}}&assetId={{asset-id}}", + "raw": "{{consumer}}/api/automated/offer?providerUrl={{provider-dsp}}&assetId={{asset-id}}", "host": [ "{{consumer}}" ], "path": [ "api", "automated", - "dataset" + "offer" ], "query": [ { @@ -242,7 +242,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{ \r\n \"@context\": {\r\n \"edc\": \"https://w3id.org/edc/v0.0.1/ns/\",\r\n \"odrl\": \"http://www.w3.org/ns/odrl/2/\"\r\n },\r\n \"providerId\": \"provider\",\r\n \"consumerId\": \"consumer\",\r\n \"connectorAddress\": \"{{provider}}\",\r\n \"protocol\": \"dataspace-protocol-http\",\r\n \"counterPartyId\": \"provider\",\r\n \"counterPartyAddress\": \"{{provider-dsp}}\",\r\n \"contractOffer\": {\r\n \"id\": \"DEFAULT_CONTRACT24:-1268910060:3886d894-3955-4b06-83c6-e86fdeb5e65a\",\r\n \"policy\": {\r\n \"permissions\": [\r\n {\r\n \"edctype\": \"dataspaceconnector:permission\",\r\n \"target\": \"{{asset-id}}\",\r\n \"action\": {\r\n \"type\": \"USE\"\r\n }\r\n }\r\n ],\r\n \"target\": \"{{asset-id}}\"\r\n },\r\n \"assetId\": \"{{asset-id}}\"\r\n }\r\n}", + "raw": "{\r\n \"@context\": {\r\n \"edc\": \"https://w3id.org/edc/v0.0.1/ns/\",\r\n \"odrl\": \"http://www.w3.org/ns/odrl/2/\"\r\n },\r\n \"providerId\": \"provider\",\r\n \"protocol\": \"dataspace-protocol-http\",\r\n \"counterPartyId\": \"provider\",\r\n \"counterPartyAddress\": \"{{provider-dsp}}\",\r\n \"contractOffer\": \r\n}", "options": { "raw": { "language": "json" @@ -267,7 +267,13 @@ "name": "3. Get data for agreement id and asset id", "request": { "method": "GET", - "header": [], + "header": [ + { + "key": "x-api-key", + "value": "password", + "type": "text" + } + ], "url": { "raw": "{{consumer}}/api/automated/transfer?providerUrl={{provider-dsp}}&agreementId={{agreement-id}}&assetId={{asset-id}}", "host": [ @@ -944,6 +950,16 @@ } ], "variable": [ + { + "key": "asset-id", + "value": "", + "type": "default" + }, + { + "key": "agreement-id", + "value": "", + "type": "default" + }, { "key": "provider", "value": "http://localhost:8181", @@ -974,26 +990,6 @@ "value": "http://localhost:9192/management", "type": "default" }, - { - "key": "asset-id", - "value": "", - "type": "default" - }, - { - "key": "contract-offer-id", - "value": "", - "type": "default" - }, - { - "key": "negotiation-id", - "value": "", - "type": "default" - }, - { - "key": "agreement-id", - "value": "", - "type": "default" - }, { "key": "docker-provider-connector-url", "value": "http://provider:8181", diff --git a/example/resources/tutorial-images/step-2 b/example/resources/tutorial-images/step-2 new file mode 100644 index 00000000..7a266435 Binary files /dev/null and b/example/resources/tutorial-images/step-2 differ diff --git a/gradle.properties b/gradle.properties index e76f90c1..7b9c0166 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ javaVersion=17 group=org.eclipse.edc -edcVersion=0.4.1 +edcVersion=0.5.1 faaastVersion=0.5.0 rsApi=3.1.0 okHttpVersion=4.10.0