diff --git a/.github/workflows/publish-swagger-hub.yml b/.github/workflows/publish-swagger-hub.yml new file mode 100644 index 0000000000..1fef7d9354 --- /dev/null +++ b/.github/workflows/publish-swagger-hub.yml @@ -0,0 +1,50 @@ +name: "Publish OpenAPI to Swaggerhub" + +on: + workflow_call: + inputs: + version: + required: true + description: Version that will be published to Swaggerhub + type: string + +jobs: + swagger-api: + runs-on: ubuntu-latest + env: + SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} + SWAGGERHUB_USER: ${{ secrets.SWAGGERHUB_USER }} + DOWNSTREAM_VERSION: ${{ inputs.version }} + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup node + uses: actions/setup-node@v4 + + - name: Install Swagger CLI + run: | + npm i -g swaggerhub-cli + + # create API, will fail if exists + - name: Create API + continue-on-error: true + run: | + swaggerhub api:create ${{ env.SWAGGERHUB_USER }}/item-relationship-service/${{ env.DOWNSTREAM_VERSION }} -f docs/src/api/irs-api.yaml --visibility=public --published=unpublish + + # Post the API to SwaggerHub as "unpublished", because published APIs cannot be overwritten + - name: Publish API Specs to SwaggerHub + run: | + if [[ ${{ env.DOWNSTREAM_VERSION }} != *-SNAPSHOT ]]; then + echo "[INFO] - no snapshot, will set the API to 'published'"; + swaggerhub api:update ${{ env.SWAGGERHUB_USER }}/item-relationship-service/${{ env.DOWNSTREAM_VERSION }} -f docs/src/api/irs-api.yaml --visibility=public --published=publish + swaggerhub api:setdefault ${{ env.SWAGGERHUB_USER }}/item-relationship-service/${{ env.DOWNSTREAM_VERSION }} + else + echo "[INFO] - snapshot, will set the API to 'unpublished'"; + swaggerhub api:update ${{ env.SWAGGERHUB_USER }}/item-relationship-service/${{ env.DOWNSTREAM_VERSION }} -f docs/src/api/irs-api.yaml --visibility=public --published=unpublish + fi diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f0b396121c..d5641838df 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -59,6 +59,17 @@ jobs: needs: - release uses: ./.github/workflows/jira-publish-release.yaml + with: + version: ${{ github.ref_name }} + secrets: inherit + + publish-to-swaggerhub: + name: "Publish OpenAPI spec to Swaggerhub" + permissions: + contents: read + needs: + - release + uses: ./.github/workflows/publish-swagger-hub.yml with: version: ${{ github.ref_name }} secrets: inherit \ No newline at end of file diff --git a/.gitignore b/.gitignore index b8702c8954..5e4582a016 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,8 @@ docs/src/diagram-replacer/package-lock.json docs/src/diagram-replacer/package.json docs/src/diagram-replacer/plantuml.jar +# Helm Chart Dependencies +/charts/irs-helm/Chart.lock +/charts/irs-helm/charts/ + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 11d444d1ea..7a6eb265ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,13 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 _**For better traceability add the corresponding GitHub issue number in each changelog entry, please.**_ ## [Unreleased] -### Changed -- EdcPolicyDefinitionService, EdcContractDefinitionService and EdcAssetService return throw AlreadyExist exceptions when Conflict is returned from EDC +## [4.8.0] - 2024-03-18 +### Changed +- Improved maintainability in EdcSubmodelClientImpl by reduced method visibility and better naming (in context of #448). +- EdcPolicyDefinitionService, EdcContractDefinitionService and EdcAssetService return throw AlreadyExist exceptions when Conflict is returned from + EDC - Added AssetAdministrationShellDescriptor specificAssetIds support for externalSubjectId required for data provisioning - Registering a job - aspects array is now accepting full urn of aspect model instead of name only, eg. 'urn:bamm:io.catenax.single_level_bom_as_built:2.0.0#SingleLevelBomAsBuilt' instead 'SingleLevelBomAsBuilt'. #439 - Changed the version of irs-registry-client from 1.6.0-SNAPSHOT to 1.6.0 +## Fixed +- Fixed missing timeouts including configuration. #448 + ## [4.7.0] - 2024-03-04 ### Added - DigitalTwinRegistryCreateShellService in irs-registry-client for creating shells in DTR directly @@ -574,7 +580,8 @@ _**For better traceability add the corresponding GitHub issue number in each cha ### Unresolved - **Select Aspects you need** You are able to select the needed aspects for which you want to collect the correct endpoint information. -[Unreleased]: https://github.com/eclipse-tractusx/item-relationship-service/compare/4.7.0...HEAD +[Unreleased]: https://github.com/eclipse-tractusx/item-relationship-service/compare/4.8.0...HEAD +[4.8.0]: https://github.com/eclipse-tractusx/item-relationship-service/compare/4.7.0...4.8.0 [4.7.0]: https://github.com/eclipse-tractusx/item-relationship-service/compare/4.6.0...4.7.0 [4.6.0]: https://github.com/eclipse-tractusx/item-relationship-service/compare/4.5.2...4.6.0 [4.5.2]: https://github.com/eclipse-tractusx/item-relationship-service/compare/4.5.1...4.5.2 diff --git a/COMPATIBILITY_MATRIX.md b/COMPATIBILITY_MATRIX.md index 00be376d6c..fe2943f5cb 100644 --- a/COMPATIBILITY_MATRIX.md +++ b/COMPATIBILITY_MATRIX.md @@ -1,6 +1,19 @@ # Compatibility matrix IRS Full changelog of IRS: [changelog](CHANGELOG.md) +## [4.8.0] - 2024-03-18 +| Dependency | Version | Helm | Comments | +|---------------------------|------------------------------|------------|-----------------| +| EDC | 0.5.3 | 0.5.3 | | +| EDC PostgresSQL | 15.1.0-debian-11-r12 | 12.1.6 | Optional | +| MIW | 0.1.0 | 0.1.0 | REST connection | +| Semantics Hub | 0.1.29 | v0.2.11-M1 | REST connection | +| DTR | 0.3.14-M1 | 0.3.22 | REST connection | +| Minio | RELEASE.2022-11-11T03-44-20Z | 5.0.1 | | +| SingleLevelBomAsBuilt | 2.0.0 | - | Model version | +| SingleLevelBomAsPlanned | 2.0.0 | - | Model version | +| SingleLevelBomAsSpecified | 1.0.0 | - | Model version | +| SingleLevelUsageAsBuilt | 2.0.0 | - | Model version | ## [4.7.0] - 2024-03-04 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3705508cf..aded5da7aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -176,13 +176,14 @@ https://github.com/google/google-java-format/blob/master/README.md#intellij-jre- - Check if the changelog entries for the release are complete. - Add the corresponding GitHub issue numbers to each entry if missing. 3. Update [COMPATIBILITY_MATRIX.md](COMPATIBILITY_MATRIX.md). -4. Create pull request from [release preparation branch to main](https://github.com/eclipse-tractusx/item-relationship-service/compare/chore/prepare-release-x.x.x) and merge to main. -5. Create Git tag for the desired release version `git tag x.x.x` +4. Update IRS API version in IrsApplication class and irs-api.yaml +5. Create pull request from [release preparation branch to main](https://github.com/eclipse-tractusx/item-relationship-service/compare/chore/prepare-release-x.x.x) and merge to main. +6. Create Git tag for the desired release version `git tag x.x.x` (note: the irs-helm tag will be created automatically by the GitHub workflow based on the version in the irs-helm changelog). -6. Push Git tag to repository `git push origin x.x.x` (this will trigger the GitHub release workflow). -7. Wait for release workflow to complete. -8. Merge the automatically opened PR by GitHub actions bot. -9. Notify about the release in IRS Matrix Chat using the following template: +7. Push Git tag to repository `git push origin x.x.x` (this will trigger the GitHub release workflow). +8. Wait for release workflow to complete. +9. Merge the automatically opened PR by GitHub actions bot. +10. Notify about the release in IRS Matrix Chat using the following template: > **IRS Release x.x.x** > diff --git a/README.md b/README.md index 90201b2d0b..603ac6b4f1 100644 --- a/README.md +++ b/README.md @@ -91,17 +91,22 @@ See [INSTALL](INSTALL.md). The following subsection provides instructions for running the infrastructure on docker-compose and the application in the IDE. -After the application is up and running the local IRS API is available at the following URLs: +Start the application with the commands in the subsection further below. +After everything is up and running the local IRS API is available at the following URLs: - Swagger UI: http://localhost:8080/api/swagger-ui - API docs: http://localhost:8080/api/api-docs - API docs in yaml: http://localhost:8080/api/api-docs.yaml - #### Docker-compose + IDE -1. Start the necessary infrastructure by running `docker-compose up` -2. Start the application from your favorite IDE. For IntelliJ, a run configuration is available in the `.run` folder. +1. `cd local/deployment` +2. Start the necessary infrastructure by running `docker-compose up` +3. Start the application from your favorite IDE. For IntelliJ, a run configuration is available in the `.run` folder. + +In case of problems connecting to MinIO check whether the MinIO console is accessible in your webbrowser +(see URL in the output of step 2). You might need to wait some time before MinIO is up and accepts connections. +If that does not help, execute `docker-compose down` and repeat from step 2. ## Usage diff --git a/charts/irs-helm/CHANGELOG.md b/charts/irs-helm/CHANGELOG.md index 617124538e..b76ebf519d 100644 --- a/charts/irs-helm/CHANGELOG.md +++ b/charts/irs-helm/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [6.17.0] - 2024-03-18 +### Added +- Added property `edc.controlplane.asyncTimeout` which allows to set a global time-to-live for all async requests over EDC + +### Changed +- Update IRS version to 4.8.0 + ## [6.16.0] - 2024-03-04 ### Added - Added configuration property `edc.controlplane.endpoint.contractAgreements` diff --git a/charts/irs-helm/Chart.yaml b/charts/irs-helm/Chart.yaml index 94f5c749e5..14a717c304 100644 --- a/charts/irs-helm/Chart.yaml +++ b/charts/irs-helm/Chart.yaml @@ -35,12 +35,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 6.16.0 +version: 6.17.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "4.7.0" +appVersion: "4.8.0" dependencies: - name: common repository: https://charts.bitnami.com/bitnami diff --git a/charts/irs-helm/templates/configmap-spring-app-config.yaml b/charts/irs-helm/templates/configmap-spring-app-config.yaml index 6c4c0fd430..2e5121cdc2 100644 --- a/charts/irs-helm/templates/configmap-spring-app-config.yaml +++ b/charts/irs-helm/templates/configmap-spring-app-config.yaml @@ -100,6 +100,7 @@ data: irs-edc-client: callback-url: {{ tpl (.Values.edc.callbackurl | default (printf "http://%s%s" .Release.Name "-irs-helm:8181/internal/endpoint-data-reference")) . | quote }} + asyncTimeout: {{ tpl .Values.edc.asyncTimeout . | default "PT10M" | quote }} controlplane: request-ttl: {{ .Values.edc.controlplane.request.ttl | default "PT10M" | quote }} endpoint: diff --git a/charts/irs-helm/values.yaml b/charts/irs-helm/values.yaml index e3a2b759d6..8d7c50c137 100644 --- a/charts/irs-helm/values.yaml +++ b/charts/irs-helm/values.yaml @@ -189,6 +189,7 @@ edc: header: "X-Api-Key" # Name of the EDC api key header field secret: "" # callbackurl: + asyncTimeout: PT10M # Timout for future.get requests as ISO 8601 Duration submodel: request: ttl: PT10M # Requests to dataplane will time out after this duration (see https://en.wikipedia.org/wiki/ISO_8601#Durations) diff --git a/docs/concept/#438-investigation-dDTR-api-endpoints/#438-investigation-dDTR-api-endpoints.md b/docs/concept/#438-investigation-dDTR-api-endpoints/#438-investigation-dDTR-api-endpoints.md new file mode 100644 index 0000000000..a5ec67807a --- /dev/null +++ b/docs/concept/#438-investigation-dDTR-api-endpoints/#438-investigation-dDTR-api-endpoints.md @@ -0,0 +1,87 @@ +# \[Concept\] \[#ID#\] Summary + +| Key | Value | +|---------------|---------------------------------------------------------------------------------| +| Creation date | 14.03.2024 | +| Ticket Id | [#438](https://github.com/eclipse-tractusx/item-relationship-service/issues/438) | +| State | WIP | + +## Table of Contents + +1. [Overview](#overview) +2. [Summary](#summary) +4. [Assessment](#assessment) +9. [Glossary](#glossary) +10. [References](#references) + +## Overview +The goal of this concept is to determine the impact on the IRS resulting from the new EDC extension "Policy Enforcement". +To this end, the API and the necessary changes to the IRS must be determined, documented and, if necessary, issues derived from them. + +## Summary +Extension of reference implementation of Digital Twin Registry (DTR) in Catena-X for scalable access management to Asset Administration Shells (AAS) and adhering Submodel server(s). + +## Assessment + +- IRS uses the EDC for any request to the dDTR and Submodel Server + +### Request Sequence (A) to API /lookup/shells + +**(1.) Data request from consumer EDC to provider EDC (A1)** +The API /lookup/shells is used in IRS. IRS used the globalAssetId to filter for a specific asset. The BPNL is injected to DTR request header via the EDC and the credential stored in the MIW. +This will work as intended. +**specificAssetIds** is not used by the IRS for filter for AAS assets. + +**(2.) API request by consumer EDC to DTR via provider EDC (A2)** +**(3.) DTR receives request and sends response to consumer EDC (A3)** +Communication only between the participants EDC and the DTR. The BPNL is injected into the request header of the dDTR by the EDC. + +[x] No impact on IRS + +### Request Sequence (B) to API /shell-descriptors/{aasIdentifier} + +**(1.) Data request from consumer EDC to provider EDC (B1)** +**(2.) API request by consumer EDC to DTR via provider EDC (B2)** +**(3.) DTR receives request and sends response to consumer EDC (B3)** +Communication only between the participants EDC and the DTR. The BPNL is injected into the request header of the dDTR by the EDC. + +[x] No impact on IRS + +### Request Sequence (C) to Submodel Server + +**(1.) Data request from consumer EDC to provider EDC (C1)** +**(2.) API request by consumer EDC to DTR for check on access (C2i)** +Communication only between the participants EDC and the DTR. The BPNL is injected into the request header of the dDTR by the EDC. + +[x] No impact on IRS + +### An additional API (outside the definition of the asset administration shells) is required to manage access rules. +Only required for data providers. + +[x] No impact on IRS + +### Impact: + +Outlines the impact of the proposed solution, including the need for an EDC extension, additional APIs in the DTR, and the benefits to data providers utilizing this access management approach. +For the IRS as a data consumer there seem to be no impact and no changes required. + +## Conclusion +No scalable access to attributes is required on the part of the irs. This is the responsibility of data providers such as bps. Trace-X. IRS uses the globalAssetIds to search for assets. For this reason, no dependency on the concept could be determined. +No impact on data consumers. + +### AAS_Access_v3_Architecture + +![AAS_Access_v3_Architecture.jpg](AAS_Access_v3_Architecture.jpg) + +## Glossary + +| Abbreviation | Name | +|--------------|---------------------------| +| CRUD | Create Read Update Delete | +| MIW | Managed Identity Wallet | +| AAS | Asset Adminstration Shell | + +## References +- [Concept of CRUD API for Access management APIs](https://github.com/eclipse-tractusx/sldt-digital-twin-registry/issues/291) +- [Implementation for Access management to Digital Twins (via EDC extension)](https://github.com/eclipse-tractusx/sig-release/issues/417) +- [EDC-based Access Control for Asset Administration Shells and Submodel Server](https://github.com/eclipse-tractusx/sig-release/issues/417#issuecomment-1883058762) \ No newline at end of file diff --git a/docs/concept/#438-investigation-dDTR-api-endpoints/AAS_Access_v3_Architecture.jpg b/docs/concept/#438-investigation-dDTR-api-endpoints/AAS_Access_v3_Architecture.jpg new file mode 100644 index 0000000000..75a306dba5 Binary files /dev/null and b/docs/concept/#438-investigation-dDTR-api-endpoints/AAS_Access_v3_Architecture.jpg differ diff --git a/docs/src/api/irs-api.yaml b/docs/src/api/irs-api.yaml index f44c23df41..1e08bb0b51 100644 --- a/docs/src/api/irs-api.yaml +++ b/docs/src/api/irs-api.yaml @@ -3,7 +3,7 @@ info: description: The API of the Item Relationship Service (IRS) for retrieving item graphs along the value chain of CATENA-X partners. title: IRS API - version: 4.7.0 + version: 4.8.0 servers: - url: http://localhost:8080 security: @@ -257,9 +257,6 @@ paths: "200": content: application/json: - examples: - complete: - $ref: '#/components/examples/complete-job-list-processing-state' schema: $ref: '#/components/schemas/PageResult' description: Paginated list of jobs with state and execution times for requested @@ -1617,6 +1614,16 @@ components: - PROCESSING - PARTIAL - ERROR + example: + batchChecksum: 1 + batches: + - batchId: f253718e-a270-4367-901b-9d50d9bd8462 + batchNumber: 1 + batchProcessingState: PARTIAL + batchUrl: https://../irs/orders/f253718e-a270-4367-901b-9d50d9bd8462/batches/f253718e-a270-4367-901b-9d50d9bd8462 + jobsInBatchChecksum: 1 + orderId: f253718e-a270-4367-901b-9d50d9bd8462 + state: COMPLETED BatchResponse: type: object additionalProperties: false @@ -1673,6 +1680,21 @@ components: type: integer format: int32 description: Total amount of jobs inside the order. + example: + batchId: f253718e-a270-4367-901b-9d50d9bd8462 + batchNumber: 1 + batchProcessingState: COMPLETED + batchTotal: 1 + completedOn: '2022-02-03T14:48:54.709Z' + jobs: + - completedOn: '2022-02-03T14:48:54.709Z' + id: 6c311d29-5753-46d4-b32c-19b918ea93b0 + startedOn: '2022-02-03T14:48:54.709Z' + state: COMPLETED + jobsInBatchChecksum: 1 + orderId: f253718e-a270-4367-901b-9d50d9bd8462 + startedOn: '2022-02-03T14:48:54.709Z' + totalJobs: 1 Bpn: type: object additionalProperties: false @@ -2066,6 +2088,120 @@ components: type: object additionalProperties: false description: Container for a job with item graph. + example: + bpns: + - manufacturerId: BPNL00000003AAXX + manufacturerName: AB CD + job: + completedOn: '2022-02-03T14:48:54.709Z' + createdOn: '2022-02-03T14:48:54.709Z' + exception: + errorDetail: Timeout while requesting Digital Registry + exception: IrsTimeoutException + exceptionDate: '2022-02-03T14:48:54.709Z' + globalAssetId: urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0 + id: e5347c88-a921-11ec-b909-0242ac120002 + lastModifiedOn: '2022-02-03T14:48:54.709Z' + parameter: + aspects: + - SerialPart + - AddressAspect + auditContractNegotiation: false + bomLifecycle: asBuilt + collectAspects: false + depth: 1 + direction: downward + lookupBPNs: false + startedOn: '2022-02-03T14:48:54.709Z' + state: COMPLETED + summary: + asyncFetchedItems: + completed: 3 + failed: 0 + running: 0 + bpnLookups: + completed: 3 + failed: 0 + relationships: + - catenaXId: urn:uuid:d9bec1c6-e47c-4d18-ba41-0a5fe8b7f447 + linkedItem: + assembledOn: '2022-02-03T14:48:54.709Z' + childCatenaXId: urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9 + hasAlternatives: false + lastModifiedOn: '2022-02-03T14:48:54.709Z' + lifecycleContext: asBuilt + quantity: + measurementUnit: + datatypeURI: urn:bamm:io.openmanufacturing:meta-model:1.0.0#piece + lexicalValue: piece + quantityNumber: 1 + shells: + - contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 + payload: + description: + - language: en + text: The shell for a vehicle + globalAssetId: urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9 + id: urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8 + idShort: future concept x + specificAssetIds: + - name: engineserialid + value: '12309481209312' + submodelDescriptors: + - description: + - language: en + text: Provides base vehicle information + endpoints: + - interface: HTTP + protocolInformation: + endpointProtocol: HTTPS + endpointProtocolVersion: + - '1.0' + href: https://catena-x.net/vehicle/basedetails/ + subprotocol: DSP + subprotocolBody: id=urn:uuid:c8159379-4613-48b8-ad52-6baed7afe923;dspEndpoint=https://irs-provider-controlplane3.dev.demo.catena-x.net + subprotocolBodyEncoding: plain + id: urn:uuid:5d25a897-6571-4800-b98c-a3352fbf996d + idShort: SingleLevelBomAsPlanned + semanticId: + keys: + - type: ExternalReference + value: urn:bamm:io.catenax.single_level_bom_as_planned:2.0.0#SingleLevelBomAsPlanned + type: ModelReference + - description: + - language: en + text: Provides base vehicle information + endpoints: + - interface: HTTP + protocolInformation: + endpointProtocol: HTTPS + endpointProtocolVersion: + - '1.0' + href: https://catena-x.net/vehicle/partdetails/ + subprotocol: DSP + subprotocolBody: id=urn:uuid:c8159379-4613-48b8-ad52-6baed7afe923;dspEndpoint=https://irs-provider-controlplane3.dev.demo.catena-x.net + subprotocolBodyEncoding: plain + id: urn:uuid:dae4d249-6d66-4818-b576-bf52f3b9ae90 + idShort: vehicle part details + semanticId: + keys: + - type: Submodel + value: urn:bamm:com.catenax.vehicle:0.1.1#PartDetails + type: ModelReference + submodels: + - aspectType: supply_chain_impacted + contractAgreementId: f253718e-a270-4367-901b-9d50d9bd8462 + identification: urn:uuid:fc784d2a-5506-4e61-8e34-21600f8cdeff + payload: + supplyChainImpacted: 'YES' + tombstones: + - catenaXId: urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0 + endpointURL: https://catena-x.net/vehicle/partdetails/ + processingError: + errorDetail: Details to reason of failure + lastAttempt: '2022-02-03T14:48:54.709Z' + processStep: SchemaValidation + retryCounter: 0 properties: bpns: type: array @@ -2216,6 +2352,16 @@ components: PageResult: type: object additionalProperties: false + example: + pageCount: 0 + pageNumber: 6 + pageSize: 1 + content: + - completedOn: test + id: 046b6c7f-0b8a-43b9-b35d-6489e6daee91 + startedOn: 2000-01-23T04:56:07.000+00:00 + state: UNSAVED + totalElements: 5 properties: content: type: array @@ -2269,6 +2415,23 @@ components: constraint: $ref: '#/components/schemas/Constraints' Policy: + example: + createdOn: '2024-03-08T15:19:41.006Z' + permissions: + - action: USE + constraint: + and: + - leftOperand: string + odrl:rightOperand: string + operator: + "@id": odrl:eq + or: + - leftOperand: string + odrl:rightOperand: string + operator: + "@id": odrl:eq + policyId: f253718e-a270-4367-901b-9d50d9bd8462 + validUntil: '2024-03-08T15:19:41.006Z' type: object additionalProperties: false properties: @@ -2750,6 +2913,19 @@ components: AspectModels: type: object additionalProperties: false + example: + lastUpdated: 2023-02-13T08:18:11.990659500Z + models: + - urn: urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt + name: SingleLevelBomAsBuilt + type: BAMM + version: 1.0.0 + status: RELEASED + - urn: urn:bamm:io.catenax.serial_part:1.0.0#SerialPart + name: SerialPart + type: BAMM + version: 1.0.0 + status: RELEASED properties: lastUpdated: type: string diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java index 6923bbd41b..35e68ec6ae 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/IrsApplication.java @@ -59,7 +59,7 @@ public class IrsApplication { /** * The IRS API version. */ - public static final String API_VERSION = "4.7.0"; + public static final String API_VERSION = "4.8.0"; /** * The URL prefix for IRS API URLs. diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/RegistryConfiguration.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/RegistryConfiguration.java index 11d7fbbf77..7b2d866bd8 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/RegistryConfiguration.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/configuration/RegistryConfiguration.java @@ -23,6 +23,7 @@ ********************************************************************************/ package org.eclipse.tractusx.irs.configuration; +import org.eclipse.tractusx.irs.edc.client.EdcConfiguration; import org.eclipse.tractusx.irs.edc.client.EdcSubmodelFacade; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; import org.eclipse.tractusx.irs.registryclient.central.CentralDigitalTwinRegistryService; @@ -30,6 +31,7 @@ import org.eclipse.tractusx.irs.registryclient.central.DigitalTwinRegistryClientImpl; import org.eclipse.tractusx.irs.registryclient.decentral.DecentralDigitalTwinRegistryClient; import org.eclipse.tractusx.irs.registryclient.decentral.DecentralDigitalTwinRegistryService; +import org.eclipse.tractusx.irs.registryclient.decentral.EdcEndpointReferenceRetriever; import org.eclipse.tractusx.irs.registryclient.decentral.EdcRetrieverException; import org.eclipse.tractusx.irs.registryclient.decentral.EndpointDataForConnectorsService; import org.eclipse.tractusx.irs.registryclient.discovery.ConnectorEndpointsService; @@ -71,16 +73,25 @@ public DecentralDigitalTwinRegistryService decentralDigitalTwinRegistryService( @Qualifier(RestTemplateConfig.EDC_REST_TEMPLATE) final RestTemplate edcRestTemplate, final ConnectorEndpointsService connectorEndpointsService, final EdcSubmodelFacade facade, @Value("${digitalTwinRegistry.shellDescriptorTemplate:}") final String shellDescriptorTemplate, - @Value("${digitalTwinRegistry.lookupShellsTemplate:}") final String lookupShellsTemplate) { - return new DecentralDigitalTwinRegistryService(connectorEndpointsService, - new EndpointDataForConnectorsService((edcConnectorEndpoint, assetType, assetValue) -> { - try { - return facade.getEndpointReferencesForAsset(edcConnectorEndpoint, assetType, assetValue); - } catch (EdcClientException e) { - throw new EdcRetrieverException(e); - } - }), - new DecentralDigitalTwinRegistryClient(edcRestTemplate, shellDescriptorTemplate, lookupShellsTemplate)); + @Value("${digitalTwinRegistry.lookupShellsTemplate:}") final String lookupShellsTemplate, + final EdcConfiguration edcConfiguration) { + + final EdcEndpointReferenceRetriever endpointReferenceRetriever = (edcConnectorEndpoint, assetType, assetValue) -> { + try { + return facade.getEndpointReferencesForAsset(edcConnectorEndpoint, assetType, assetValue); + } catch (EdcClientException e) { + throw new EdcRetrieverException(e); + } + }; + + final DecentralDigitalTwinRegistryClient digitalTwinRegistryClient = new DecentralDigitalTwinRegistryClient( + edcRestTemplate, shellDescriptorTemplate, lookupShellsTemplate); + + final EndpointDataForConnectorsService endpointDataForConnectorsService = new EndpointDataForConnectorsService( + endpointReferenceRetriever); + + return new DecentralDigitalTwinRegistryService(connectorEndpointsService, endpointDataForConnectorsService, + digitalTwinRegistryClient, edcConfiguration); } @Bean diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java index a8a23c11c4..13f8bf923a 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/controllers/IrsController.java @@ -240,9 +240,7 @@ public Job cancelJobByJobId( @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Paginated list of jobs with state and execution times for requested job states.", content = { @Content(mediaType = APPLICATION_JSON_VALUE, - schema = @Schema(implementation = PageResult.class), - examples = @ExampleObject(name = "complete", - ref = "#/components/examples/complete-job-list-processing-state")) + schema = @Schema(implementation = PageResult.class)) }), @ApiResponse(responseCode = "400", description = "Return jobs for requested job states failed.", diff --git a/irs-api/src/main/java/org/eclipse/tractusx/irs/semanticshub/AspectModels.java b/irs-api/src/main/java/org/eclipse/tractusx/irs/semanticshub/AspectModels.java index 54a3d78786..7011e55ca3 100644 --- a/irs-api/src/main/java/org/eclipse/tractusx/irs/semanticshub/AspectModels.java +++ b/irs-api/src/main/java/org/eclipse/tractusx/irs/semanticshub/AspectModels.java @@ -25,6 +25,7 @@ import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; /** @@ -32,6 +33,8 @@ * @param lastUpdated timestamp of latest update */ @Builder +@Schema(example = AspectModels.EXAMPLE) public record AspectModels(List models, String lastUpdated) { - + public static final String EXAMPLE = "{\"lastUpdated\"=\"2023-02-13T08:18:11.990659500Z\", \"models\"=[{\"name\"=\"SingleLevelBomAsBuilt\", \"status\"=\"RELEASED\", \"type\"=\"BAMM\", \"urn\"=\"urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt\", \"version\"=\"1.0.0\"},\n" + + " {\"name\"=\"SerialPart\", \"status\"=\"RELEASED\", \"type\"=\"BAMM\", \"urn\"=\"urn:bamm:io.catenax.serial_part:1.0.0#SerialPart\", \"version\"=\"1.0.0\"}]}"; } diff --git a/irs-api/src/main/resources/application-local.yml b/irs-api/src/main/resources/application-local.yml index ecf0b3c1cd..2cceeeb48c 100644 --- a/irs-api/src/main/resources/application-local.yml +++ b/irs-api/src/main/resources/application-local.yml @@ -27,6 +27,8 @@ irs: admin: 01234567890123456789 regular: 09876543210987654321 + + spring: security: oauth2: @@ -38,4 +40,7 @@ spring: # ESS Module specific properties ess: irs: - url: http://localhost:8080 \ No newline at end of file + url: http://localhost:8080 + +irs-edc-client: + async-timeout: PT10M \ No newline at end of file diff --git a/irs-api/src/main/resources/application.yml b/irs-api/src/main/resources/application.yml index 54715476c1..ca68120f0d 100644 --- a/irs-api/src/main/resources/application.yml +++ b/irs-api/src/main/resources/application.yml @@ -130,6 +130,7 @@ resilience4j: irs-edc-client: callback-url: ${EDC_TRANSFER_CALLBACK_URL:} # The URL where the EDR token callback will be sent to. + asyncTimeout: PT10M # Timout for future.get requests as ISO 8601 Duration controlplane: request-ttl: ${EDC_CONTROLPLANE_REQUEST_TTL:PT10M} # How long to wait for an async EDC negotiation request to finish, ISO 8601 Duration endpoint: diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java index 26c8b97ad4..8fc619a1e5 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/IrsApplicationTests.java @@ -84,7 +84,16 @@ void generatedOpenApiMatchesContract() throws Exception { final Map fixedYamlMap = mapper.readerForMapOf(Object.class).readValue(fixedYaml); final Map generatedYamlMap = mapper.readerForMapOf(Object.class).readValue(generatedYaml); - assertThat(generatedYamlMap).isEqualTo(fixedYamlMap); + // To correctly display both documentations examples - manual and generated by annotations - + // we need to remove verification for some "examples", otherwise one or another won't display correctly + assertThat(generatedYamlMap).usingRecursiveComparison() + .ignoringFields("components.schemas.PageResult.example") + .ignoringFields("components.schemas.AspectModels.example") + .ignoringFields("components.schemas.BatchOrderResponse.example") + .ignoringFields("components.schemas.Jobs.example") + .ignoringFields("components.schemas.Policy") + .ignoringFields("components.schemas.BatchResponse.example") + .isEqualTo(fixedYamlMap); } @Test diff --git a/irs-api/src/test/java/org/eclipse/tractusx/irs/connector/job/InMemoryJobStoreTest.java b/irs-api/src/test/java/org/eclipse/tractusx/irs/connector/job/InMemoryJobStoreTest.java index e6393df7f0..fc88018469 100644 --- a/irs-api/src/test/java/org/eclipse/tractusx/irs/connector/job/InMemoryJobStoreTest.java +++ b/irs-api/src/test/java/org/eclipse/tractusx/irs/connector/job/InMemoryJobStoreTest.java @@ -37,6 +37,7 @@ import org.assertj.core.api.SoftAssertions; import org.eclipse.tractusx.irs.component.Job; import org.eclipse.tractusx.irs.component.JobErrorDetails; +import org.eclipse.tractusx.irs.component.Jobs; import org.eclipse.tractusx.irs.component.enums.JobState; import org.eclipse.tractusx.irs.util.TestMother; import org.junit.jupiter.api.Test; diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/ContractNegotiationService.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/ContractNegotiationService.java index ebe0689cc3..9abecd843e 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/ContractNegotiationService.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/ContractNegotiationService.java @@ -27,6 +27,8 @@ 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 lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -100,8 +102,7 @@ public NegotiationResponse negotiate(final String providerConnectorUrl, final Ca } case VALID -> throw new IllegalStateException( "Token is present and valid. Contract negotiation should not be started."); - default -> throw new IllegalStateException( - "Unknown token status."); + default -> throw new IllegalStateException("Unknown token status."); } final TransferProcessRequest transferProcessRequest = createTransferProcessRequest(providerConnectorUrl, @@ -124,7 +125,8 @@ private CompletableFuture startNewNegotiation(final String if (!policyCheckerService.isValid(catalogItem.getPolicy())) { log.info("Policy was not allowed, canceling negotiation."); - throw new UsagePolicyException(catalogItem.getItemId(), catalogItem.getPolicy(), catalogItem.getConnectorId()); + throw new UsagePolicyException(catalogItem.getItemId(), catalogItem.getPolicy(), + catalogItem.getConnectorId()); } final NegotiationRequest negotiationRequest = createNegotiationRequestFromCatalogItem(providerConnectorUrl, @@ -176,10 +178,10 @@ private NegotiationRequest createNegotiationRequestFromCatalogItem(final String private NegotiationResponse getNegotiationResponse(final CompletableFuture negotiationResponse) throws ContractNegotiationException { try { - return negotiationResponse.get(); + return negotiationResponse.get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - } catch (ExecutionException e) { + } catch (TimeoutException | ExecutionException e) { throw new ContractNegotiationException(e); } return null; @@ -188,10 +190,10 @@ private NegotiationResponse getNegotiationResponse(final CompletableFuture transferProcessResponse) throws TransferProcessException { try { - return transferProcessResponse.get(); + return transferProcessResponse.get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - } catch (ExecutionException e) { + } catch (TimeoutException | ExecutionException e) { throw new TransferProcessException(e); } return null; diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcConfiguration.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcConfiguration.java index 019781a863..a5dc23d0ff 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcConfiguration.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcConfiguration.java @@ -38,15 +38,24 @@ @Data public class EdcConfiguration { + private static final int ASYNC_TIMEOUT_MINUTES_DEFAULT = 10; + private ControlplaneConfig controlplane = new ControlplaneConfig(); private SubmodelConfig submodel = new SubmodelConfig(); private String callbackUrl; + private Duration asyncTimeout = Duration.ofMinutes(ASYNC_TIMEOUT_MINUTES_DEFAULT); + + public Long getAsyncTimeoutMillis() { + return asyncTimeout.toMillis(); + } + /** * Container for controlplane config */ @Data public static class ControlplaneConfig { + private EndpointConfig endpoint = new EndpointConfig(); private String providerSuffix; @@ -98,5 +107,4 @@ public static class SubmodelConfig { private String urnPrefix; } - } diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java index c5560a4d15..4160a53f84 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientImpl.java @@ -148,46 +148,49 @@ private Optional sendSubmodelNotification(final String @Override public CompletableFuture getSubmodelPayload(final String connectorEndpoint, final String submodelDataplaneUrl, final String assetId) throws EdcClientException { - return execute(connectorEndpoint, () -> { + + final CheckedSupplier> waitingForSubmodelRetrieval = () -> { + log.info("Requesting raw SubmodelPayload for endpoint '{}'.", connectorEndpoint); final StopWatch stopWatch = new StopWatch(); stopWatch.start("Get EDC Submodel task for raw payload, endpoint " + connectorEndpoint); - final EndpointDataReference endpointDataReference = getEndpointDataReference(connectorEndpoint, assetId); + final EndpointDataReference dataReference = getEndpointDataReference(connectorEndpoint, assetId); return pollingService.createJob() - .action(() -> retrieveSubmodelData(submodelDataplaneUrl, stopWatch, - endpointDataReference)) + .action(() -> retrieveSubmodelData(submodelDataplaneUrl, stopWatch, dataReference)) .timeToLive(config.getSubmodel().getRequestTtl()) .description("waiting for submodel retrieval") .build() .schedule(); - }); + }; + + return execute(connectorEndpoint, waitingForSubmodelRetrieval); } private EndpointDataReference getEndpointDataReference(final String connectorEndpoint, final String assetId) throws EdcClientException { + + final EndpointDataReference result; + log.info("Retrieving endpoint data reference from cache for asset id: {}", assetId); - final EndpointDataReferenceStatus cachedEndpointDataReference = endpointDataReferenceCacheService.getEndpointDataReference( - assetId); - EndpointDataReference endpointDataReference; + final var cachedReference = endpointDataReferenceCacheService.getEndpointDataReference(assetId); - if (cachedEndpointDataReference.tokenStatus() == TokenStatus.VALID) { + if (cachedReference.tokenStatus() == TokenStatus.VALID) { log.info("Endpoint data reference found in cache with token status valid, reusing cache record."); - endpointDataReference = cachedEndpointDataReference.endpointDataReference(); + result = cachedReference.endpointDataReference(); } else { - endpointDataReference = getEndpointDataReferenceAndAddToStorage(connectorEndpoint, assetId, - cachedEndpointDataReference); + result = getEndpointDataReferenceAndAddToStorage(connectorEndpoint, assetId, cachedReference); } - return endpointDataReference; + return result; } private EndpointDataReference getEndpointDataReferenceAndAddToStorage(final String connectorEndpoint, final String assetId, final EndpointDataReferenceStatus cachedEndpointDataReference) throws EdcClientException { try { - final EndpointDataReference endpointDataReference = getEndpointReferenceForAsset(connectorEndpoint, + final EndpointDataReference endpointDataReference = awaitEndpointReferenceForAsset(connectorEndpoint, NAMESPACE_EDC_ID, assetId, cachedEndpointDataReference).get(); endpointDataReferenceCacheService.putEndpointDataReferenceIntoStorage(assetId, endpointDataReference); @@ -203,6 +206,7 @@ private EndpointDataReference getEndpointDataReferenceAndAddToStorage(final Stri @Override public CompletableFuture sendNotification(final String connectorEndpoint, final String assetId, final EdcNotification notification) throws EdcClientException { + return execute(connectorEndpoint, () -> { final StopWatch stopWatch = new StopWatch(); stopWatch.start("Send EDC notification task, endpoint " + connectorEndpoint); @@ -281,7 +285,7 @@ private NegotiationResponse negotiateContract(final EndpointDataReferenceStatus return response; } - public CompletableFuture getEndpointReferenceForAsset(final String endpointAddress, + private CompletableFuture awaitEndpointReferenceForAsset(final String endpointAddress, final String filterKey, final String filterValue, final EndpointDataReferenceStatus endpointDataReferenceStatus) throws EdcClientException { final StopWatch stopWatch = new StopWatch(); diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacade.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacade.java index 16040d640e..20ee2f59bc 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacade.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacade.java @@ -26,6 +26,8 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -46,11 +48,14 @@ public class EdcSubmodelFacade { private final EdcSubmodelClient client; + private final EdcConfiguration config; + @SuppressWarnings("PMD.PreserveStackTrace") public SubmodelDescriptor getSubmodelPayload(final String connectorEndpoint, final String submodelDataplaneUrl, final String assetId) throws EdcClientException { try { - return client.getSubmodelPayload(connectorEndpoint, submodelDataplaneUrl, assetId).get(); + return client.getSubmodelPayload(connectorEndpoint, submodelDataplaneUrl, assetId) + .get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.debug("InterruptedException occurred.", e); Thread.currentThread().interrupt(); @@ -62,6 +67,8 @@ public SubmodelDescriptor getSubmodelPayload(final String connectorEndpoint, fin throw exceptionCause; } throw new EdcClientException(cause); + } catch (TimeoutException e) { + throw new EdcClientException("Timeout while getting submodel payload", e); } } @@ -70,7 +77,8 @@ public EdcNotificationResponse sendNotification(final String submodelEndpointAdd final EdcNotification notification) throws EdcClientException { try { log.debug("Sending EDC Notification '{}'", notification); - return client.sendNotification(submodelEndpointAddress, assetId, notification).get(); + return client.sendNotification(submodelEndpointAddress, assetId, notification) + .get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; @@ -80,9 +88,11 @@ public EdcNotificationResponse sendNotification(final String submodelEndpointAdd throw exceptionCause; } throw new EdcClientException(cause); + } catch (TimeoutException e) { + throw new EdcClientException("Timeout while sending notification", e); } } - + public List> getEndpointReferencesForAsset(final String endpointAddress, final String filterKey, final String filterValue) throws EdcClientException { return client.getEndpointReferencesForAsset(endpointAddress, filterKey, filterValue); diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/PollingJob.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/PollingJob.java index 7b5a24cece..39268b393b 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/PollingJob.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/PollingJob.java @@ -58,9 +58,11 @@ public CompletableFuture schedule() { final Runnable actionToUse = () -> action.get().ifPresent(completableFuture::complete); - final ScheduledFuture scheduledFuture = scheduler.scheduleWithFixedDelay( - wrapWithErrorHandler(wrapWithTimeout(actionToUse), completableFuture), 0, pollInterval.toMillis(), - TimeUnit.MILLISECONDS); + final Runnable actionWithTimeoutAndErrorHandling = wrapWithErrorHandler(wrapWithTimeout(actionToUse), + completableFuture); + + final ScheduledFuture scheduledFuture = scheduler.scheduleWithFixedDelay(actionWithTimeoutAndErrorHandling, + 0, pollInterval.toMillis(), TimeUnit.MILLISECONDS); completableFuture.whenComplete((result, thrown) -> scheduledFuture.cancel(true)); diff --git a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/cache/endpointdatareference/EndpointDataReferenceCacheService.java b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/cache/endpointdatareference/EndpointDataReferenceCacheService.java index 4fefed5726..60fa69af58 100644 --- a/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/cache/endpointdatareference/EndpointDataReferenceCacheService.java +++ b/irs-edc-client/src/main/java/org/eclipse/tractusx/irs/edc/client/cache/endpointdatareference/EndpointDataReferenceCacheService.java @@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.irs.edc.client.EndpointDataReferenceStorage; +import org.eclipse.tractusx.irs.edc.client.cache.endpointdatareference.EndpointDataReferenceStatus.TokenStatus; import org.eclipse.tractusx.irs.edc.client.model.EDRAuthCode; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Service; @@ -67,19 +68,17 @@ public EndpointDataReferenceStatus getEndpointDataReference(final String assetId if (isTokenExpired(authCode)) { log.info("Endpoint data reference with expired token and id: {} for assetId: {} found in storage.", endpointDataReference.getId(), assetId); - return new EndpointDataReferenceStatus(endpointDataReference, - EndpointDataReferenceStatus.TokenStatus.EXPIRED); + return new EndpointDataReferenceStatus(endpointDataReference, TokenStatus.EXPIRED); } else { log.info("Endpoint data reference with id: {} for assetId: {} found in storage.", endpointDataReference.getId(), assetId); - return new EndpointDataReferenceStatus(endpointDataReference, - EndpointDataReferenceStatus.TokenStatus.VALID); + return new EndpointDataReferenceStatus(endpointDataReference, TokenStatus.VALID); } } } log.info("Endpoint data reference for asset id: {} not found in storage.", assetId); - return new EndpointDataReferenceStatus(null, EndpointDataReferenceStatus.TokenStatus.REQUIRED_NEW); + return new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW); } public Optional getEndpointDataReferenceFromStorage(final String storageId) { diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientTest.java index d1c282fa63..c65a83b62c 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelClientTest.java @@ -79,8 +79,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Answers; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) @@ -89,24 +89,29 @@ class EdcSubmodelClientTest extends LocalTestDataConfigurationAware { private static final String ENDPOINT_ADDRESS = "http://localhost/d46b51ae-08b6-42d7-a30d-0f8d118c8e0d-ce85f148-e3cf-42fe-9381-d1f276333fc4/submodel"; private static final String ASSET_ID = "d46b51ae-08b6-42d7-a30d-0f8d118c8e0d-ce85f148-e3cf-42fe-9381-d1f276333fc4"; private static final String PROVIDER_SUFFIX = "/test"; - - private final static String CONNECTOR_ENDPOINT = "https://connector.endpoint.com"; - private final static String SUBMODEL_SUFIX = "/shells/{aasIdentifier}/submodels/{submodelIdentifier}/submodel"; + private static final String CONNECTOR_ENDPOINT = "https://connector.endpoint.com"; private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final TimeMachine clock = new TimeMachine(); private final AsyncPollingService pollingService = new AsyncPollingService(clock, scheduler); - @Spy - private final EdcConfiguration config = new EdcConfiguration(); + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private EdcConfiguration config; + private final RetryRegistry retryRegistry = RetryRegistry.ofDefaults(); + @Mock private ContractNegotiationService contractNegotiationService; + @Mock private EdcDataPlaneClient edcDataPlaneClient; + @Mock private EDCCatalogFacade catalogFacade; + @Mock private EndpointDataReferenceCacheService endpointDataReferenceCacheService; + private EdcSubmodelClient testee; EdcSubmodelClientTest() throws IOException { @@ -115,15 +120,8 @@ class EdcSubmodelClientTest extends LocalTestDataConfigurationAware { @BeforeEach void setUp() { - config.setControlplane(new EdcConfiguration.ControlplaneConfig()); - config.getControlplane().setEndpoint(new EdcConfiguration.ControlplaneConfig.EndpointConfig()); - config.getControlplane().getEndpoint().setData("https://irs-consumer-controlplane.dev.demo.catena-x.net/data"); - config.getControlplane().setRequestTtl(Duration.ofMinutes(10)); - config.getControlplane().setProviderSuffix(PROVIDER_SUFFIX); - - config.setSubmodel(new EdcConfiguration.SubmodelConfig()); - config.getSubmodel().setUrnPrefix("/urn"); - config.getSubmodel().setRequestTtl(Duration.ofMinutes(10)); + when(config.getControlplane().getRequestTtl()).thenReturn(Duration.ofMinutes(10)); + when(config.getSubmodel().getRequestTtl()).thenReturn(Duration.ofMinutes(10)); testee = new EdcSubmodelClientImpl(config, contractNegotiationService, edcDataPlaneClient, pollingService, retryRegistry, catalogFacade, endpointDataReferenceCacheService); @@ -132,6 +130,8 @@ void setUp() { @Test void shouldRetrieveValidRelationship() throws Exception { // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String agreementId = "agreementId"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId("itemId").build())); @@ -157,6 +157,8 @@ void shouldRetrieveValidRelationship() throws Exception { @Test void shouldSendNotificationSuccessfully() throws Exception { // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String agreementId = "agreementId"; final EdcNotification notification = EdcNotification.builder().build(); when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( @@ -191,6 +193,9 @@ private String readSingleLevelBomAsBuiltData() throws IOException { @Test void shouldReturnRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelBomAsBuilt() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String existingCatenaXId = "urn:uuid:b00df8b5-7826-4b87-b0f6-7c1e4cc7b444"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId(existingCatenaXId).build())); @@ -198,15 +203,20 @@ void shouldReturnRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelBomAsBuil when(endpointDataReferenceCacheService.getEndpointDataReference(any())).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("http://localhost/", "/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).contains(existingCatenaXId); } @Test void shouldReturnRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelBomAsPlanned() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String catenaXId = "urn:uuid:aad27ddb-43aa-4e42-98c2-01e529ef127c"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId(catenaXId).build())); @@ -214,15 +224,20 @@ void shouldReturnRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelBomAsPlan when(endpointDataReferenceCacheService.getEndpointDataReference(any())).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("http://localhost/", "/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).contains("urn:uuid:e5c96ab5-896a-482c-8761-efd74777ca97"); } @Test void shouldReturnRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelBomAsSpecified() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String catenaXId = "urn:uuid:644c0988-6949-4586-b304-7f46f412a5cc"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId(catenaXId).build())); @@ -230,15 +245,20 @@ void shouldReturnRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelBomAsSpec when(endpointDataReferenceCacheService.getEndpointDataReference(any())).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("http://localhost/", "/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).contains("urn:uuid:2afbac90-a662-4f16-9058-4f030e692631"); } @Test void shouldReturnEmptyRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelUsageAsBuilt() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String catenaXId = "urn:uuid:61c83b41-def0-4742-a1a8-e4e8a8cb210e"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId(catenaXId).build())); @@ -246,16 +266,21 @@ void shouldReturnEmptyRelationshipsWhenRequestingWithCatenaXIdAndSingleLevelUsag when(endpointDataReferenceCacheService.getEndpointDataReference(any())).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("http://localhost/", "/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).isNotEmpty(); } @Test void shouldReturnEmptyRelationshipsWhenRequestingWithNotExistingCatenaXIdAndSingleLevelBomAsBuilt() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String catenaXId = "urn:uuid:8a61c8db-561e-4db0-84ec-a693fc5ffdf6"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId(catenaXId).build())); @@ -263,15 +288,20 @@ void shouldReturnEmptyRelationshipsWhenRequestingWithNotExistingCatenaXIdAndSing when(endpointDataReferenceCacheService.getEndpointDataReference(ASSET_ID)).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("http://localhost/", "/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).isEqualTo("{}"); } @Test void shouldReturnRawSerialPartWhenExisting() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String existingCatenaXId = "urn:uuid:b00df8b5-7826-4b87-b0f6-7c1e4cc7b444"; when(catalogFacade.fetchCatalogByFilter("https://connector.endpoint.com" + PROVIDER_SUFFIX, "https://w3id.org/edc/v0.0.1/ns/id", ASSET_ID)).thenReturn(createCatalog(ASSET_ID, 3)); @@ -279,17 +309,22 @@ void shouldReturnRawSerialPartWhenExisting() throws Exception { when(endpointDataReferenceCacheService.getEndpointDataReference(ASSET_ID)).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("https://connector.endpoint.com", "/shells/{aasIdentifier}/submodels/{submodelIdentifier}/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).startsWith( "{\"localIdentifiers\":[{\"value\":\"BPNL00000003AVTH\",\"key\":\"manufacturerId\"}"); } @Test void shouldUseDecodedTargetId() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String existingCatenaXId = "urn:uuid:b00df8b5-7826-4b87-b0f6-7c1e4cc7b444"; prepareTestdata(existingCatenaXId, "_serialPart"); final String target = URLEncoder.encode(ASSET_ID, StandardCharsets.UTF_8); @@ -298,17 +333,22 @@ void shouldUseDecodedTargetId() throws Exception { when(endpointDataReferenceCacheService.getEndpointDataReference(ASSET_ID)).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + // act final String submodelResponse = testee.getSubmodelPayload("https://connector.endpoint.com", "/shells/{aasIdentifier}/submodels/{submodelIdentifier}/submodel", ASSET_ID) .get(5, TimeUnit.SECONDS) .getPayload(); + // assert assertThat(submodelResponse).startsWith( "{\"localIdentifiers\":[{\"value\":\"BPNL00000003AVTH\",\"key\":\"manufacturerId\"}"); } @Test void shouldReturnSameRelationshipsForDifferentDirections() throws Exception { + // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String parentCatenaXId = "urn:uuid:a65c35a8-8d31-4a86-899b-57912de33675"; final BomLifecycle asBuilt = BomLifecycle.AS_BUILT; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( @@ -316,6 +356,8 @@ void shouldReturnSameRelationshipsForDifferentDirections() throws Exception { prepareTestdata(parentCatenaXId, "_singleLevelBomAsBuilt"); when(endpointDataReferenceCacheService.getEndpointDataReference(ASSET_ID)).thenReturn( new EndpointDataReferenceStatus(null, TokenStatus.REQUIRED_NEW)); + + // act final String relationshipsJson = testee.getSubmodelPayload("http://localhost/", "_singleLevelBomAsBuilt", ASSET_ID).get(5, TimeUnit.SECONDS).getPayload(); @@ -334,6 +376,7 @@ void shouldReturnSameRelationshipsForDifferentDirections() throws Exception { final var singleLevelUsageRelationships = StringMapper.mapFromString(singleLevelUsageRelationshipsJson, RelationshipAspect.from(asBuilt, Direction.UPWARD).getSubmodelClazz()).asRelationships(); + // assert assertThat(relationships).isNotNull(); assertThat(singleLevelUsageRelationships).isNotEmpty(); assertThat(relationships.get(0).getCatenaXId()).isEqualTo(singleLevelUsageRelationships.get(0).getCatenaXId()); @@ -344,6 +387,8 @@ void shouldReturnSameRelationshipsForDifferentDirections() throws Exception { @Test void shouldRetrieveEndpointReferenceForAsset() throws Exception { // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String filterKey = "filter-key"; final String filterValue = "filter-value"; final String agreementId = "agreementId"; @@ -368,6 +413,8 @@ void shouldRetrieveEndpointReferenceForAsset() throws Exception { @Test void shouldRetrieveEndpointReferenceForAsset2() throws Exception { // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String filterKey = "filter-key"; final String filterValue = "filter-value"; final String agreementId = "agreementId"; @@ -391,16 +438,16 @@ void shouldRetrieveEndpointReferenceForAsset2() throws Exception { @Test void shouldUseCachedEndpointReferenceValueWhenTokenIsValid() throws EdcClientException, ExecutionException, InterruptedException { - // given + // arrange when(endpointDataReferenceCacheService.getEndpointDataReference(any())).thenReturn( new EndpointDataReferenceStatus(TestMother.endpointDataReference("assetId"), TokenStatus.VALID)); final String value = "result"; when(edcDataPlaneClient.getData(any(), any())).thenReturn(value); - // when + // act final var resultFuture = testee.getSubmodelPayload(ENDPOINT_ADDRESS, "suffix", "assetId"); - // then + // assert final String result = resultFuture.get().getPayload(); verify(contractNegotiationService, never()).negotiate(any(), any(), any()); assertThat(result).isEqualTo(value); @@ -409,6 +456,8 @@ void shouldUseCachedEndpointReferenceValueWhenTokenIsValid() @Test void shouldCreateCacheRecordWhenTokenIsNotValid() throws EdcClientException { // arrange + when(config.getControlplane().getProviderSuffix()).thenReturn(PROVIDER_SUFFIX); + final String agreementId = "agreementId"; when(catalogFacade.fetchCatalogByFilter(any(), any(), any())).thenReturn( List.of(CatalogItem.builder().itemId("itemId").build())); diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacadeTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacadeTest.java index e5bc52abb3..2c0b6884e7 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacadeTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/EdcSubmodelFacadeTest.java @@ -32,17 +32,19 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.assertj.core.api.ThrowableAssert; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; import org.eclipse.tractusx.irs.edc.client.model.SubmodelDescriptor; import org.eclipse.tractusx.irs.edc.client.model.notification.EdcNotificationResponse; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -53,12 +55,18 @@ class EdcSubmodelFacadeTest { private final static String SUBMODEL_SUFIX = "/shells/{aasIdentifier}/submodels/{submodelIdentifier}/submodel"; private final static String ASSET_ID = "9300395e-c0a5-4e88-bc57-a3973fec4c26"; - @InjectMocks private EdcSubmodelFacade testee; @Mock private EdcSubmodelClient client; + private EdcConfiguration config = new EdcConfiguration(); + + @BeforeEach + public void beforeEach() { + this.testee = new EdcSubmodelFacade(client, config); + } + @Nested @DisplayName("getSubmodelRawPayload") class GetSubmodelRawPayloadTests { @@ -94,11 +102,11 @@ void shouldThrowEdcClientExceptionForSubmodel() throws EdcClientException { @Test void shouldRestoreInterruptOnInterruptExceptionForSubmodel() - throws EdcClientException, ExecutionException, InterruptedException { + throws EdcClientException, ExecutionException, InterruptedException, TimeoutException { // arrange final CompletableFuture future = mock(CompletableFuture.class); final InterruptedException e = new InterruptedException(); - when(future.get()).thenThrow(e); + when(future.get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS)).thenThrow(e); when(client.getSubmodelPayload(any(), any(), any())).thenReturn(future); // act @@ -116,11 +124,11 @@ class SendNotificationTests { @Test void shouldRestoreInterruptOnInterruptExceptionForNotification() - throws EdcClientException, ExecutionException, InterruptedException { + throws EdcClientException, ExecutionException, InterruptedException, TimeoutException { // arrange final CompletableFuture future = mock(CompletableFuture.class); final InterruptedException e = new InterruptedException(); - when(future.get()).thenThrow(e); + when(future.get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS)).thenThrow(e); when(client.sendNotification(any(), any(), any())).thenReturn(future); // act diff --git a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelRetryerTest.java b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelRetryerTest.java index 2e77ca8da2..75e33e15c4 100644 --- a/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelRetryerTest.java +++ b/irs-edc-client/src/test/java/org/eclipse/tractusx/irs/edc/client/SubmodelRetryerTest.java @@ -83,7 +83,7 @@ void setUp() { final EdcSubmodelClient client = new EdcSubmodelClientImpl(config, negotiationService, dataPlaneClient, pollingService, retryRegistry, catalogFacade, endpointDataReferenceCacheService); - testee = new EdcSubmodelFacade(client); + testee = new EdcSubmodelFacade(client, config); } @Test diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchOrderResponse.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchOrderResponse.java index eef20b6897..9dae6c4153 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchOrderResponse.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchOrderResponse.java @@ -37,13 +37,15 @@ /** * BatchOrderAck Payload Response */ -@Schema(description = "BatchOrderAck Payload Response.") +@Schema(description = "BatchOrderAck Payload Response.", example = BatchOrderResponse.EXAMPLE) @Value @Builder @AllArgsConstructor @Jacksonized public class BatchOrderResponse { + public static final String EXAMPLE = "{\"batchChecksum\"=1, \"batches\"=[{\"batchId\"=\"f253718e-a270-4367-901b-9d50d9bd8462\", \"batchNumber\"=1, \"batchProcessingState\"=\"PARTIAL\", \"batchUrl\"=\"https://../irs/orders/f253718e-a270-4367-901b-9d50d9bd8462/batches/f253718e-a270-4367-901b-9d50d9bd8462\", \"jobsInBatchChecksum\"=1}], \"orderId\"=\"f253718e-a270-4367-901b-9d50d9bd8462\", \"state\"=\"COMPLETED\"}"; + private static final int UUID_LENGTH = 36; @Schema(description = "Id of the order.", minLength = UUID_LENGTH, diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchResponse.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchResponse.java index 2aad0b90e5..c421061329 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchResponse.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/BatchResponse.java @@ -38,7 +38,7 @@ /** * Batch model */ -@Schema(description = "Batch model.") +@Schema(description = "Batch model.", example = BatchResponse.EXAMPLE) @Value @Builder @AllArgsConstructor @@ -46,6 +46,7 @@ public class BatchResponse { private static final int UUID_LENGTH = 36; + public static final String EXAMPLE = "{\"batchId\"=\"f253718e-a270-4367-901b-9d50d9bd8462\", \"batchNumber\"=1, \"batchProcessingState\"=\"COMPLETED\", \"batchTotal\"=1, \"completedOn\"=\"2022-02-03T14:48:54.709Z\", \"jobs\"=[{\"completedOn\"=\"2022-02-03T14:48:54.709Z\", \"id\"=\"6c311d29-5753-46d4-b32c-19b918ea93b0\", \"startedOn\"=\"2022-02-03T14:48:54.709Z\", \"state\"=\"COMPLETED\"}], \"jobsInBatchChecksum\"=1, \"orderId\"=\"f253718e-a270-4367-901b-9d50d9bd8462\", \"startedOn\"=\"2022-02-03T14:48:54.709Z\", \"totalJobs\"=1}"; @Schema(description = "Id of the order.", minLength = UUID_LENGTH, maxLength = UUID_LENGTH, implementation = UUID.class, diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Jobs.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Jobs.java index 42b53f46b5..e9ac4c3ffa 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Jobs.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/Jobs.java @@ -37,7 +37,7 @@ /** * List of Job and relationship to parts */ -@Schema(description = "Container for a job with item graph.") +@Schema(description = "Container for a job with item graph.", example = Jobs.EXAMPLE) @Value @Builder(toBuilder = true) @AllArgsConstructor @@ -67,4 +67,7 @@ public class Jobs { @Singular private Set bpns; + /* package */ static final String EXAMPLE = "{\"bpns\"=[{\"manufacturerId\"=\"BPNL00000003AAXX\", \"manufacturerName\"=\"AB CD\"}], \"job\"={\"completedOn\"=\"2022-02-03T14:48:54.709Z\", \"createdOn\"=\"2022-02-03T14:48:54.709Z\", \"exception\"={\"errorDetail\"=\"Timeout while requesting Digital Registry\", \"exception\"=\"IrsTimeoutException\", \"exceptionDate\"=\"2022-02-03T14:48:54.709Z\"}, \"globalAssetId\"=\"urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0\", \"id\"=\"e5347c88-a921-11ec-b909-0242ac120002\", \"lastModifiedOn\"=\"2022-02-03T14:48:54.709Z\", \"parameter\"={\"aspects\"=[\"SerialPart\", \"AddressAspect\"], \"auditContractNegotiation\"=false, \"bomLifecycle\"=\"asBuilt\", \"collectAspects\"=false, \"depth\"=1, \"direction\"=\"downward\", \"lookupBPNs\"=false}, \"startedOn\"=\"2022-02-03T14:48:54.709Z\", \"state\"=\"COMPLETED\", \"summary\"={\"asyncFetchedItems\"={\"completed\"=3, \"failed\"=0, \"running\"=0}, \"bpnLookups\"={\"completed\"=3, \"failed\"=0}}}, \"relationships\"=[{\"catenaXId\"=\"urn:uuid:d9bec1c6-e47c-4d18-ba41-0a5fe8b7f447\", \"linkedItem\"={\"assembledOn\"=\"2022-02-03T14:48:54.709Z\", \"childCatenaXId\"=\"urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9\", \"hasAlternatives\"=false, \"lastModifiedOn\"=\"2022-02-03T14:48:54.709Z\", \"lifecycleContext\"=\"asBuilt\", \"quantity\"={\"measurementUnit\"={\"datatypeURI\"=\"urn:bamm:io.openmanufacturing:meta-model:1.0.0#piece\", \"lexicalValue\"=\"piece\"}, \"quantityNumber\"=1}}}], \"shells\"=[{\"contractAgreementId\"=\"f253718e-a270-4367-901b-9d50d9bd8462\", \"payload\"={\"description\"=[{\"language\"=\"en\", \"text\"=\"The shell for a vehicle\"}], \"globalAssetId\"=\"urn:uuid:a45a2246-f6e1-42da-b47d-5c3b58ed62e9\", \"id\"=\"urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8\", \"idShort\"=\"future concept x\", \"specificAssetIds\"=[{\"name\"=\"engineserialid\", \"value\"=\"12309481209312\"}], \"submodelDescriptors\"=[{\"description\"=[{\"language\"=\"en\", \"text\"=\"Provides base vehicle information\"}], \"endpoints\"=[{\"interface\"=\"HTTP\", \"protocolInformation\"={\"endpointProtocol\"=\"HTTPS\", \"endpointProtocolVersion\"=[\"1.0\"], \"href\"=\"https://catena-x.net/vehicle/basedetails/\", \"subprotocol\"=\"DSP\", \"subprotocolBody\"=\"id=urn:uuid:c8159379-4613-48b8-ad52-6baed7afe923;dspEndpoint=https://irs-provider-controlplane3.dev.demo.catena-x.net\", \"subprotocolBodyEncoding\"=\"plain\"}}], \"id\"=\"urn:uuid:5d25a897-6571-4800-b98c-a3352fbf996d\", \"idShort\"=\"SingleLevelBomAsPlanned\", \"semanticId\"={\"keys\"=[{\"type\"=\"ExternalReference\", \"value\"=\"urn:bamm:io.catenax.single_level_bom_as_planned:2.0.0#SingleLevelBomAsPlanned\"}], \"type\"=\"ModelReference\"}},\n" + + " {\"description\"=[{\"language\"=\"en\", \"text\"=\"Provides base vehicle information\"}], \"endpoints\"=[{\"interface\"=\"HTTP\", \"protocolInformation\"={\"endpointProtocol\"=\"HTTPS\", \"endpointProtocolVersion\"=[\"1.0\"], \"href\"=\"https://catena-x.net/vehicle/partdetails/\", \"subprotocol\"=\"DSP\", \"subprotocolBody\"=\"id=urn:uuid:c8159379-4613-48b8-ad52-6baed7afe923;dspEndpoint=https://irs-provider-controlplane3.dev.demo.catena-x.net\", \"subprotocolBodyEncoding\"=\"plain\"}}], \"id\"=\"urn:uuid:dae4d249-6d66-4818-b576-bf52f3b9ae90\", \"idShort\"=\"vehicle part details\", \"semanticId\"={\"keys\"=[{\"type\"=\"Submodel\", \"value\"=\"urn:bamm:com.catenax.vehicle:0.1.1#PartDetails\"}], \"type\"=\"ModelReference\"}}]}}], \"submodels\"=[{\"aspectType\"=\"supply_chain_impacted\", \"contractAgreementId\"=\"f253718e-a270-4367-901b-9d50d9bd8462\", \"identification\"=\"urn:uuid:fc784d2a-5506-4e61-8e34-21600f8cdeff\", \"payload\"={\"supplyChainImpacted\"=\"YES\"}}], \"tombstones\"=[{\"catenaXId\"=\"urn:uuid:6c311d29-5753-46d4-b32c-19b918ea93b0\", \"endpointURL\"=\"https://catena-x.net/vehicle/partdetails/\", \"processingError\"={\"errorDetail\"=\"Details to reason of failure\", \"lastAttempt\"=\"2022-02-03T14:48:54.709Z\", \"processStep\"=\"SchemaValidation\", \"retryCounter\"=0}}]}"; + } diff --git a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/PageResult.java b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/PageResult.java index d92daadece..a6b72a6ecc 100644 --- a/irs-models/src/main/java/org/eclipse/tractusx/irs/component/PageResult.java +++ b/irs-models/src/main/java/org/eclipse/tractusx/irs/component/PageResult.java @@ -25,20 +25,26 @@ import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.beans.support.PagedListHolder; /** * Paginated results for {@link JobStatusResult} content */ +@Schema(example = PageResult.EXAMPLE) public record PageResult( + List content, Integer pageNumber, Integer pageCount, Integer pageSize, Integer totalElements) { + public static final String EXAMPLE = "{\"content\"=[{\"completedOn\"=\"test\", \"id\"=\"046b6c7f-0b8a-43b9-b35d-6489e6daee91\", \"startedOn\"=\"2000-01-23T04:56:07.000+00:00\", \"state\"=\"UNSAVED\"}], \"pageCount\"=0, \"pageNumber\"=6, \"pageSize\"=1, \"totalElements\"=5}"; + public PageResult(final PagedListHolder pagedListHolder) { this(pagedListHolder.getPageList(), pagedListHolder.getPage(), pagedListHolder.getPageCount(), pagedListHolder.getPageSize(), pagedListHolder.getNrOfElements()); } + } diff --git a/irs-registry-client/README.md b/irs-registry-client/README.md index 97ca615da2..449f64e032 100644 --- a/irs-registry-client/README.md +++ b/irs-registry-client/README.md @@ -3,7 +3,6 @@ # Digital Twin Registry Client Library This library assists in communicating with the Digital Twin Registry in a central or decentral approach. - In the decentral approach, it also handles the communication via the Discovery Finder and EDC. @@ -29,7 +28,7 @@ Add the following configuration to your `application.yaml`: ```yaml digitalTwinRegistryClient: - type: "central" # or "decentral" + type: "decentral" # The type of DTR. This can be either "central" or "decentral". If "decentral", descriptorEndpoint, shellLookupEndpoint and oAuthClientId is not required. discoveryFinderUrl: "" # required if type is "decentral" @@ -40,6 +39,7 @@ digitalTwinRegistryClient: irs-edc-client: callback-url: "" # The URL where the EDR token callback will be sent to. This defaults to {BASE_URL}/internal/endpoint-data-reference. If you want to use a different mapping, you can override it with irs-edc-client.callback.mapping. + asyncTimeout: PT10M # Timout for future.get requests as ISO 8601 Duration controlplane: request-ttl: PT10M # How long to wait for an async EDC negotiation request to finish, ISO 8601 Duration endpoint: diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DefaultConfiguration.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DefaultConfiguration.java index 307a80fac5..cb15729950 100644 --- a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DefaultConfiguration.java +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/DefaultConfiguration.java @@ -43,6 +43,7 @@ import org.eclipse.tractusx.irs.registryclient.central.DigitalTwinRegistryClientImpl; import org.eclipse.tractusx.irs.registryclient.decentral.DecentralDigitalTwinRegistryClient; import org.eclipse.tractusx.irs.registryclient.decentral.DecentralDigitalTwinRegistryService; +import org.eclipse.tractusx.irs.registryclient.decentral.EdcEndpointReferenceRetriever; import org.eclipse.tractusx.irs.registryclient.decentral.EdcRetrieverException; import org.eclipse.tractusx.irs.registryclient.decentral.EndpointDataForConnectorsService; import org.eclipse.tractusx.irs.registryclient.discovery.ConnectorEndpointsService; @@ -93,9 +94,10 @@ public DigitalTwinRegistryClient digitalTwinRegistryClientImpl( public DecentralDigitalTwinRegistryService decentralDigitalTwinRegistryService( final ConnectorEndpointsService connectorEndpointsService, final EndpointDataForConnectorsService endpointDataForConnectorsService, - final DecentralDigitalTwinRegistryClient decentralDigitalTwinRegistryClient) { + final DecentralDigitalTwinRegistryClient decentralDigitalTwinRegistryClient, + final EdcConfiguration edcConfiguration) { return new DecentralDigitalTwinRegistryService(connectorEndpointsService, endpointDataForConnectorsService, - decentralDigitalTwinRegistryClient); + decentralDigitalTwinRegistryClient, edcConfiguration); } @Bean @@ -115,18 +117,21 @@ public ConnectorEndpointsService connectorEndpointsService(final DiscoveryFinder @Bean @ConditionalOnProperty(prefix = CONFIG_PREFIX, name = CONFIG_FIELD_TYPE, havingValue = CONFIG_VALUE_DECENTRAL) public EndpointDataForConnectorsService endpointDataForConnectorsService(final EdcSubmodelFacade facade) { - return new EndpointDataForConnectorsService((edcConnectorEndpoint, assetType, assetValue) -> { + + final EdcEndpointReferenceRetriever edcEndpointReferenceRetriever = (edcConnectorEndpoint, assetType, assetValue) -> { try { return facade.getEndpointReferencesForAsset(edcConnectorEndpoint, assetType, assetValue); } catch (EdcClientException e) { throw new EdcRetrieverException(e); } - }); + }; + + return new EndpointDataForConnectorsService(edcEndpointReferenceRetriever); } @Bean - public EdcSubmodelFacade edcSubmodelFacade(final EdcSubmodelClient client) { - return new EdcSubmodelFacade(client); + public EdcSubmodelFacade edcSubmodelFacade(final EdcSubmodelClient client, final EdcConfiguration config) { + return new EdcSubmodelFacade(client, config); } @Bean diff --git a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java index 1dae81a70f..ab5b05e50c 100644 --- a/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java +++ b/irs-registry-client/src/main/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryService.java @@ -31,6 +31,8 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; 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; @@ -41,6 +43,7 @@ import org.eclipse.tractusx.irs.component.Shell; import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; import org.eclipse.tractusx.irs.component.assetadministrationshell.IdentifierKeyValuePair; +import org.eclipse.tractusx.irs.edc.client.EdcConfiguration; import org.eclipse.tractusx.irs.edc.client.model.EDRAuthCode; import org.eclipse.tractusx.irs.registryclient.DigitalTwinRegistryKey; import org.eclipse.tractusx.irs.registryclient.DigitalTwinRegistryService; @@ -63,6 +66,7 @@ public class DecentralDigitalTwinRegistryService implements DigitalTwinRegistryS private final ConnectorEndpointsService connectorEndpointsService; private final EndpointDataForConnectorsService endpointDataForConnectorsService; private final DecentralDigitalTwinRegistryClient decentralDigitalTwinRegistryClient; + private final EdcConfiguration config; private ResultFinder resultFinder = new ResultFinder(); @@ -98,7 +102,7 @@ public Collection fetchShells(final Collection ke try { return fetchShellDescriptors(entry, calledEndpoints); - } catch (RuntimeException e) { + } catch (TimeoutException | RuntimeException e) { // catching generic exception is intended here, // otherwise Jobs stay in state RUNNING forever log.warn(e.getMessage(), e); @@ -122,12 +126,12 @@ public Collection fetchShells(final Collection ke } private Stream fetchShellDescriptors(final Map.Entry> entry, - final Set calledEndpoints) { + final Set calledEndpoints) throws TimeoutException { try { final var futures = fetchShellDescriptors(calledEndpoints, entry.getKey(), entry.getValue()); - final var shellDescriptors = futures.get(); + final var shellDescriptors = futures.get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS); return shellDescriptors.stream(); } catch (InterruptedException e) { @@ -305,7 +309,8 @@ private Collection lookupShellIds(final String bpn, edr -> CompletableFuture.supplyAsync( () -> lookupShellIds(bpn, edr)))) .toList(); - final var shellIds = resultFinder.getFastestResult(futures).get(); + final var shellIds = resultFinder.getFastestResult(futures) + .get(config.getAsyncTimeoutMillis(), TimeUnit.MILLISECONDS); log.info("Found {} shell id(s) in total", shellIds.size()); return shellIds; @@ -319,6 +324,8 @@ private Collection lookupShellIds(final String bpn, throw new RegistryServiceException( "%s occurred while looking up shell ids for bpn '%s'".formatted(e.getClass().getSimpleName(), bpn), e); + } catch (TimeoutException e) { + throw new RegistryServiceException("Timeout during shell ID lookup", e); } } diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/DefaultConfigurationTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/DefaultConfigurationTest.java index 274d8387b2..79fff0dda1 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/DefaultConfigurationTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/DefaultConfigurationTest.java @@ -36,6 +36,7 @@ import java.util.concurrent.ExecutionException; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; +import org.eclipse.tractusx.irs.edc.client.EdcConfiguration; import org.eclipse.tractusx.irs.edc.client.EdcSubmodelClient; import org.eclipse.tractusx.irs.edc.client.EdcSubmodelFacade; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; @@ -46,6 +47,8 @@ class DefaultConfigurationTest { private final DefaultConfiguration testee = new DefaultConfiguration(); + + private final EdcConfiguration edcConfiguration = new EdcConfiguration(); private final String descriptorTemplate = "descriptor/{aasIdentifier}"; private final String shellLookupTemplate = "shell?{assetIds}"; @@ -63,7 +66,8 @@ void decentralDigitalTwinRegistryService() { final var service = testee.decentralDigitalTwinRegistryService( testee.connectorEndpointsService(testee.discoveryFinderClient(new RestTemplate(), "finder")), testee.endpointDataForConnectorsService(facadeMock), - testee.decentralDigitalTwinRegistryClient(new RestTemplate(), descriptorTemplate, shellLookupTemplate)); + testee.decentralDigitalTwinRegistryClient(new RestTemplate(), descriptorTemplate, shellLookupTemplate), + edcConfiguration); assertThat(service).isNotNull(); } @@ -71,7 +75,7 @@ void decentralDigitalTwinRegistryService() { @Test void edcSubmodelFacade() { final EdcSubmodelClient facadeMock = mock(EdcSubmodelClient.class); - final EdcSubmodelFacade edcSubmodelFacade = testee.edcSubmodelFacade(facadeMock); + final EdcSubmodelFacade edcSubmodelFacade = testee.edcSubmodelFacade(facadeMock, edcConfiguration); assertThat(edcSubmodelFacade).isNotNull(); } diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java index abf3eb481d..a2c2dace14 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceTest.java @@ -29,12 +29,15 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.assertj.core.api.ThrowableAssert.ThrowingCallable; import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; @@ -43,6 +46,7 @@ import org.eclipse.tractusx.irs.component.assetadministrationshell.AssetAdministrationShellDescriptor; import org.eclipse.tractusx.irs.component.assetadministrationshell.IdentifierKeyValuePair; import org.eclipse.tractusx.irs.component.assetadministrationshell.SubmodelDescriptor; +import org.eclipse.tractusx.irs.edc.client.EdcConfiguration; import org.eclipse.tractusx.irs.registryclient.DigitalTwinRegistryKey; import org.eclipse.tractusx.irs.registryclient.discovery.ConnectorEndpointsService; import org.eclipse.tractusx.irs.registryclient.exceptions.RegistryServiceException; @@ -64,7 +68,8 @@ class DecentralDigitalTwinRegistryServiceTest { DecentralDigitalTwinRegistryClient.class); private final DecentralDigitalTwinRegistryService sut = new DecentralDigitalTwinRegistryService( - connectorEndpointsService, endpointDataForConnectorsService, decentralDigitalTwinRegistryClient); + connectorEndpointsService, endpointDataForConnectorsService, decentralDigitalTwinRegistryClient, + new EdcConfiguration()); public static AssetAdministrationShellDescriptor shellDescriptor( final List submodelDescriptors) { @@ -110,7 +115,7 @@ void shouldReturnExpectedShell() throws RegistryServiceException { } @Test - void whenInterruptedExceptionOccurs() throws ExecutionException, InterruptedException { + void whenInterruptedExceptionOccurs() throws ExecutionException, InterruptedException, TimeoutException { // given simulateResultFinderInterrupted(); @@ -190,10 +195,11 @@ private void simulateGetFastestResultFailedFuture() { sut.setResultFinder(resultFinderMock); } - private void simulateResultFinderInterrupted() throws InterruptedException, ExecutionException { + private void simulateResultFinderInterrupted() throws InterruptedException, ExecutionException, TimeoutException { final ResultFinder resultFinderMock = mock(ResultFinder.class); final CompletableFuture completableFutureMock = mock(CompletableFuture.class); - when(completableFutureMock.get()).thenThrow(new InterruptedException("interrupted")); + when(completableFutureMock.get(anyLong(), any(TimeUnit.class))).thenThrow( + new InterruptedException("interrupted")); when(resultFinderMock.getFastestResult(any())).thenReturn(completableFutureMock); sut.setResultFinder(resultFinderMock); } @@ -240,7 +246,7 @@ void shouldReturnTheExpectedGlobalAssetId() throws RegistryServiceException { } @Test - void whenInterruptedExceptionOccurs() throws ExecutionException, InterruptedException { + void whenInterruptedExceptionOccurs() throws ExecutionException, InterruptedException, TimeoutException { // given simulateResultFinderInterrupted(); diff --git a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java index 31418299ee..e0f085bab9 100644 --- a/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java +++ b/irs-registry-client/src/test/java/org/eclipse/tractusx/irs/registryclient/decentral/DecentralDigitalTwinRegistryServiceWiremockTest.java @@ -66,6 +66,7 @@ import org.eclipse.edc.spi.types.domain.edr.EndpointDataReference; import org.eclipse.tractusx.irs.component.Shell; import org.eclipse.tractusx.irs.data.StringMapper; +import org.eclipse.tractusx.irs.edc.client.EdcConfiguration; import org.eclipse.tractusx.irs.edc.client.configuration.JsonLdConfiguration; import org.eclipse.tractusx.irs.edc.client.exceptions.EdcClientException; import org.eclipse.tractusx.irs.edc.client.model.EDRAuthCode; @@ -102,7 +103,7 @@ void setUp(WireMockRuntimeInfo wireMockRuntimeInfo) { final var decentralDigitalTwinRegistryClient = new DecentralDigitalTwinRegistryClient(restTemplate, SHELL_DESCRIPTORS_TEMPLATE, LOOKUP_SHELLS_TEMPLATE); decentralDigitalTwinRegistryService = new DecentralDigitalTwinRegistryService(connectorEndpointsService, - endpointDataForConnectorsService, decentralDigitalTwinRegistryClient); + endpointDataForConnectorsService, decentralDigitalTwinRegistryClient, new EdcConfiguration()); } @Nested