diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5a4826fab4..0524f65da9 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -70,7 +70,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 diff --git a/.github/workflows/dependencies.yaml b/.github/workflows/dependencies.yaml index 1fb53fd4b8..3f6ae4425f 100644 --- a/.github/workflows/dependencies.yaml +++ b/.github/workflows/dependencies.yaml @@ -28,7 +28,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 diff --git a/.github/workflows/docker-image-branch_frontend.yml b/.github/workflows/docker-image-branch_frontend.yml index 45cf8052d9..d19c6e476f 100644 --- a/.github/workflows/docker-image-branch_frontend.yml +++ b/.github/workflows/docker-image-branch_frontend.yml @@ -37,7 +37,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.ref }} @@ -45,7 +45,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER == '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} @@ -65,7 +65,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER != '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} diff --git a/.github/workflows/docker-image-main_backend.yml b/.github/workflows/docker-image-main_backend.yml index b03eba0fbc..83177dd098 100644 --- a/.github/workflows/docker-image-main_backend.yml +++ b/.github/workflows/docker-image-main_backend.yml @@ -38,7 +38,7 @@ jobs: packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: '${{ env.JAVA_VERSION }}' @@ -49,7 +49,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER == '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} @@ -69,7 +69,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER != '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} diff --git a/.github/workflows/docker-image-main_frontend.yml b/.github/workflows/docker-image-main_frontend.yml index f4e6d48a9f..054e6edb03 100644 --- a/.github/workflows/docker-image-main_frontend.yml +++ b/.github/workflows/docker-image-main_frontend.yml @@ -40,13 +40,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Login to GHCR Registry env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER == '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} @@ -66,7 +66,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER != '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ env.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} diff --git a/.github/workflows/docker-image-tag-release.yaml b/.github/workflows/docker-image-tag-release.yaml index e289fb118f..0bff05b2b6 100644 --- a/.github/workflows/docker-image-tag-release.yaml +++ b/.github/workflows/docker-image-tag-release.yaml @@ -43,13 +43,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Login to GHCR Registry env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER == '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} @@ -69,7 +69,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER != '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} @@ -105,7 +105,7 @@ jobs: packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: '${{ env.JAVA_VERSION }}' @@ -123,7 +123,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER == '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} @@ -143,7 +143,7 @@ jobs: env: DOCKER_HUB_USER: ${{ secrets.DOCKER_HUB_USER }} if: env.DOCKER_HUB_USER != '' - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_HUB_USER }} password: ${{ secrets.DOCKER_HUB_TOKEN }} diff --git a/.github/workflows/e2e-tests-xray_frontend.yml b/.github/workflows/e2e-tests-xray_frontend.yml index 5660c897b6..8575e113d4 100644 --- a/.github/workflows/e2e-tests-xray_frontend.yml +++ b/.github/workflows/e2e-tests-xray_frontend.yml @@ -44,7 +44,7 @@ jobs: # Install YARN dependencies, cache them correctly steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Use Node.js 18.x uses: actions/setup-node@v3 @@ -87,7 +87,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download the cypress/e2e folder uses: actions/download-artifact@v3 @@ -101,7 +101,7 @@ jobs: node-version: 18.x - name: Cypress run all tests - uses: cypress-io/github-action@v6.0.0 # use the explicit version number + uses: cypress-io/github-action@v6.5.0 # use the explicit version number with: start: npm start wait-on: "http://localhost:4200" @@ -142,7 +142,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download the cypress/e2e folder uses: actions/download-artifact@v3 @@ -156,7 +156,7 @@ jobs: node-version: 18.x - name: Cypress run all tests - uses: cypress-io/github-action@v6.0.0 # use the explicit version number + uses: cypress-io/github-action@v6.5.0 # use the explicit version number with: start: npm start wait-on: "http://localhost:4200" @@ -192,7 +192,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download the cypress/e2e folder uses: actions/download-artifact@v3 @@ -219,7 +219,7 @@ jobs: run: npx playwright install --with-deps webkit - name: Cypress run all tests - uses: cypress-io/github-action@v6.0.0 # use the explicit version number + uses: cypress-io/github-action@v6.5.0 # use the explicit version number with: start: npm start wait-on: "http://localhost:4200" diff --git a/.github/workflows/eclipse-dash.yml b/.github/workflows/eclipse-dash.yml index a54778f87f..779a529c9f 100644 --- a/.github/workflows/eclipse-dash.yml +++ b/.github/workflows/eclipse-dash.yml @@ -41,7 +41,7 @@ jobs: packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: IP dependency check with eclipse dash tool run: cd frontend && npm run dependencies:generate @@ -56,7 +56,7 @@ jobs: build-backend: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 diff --git a/.github/workflows/helm-chart-release.yaml b/.github/workflows/helm-chart-release.yaml index 386ca48a17..ecd43e84a3 100644 --- a/.github/workflows/helm-chart-release.yaml +++ b/.github/workflows/helm-chart-release.yaml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -61,7 +61,7 @@ jobs: RELEASE_VERSION: "${{needs.Get-helm-charts-versions.outputs.current_version}}" steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/helm-test.yaml b/.github/workflows/helm-test.yaml index 4a32703534..df00ee8c42 100644 --- a/.github/workflows/helm-test.yaml +++ b/.github/workflows/helm-test.yaml @@ -41,7 +41,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/helm-upgrade.yaml b/.github/workflows/helm-upgrade.yaml index 16984190ad..4909d9d2c2 100644 --- a/.github/workflows/helm-upgrade.yaml +++ b/.github/workflows/helm-upgrade.yaml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/jira-publish-release.yaml b/.github/workflows/jira-publish-release.yaml index 04b6b06e34..39ce1b68eb 100644 --- a/.github/workflows/jira-publish-release.yaml +++ b/.github/workflows/jira-publish-release.yaml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set current date as env variable run: echo "NOW=$(date +'%Y-%m-%d')" >> $GITHUB_ENV diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index 7b44295fd6..a1f7a0dbfc 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -44,7 +44,7 @@ jobs: security-events: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: KICS scan uses: checkmarx/kics-github-action@master @@ -55,6 +55,7 @@ jobs: # - docker-compose.yml - used only on local env # - in cypress dir docker related files used only on local env exclude_paths: "docker-compose.yml,cypress/docker-compose.yml,cypress/Dockerfile" + exclude_severities: "info,low" # Fail on HIGH severity results fail_on: high # Disable secrets detection - we use GitGuardian @@ -92,14 +93,14 @@ jobs: security-events: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: KICS scan uses: checkmarx/kics-github-action@master with: # Scanning directory . path: "./tx-backend" - exclude_queries: 9f88c88d-824d-4d9a-b985-e22977046042,8c8261c2-19a9-4ef7-ad37-b8bc7bdd4d85,181bd815-767e-4e95-a24d-bb3c87328e19,00b78adf-b83f-419c-8ed8-c6018441dd3a + exclude_queries: 2ea04bef-c769-409e-9179-ee3a50b5c0ac,6998389e-66b2-473d-8d05-c8d71ac4d04d,a8e859da-4a43-4e7f-94b8-25d6e3bf8e90,d172a060-8569-4412-8045-3560ebd477e8,2e9b6612-8f69-42e0-a5b8-ed17739c2f3a,d172a060-8569-4412-8045-3560ebd477e8,9f88c88d-824d-4d9a-b985-e22977046042,8c8261c2-19a9-4ef7-ad37-b8bc7bdd4d85,181bd815-767e-4e95-a24d-bb3c87328e19,00b78adf-b83f-419c-8ed8-c6018441dd3a enable_comments: true # Fail on HIGH severity results fail_on: high diff --git a/.github/workflows/publish-documentation.yaml b/.github/workflows/publish-documentation.yaml index 8231897098..d58dd4e66e 100644 --- a/.github/workflows/publish-documentation.yaml +++ b/.github/workflows/publish-documentation.yaml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/pull-request_backend.yml b/.github/workflows/pull-request_backend.yml index 988f79ab1b..34a25850c4 100644 --- a/.github/workflows/pull-request_backend.yml +++ b/.github/workflows/pull-request_backend.yml @@ -33,7 +33,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: '${{ env.JAVA_VERSION }}' @@ -84,7 +84,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: @@ -94,7 +94,7 @@ jobs: - name: Login to GHCR Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ${{ env.GHCR_REGISTRY }} username: ${{ github.actor }} @@ -110,7 +110,7 @@ jobs: Dependency-analysis: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: '${{ env.JAVA_VERSION }}' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 73c07e70dc..757f053022 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -11,48 +11,48 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Calculate Helm release version from CHANGELOG run: echo HELM_VERSION=$(cat charts/traceability-foss/CHANGELOG.md | sed -n 's/.*\[\([0-9]\+\.[0-9]\+\.[0-9]\+\)\].*/\1/p' | head -n 1) >> $GITHUB_ENV - name: Update Chart.yaml appVersion - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.appVersion = "${{ github.ref_name }}"' charts/traceability-foss/Chart.yaml - name: Update Chart.yaml version - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.version = "${{ env.HELM_VERSION }}"' charts/traceability-foss/Chart.yaml - name: Update frontend dependency version in Chart.yaml - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.dependencies[0].version = "${{ env.HELM_VERSION }}"' charts/traceability-foss/Chart.yaml - name: Update backend dependency version in Chart.yaml - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.dependencies[1].version = "${{ env.HELM_VERSION }}"' charts/traceability-foss/Chart.yaml - name: Update frontend version in frontend/Chart.yaml - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.version = "${{ env.HELM_VERSION }}"' charts/traceability-foss/charts/frontend/Chart.yaml - name: Update frontend appVersion in frontend/Chart.yaml - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.appVersion = "${{ github.ref_name }}"' charts/traceability-foss/charts/frontend/Chart.yaml - name: Update backend version in backend/Chart.yaml - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.version = "${{ env.HELM_VERSION }}"' charts/traceability-foss/charts/backend/Chart.yaml - name: Update backend appVersion in frontend/Chart.yaml - uses: mikefarah/yq@v4.34.2 + uses: mikefarah/yq@v4.35.1 with: cmd: yq -i eval '.appVersion = "${{ github.ref_name }}"' charts/traceability-foss/charts/backend/Chart.yaml diff --git a/.github/workflows/sonar-scan-backend.yml b/.github/workflows/sonar-scan-backend.yml index aa831aec06..62b7f32568 100644 --- a/.github/workflows/sonar-scan-backend.yml +++ b/.github/workflows/sonar-scan-backend.yml @@ -34,7 +34,7 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis diff --git a/.github/workflows/sonar-scan-frontend.yml b/.github/workflows/sonar-scan-frontend.yml index 7327df1dfd..e10ff7f320 100644 --- a/.github/workflows/sonar-scan-frontend.yml +++ b/.github/workflows/sonar-scan-frontend.yml @@ -33,7 +33,7 @@ jobs: working-directory: frontend steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js 18.x uses: actions/setup-node@v3 with: diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/spotbugs.yml index b9d6c0853d..bd6cb3db70 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/spotbugs.yml @@ -44,7 +44,7 @@ jobs: name: spotbugs-check steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 0ae07fbc2c..617b4e6afc 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -66,13 +66,13 @@ jobs: working-directory: frontend steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build an image from Dockerfile run: docker build -t localhost:5000/traceability-foss:fe_${{ github.sha }} . - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@0.11.2 + uses: aquasecurity/trivy-action@0.12.0 with: image-ref: 'localhost:5000/traceability-foss:fe_${{ github.sha }}' format: "sarif" @@ -94,7 +94,7 @@ jobs: check_sha: ${{ steps.step1.outputs.check_sha }} steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{needs.prepare-env.outputs.check_sha}} - name: Set commit SHA to check @@ -126,12 +126,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{needs.prepare-env.outputs.check_sha}} - name: Run Trivy vulnerability scanner in repo mode - uses: aquasecurity/trivy-action@0.11.2 + uses: aquasecurity/trivy-action@0.12.0 with: scan-type: "config" exit-code: "1" @@ -161,7 +161,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: @@ -177,7 +177,7 @@ jobs: tags: localhost:5000/traceability-foss:trivy - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@0.11.2 + uses: aquasecurity/trivy-action@0.12.0 with: image-ref: localhost:5000/traceability-foss:trivy trivyignores: "./.github/workflows/.trivyignore" @@ -191,3 +191,4 @@ jobs: uses: github/codeql-action/upload-sarif@v2 with: sarif_file: "trivy-results2.sarif" + diff --git a/.github/workflows/unit-test_frontend.yml b/.github/workflows/unit-test_frontend.yml index 7ab37ffe3e..1e231dfbce 100644 --- a/.github/workflows/unit-test_frontend.yml +++ b/.github/workflows/unit-test_frontend.yml @@ -33,7 +33,7 @@ jobs: working-directory: frontend steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Use Node.js 18.x uses: actions/setup-node@v3 with: diff --git a/.github/workflows/update-helm-environment.yml b/.github/workflows/update-helm-environment.yml index d73722cd88..15cbf22cb8 100644 --- a/.github/workflows/update-helm-environment.yml +++ b/.github/workflows/update-helm-environment.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout 🛎 - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # check out all branches fetch-depth: 0 diff --git a/.github/workflows/veracode_backend.yml b/.github/workflows/veracode_backend.yml index 170f8a2e4e..9c03c4ae29 100644 --- a/.github/workflows/veracode_backend.yml +++ b/.github/workflows/veracode_backend.yml @@ -39,7 +39,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: diff --git a/.github/workflows/veracode_frontend.yml b/.github/workflows/veracode_frontend.yml index f86ce05c00..cfb49e6649 100644 --- a/.github/workflows/veracode_frontend.yml +++ b/.github/workflows/veracode_frontend.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: '' diff --git a/.github/workflows/xray-cucumber.yaml b/.github/workflows/xray-cucumber.yaml index 4a37e05913..db04183e59 100644 --- a/.github/workflows/xray-cucumber.yaml +++ b/.github/workflows/xray-cucumber.yaml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index 53132a9e99..aa1ad04926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - NEW API DELETE /api/registry - NEW API GET /api/shelldescriptors - cascading sorting functionality by allowing multiple sort query parameters on APIs +- cascading sorting functionality for Parts and OtherParts tables in FE - NEW API GET /api/assets/as-planned/distinctFilterValues - NEW API GET /api/assets/as-built/distinctFilterValues +- Added Batch 2.0.0 support +- Updated some patch version for used dependencies. + ### Changed - API BREAKING CHANGE: /api/assets changed to /api/assets/as-built @@ -44,6 +48,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Migrated groovy integration tests to SpringBootTests - API GET/api/assets/as-built allow Search criteria to be provided - API GET/api/assets/as-planned allow Search criteria to be provided +- Upgraded aquasecurity/trivy-action from 0.11.2 to 0.12.0 +- Upgraded actions/checkout from 3 to 4 +- Upgraded maven-checkstyle-plugin from 3.2.1 to 3.3.0 +- Upgraded swagger-annotations from 1.6.10 to 1.6.11 +- Upgraded spring-boot-maven-plugin from 3.0.2 to 3.1.3 +- Upgraded shedlock.version from 5.5.0 to 5.7.0 +- Upgraded mikefarah/yq from 4.34.2 to 4.35.1 +- Upgraded snakeyaml from 2.0 to 2.2 +- Upgraded docker/login-action from 2 to 3 +- Upgraded cypress-io/github-action 6.0.0 to 6.5.0 ### Removed diff --git a/DEPENDENCIES_BACKEND b/DEPENDENCIES_BACKEND index f5ace21400..cf2805c6a1 100644 --- a/DEPENDENCIES_BACKEND +++ b/DEPENDENCIES_BACKEND @@ -1,5 +1,5 @@ -maven/mavencentral/ch.qos.logback/logback-classic/1.4.8, EPL-1.0 OR LGPL-2.1-only, approved, #3435 -maven/mavencentral/ch.qos.logback/logback-core/1.4.8, EPL-1.0 OR LGPL-2.1-only, approved, #3373 +maven/mavencentral/ch.qos.logback/logback-classic/1.4.11, EPL-1.0 OR LGPL-2.1-only, approved, #3435 +maven/mavencentral/ch.qos.logback/logback-core/1.4.11, EPL-1.0 OR LGPL-2.1-only, approved, #3373 maven/mavencentral/com.apicatalog/titanium-json-ld/1.3.2, Apache-2.0, approved, #8912 maven/mavencentral/com.auth0/java-jwt/4.4.0, MIT, approved, #8459 maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.15.2, Apache-2.0, approved, #7947 @@ -60,13 +60,14 @@ maven/mavencentral/io.github.resilience4j/resilience4j-spring/2.0.2, Apache-2.0, maven/mavencentral/io.github.resilience4j/resilience4j-spring/2.1.0, Apache-2.0, approved, #10169 maven/mavencentral/io.github.resilience4j/resilience4j-timelimiter/2.0.2, Apache-2.0, approved, clearlydefined maven/mavencentral/io.github.resilience4j/resilience4j-timelimiter/2.1.0, Apache-2.0, approved, #10166 -maven/mavencentral/io.micrometer/micrometer-commons/1.11.2, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 -maven/mavencentral/io.micrometer/micrometer-core/1.11.2, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 -maven/mavencentral/io.micrometer/micrometer-observation/1.11.2, Apache-2.0, approved, #9242 +maven/mavencentral/io.micrometer/micrometer-commons/1.11.3, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9243 +maven/mavencentral/io.micrometer/micrometer-core/1.11.3, Apache-2.0 AND (Apache-2.0 AND MIT), approved, #9238 +maven/mavencentral/io.micrometer/micrometer-observation/1.11.3, Apache-2.0, approved, #9242 maven/mavencentral/io.swagger.core.v3/swagger-annotations-jakarta/2.2.8, Apache-2.0, approved, #5947 maven/mavencentral/io.swagger.core.v3/swagger-core-jakarta/2.2.8, Apache-2.0, approved, #5929 maven/mavencentral/io.swagger.core.v3/swagger-models-jakarta/2.2.8, Apache-2.0, approved, #5919 maven/mavencentral/io.swagger/swagger-annotations/1.6.10, Apache-2.0, approved, #3792 +maven/mavencentral/io.swagger/swagger-annotations/1.6.11, Apache-2.0, approved, #3792 maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.0, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.activation/jakarta.activation-api/2.1.2, EPL-2.0 OR BSD-3-Clause OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jaf maven/mavencentral/jakarta.annotation/jakarta.annotation-api/2.1.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca @@ -77,9 +78,9 @@ maven/mavencentral/jakarta.transaction/jakarta.transaction-api/2.0.1, EPL-2.0 OR maven/mavencentral/jakarta.validation/jakarta.validation-api/3.0.2, Apache-2.0, approved, clearlydefined maven/mavencentral/jakarta.ws.rs/jakarta.ws.rs-api/3.1.0, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.rest maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/4.0.0, BSD-3-Clause, approved, ee4j.jaxb -maven/mavencentral/net.javacrumbs.shedlock/shedlock-core/5.5.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/net.javacrumbs.shedlock/shedlock-provider-jdbc-template/5.5.0, Apache-2.0, approved, clearlydefined -maven/mavencentral/net.javacrumbs.shedlock/shedlock-spring/5.5.0, Apache-2.0, approved, #10167 +maven/mavencentral/net.javacrumbs.shedlock/shedlock-core/5.7.0, Apache-2.0, approved, #10582 +maven/mavencentral/net.javacrumbs.shedlock/shedlock-provider-jdbc-template/5.7.0, Apache-2.0, approved, #10584 +maven/mavencentral/net.javacrumbs.shedlock/shedlock-spring/5.7.0, Apache-2.0, approved, #10583 maven/mavencentral/net.minidev/accessors-smart/2.4.9, Apache-2.0, approved, #7515 maven/mavencentral/net.minidev/json-smart/2.4.10, Apache-2.0, approved, #3288 maven/mavencentral/org.antlr/antlr4-runtime/4.10.1, BSD-3-Clause AND LicenseRef-Public-domain AND MIT AND LicenseRef-Unicode-TOU, approved, #7065 @@ -89,11 +90,11 @@ maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved maven/mavencentral/org.apache.commons/commons-text/1.10.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.logging.log4j/log4j-api/2.20.0, Apache-2.0, approved, clearlydefined maven/mavencentral/org.apache.logging.log4j/log4j-to-slf4j/2.20.0, Apache-2.0, approved, #8799 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.11, Apache-2.0 AND (EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND (CDDL-1.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND W3C AND CC0-1.0, approved, #5949 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.11, Apache-2.0, approved, #6997 -maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.11, Apache-2.0, approved, #7920 -maven/mavencentral/org.aspectj/aspectjweaver/1.9.19, EPL-1.0, approved, tools.aspectj -maven/mavencentral/org.attoparser/attoparser/2.0.6.RELEASE, Apache-2.0, approved, CQ18900 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-core/10.1.12, Apache-2.0 AND (EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND (CDDL-1.0 OR GPL-2.0-only WITH Classpath-exception-2.0) AND W3C AND CC0-1.0, approved, #5949 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/10.1.12, Apache-2.0, approved, #6997 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-websocket/10.1.12, Apache-2.0, approved, #7920 +maven/mavencentral/org.aspectj/aspectjweaver/1.9.20, EPL-1.0, approved, tools.aspectj +maven/mavencentral/org.attoparser/attoparser/2.0.7.RELEASE, Apache-2.0, approved, CQ18900 maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.69, MIT, approved, clearlydefined maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.69, MIT, approved, clearlydefined maven/mavencentral/org.bouncycastle/bcutil-jdk15on/1.69, MIT, approved, clearlydefined @@ -144,7 +145,7 @@ maven/mavencentral/org.eclipse.tractusx.traceability/tx-backend/0.0.1-SNAPSHOT, maven/mavencentral/org.eclipse.tractusx.traceability/tx-models/0.0.1-SNAPSHOT, Apache-2.0, approved, automotive.tractusx maven/mavencentral/org.flywaydb/flyway-core/9.16.3, Apache-2.0, approved, #7935 maven/mavencentral/org.glassfish/jakarta.json/2.0.1, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.jsonp -maven/mavencentral/org.hibernate.orm/hibernate-core/6.2.6.Final, LGPL-2.1-only AND Apache-2.0 AND MIT AND CC-PDDC AND (EPL-2.0 OR BSD-3-Clause), approved, #9121 +maven/mavencentral/org.hibernate.orm/hibernate-core/6.2.7.Final, LGPL-2.1-only AND Apache-2.0 AND MIT AND CC-PDDC AND (EPL-2.0 OR BSD-3-Clause), approved, #9121 maven/mavencentral/org.hibernate.validator/hibernate-validator/8.0.1.Final, Apache-2.0, approved, clearlydefined maven/mavencentral/org.jboss.logging/jboss-logging/3.5.0.Final, Apache-2.0, approved, #9471 maven/mavencentral/org.jboss.logging/jboss-logging/3.5.3.Final, Apache-2.0, approved, #9471 @@ -167,56 +168,57 @@ maven/mavencentral/org.slf4j/slf4j-api/2.0.7, MIT, approved, #5915 maven/mavencentral/org.springdoc/springdoc-openapi-starter-common/2.0.4, Apache-2.0, approved, #5920 maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-api/2.0.4, Apache-2.0, approved, #5950 maven/mavencentral/org.springdoc/springdoc-openapi-starter-webmvc-ui/2.0.4, Apache-2.0, approved, #5923 -maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/3.1.2, Apache-2.0, approved, #9348 -maven/mavencentral/org.springframework.boot/spring-boot-actuator/3.1.2, Apache-2.0, approved, #9342 -maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.2, Apache-2.0, approved, #9341 -maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/3.1.2, Apache-2.0, approved, #9344 -maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.2, Apache-2.0, approved, #9338 -maven/mavencentral/org.springframework.boot/spring-boot-starter-data-jpa/3.1.2, Apache-2.0, approved, #9733 -maven/mavencentral/org.springframework.boot/spring-boot-starter-jdbc/3.1.2, Apache-2.0, approved, #9737 -maven/mavencentral/org.springframework.boot/spring-boot-starter-json/3.1.2, Apache-2.0, approved, #9336 -maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/3.1.2, Apache-2.0, approved, #9343 -maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-client/3.1.2, Apache-2.0, approved, #8806 -maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-resource-server/3.1.2, Apache-2.0, approved, #8804 -maven/mavencentral/org.springframework.boot/spring-boot-starter-security/3.1.2, Apache-2.0, approved, #9337 -maven/mavencentral/org.springframework.boot/spring-boot-starter-thymeleaf/3.1.2, Apache-2.0, approved, #10092 -maven/mavencentral/org.springframework.boot/spring-boot-starter-tomcat/3.1.2, Apache-2.0, approved, #9351 -maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/3.1.2, Apache-2.0, approved, #9335 -maven/mavencentral/org.springframework.boot/spring-boot-starter-web/3.1.2, Apache-2.0, approved, #9347 -maven/mavencentral/org.springframework.boot/spring-boot-starter/3.1.2, Apache-2.0, approved, #9349 -maven/mavencentral/org.springframework.boot/spring-boot/3.1.2, Apache-2.0, approved, #9352 +maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/3.1.3, Apache-2.0, approved, #9348 +maven/mavencentral/org.springframework.boot/spring-boot-actuator/3.1.3, Apache-2.0, approved, #9342 +maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/3.1.3, Apache-2.0, approved, #9341 +maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/3.1.3, Apache-2.0, approved, #9344 +maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/3.1.3, Apache-2.0, approved, #9338 +maven/mavencentral/org.springframework.boot/spring-boot-starter-data-jpa/3.1.3, Apache-2.0, approved, #9733 +maven/mavencentral/org.springframework.boot/spring-boot-starter-jdbc/3.1.3, Apache-2.0, approved, #9737 +maven/mavencentral/org.springframework.boot/spring-boot-starter-json/3.1.3, Apache-2.0, approved, #9336 +maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/3.1.3, Apache-2.0, approved, #9343 +maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-client/3.1.3, Apache-2.0, approved, #8806 +maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-resource-server/3.1.3, Apache-2.0, approved, #8804 +maven/mavencentral/org.springframework.boot/spring-boot-starter-security/3.1.3, Apache-2.0, approved, #9337 +maven/mavencentral/org.springframework.boot/spring-boot-starter-thymeleaf/3.1.3, Apache-2.0, approved, #10092 +maven/mavencentral/org.springframework.boot/spring-boot-starter-tomcat/3.1.3, Apache-2.0, approved, #9351 +maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/3.1.3, Apache-2.0, approved, #9335 +maven/mavencentral/org.springframework.boot/spring-boot-starter-web/3.1.3, Apache-2.0, approved, #9347 +maven/mavencentral/org.springframework.boot/spring-boot-starter/3.1.3, Apache-2.0, approved, #9349 +maven/mavencentral/org.springframework.boot/spring-boot/3.1.3, Apache-2.0, approved, #9352 maven/mavencentral/org.springframework.cloud/spring-cloud-commons/4.0.3, Apache-2.0, approved, #7292 maven/mavencentral/org.springframework.cloud/spring-cloud-context/4.0.3, Apache-2.0, approved, #7306 maven/mavencentral/org.springframework.cloud/spring-cloud-openfeign-core/4.0.3, Apache-2.0, approved, #7305 maven/mavencentral/org.springframework.cloud/spring-cloud-starter-openfeign/4.0.3, Apache-2.0, approved, #7302 maven/mavencentral/org.springframework.cloud/spring-cloud-starter/4.0.3, Apache-2.0, approved, #7299 -maven/mavencentral/org.springframework.data/spring-data-commons/3.1.2, Apache-2.0, approved, #8805 -maven/mavencentral/org.springframework.data/spring-data-jpa/3.1.2, Apache-2.0, approved, #9120 -maven/mavencentral/org.springframework.security/spring-security-config/6.0.5, Apache-2.0, approved, #7338 -maven/mavencentral/org.springframework.security/spring-security-core/6.1.2, Apache-2.0, approved, #9801 -maven/mavencentral/org.springframework.security/spring-security-crypto/6.1.2, Apache-2.0 AND ISC, approved, #9735 -maven/mavencentral/org.springframework.security/spring-security-oauth2-client/6.1.2, Apache-2.0, approved, #9740 -maven/mavencentral/org.springframework.security/spring-security-oauth2-core/6.1.2, Apache-2.0, approved, #9741 -maven/mavencentral/org.springframework.security/spring-security-oauth2-jose/6.1.2, Apache-2.0, approved, #9345 -maven/mavencentral/org.springframework.security/spring-security-oauth2-resource-server/6.1.2, Apache-2.0, approved, #8798 +maven/mavencentral/org.springframework.data/spring-data-commons/3.1.3, Apache-2.0, approved, #8805 +maven/mavencentral/org.springframework.data/spring-data-jpa/3.1.3, Apache-2.0, approved, #9120 +maven/mavencentral/org.springframework.security/spring-security-config/6.1.3, Apache-2.0, approved, #9736 +maven/mavencentral/org.springframework.security/spring-security-core/6.1.3, Apache-2.0, approved, #9801 +maven/mavencentral/org.springframework.security/spring-security-crypto/6.1.3, Apache-2.0 AND ISC, approved, #9735 +maven/mavencentral/org.springframework.security/spring-security-oauth2-client/6.1.3, Apache-2.0, approved, #9740 +maven/mavencentral/org.springframework.security/spring-security-oauth2-core/6.1.3, Apache-2.0, approved, #9741 +maven/mavencentral/org.springframework.security/spring-security-oauth2-jose/6.1.3, Apache-2.0, approved, #9345 +maven/mavencentral/org.springframework.security/spring-security-oauth2-resource-server/6.1.3, Apache-2.0, approved, #8798 maven/mavencentral/org.springframework.security/spring-security-rsa/1.0.11.RELEASE, Apache-2.0, approved, CQ20647 -maven/mavencentral/org.springframework.security/spring-security-web/6.1.2, Apache-2.0, approved, #9800 +maven/mavencentral/org.springframework.security/spring-security-web/6.1.3, Apache-2.0, approved, #9800 maven/mavencentral/org.springframework/spring-aop/6.0.11, Apache-2.0, approved, #5940 maven/mavencentral/org.springframework/spring-aspects/6.0.11, Apache-2.0, approved, #5930 maven/mavencentral/org.springframework/spring-beans/6.0.11, Apache-2.0, approved, #5937 maven/mavencentral/org.springframework/spring-context/6.0.11, Apache-2.0, approved, #5936 -maven/mavencentral/org.springframework/spring-core/6.0.11, Apache-2.0 AND BSD-3-Clause, approved, #5948 +maven/mavencentral/org.springframework/spring-core/6.0.12, Apache-2.0 AND BSD-3-Clause, approved, #5948 maven/mavencentral/org.springframework/spring-expression/6.0.11, Apache-2.0, approved, #3284 +maven/mavencentral/org.springframework/spring-expression/6.0.12, Apache-2.0, approved, #3284 maven/mavencentral/org.springframework/spring-jcl/6.0.11, Apache-2.0, approved, #3283 -maven/mavencentral/org.springframework/spring-jdbc/6.0.10, Apache-2.0, approved, #5924 +maven/mavencentral/org.springframework/spring-jcl/6.0.12, Apache-2.0, approved, #3283 maven/mavencentral/org.springframework/spring-jdbc/6.0.11, Apache-2.0, approved, #5924 maven/mavencentral/org.springframework/spring-orm/6.0.11, Apache-2.0, approved, #5925 maven/mavencentral/org.springframework/spring-tx/6.0.11, Apache-2.0, approved, #5926 maven/mavencentral/org.springframework/spring-web/6.0.11, Apache-2.0, approved, #5942 maven/mavencentral/org.springframework/spring-webmvc/6.0.11, Apache-2.0, approved, #5944 -maven/mavencentral/org.thymeleaf/thymeleaf-spring6/3.1.1.RELEASE, Apache-2.0, approved, clearlydefined -maven/mavencentral/org.thymeleaf/thymeleaf/3.1.1.RELEASE, Apache-2.0, approved, CQ23960 +maven/mavencentral/org.thymeleaf/thymeleaf-spring6/3.1.2.RELEASE, Apache-2.0, approved, #10581 +maven/mavencentral/org.thymeleaf/thymeleaf/3.1.2.RELEASE, Apache-2.0, approved, CQ23960 maven/mavencentral/org.unbescape/unbescape/1.1.6.RELEASE, Apache-2.0, approved, CQ18904 maven/mavencentral/org.webjars/swagger-ui/4.18.1, Apache-2.0, approved, #7850 maven/mavencentral/org.webjars/webjars-locator-core/0.52, MIT, approved, clearlydefined -maven/mavencentral/org.yaml/snakeyaml/2.1, Apache-2.0, approved, #9847 +maven/mavencentral/org.yaml/snakeyaml/2.2, Apache-2.0 AND (Apache-2.0 OR BSD-3-Clause OR EPL-1.0 OR GPL-2.0-or-later OR LGPL-2.1-or-later), approved, #10232 diff --git a/charts/traceability-foss/CHANGELOG.md b/charts/traceability-foss/CHANGELOG.md index 5a72e1531f..10fbd7297c 100644 --- a/charts/traceability-foss/CHANGELOG.md +++ b/charts/traceability-foss/CHANGELOG.md @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] ### Added +- Init pods on backend startup to check dependant services readiness status ( irs, edc-controlplane ) ### Changed diff --git a/charts/traceability-foss/charts/backend/templates/deployment.yaml b/charts/traceability-foss/charts/backend/templates/deployment.yaml index 32ad409e95..7170c3b973 100644 --- a/charts/traceability-foss/charts/backend/templates/deployment.yaml +++ b/charts/traceability-foss/charts/backend/templates/deployment.yaml @@ -56,6 +56,15 @@ spec: serviceAccountName: {{ include "traceability-foss-backend.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} + {{- if .Values.dependencies.enabled }} + initContainers: + - name: wait-for-edc-controlplane + image: appropriate/curl:latest + command: [ 'sh', '-c', 'until curl -s http://{{ tpl (.Values.dependencies.edc | default "") . }}-controlplane:8080/api/check/liveness; do echo waiting for other pod; sleep 2; done;' ] + - name: wait-for-irs + image: appropriate/curl:latest + command: [ 'sh', '-c', 'until curl -s http://{{ tpl (.Values.dependencies.irs | default "") . }}:8080/actuator/health/liveness; do echo waiting for other pod; sleep 2; done;' ] + {{- end }} containers: - name: {{ .Chart.Name }} securityContext: diff --git a/charts/traceability-foss/charts/backend/values.yaml b/charts/traceability-foss/charts/backend/values.yaml index 322c1e98e8..b2f5823ca6 100644 --- a/charts/traceability-foss/charts/backend/values.yaml +++ b/charts/traceability-foss/charts/backend/values.yaml @@ -170,3 +170,8 @@ portal: config: allowedCorsOriginFirst: "https://replace.me" allowedCorsOriginSecond: "https://replace.me" + +dependencies: + enabled: false + irs: CHANGEME # + edc: CHANGEME #-controlplane diff --git a/charts/traceability-foss/values.yaml b/charts/traceability-foss/values.yaml index 47cde04ce8..f94ae6f9cb 100644 --- a/charts/traceability-foss/values.yaml +++ b/charts/traceability-foss/values.yaml @@ -305,6 +305,12 @@ backend: allowedCorsOriginFirst: "https://replace.me" allowedCorsOriginSecond: "https://replace.me" + # required for init containers checking for dependant pod readiness before starting up backend + dependencies: + enabled: false # enable dependency check init containers + irs: "CHANGEME" # + edc: "CHANGEME" # --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC +python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.9.json -s https://tracex-submodel-server.dev.demo.catena-x.net -edc https://trace-x-edc.dev.demo.catena-x.net -a https://trace-x-registry.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC ``` Sample invocation (TEST) ``` -python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.8.json -s https://tracex-submodel-server-test.dev.demo.catena-x.net -edc https://trace-x-test-edc.dev.demo.catena-x.net -a https://trace-x-registry-test.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-test-edc-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC +python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.9.json -s https://tracex-submodel-server-test.dev.demo.catena-x.net -edc https://trace-x-test-edc.dev.demo.catena-x.net -a https://trace-x-registry-test.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-test-edc-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC ``` Sample invocation (E2E A) ``` -python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.8.json -s https://tracex-submodel-server-e2e-a.dev.demo.catena-x.net -edc https://trace-x-edc-e2e-a.dev.demo.catena-x.net -a https://trace-x-registry-e2e-a.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-e2e-a-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC +python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.9.json -s https://tracex-submodel-server-e2e-a.dev.demo.catena-x.net -edc https://trace-x-edc-e2e-a.dev.demo.catena-x.net -a https://trace-x-registry-e2e-a.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-e2e-a-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC ``` Sample invocation (E2E B) ``` -python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.8.json -s https://tracex-submodel-server-e2e-b.dev.demo.catena-x.net -edc https://trace-x-edc-e2e-b.dev.demo.catena-x.net -a https://trace-x-registry-e2e-b.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-e2e-b-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC +python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.9.json -s https://tracex-submodel-server-e2e-b.dev.demo.catena-x.net -edc https://trace-x-edc-e2e-b.dev.demo.catena-x.net -a https://trace-x-registry-e2e-b.dev.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-e2e-b-dataplane.dev.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC ``` Sample invocation (INT A) ``` -python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.8.json -s https://tracex-submodel-server-int-a.int.demo.catena-x.net -edc https://trace-x-edc-int-a.int.demo.catena-x.net -a https://trace-x-registry-int-a.int.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-int-a-dataplane.int.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC BPNL00000003AZQP BPNL00000003CSGV +python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.9.json -s https://tracex-submodel-server-int-a.int.demo.catena-x.net -edc https://trace-x-edc-int-a.int.demo.catena-x.net -a https://trace-x-registry-int-a.int.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-int-a-dataplane.int.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC BPNL00000003AZQP BPNL00000003CSGV ``` Sample invocation (INT B) ``` -python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.8.json -s https://tracex-submodel-server-int-b.int.demo.catena-x.net -edc https://trace-x-edc-int-b.int.demo.catena-x.net -a https://trace-x-registry-int-b.int.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-int-b-dataplane.int.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC BPNL00000003AZQP BPNL00000003CSGV +python transform-and-upload.py -f CX_Testdata_MessagingTest_v0.0.9.json -s https://tracex-submodel-server-int-b.int.demo.catena-x.net -edc https://trace-x-edc-int-b.int.demo.catena-x.net -a https://trace-x-registry-int-b.int.demo.catena-x.net/semantics/registry/api/v3.0 -d https://trace-x-edc-int-b-dataplane.int.demo.catena-x.net -p id-3.0-trace -k --aas3 --allowedBPNs BPNL00000003CML1 BPNL00000003CNKC BPNL00000003AZQP BPNL00000003CSGV ``` where: diff --git a/frontend/src/app/modules/page/alerts/core/alert-detail.facade.spec.ts b/frontend/src/app/modules/page/alerts/core/alert-detail.facade.spec.ts index 974c92957f..026ff6669c 100644 --- a/frontend/src/app/modules/page/alerts/core/alert-detail.facade.spec.ts +++ b/frontend/src/app/modules/page/alerts/core/alert-detail.facade.spec.ts @@ -22,6 +22,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { AlertDetailFacade } from '@page/alerts/core/alert-detail.facade'; import { AlertDetailState } from '@page/alerts/core/alert-detail.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { FormatPartlistSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; @@ -74,7 +75,7 @@ describe('AlertDetailFacade', () => { beforeEach(function() { - part = PartsAssembler.assemblePart(MOCK_part_1); + part = PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT); this.spy = spyOn(partService, 'sortParts').and.callFake(() => [ part ]); }); diff --git a/frontend/src/app/modules/page/investigations/core/investigation-detail.facade.spec.ts b/frontend/src/app/modules/page/investigations/core/investigation-detail.facade.spec.ts index 8d48b7168c..3e0a63cfe6 100644 --- a/frontend/src/app/modules/page/investigations/core/investigation-detail.facade.spec.ts +++ b/frontend/src/app/modules/page/investigations/core/investigation-detail.facade.spec.ts @@ -21,12 +21,13 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; import { InvestigationDetailFacade } from '@page/investigations/core/investigation-detail.facade'; import { InvestigationDetailState } from '@page/investigations/core/investigation-detail.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { FormatPartlistSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; import { PartsService } from '@shared/service/parts.service'; import { KeycloakService } from 'keycloak-angular'; -import { MOCK_part_1 } from '../../../../mocks/services/parts-mock/partsAsPlanned/partsAsPlanned.test.model'; +import { MOCK_part_1 } from '../../../../mocks/services/parts-mock/partsAsBuilt/partsAsBuilt.test.model'; describe('InvestigationDetailFacade', () => { let investigationDetailFacade: InvestigationDetailFacade; @@ -73,7 +74,7 @@ describe('InvestigationDetailFacade', () => { beforeEach(function() { - part = PartsAssembler.assemblePart(MOCK_part_1); + part = PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT); this.spy = spyOn(partService, 'sortParts').and.callFake(() => [ part ]); }); diff --git a/frontend/src/app/modules/page/other-parts/core/other-parts.facade.spec.ts b/frontend/src/app/modules/page/other-parts/core/other-parts.facade.spec.ts index 44544d61b8..21af986891 100644 --- a/frontend/src/app/modules/page/other-parts/core/other-parts.facade.spec.ts +++ b/frontend/src/app/modules/page/other-parts/core/other-parts.facade.spec.ts @@ -20,28 +20,32 @@ import { OtherPartsFacade } from '@page/other-parts/core/other-parts.facade'; import { OtherPartsService } from '@page/other-parts/core/other-parts.service'; import { OtherPartsState } from '@page/other-parts/core/other-parts.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { PartsService } from '@shared/service/parts.service'; import { waitFor } from '@testing-library/angular'; import { BehaviorSubject, firstValueFrom, of } from 'rxjs'; import { map } from 'rxjs/operators'; -import { mockAssetList, mockAssets } from '../../../../mocks/services/parts-mock/partsAsPlanned/partsAsPlanned.test.model'; +import { + mockAssetList, + mockAssets, +} from '../../../../mocks/services/parts-mock/partsAsPlanned/partsAsPlanned.test.model'; describe('OtherPartsFacade', () => { let otherPartsFacade: OtherPartsFacade, otherPartsState: OtherPartsState, partsServiceMok: PartsService, otherPartsServiceMock: OtherPartsService; beforeEach(() => { partsServiceMok = { - getPart: id => new BehaviorSubject(mockAssetList[id]).pipe(map(part => PartsAssembler.assemblePart(part))), + getPart: id => new BehaviorSubject(mockAssetList[id]).pipe(map(part => PartsAssembler.assemblePart(part, MainAspectType.AS_BUILT))), getPartsAsBuilt: (_page, _pageSize, _sorting) => - of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts))), + of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_BUILT))), getPartsAsPlanned: (_page, _pageSize, _sorting) => - of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts))), + of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_PLANNED))), } as PartsService; otherPartsServiceMock = { - getSupplierParts: (_page, _pageSize, _sorting) => - of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts))), + getOtherParts: (_page, _pageSize, _sorting, _owner) => + of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_BUILT))), } as OtherPartsService; otherPartsState = new OtherPartsState(); @@ -51,7 +55,7 @@ describe('OtherPartsFacade', () => { describe('setActiveInvestigationForParts', () => { it('should set parts if request is successful', async () => { - const otherParts = PartsAssembler.assembleOtherParts(mockAssets); + const otherParts = PartsAssembler.assembleOtherParts(mockAssets, MainAspectType.AS_BUILT); otherPartsState.supplierParts = { data: otherParts}; otherPartsFacade.setActiveInvestigationForParts(otherParts.content); @@ -72,7 +76,7 @@ describe('OtherPartsFacade', () => { it('should not set parts if no data in state', async () => { - const otherParts = PartsAssembler.assembleOtherParts(mockAssets); + const otherParts = PartsAssembler.assembleOtherParts(mockAssets, MainAspectType.AS_BUILT); otherPartsFacade.setActiveInvestigationForParts(otherParts.content); const parts = await firstValueFrom(otherPartsState.supplierParts$); diff --git a/frontend/src/app/modules/page/other-parts/core/other-parts.facade.ts b/frontend/src/app/modules/page/other-parts/core/other-parts.facade.ts index acda21a2aa..3f55e962d2 100644 --- a/frontend/src/app/modules/page/other-parts/core/other-parts.facade.ts +++ b/frontend/src/app/modules/page/other-parts/core/other-parts.facade.ts @@ -23,6 +23,7 @@ import { Injectable } from '@angular/core'; import { Pagination } from '@core/model/pagination.model'; import { OtherPartsService } from '@page/other-parts/core/other-parts.service'; import { OtherPartsState } from '@page/other-parts/core/other-parts.state'; +import { Owner } from '@page/parts/model/owner.enum'; import { Part } from '@page/parts/model/parts.model'; import { TableHeaderSort } from '@shared/components/table/table.model'; import { View } from '@shared/model/view.model'; @@ -48,18 +49,18 @@ export class OtherPartsFacade { public get supplierParts$(): Observable>> { return this.otherPartsState.supplierParts$; } - - public setCustomerParts(page = 0, pageSize = 50, sorting: TableHeaderSort = null): void { +// TODO: remove OtherPartsService and integrate in PartService + public setCustomerParts(page = 0, pageSize = 50, sorting: TableHeaderSort[] = []): void { this.customerPartsSubscription?.unsubscribe(); - this.customerPartsSubscription = this.otherPartsService.getCustomerParts(page, pageSize, sorting).subscribe({ + this.customerPartsSubscription = this.otherPartsService.getOtherParts(page, pageSize, sorting, Owner.CUSTOMER).subscribe({ next: data => (this.otherPartsState.customerParts = { data }), error: error => (this.otherPartsState.customerParts = { error }), }); } - public setSupplierParts(page = 0, pageSize = 50, sorting: TableHeaderSort = null): void { + public setSupplierParts(page = 0, pageSize = 50, sorting: TableHeaderSort[] = []): void { this.supplierPartsSubscription?.unsubscribe(); - this.supplierPartsSubscription = this.otherPartsService.getSupplierParts(page, pageSize, sorting).subscribe({ + this.supplierPartsSubscription = this.otherPartsService.getOtherParts(page, pageSize, sorting, Owner.SUPPLIER).subscribe({ next: data => (this.otherPartsState.supplierParts = { data }), error: error => (this.otherPartsState.supplierParts = { error }), }); diff --git a/frontend/src/app/modules/page/other-parts/core/other-parts.service.ts b/frontend/src/app/modules/page/other-parts/core/other-parts.service.ts index 4013f027c8..b78ce4284c 100644 --- a/frontend/src/app/modules/page/other-parts/core/other-parts.service.ts +++ b/frontend/src/app/modules/page/other-parts/core/other-parts.service.ts @@ -24,6 +24,8 @@ import { Injectable } from '@angular/core'; import { ApiService } from '@core/api/api.service'; import { Pagination } from '@core/model/pagination.model'; import { environment } from '@env'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; +import { Owner } from '@page/parts/model/owner.enum'; import { Part, PartsResponse } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { TableHeaderSort } from '@shared/components/table/table.model'; @@ -36,29 +38,20 @@ export class OtherPartsService { constructor(private readonly apiService: ApiService) {} - public getSupplierParts(page: number, pageSize: number, sorting: TableHeaderSort): Observable> { - const sort = PartsAssembler.mapSortToApiSort(sorting); - const params = new HttpParams() - .set('page', page) - .set('size', pageSize) - .set('sort', sort) - .set('owner', 'SUPPLIER'); - return this.apiService - .getBy(`${this.url}/assets/as-built`, params) - .pipe(map(parts => PartsAssembler.assembleOtherParts(parts))); - } - - public getCustomerParts(page: number, pageSize: number, sorting: TableHeaderSort): Observable> { - const sort = PartsAssembler.mapSortToApiSort(sorting); - const params = new HttpParams() + public getOtherParts(page: number, pageSize: number, sorting: TableHeaderSort[], owner: Owner): Observable> { + let sort = sorting.map(sortingItem => PartsAssembler.mapSortToApiSort(sortingItem)); + let params = new HttpParams() .set('page', page) .set('size', pageSize) - .set('sort', sort) - .set('owner', 'CUSTOMER'); + .set('owner', owner); + + sort.forEach(sortingItem => { + params = params.append('sort', sortingItem); + }) return this.apiService .getBy(`${this.url}/assets/as-built`, params) - .pipe(map(parts => PartsAssembler.assembleOtherParts(parts))); + .pipe(map(parts => PartsAssembler.assembleOtherParts(parts, MainAspectType.AS_BUILT))); } } diff --git a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html index 6344700e2a..d493b15ea3 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html +++ b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.html @@ -35,6 +35,7 @@ [labelId]="customContext.labelId" (selected)="onSelectItem($event)" (configChanged)="onTableConfigChange($event)" + [multiSortList]="tableCustomerSortList" > diff --git a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.spec.ts b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.spec.ts index b6d1a4f6ea..c1ea18c7af 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.spec.ts +++ b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.spec.ts @@ -20,7 +20,7 @@ import { OtherPartsState } from '@page/other-parts/core/other-parts.state'; import { OtherPartsModule } from '@page/other-parts/other-parts.module'; import { PartsState } from '@page/parts/core/parts.state'; -import { screen, waitFor } from '@testing-library/angular'; +import { fireEvent, screen, waitFor } from '@testing-library/angular'; import { renderComponent } from '@tests/test-render.utils'; import { CustomerPartsComponent } from './customer-parts.component'; @@ -29,11 +29,11 @@ describe('CustomerPartsComponent', () => { let otherPartsState: OtherPartsState; beforeEach(() => (otherPartsState = new OtherPartsState())); - const renderCustomerParts = ({ roles = [] } = {}) => + const renderCustomerParts = () => renderComponent(CustomerPartsComponent, { imports: [OtherPartsModule], providers: [{ provide: OtherPartsState, useFactory: () => otherPartsState }, { provide: PartsState }], - roles, + roles: ['admin', 'wip'], }); it('should render part table', async () => { @@ -45,9 +45,73 @@ describe('CustomerPartsComponent', () => { it('should render table and display correct amount of rows', async () => { await renderCustomerParts(); - const tableElement = await waitFor(() => screen.getByTestId('table-component--test-id')); expect(tableElement).toBeInTheDocument(); expect(tableElement.children[1].childElementCount).toEqual(5); }); + + it('sort customer parts after name column', async () => { + const {fixture} = await renderCustomerParts(); + const customerPartsComponent = fixture.componentInstance; + + let nameHeader = await screen.findByText('table.column.name'); + fireEvent.click(nameHeader); + + expect(customerPartsComponent['tableCustomerSortList']).toEqual([["name", "asc"]]); + + }); + + it('should multisort after column name and semanticModelId', async () => { + const {fixture} = await renderCustomerParts(); + const customerPartsComponent = fixture.componentInstance; + + let nameHeader = await screen.findByText('table.column.name'); + fireEvent.click(nameHeader); + let semanticModelIdHeader = await screen.findByText('table.column.semanticModelId') + + await waitFor(() => {fireEvent.keyDown(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + expect(customerPartsComponent['ctrlKeyState']).toBeTruthy(); + await waitFor(() => { + fireEvent.click(semanticModelIdHeader) + }); + + await waitFor(() => {fireEvent.keyUp(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => {fireEvent.click(semanticModelIdHeader)}); + expect(customerPartsComponent['tableCustomerSortList']).toEqual([["name", "asc"], ["semanticModelId", "desc"]]); + }); + + it('should reset sorting on third click', async () => { + const {fixture} = await renderCustomerParts(); + const customerPartsComponent = fixture.componentInstance; + + let nameHeader = await screen.findByText('table.column.name'); + fireEvent.click(nameHeader); + let semanticModelIdHeader = await screen.findByText('table.column.semanticModelId') + + await waitFor(() => {fireEvent.keyDown(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + expect(customerPartsComponent['ctrlKeyState']).toBeTruthy(); + await waitFor(() => { + fireEvent.click(semanticModelIdHeader) + }); + + await waitFor(() => {fireEvent.keyUp(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => {fireEvent.click(semanticModelIdHeader)}); + await waitFor(() => {fireEvent.click(semanticModelIdHeader)}); + expect(customerPartsComponent['tableCustomerSortList']).toEqual([]); + }); + }); diff --git a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.ts b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.ts index a4005cebfd..7ed507dc64 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.ts +++ b/frontend/src/app/modules/page/other-parts/presentation/customer-parts/customer-parts.component.ts @@ -22,7 +22,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Pagination } from '@core/model/pagination.model'; import { OtherPartsFacade } from '@page/other-parts/core/other-parts.facade'; import { Part } from '@page/parts/model/parts.model'; -import { CreateHeaderFromColumns, TableConfig, TableEventConfig } from '@shared/components/table/table.model'; +import { + CreateHeaderFromColumns, + TableConfig, + TableEventConfig, + TableHeaderSort, +} from '@shared/components/table/table.model'; import { View } from '@shared/model/view.model'; import { PartDetailsFacade } from '@shared/modules/part-details/core/partDetails.facade'; import { StaticIdService } from '@shared/service/staticId.service'; @@ -43,12 +48,12 @@ export class CustomerPartsComponent implements OnInit, OnDestroy { ]; public readonly sortableColumns: Record = { + semanticDataModel: true, name: true, manufacturer: true, - partNumber: true, + partId: true, semanticModelId: true, - productionDate: true, - semanticDataModel: true, + manufacturingDate: true, }; public readonly tableConfig: TableConfig = { @@ -61,12 +66,23 @@ export class CustomerPartsComponent implements OnInit, OnDestroy { public readonly customerTabLabelId = this.staticIdService.generateId('OtherParts.customerTabLabel'); + public tableCustomerSortList: TableHeaderSort[]; + + private ctrlKeyState = false; constructor( private readonly otherPartsFacade: OtherPartsFacade, private readonly partDetailsFacade: PartDetailsFacade, private readonly staticIdService: StaticIdService, ) { this.customerParts$ = this.otherPartsFacade.customerParts$; + this.tableCustomerSortList = []; + + window.addEventListener('keydown', (event) => { + this.ctrlKeyState = event.ctrlKey; + }); + window.addEventListener('keyup', (event) => { + this.ctrlKeyState = event.ctrlKey; + }); } public ngOnInit(): void { @@ -82,6 +98,35 @@ export class CustomerPartsComponent implements OnInit, OnDestroy { } public onTableConfigChange({ page, pageSize, sorting }: TableEventConfig): void { - this.otherPartsFacade.setCustomerParts(page, pageSize, sorting); + this.setTableSortingList(sorting); + this.otherPartsFacade.setCustomerParts(page, pageSize, this.tableCustomerSortList); + } + + + private setTableSortingList(sorting: TableHeaderSort): void { + if(!sorting && this.tableCustomerSortList) { + this.tableCustomerSortList = []; + return; + } + if(this.ctrlKeyState) { + const [columnName] = sorting; + const tableSortList = this.tableCustomerSortList; + + // Find the index of the existing entry with the same first item + const index = tableSortList.findIndex( + ([itemColumnName]) => itemColumnName === columnName + ); + + if (index !== -1) { + // Replace the existing entry + tableSortList[index] = sorting; + } else { + // Add the new entry if it doesn't exist + tableSortList.push(sorting); + } + this.tableCustomerSortList = tableSortList; + } else { + this.tableCustomerSortList = [sorting]; + } } } diff --git a/frontend/src/app/modules/page/other-parts/presentation/other-parts.component.spec.ts b/frontend/src/app/modules/page/other-parts/presentation/other-parts.component.spec.ts index c03268385f..92e966ed72 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/other-parts.component.spec.ts +++ b/frontend/src/app/modules/page/other-parts/presentation/other-parts.component.spec.ts @@ -21,6 +21,7 @@ import { OtherPartsState } from '@page/other-parts/core/other-parts.state'; import { PartsState } from '@page/parts/core/parts.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { FormatPartSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe'; import { fireEvent, screen, waitFor } from '@testing-library/angular'; @@ -101,7 +102,7 @@ describe('Other Parts', () => { }); - describe('onTableConfigChange', () => { + describe('onAsBuiltTableConfigChange', () => { let formatPartSemanticToCamelCase: FormatPartSemanticDataModelToCamelCasePipe; beforeEach(() => { formatPartSemanticToCamelCase = new FormatPartSemanticDataModelToCamelCasePipe(); @@ -118,10 +119,10 @@ describe('Other Parts', () => { expect(supplierParts).toEqual({ data: { content: [ - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_7)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_8)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_9)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_7, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_8, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_9, MainAspectType.AS_BUILT)), ], page: 0, pageCount: 1, @@ -147,11 +148,11 @@ describe('Other Parts', () => { expect(customerParts).toEqual({ data: { content: [ - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_1)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_2)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_3)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_4)), - formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_5)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_1, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_2, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_3, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_4, MainAspectType.AS_BUILT)), + formatPartSemanticToCamelCase.transform(PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_5, MainAspectType.AS_BUILT)), ], page: 0, pageCount: 1, diff --git a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html index dce9f593a9..c7a6d6b13d 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html +++ b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.html @@ -41,6 +41,7 @@ (configChanged)="onTableConfigChange($event)" (multiSelect)="onMultiSelect($event)" (clickSelectAction)="isInvestigationOpen$.next(true)" + [multiSortList]="tableSupplierSortList" > diff --git a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.spec.ts b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.spec.ts index 3c4f2b2e33..81552e9845 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.spec.ts +++ b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.spec.ts @@ -21,6 +21,7 @@ import { OtherPartsState } from '@page/other-parts/core/other-parts.state'; import { OtherPartsModule } from '@page/other-parts/other-parts.module'; import { PartsState } from '@page/parts/core/parts.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { fireEvent, screen, waitFor } from '@testing-library/angular'; import { getTableCheckbox, renderComponent } from '@tests/test-render.utils'; @@ -56,7 +57,7 @@ describe('SupplierPartsComponent', () => { it('should add item to current list and then remove', async () => { const { fixture } = await renderSupplierParts({ roles: ['user'] }); - const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6); + const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6, MainAspectType.AS_BUILT); // first click to check checkbox fireEvent.click(await getTableCheckbox(screen, 0)); @@ -76,7 +77,7 @@ describe('SupplierPartsComponent', () => { it('test addItemToSelection method', async () => { const { fixture } = await renderSupplierParts(); - const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6); + const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6, MainAspectType.AS_BUILT); fixture.componentInstance.addItemToSelection(expectedPart); expect(fixture.componentInstance.currentSelectedItems).toEqual([expectedPart]); @@ -85,7 +86,7 @@ describe('SupplierPartsComponent', () => { it('test removeItemFromSelection method', async () => { const { fixture } = await renderSupplierParts(); - const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6); + const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6, MainAspectType.AS_BUILT); fixture.componentInstance.currentSelectedItems = [expectedPart]; @@ -96,11 +97,76 @@ describe('SupplierPartsComponent', () => { it('test clearSelected method', async () => { const { fixture } = await renderSupplierParts(); - const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6); + const expectedPart = PartsAssembler.assembleOtherPart(OTHER_PARTS_MOCK_6, MainAspectType.AS_BUILT); fixture.componentInstance.currentSelectedItems = [expectedPart]; fixture.componentInstance.clearSelected(); expect(fixture.componentInstance.currentSelectedItems).toEqual([]); }) + + it('sort supplier parts after name column', async () => { + const {fixture} = await renderSupplierParts({ roles: ['admin'] }); + const supplierPartsComponent = fixture.componentInstance; + + let nameHeader = await screen.findByText('table.column.name'); + fireEvent.click(nameHeader); + + expect(supplierPartsComponent['tableSupplierSortList']).toEqual([["name", "asc"]]); + + }); + + it('should multisort after column name and semanticModelId', async () => { + const {fixture} = await renderSupplierParts({ roles: ['admin'] }); + const supplierPartsComponent = fixture.componentInstance; + + let nameHeader = await screen.findByText('table.column.name'); + fireEvent.click(nameHeader); + let semanticModelIdHeader = await screen.findByText('table.column.semanticModelId') + + await waitFor(() => {fireEvent.keyDown(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + expect(supplierPartsComponent['ctrlKeyState']).toBeTruthy(); + await waitFor(() => { + fireEvent.click(semanticModelIdHeader) + }); + + await waitFor(() => {fireEvent.keyUp(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => {fireEvent.click(semanticModelIdHeader)}); + expect(supplierPartsComponent['tableSupplierSortList']).toEqual([["name", "asc"], ["semanticModelId", "desc"]]); + }); + + it('should reset sorting on third click', async () => { + const {fixture} = await renderSupplierParts({ roles: ['admin'] }); + const supplierPartsComponent = fixture.componentInstance; + + let nameHeader = await screen.findByText('table.column.name'); + fireEvent.click(nameHeader); + let semanticModelIdHeader = await screen.findByText('table.column.semanticModelId') + + await waitFor(() => {fireEvent.keyDown(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + expect(supplierPartsComponent['ctrlKeyState']).toBeTruthy(); + await waitFor(() => { + fireEvent.click(semanticModelIdHeader) + }); + + await waitFor(() => {fireEvent.keyUp(semanticModelIdHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => {fireEvent.click(semanticModelIdHeader)}); + await waitFor(() => {fireEvent.click(semanticModelIdHeader)}); + expect(supplierPartsComponent['tableSupplierSortList']).toEqual([]); + }); + }); diff --git a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.ts b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.ts index 9d7450de19..99fcc5f0a8 100644 --- a/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.ts +++ b/frontend/src/app/modules/page/other-parts/presentation/supplier-parts/supplier-parts.component.ts @@ -22,7 +22,12 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Pagination } from '@core/model/pagination.model'; import { OtherPartsFacade } from '@page/other-parts/core/other-parts.facade'; import { Part, SemanticDataModel } from '@page/parts/model/parts.model'; -import { CreateHeaderFromColumns, TableConfig, TableEventConfig } from '@shared/components/table/table.model'; +import { + CreateHeaderFromColumns, + TableConfig, + TableEventConfig, + TableHeaderSort, +} from '@shared/components/table/table.model'; import { View } from '@shared/model/view.model'; import { PartDetailsFacade } from '@shared/modules/part-details/core/partDetails.facade'; import { StaticIdService } from '@shared/service/staticId.service'; @@ -44,12 +49,13 @@ export class SupplierPartsComponent implements OnInit, OnDestroy { ]; public readonly sortableColumns: Record = { + semanticDataModel: true, name: true, manufacturer: true, - partNumber: true, + partId: true, semanticModelId: true, - productionDate: true, - semanticDataModel: true, + manufacturingDate: true, + }; public readonly tableConfig: TableConfig = { @@ -68,12 +74,24 @@ export class SupplierPartsComponent implements OnInit, OnDestroy { public readonly supplierTabLabelId = this.staticIdService.generateId('OtherParts.supplierTabLabel'); + public tableSupplierSortList: TableHeaderSort[]; + + private ctrlKeyState = false; + constructor( private readonly otherPartsFacade: OtherPartsFacade, private readonly partDetailsFacade: PartDetailsFacade, private readonly staticIdService: StaticIdService, ) { this.supplierParts$ = this.otherPartsFacade.supplierParts$; + this.tableSupplierSortList = []; + + window.addEventListener('keydown', (event) => { + this.ctrlKeyState = event.ctrlKey; + }); + window.addEventListener('keyup', (event) => { + this.ctrlKeyState = event.ctrlKey; + }); } public get currentSelectedItems(): Part[] { @@ -101,7 +119,8 @@ export class SupplierPartsComponent implements OnInit, OnDestroy { } public onTableConfigChange({ page, pageSize, sorting }: TableEventConfig): void { - this.otherPartsFacade.setSupplierParts(page, pageSize, sorting); + this.setTableSortingList(sorting); + this.otherPartsFacade.setSupplierParts(page, pageSize, this.tableSupplierSortList); } public onMultiSelect(event: unknown[]): void { @@ -127,4 +146,34 @@ export class SupplierPartsComponent implements OnInit, OnDestroy { this.otherPartsFacade.setActiveInvestigationForParts(this.currentSelectedItems); this.isInvestigationOpen$.next(false); } + + + private setTableSortingList(sorting: TableHeaderSort): void { + if(!sorting && this.tableSupplierSortList) { + this.tableSupplierSortList = []; + return; + } + + if(this.ctrlKeyState) { + const [columnName] = sorting; + const tableSortList = this.tableSupplierSortList; + + // Find the index of the existing entry with the same first item + const index = tableSortList.findIndex( + ([itemColumnName]) => itemColumnName === columnName + ); + + if (index !== -1) { + // Replace the existing entry + tableSortList[index] = sorting; + } else { + // Add the new entry if it doesn't exist + tableSortList.push(sorting); + } + this.tableSupplierSortList = tableSortList; + } else { + this.tableSupplierSortList = [sorting]; + } + } + } diff --git a/frontend/src/app/modules/page/parts/core/parts.facade.spec.ts b/frontend/src/app/modules/page/parts/core/parts.facade.spec.ts index 15a4f45f28..e4bbf24f66 100644 --- a/frontend/src/app/modules/page/parts/core/parts.facade.spec.ts +++ b/frontend/src/app/modules/page/parts/core/parts.facade.spec.ts @@ -22,6 +22,7 @@ import { Pagination } from '@core/model/pagination.model'; import { PartsFacade } from '@page/parts/core/parts.facade'; import { PartsState } from '@page/parts/core/parts.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { PartsService } from '@shared/service/parts.service'; @@ -35,11 +36,11 @@ describe('Parts facade', () => { beforeEach(() => { partsServiceMok = { - getPart: id => new BehaviorSubject(mockAssetList[id]).pipe(map(part => PartsAssembler.assemblePart(part))), + getPart: id => new BehaviorSubject(mockAssetList[id]).pipe(map(part => PartsAssembler.assemblePart(part, MainAspectType.AS_BUILT))), getPartsAsBuilt: (_page, _pageSize, _sorting) => - of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts))), + of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_BUILT))), getPartsAsPlanned: (_page, _pageSize, _sorting) => - of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts))), + of(mockAssets).pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_PLANNED))), } as PartsService; partsState = new PartsState(); @@ -49,19 +50,19 @@ describe('Parts facade', () => { describe('setParts', () => { it('should set parts if request is successful', async () => { const serviceSpy = spyOn(partsServiceMok, 'getPartsAsBuilt').and.returnValue( - of>(PartsAssembler.assembleParts(mockAssets)), + of>(PartsAssembler.assembleParts(mockAssets, MainAspectType.AS_BUILT)), ); partsFacade.setPartsAsBuilt(0, 10); await waitFor(() => expect(serviceSpy).toHaveBeenCalledTimes(1)); - await waitFor(() => expect(serviceSpy).toHaveBeenCalledWith(0, 10, null)); + await waitFor(() => expect(serviceSpy).toHaveBeenCalledWith(0, 10, [])); const parts = await firstValueFrom(partsState.partsAsBuilt$); await waitFor(() => expect(parts).toEqual({ error: undefined, loader: undefined, - data: PartsAssembler.assembleParts(mockAssets), + data: PartsAssembler.assembleParts(mockAssets, MainAspectType.AS_BUILT), }), ); }); diff --git a/frontend/src/app/modules/page/parts/core/parts.facade.ts b/frontend/src/app/modules/page/parts/core/parts.facade.ts index d39fd14136..fdceeaafdf 100644 --- a/frontend/src/app/modules/page/parts/core/parts.facade.ts +++ b/frontend/src/app/modules/page/parts/core/parts.facade.ts @@ -44,7 +44,7 @@ export class PartsFacade { return this.partsState.partsAsPlanned$; } - public setPartsAsBuilt(page = 0, pageSize = 50, sorting: TableHeaderSort = null): void { + public setPartsAsBuilt(page = 0, pageSize = 50, sorting: TableHeaderSort[] = []): void { this.partsAsBuiltSubscription?.unsubscribe(); this.partsAsBuiltSubscription = this.partsService.getPartsAsBuilt(page, pageSize, sorting).subscribe({ next: data => (this.partsState.partsAsBuilt = { data }), @@ -52,7 +52,7 @@ export class PartsFacade { }); } - public setPartsAsPlanned(page = 0, pageSize = 50, sorting: TableHeaderSort = null): void { + public setPartsAsPlanned(page = 0, pageSize = 50, sorting: TableHeaderSort[] = []): void { this.partsAsPlannedSubscription?.unsubscribe(); this.partsAsPlannedSubscription = this.partsService.getPartsAsPlanned(page, pageSize, sorting).subscribe({ next: data => (this.partsState.partsAsPlanned = { data }), diff --git a/frontend/src/app/modules/page/parts/model/semanticModel.model.ts b/frontend/src/app/modules/page/parts/model/mainAspectType.enum.ts similarity index 91% rename from frontend/src/app/modules/page/parts/model/semanticModel.model.ts rename to frontend/src/app/modules/page/parts/model/mainAspectType.enum.ts index 97ec4e5919..953879eee4 100644 --- a/frontend/src/app/modules/page/parts/model/semanticModel.model.ts +++ b/frontend/src/app/modules/page/parts/model/mainAspectType.enum.ts @@ -16,7 +16,10 @@ * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - +export enum MainAspectType { + AS_BUILT = "as_built", + AS_PLANNED = "as_planned" +} diff --git a/frontend/src/app/modules/page/parts/model/parts.model.ts b/frontend/src/app/modules/page/parts/model/parts.model.ts index 8e64280895..9d3cf51224 100644 --- a/frontend/src/app/modules/page/parts/model/parts.model.ts +++ b/frontend/src/app/modules/page/parts/model/parts.model.ts @@ -22,6 +22,7 @@ import type { PaginationResponse } from '@core/model/pagination.model'; import { SemanticModel } from '@page/parts/model/aspectModels.model'; import { DetailAspectModel } from '@page/parts/model/detailAspectModel.model'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Owner } from '@page/parts/model/owner.enum'; export interface Part { @@ -44,6 +45,8 @@ export interface Part { semanticDataModel: SemanticDataModel; classification: string; + mainAspectType: MainAspectType; + // aspectmodel props are temporarely hardcoded here because Tables and Views only accept root level prop array // as built partId?: string; diff --git a/frontend/src/app/modules/page/parts/presentation/parts.component.html b/frontend/src/app/modules/page/parts/presentation/parts.component.html index 838bf66988..060bffe56c 100644 --- a/frontend/src/app/modules/page/parts/presentation/parts.component.html +++ b/frontend/src/app/modules/page/parts/presentation/parts.component.html @@ -33,10 +33,11 @@ [deselectTrigger]="deselectPartTrigger$ | async" [addTrigger]="addPartTrigger$ | async" (selected)="onSelectItem($event)" - (configChanged)="onTableConfigChange($event)" + (configChanged)="onAsBuiltTableConfigChange($event)" (multiSelect)="currentSelectedItems$.next($event)" (clickSelectAction)="isAlertOpen$.next(true)" [tableHeader]="'page.asBuiltParts'" + [multiSortList]="tableAsBuiltSortList" > @@ -50,10 +51,11 @@ [deselectTrigger]="deselectPartTrigger$ | async" [addTrigger]="addPartTrigger$ | async" (selected)="onSelectItem($event)" - (configChanged)="onTableConfigChange($event)" + (configChanged)="onAsPlannedTableConfigChange($event)" (multiSelect)="currentSelectedItems$.next($event)" (clickSelectAction)="isAlertOpen$.next(true)" [tableHeader]="'page.asPlannedParts'" + [multiSortList]="tableAsPlannedSortList" > diff --git a/frontend/src/app/modules/page/parts/presentation/parts.component.spec.ts b/frontend/src/app/modules/page/parts/presentation/parts.component.spec.ts index 51ccb42726..f8846bbc47 100644 --- a/frontend/src/app/modules/page/parts/presentation/parts.component.spec.ts +++ b/frontend/src/app/modules/page/parts/presentation/parts.component.spec.ts @@ -19,13 +19,14 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +import { By } from '@angular/platform-browser'; import { LayoutModule } from '@layout/layout.module'; import { SidenavComponent } from '@layout/sidenav/sidenav.component'; import { SidenavService } from '@layout/sidenav/sidenav.service'; import { OtherPartsModule } from '@page/other-parts/other-parts.module'; import { PartsComponent } from '@page/parts/presentation/parts.component'; import { SharedModule } from '@shared/shared.module'; -import { screen, waitFor } from '@testing-library/angular'; +import { fireEvent, screen, waitFor } from '@testing-library/angular'; import { renderComponent } from '@tests/test-render.utils'; import { PartsModule } from '../parts.module'; @@ -68,4 +69,80 @@ describe('Parts', () => { expect(selectedPartsInfo).toBeInTheDocument(); }); + + it('should sort asBuilt after column id', async () => { + const { fixture } = await renderParts(); + const partsComponent = await fixture.debugElement.query(By.directive(PartsComponent)).componentInstance; + + let setTableFunctionSpy = spyOn(partsComponent, "setTableSortingList").and.callThrough(); + let idColumnHeader = await screen.findByText('table.column.id'); + await waitFor(() => {fireEvent.click(idColumnHeader);}, {timeout: 3000}); + + + expect(setTableFunctionSpy).toHaveBeenCalledWith(['id', 'asc'], "as_built" ); + + expect(partsComponent['tableAsBuiltSortList']).toEqual([["id", "asc"]]); + }); + + it('should multisort after column id and idShort', async () => { + const { fixture } = await renderParts(); + const partsComponent = await fixture.debugElement.query(By.directive(PartsComponent)).componentInstance; + + let setTableFunctionSpy = spyOn(partsComponent, "setTableSortingList").and.callThrough(); + let idColumnHeader = await screen.findByText('table.column.id'); + await waitFor(() => {fireEvent.click(idColumnHeader);}, {timeout: 3000}); + let idShortHeader = await screen.findByText('table.column.idShort') + + await waitFor(() => {fireEvent.keyDown(idShortHeader, { + ctrlKey: true, + charCode: 17 + })}) + expect(partsComponent['ctrlKeyState']).toBeTruthy(); + await waitFor(() => { + fireEvent.click(idShortHeader) + }); + + await waitFor(() => {fireEvent.keyUp(idShortHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => {fireEvent.click(idShortHeader)}); + + + expect(setTableFunctionSpy).toHaveBeenCalledWith(['id', 'asc'], "as_built" ); + expect(setTableFunctionSpy).toHaveBeenCalledWith(['idShort', 'asc'], "as_built" ); + console.warn(partsComponent.tableAsBuiltSortList); + expect(partsComponent['tableAsBuiltSortList']).toEqual([["id", "asc"], ["idShort", "desc"]]); + }); + + it('should reset sorting after third click', async () => { + const { fixture } = await renderParts(); + const partsComponent = await fixture.debugElement.query(By.directive(PartsComponent)).componentInstance; + + let idColumnHeader = await screen.findByText('table.column.id'); + await waitFor(() => {fireEvent.click(idColumnHeader);}, {timeout: 3000}); + let idShortHeader = await screen.findByText('table.column.idShort') + + await waitFor(() => {fireEvent.keyDown(idShortHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => { + fireEvent.click(idShortHeader) + }); + + await waitFor(() => {fireEvent.keyUp(idShortHeader, { + ctrlKey: true, + charCode: 17 + })}) + + await waitFor(() => {fireEvent.click(idShortHeader)}); + + await waitFor(() => {fireEvent.click(idShortHeader)}); + + expect(partsComponent['tableAsBuiltSortList']).toEqual([]); + }); + }); diff --git a/frontend/src/app/modules/page/parts/presentation/parts.component.ts b/frontend/src/app/modules/page/parts/presentation/parts.component.ts index 00b3fc6404..56b433d27f 100644 --- a/frontend/src/app/modules/page/parts/presentation/parts.component.ts +++ b/frontend/src/app/modules/page/parts/presentation/parts.component.ts @@ -22,8 +22,14 @@ import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; import { Pagination } from '@core/model/pagination.model'; import { PartsFacade } from '@page/parts/core/parts.facade'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part } from '@page/parts/model/parts.model'; -import { CreateHeaderFromColumns, TableConfig, TableEventConfig } from '@shared/components/table/table.model'; +import { + CreateHeaderFromColumns, + TableConfig, + TableEventConfig, + TableHeaderSort, +} from '@shared/components/table/table.model'; import { View } from '@shared/model/view.model'; import { PartDetailsFacade } from '@shared/modules/part-details/core/partDetails.facade'; import { StaticIdService } from '@shared/service/staticId.service'; @@ -75,24 +81,36 @@ export class PartsComponent implements OnInit, OnDestroy, AfterViewInit { public readonly sortableColumnsAsBuilt: Record = { id: true, - semanticDataModel: true, + idShort: true, name: true, manufacturer: true, - partNumber: true, + partId: true, + manufacturerPartId: true, + customerPartId: true, + classification: true, + nameAtCustomer: true, semanticModelId: true, - productionDate: true, - productionCountry: true, + semanticDataModel: true, + manufacturingDate: true, + manufacturingCountry: true, + }; public readonly sortableColumnsAsPlanned: Record = { id: true, - semanticDataModel: true, + idShort: true, name: true, manufacturer: true, - partNumber: true, + manufacturerPartId: true, + classification: true, + semanticDataModel: true, semanticModelId: true, - productionDate: true, - productionCountry: true, + validityPeriodFrom: true, + validityPeriodTo: true, + psFunction: true, + catenaXSiteId: true, + functionValidFrom: true, + functionValidUntil: true, }; public readonly titleId = this.staticIdService.generateId('PartsComponent.title'); @@ -105,9 +123,14 @@ export class PartsComponent implements OnInit, OnDestroy, AfterViewInit { public readonly addPartTrigger$ = new Subject(); public readonly currentSelectedItems$ = new BehaviorSubject([]); + public tableAsBuiltSortList: TableHeaderSort[]; + public tableAsPlannedSortList: TableHeaderSort[]; + public tableConfigAsBuilt: TableConfig; public tableConfigAsPlanned: TableConfig; + private ctrlKeyState = false; + constructor( private readonly partsFacade: PartsFacade, private readonly partDetailsFacade: PartDetailsFacade, @@ -115,6 +138,15 @@ export class PartsComponent implements OnInit, OnDestroy, AfterViewInit { ) { this.partsAsBuilt$ = this.partsFacade.partsAsBuilt$; this.partsAsPlanned$ = this.partsFacade.partsAsPlanned$; + this.tableAsBuiltSortList = []; + this.tableAsPlannedSortList = []; + + window.addEventListener('keydown', (event) => { + this.ctrlKeyState = event.ctrlKey; + }); + window.addEventListener('keyup', (event) => { + this.ctrlKeyState = event.ctrlKey; + }); } public ngOnInit(): void { @@ -143,8 +175,60 @@ export class PartsComponent implements OnInit, OnDestroy, AfterViewInit { this.partDetailsFacade.selectedPart = $event as unknown as Part; } - public onTableConfigChange({ page, pageSize, sorting }: TableEventConfig): void { - this.partsFacade.setPartsAsBuilt(page, pageSize, sorting); - this.partsFacade.setPartsAsPlanned(page, pageSize, sorting); + public onAsBuiltTableConfigChange({ page, pageSize, sorting }: TableEventConfig): void { + this.setTableSortingList(sorting,MainAspectType.AS_BUILT); + this.partsFacade.setPartsAsBuilt(page, pageSize, this.tableAsBuiltSortList); } + + public onAsPlannedTableConfigChange({ page, pageSize, sorting }: TableEventConfig): void { + this.setTableSortingList(sorting,MainAspectType.AS_PLANNED); + this.partsFacade.setPartsAsPlanned(page, pageSize, this.tableAsPlannedSortList); + } + + private setTableSortingList(sorting: TableHeaderSort, partTable: MainAspectType): void { + // if a sorting Columnlist exists but a column gets resetted: + if(!sorting && (this.tableAsBuiltSortList || this.tableAsPlannedSortList)) { + this.resetTableSortingList(partTable); + return; + } + + // if CTRL is pressed at to sortList + if(this.ctrlKeyState) { + const [columnName] = sorting; + const tableSortList = partTable === MainAspectType.AS_BUILT ? this.tableAsBuiltSortList : this.tableAsPlannedSortList + + // Find the index of the existing entry with the same first item + const index = tableSortList.findIndex( + ([itemColumnName]) => itemColumnName === columnName + ); + + if (index !== -1) { + // Replace the existing entry + tableSortList[index] = sorting; + } else { + // Add the new entry if it doesn't exist + tableSortList.push(sorting); + } + if(partTable === MainAspectType.AS_BUILT) { + this.tableAsBuiltSortList = tableSortList + } else { + this.tableAsPlannedSortList = tableSortList + } + } + // If CTRL is not pressed just add a list with one entry + else if(partTable === MainAspectType.AS_BUILT) { + this.tableAsBuiltSortList = [sorting]; + } else { + this.tableAsPlannedSortList = [sorting] + } + } + + private resetTableSortingList(partTable: MainAspectType): void { + if(partTable === MainAspectType.AS_BUILT) { + this.tableAsBuiltSortList = []; + } else { + this.tableAsPlannedSortList= []; + } + } + } diff --git a/frontend/src/app/modules/shared/assembler/parts.assembler.spec.ts b/frontend/src/app/modules/shared/assembler/parts.assembler.spec.ts index 6157fff167..fe3b7c5a6f 100644 --- a/frontend/src/app/modules/shared/assembler/parts.assembler.spec.ts +++ b/frontend/src/app/modules/shared/assembler/parts.assembler.spec.ts @@ -22,6 +22,7 @@ import { Pagination } from '@core/model/pagination.model'; import { AsBuiltAspectModel } from '@page/parts/model/aspectModels.model'; import { DetailAspectType } from '@page/parts/model/detailAspectModel.model'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part, QualityType, SemanticDataModel } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { of } from 'rxjs'; @@ -38,8 +39,8 @@ describe('PartsAssembler', () => { describe('assembleParts', () => { it('should return null if array is empty or undefined', () => { const emptyPage = { content: [], page: 0, pageCount: 0, pageSize: 0, totalItems: 0 }; - expect(PartsAssembler.assembleParts(null)).toEqual(emptyPage); - expect(PartsAssembler.assembleParts(page([]))).toEqual(emptyPage); + expect(PartsAssembler.assembleParts(null, MainAspectType.AS_BUILT)).toEqual(emptyPage); + expect(PartsAssembler.assembleParts(page([]), MainAspectType.AS_BUILT)).toEqual(emptyPage); }); it('should format the object correctly', () => { @@ -81,6 +82,8 @@ describe('PartsAssembler', () => { } ]; + const mainAspectType = 'as_built'; + testData.push({ id, idShort, @@ -100,6 +103,7 @@ describe('PartsAssembler', () => { classification, semanticModel, detailAspectModels, + mainAspectType }); const partId = (detailAspectModels[0].data as AsBuiltAspectModel)?.partId; @@ -129,6 +133,8 @@ describe('PartsAssembler', () => { semanticModel: semanticModel, + mainAspectType: mainAspectType, + partId: partId, // is partInstance, BatchId, jisNumber customerPartId: customerPartId, nameAtCustomer: nameAtCustomer, @@ -137,7 +143,7 @@ describe('PartsAssembler', () => { }); - expect(JSON.stringify(PartsAssembler.assembleParts(page(testData)).content)).toEqual(JSON.stringify(expected)); + expect(JSON.stringify(PartsAssembler.assembleParts(page(testData), MainAspectType.AS_BUILT).content)).toEqual(JSON.stringify(expected)); }); }); @@ -167,9 +173,10 @@ describe('PartsAssembler', () => { const manufacturerPartId = 'manufacturerPartId'; const nameAtManufacturer = 'nameAtManufacturer'; const van = 'van'; + const mainAspectType = MainAspectType.AS_BUILT it('should clean up data for manufacturer view', done => { - const data = { manufacturer, manufacturerPartId, nameAtManufacturer, test: '', van } as unknown as Part; + const data = { manufacturer, manufacturerPartId, nameAtManufacturer, test: '', van, mainAspectType } as unknown as Part; of({ data }) .pipe(PartsAssembler.mapPartForManufacturerView()) .subscribe(result => { diff --git a/frontend/src/app/modules/shared/assembler/parts.assembler.ts b/frontend/src/app/modules/shared/assembler/parts.assembler.ts index f6a0f37ce7..e415487edf 100644 --- a/frontend/src/app/modules/shared/assembler/parts.assembler.ts +++ b/frontend/src/app/modules/shared/assembler/parts.assembler.ts @@ -27,6 +27,7 @@ import { PartSiteInformationAsPlanned, SemanticModel, } from '@page/parts/model/aspectModels.model'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part, PartResponse, QualityType } from '@page/parts/model/parts.model'; import { TableHeaderSort } from '@shared/components/table/table.model'; import { View } from '@shared/model/view.model'; @@ -44,7 +45,7 @@ export class PartsAssembler { return proplist; } - public static assemblePart(partResponse: PartResponse): Part { + public static assemblePart(partResponse: PartResponse, mainAspectType: MainAspectType): Part { if (!partResponse) { return null; } @@ -84,6 +85,9 @@ export class PartsAssembler { semanticDataModel: partResponse.semanticDataModel, classification: partResponse.classification, semanticModel: createdSemanticModel, + + mainAspectType: mainAspectType, + // as built partId: partId, // is partInstance, BatchId, jisNumber customerPartId: customerPartId, @@ -100,27 +104,28 @@ export class PartsAssembler { functionValidFrom: functionValidFrom, functionValidUntil: functionValidUntil, } + return mappedPart; } - public static assembleOtherPart(partResponse: PartResponse): Part { + public static assembleOtherPart(partResponse: PartResponse, mainAspectType: MainAspectType): Part { if (!partResponse) { return null; } - return { ...PartsAssembler.assemblePart(partResponse), qualityType: partResponse.qualityType }; + return { ...PartsAssembler.assemblePart(partResponse, mainAspectType), qualityType: partResponse.qualityType }; } - public static assembleParts(parts: PaginationResponse): Pagination { - return PaginationAssembler.assemblePagination(PartsAssembler.assemblePart, parts); + public static assembleParts(parts: PaginationResponse, mainAspectType: MainAspectType): Pagination { + return PaginationAssembler.assemblePagination(PartsAssembler.assemblePart, parts, mainAspectType); } - public static assemblePartList(parts: PartResponse[]): Part[] { + public static assemblePartList(parts: PartResponse[], mainAspectType: MainAspectType): Part[] { const partCopy = [...parts]; - return partCopy.map(part => PartsAssembler.assemblePart(part)); + return partCopy.map(part => PartsAssembler.assemblePart(part, mainAspectType)); } - public static assembleOtherParts(parts: PaginationResponse): Pagination { - return PaginationAssembler.assemblePagination(PartsAssembler.assembleOtherPart, parts); + public static assembleOtherParts(parts: PaginationResponse, mainAspectType: MainAspectType): Pagination { + return PaginationAssembler.assemblePagination(PartsAssembler.assembleOtherPart, parts, mainAspectType); } public static filterPartForView(viewData: View): View { @@ -159,14 +164,23 @@ export class PartsAssembler { return viewData; } - const { - manufacturer, - manufacturerPartId, - nameAtManufacturer, - van, - - } = viewData.data; - return { data: { manufacturer, manufacturerPartId, nameAtManufacturer, van } as Part }; + // exclude 'van' if is a partAsPlanned + if(viewData.data?.mainAspectType === MainAspectType.AS_BUILT) { + const { + manufacturer, + manufacturerPartId, + nameAtManufacturer, + van, + } = viewData.data; + return { data: { manufacturer, manufacturerPartId, nameAtManufacturer, van } as Part }; + } else { + const { + manufacturer, + manufacturerPartId, + nameAtManufacturer, + } = viewData.data; + return { data: { manufacturer, manufacturerPartId, nameAtManufacturer } as Part }; + } }); } @@ -195,18 +209,33 @@ export class PartsAssembler { const localToApiMapping = new Map([ ['id', 'id'], - ['semanticDataModel', 'semanticDataModel'], - ['name', 'nameAtManufacturer'], + ['idShort', 'idShort'], + ['semanticModelId', 'semanticModelId'], ['manufacturer', 'manufacturerName'], - ['semanticModelId', 'manufacturerPartId'], - ['partNumber', 'customerPartId'], - ['productionCountry', 'manufacturingCountry'], - ['nameAtCustomer', 'nameAtCustomer'], - ['customerPartId', 'customerPartId'], + ['manufacturerPartId', 'manufacturerPartId'], + ['partId', "manufacturerPartId"], + ['nameAtManufacturer', 'nameAtManufacturer'], + ['businessPartner', 'businessPartner'], + ['name', 'nameAtManufacturer'], ['qualityType', 'qualityType'], - ['productionDate', 'manufacturingDate'], + ['van', 'van'], + ['semanticDataModel', 'semanticDataModel'], + ['classification', 'classification'], + ['customerPartId', 'customerPartId'], + ['nameAtCustomer', 'nameAtCustomer'], + ['manufacturingDate', 'manufacturingDate'], + ['manufacturingCountry', 'manufacturingCountry'], + ['validityPeriodFrom', 'validityPeriodFrom'], + ['validityPeriodTo', 'validityPeriodTo'], + ['catenaXSiteId', 'catenaXSiteId'], + ['psFunction', 'function'], + ['functionValidFrom', 'functionValidFrom'], + ['functionValidUntil', 'functionValidUntil'], + ]); + + return `${localToApiMapping.get(sorting[0]) || sorting},${sorting[1]}`; } } diff --git a/frontend/src/app/modules/shared/components/table/table.component.html b/frontend/src/app/modules/shared/components/table/table.component.html index 8ebe6db328..5e076db71d 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.html +++ b/frontend/src/app/modules/shared/components/table/table.component.html @@ -187,6 +187,13 @@

{{ 'table.noResultFound' | i18n }}

{{ 'table.noResultFound' | i18n }} > {{ tableConfig?.header?.[column] | i18n }}{{ tableConfig?.header?.[column] | i18n }} + + + +
+ + {{ (i+1) + "." }}{{ item[1] === 'asc' ? '↑' : item[1] === 'desc' ? '↓' : '' }} +
+
+
+
+ ) { this.totalItems = totalItems; diff --git a/frontend/src/app/modules/shared/modules/part-details/presentation/part-detail.component.spec.ts b/frontend/src/app/modules/shared/modules/part-details/presentation/part-detail.component.spec.ts index dcb8e52d96..ed8c2dfe93 100644 --- a/frontend/src/app/modules/shared/modules/part-details/presentation/part-detail.component.spec.ts +++ b/frontend/src/app/modules/shared/modules/part-details/presentation/part-detail.component.spec.ts @@ -23,6 +23,7 @@ import { LayoutModule } from '@layout/layout.module'; import { SidenavComponent } from '@layout/sidenav/sidenav.component'; import { SidenavService } from '@layout/sidenav/sidenav.service'; import { PartsState } from '@page/parts/core/parts.state'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { PartDetailsFacade } from '@shared/modules/part-details/core/partDetails.facade'; import { PartDetailsState } from '@shared/modules/part-details/core/partDetails.state'; @@ -35,7 +36,7 @@ import { PartDetailComponent } from './part-detail.component'; let PartsStateMock: PartsState; let PartDetailsStateMock: PartDetailsState; -const part = PartsAssembler.assemblePart(MOCK_part_1); +const part = PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT); describe('PartDetailComponent', () => { beforeEach(() => { diff --git a/frontend/src/app/modules/shared/modules/part-details/presentation/start-investigation/start-investigation.component.spec.ts b/frontend/src/app/modules/shared/modules/part-details/presentation/start-investigation/start-investigation.component.spec.ts index 54207d4089..4c1a24d3f1 100644 --- a/frontend/src/app/modules/shared/modules/part-details/presentation/start-investigation/start-investigation.component.spec.ts +++ b/frontend/src/app/modules/shared/modules/part-details/presentation/start-investigation/start-investigation.component.spec.ts @@ -21,6 +21,7 @@ import { LayoutModule } from '@layout/layout.module'; import { OtherPartsModule } from '@page/other-parts/other-parts.module'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { PartsModule } from '@page/parts/parts.module'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { PartDetailsModule } from '@shared/modules/part-details/partDetails.module'; @@ -32,9 +33,8 @@ import { MOCK_part_1, MOCK_part_2 } from '../../../../../../mocks/services/parts import { StartInvestigationComponent } from './start-investigation.component'; describe('StartInvestigationComponent', () => { - const part = { data: PartsAssembler.assemblePart(MOCK_part_1) }; - const firstChild = PartsAssembler.assemblePart(MOCK_part_2); - console.log(firstChild) + const part = { data: PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT) }; + const firstChild = PartsAssembler.assemblePart(MOCK_part_2, MainAspectType.AS_BUILT); const renderStartInvestigation = async () => { const { fixture } = await renderComponent(StartInvestigationComponent, { diff --git a/frontend/src/app/modules/shared/modules/relations/core/relations.facade.spec.ts b/frontend/src/app/modules/shared/modules/relations/core/relations.facade.spec.ts index 9a7440e36b..a36478d3de 100644 --- a/frontend/src/app/modules/shared/modules/relations/core/relations.facade.spec.ts +++ b/frontend/src/app/modules/shared/modules/relations/core/relations.facade.spec.ts @@ -19,6 +19,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { RelationComponentState } from '@shared/modules/relations/core/component.state'; import { LoadedElementsFacade } from '@shared/modules/relations/core/loaded-elements.facade'; @@ -44,9 +45,9 @@ describe('Relations facade', () => { beforeEach(() => { const partsServiceMok = { - getPart: id => of(mockAssetList[id]).pipe(map(part => PartsAssembler.assemblePart(part))), + getPart: id => of(mockAssetList[id]).pipe(map(part => PartsAssembler.assemblePart(part, MainAspectType.AS_BUILT))), getPartDetailOfIds: assetIds => - of(assetIds.map(id => mockAssetList[id])).pipe(map(parts => PartsAssembler.assemblePartList(parts))), + of(assetIds.map(id => mockAssetList[id])).pipe(map(parts => PartsAssembler.assemblePartList(parts, MainAspectType.AS_BUILT))), } as PartsService; loadedElementsFacade = new LoadedElementsFacade(new LoadedElementsState()); diff --git a/frontend/src/app/modules/shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe.spec.ts b/frontend/src/app/modules/shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe.spec.ts index 0acf4d509a..2749a55a17 100644 --- a/frontend/src/app/modules/shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe.spec.ts +++ b/frontend/src/app/modules/shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe.spec.ts @@ -19,6 +19,7 @@ import { TestBed } from '@angular/core/testing'; import { Pagination } from '@core/model/pagination.model'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { SemanticDataModel } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { FormatPaginationSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-pagination-semantic-data-model-to-camelcase.pipe'; @@ -53,7 +54,7 @@ describe('FormatPaginationSemanticDataModelToCamelCasePipe', () => { it(`should transform semanticDataModel from ${object.option} to ${object.expected}`, function() { - let partList = [PartsAssembler.assemblePart(MOCK_part_1), PartsAssembler.assemblePart(MOCK_part_2)]; + let partList = [PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT), PartsAssembler.assemblePart(MOCK_part_2, MainAspectType.AS_BUILT)]; let paginationData: Pagination = { page: 0, diff --git a/frontend/src/app/modules/shared/pipes/format-part-semantic-data-model-to-camelcase.pipe.spec.ts b/frontend/src/app/modules/shared/pipes/format-part-semantic-data-model-to-camelcase.pipe.spec.ts index abcd5cb5bf..5d2a5ca8fd 100644 --- a/frontend/src/app/modules/shared/pipes/format-part-semantic-data-model-to-camelcase.pipe.spec.ts +++ b/frontend/src/app/modules/shared/pipes/format-part-semantic-data-model-to-camelcase.pipe.spec.ts @@ -18,6 +18,7 @@ ********************************************************************************/ import { TestBed } from '@angular/core/testing'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { SemanticDataModel } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { FormatPartSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-part-semantic-data-model-to-camelcase.pipe'; @@ -51,7 +52,7 @@ describe('FormatPartSemanticDataModelToCamelCasePipe', () => { ].forEach(object => { it(`should transform semanticDataModel from ${object.option} to ${object.expected}`, function() { - let partData = PartsAssembler.assemblePart(MOCK_part_1); + let partData = PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT); partData.semanticDataModel = object.option; expect(partData.semanticDataModel).toEqual(object.option) diff --git a/frontend/src/app/modules/shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe.spec.ts b/frontend/src/app/modules/shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe.spec.ts index 36e57d34f7..3cbb69f3af 100644 --- a/frontend/src/app/modules/shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe.spec.ts +++ b/frontend/src/app/modules/shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe.spec.ts @@ -18,6 +18,7 @@ ********************************************************************************/ import { TestBed } from '@angular/core/testing'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { SemanticDataModel } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { FormatPartlistSemanticDataModelToCamelCasePipe } from '@shared/pipes/format-partlist-semantic-data-model-to-camelcase.pipe'; @@ -51,7 +52,7 @@ describe('FormatPartlistSemanticDataModelToCamelCasePipe', () => { ].forEach(object => { it(`should transform semanticDataModel from ${object.option} to ${object.expected}`, function() { - let partList = [PartsAssembler.assemblePart(MOCK_part_1), PartsAssembler.assemblePart(MOCK_part_2)]; + let partList = [PartsAssembler.assemblePart(MOCK_part_1, MainAspectType.AS_BUILT), PartsAssembler.assemblePart(MOCK_part_2, MainAspectType.AS_BUILT)]; partList.forEach(part => { part.semanticDataModel = object.option diff --git a/frontend/src/app/modules/shared/service/parts.service.ts b/frontend/src/app/modules/shared/service/parts.service.ts index 75380afda4..f0f984060d 100644 --- a/frontend/src/app/modules/shared/service/parts.service.ts +++ b/frontend/src/app/modules/shared/service/parts.service.ts @@ -24,6 +24,7 @@ import { Injectable } from '@angular/core'; import { ApiService } from '@core/api/api.service'; import { Pagination } from '@core/model/pagination.model'; import { environment } from '@env'; +import { MainAspectType } from '@page/parts/model/mainAspectType.enum'; import { Part, PartResponse, PartsResponse } from '@page/parts/model/parts.model'; import { PartsAssembler } from '@shared/assembler/parts.assembler'; import { TableHeaderSort } from '@shared/components/table/table.model'; @@ -38,39 +39,45 @@ export class PartsService { constructor(private readonly apiService: ApiService) {} - public getPartsAsBuilt(page: number, pageSize: number, sorting: TableHeaderSort): Observable> { - const sort = PartsAssembler.mapSortToApiSort(sorting); - const params = new HttpParams() + public getPartsAsBuilt(page: number, pageSize: number, sorting: TableHeaderSort[]): Observable> { + let sort = sorting.map(sortingItem => PartsAssembler.mapSortToApiSort(sortingItem)); + let params = new HttpParams() .set('page', page) .set('size', pageSize) - .set('sort', sort) .set('owner', 'OWN'); + sort.forEach(sortingItem => { + params = params.append('sort', sortingItem); + }) + return this.apiService .getBy(`${this.url}/assets/as-built`, params) - .pipe(map(parts => PartsAssembler.assembleParts(parts))); + .pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_BUILT))); } - public getPartsAsPlanned(page: number, pageSize: number, sorting: TableHeaderSort): Observable> { - const sort = PartsAssembler.mapSortToApiSort(sorting); - const params = new HttpParams() + public getPartsAsPlanned(page: number, pageSize: number, sorting: TableHeaderSort[]): Observable> { + let sort = sorting.map(sortingItem => PartsAssembler.mapSortToApiSort(sortingItem)); + let params = new HttpParams() .set('page', page) .set('size', pageSize) - .set('sort', sort) .set('owner', 'OWN'); + sort.forEach(sortingItem => { + params= params.append('sort', sortingItem); + }) + return this.apiService .getBy(`${this.url}/assets/as-planned`, params) - .pipe(map(parts => PartsAssembler.assembleParts(parts))); + .pipe(map(parts => PartsAssembler.assembleParts(parts, MainAspectType.AS_PLANNED))); } public getPart(id: string): Observable { let resultsAsBuilt = this.apiService.get(`${ this.url }/assets/as-built/${ id }`) - .pipe(map(part => PartsAssembler.assemblePart(part))); + .pipe(map(part => PartsAssembler.assemblePart(part, MainAspectType.AS_BUILT))); let resultsAsPlanned = this.apiService.get(`${ this.url }/assets/as-planned/${ id }`) - .pipe(map(part => PartsAssembler.assemblePart(part))); + .pipe(map(part => PartsAssembler.assemblePart(part, MainAspectType.AS_PLANNED))); return resultsAsBuilt || resultsAsPlanned; @@ -81,11 +88,11 @@ export class PartsService { let resultsAsBuilt = this.apiService .post(`${this.url}/assets/as-built/detail-information`, { assetIds }) - .pipe(map(parts => PartsAssembler.assemblePartList(parts))); + .pipe(map(parts => PartsAssembler.assemblePartList(parts, MainAspectType.AS_BUILT))); let resultsAsPlanned = this.apiService .post(`${this.url}/assets/as-planned/detail-information`, { assetIds }) - .pipe(map(parts => PartsAssembler.assemblePartList(parts))); + .pipe(map(parts => PartsAssembler.assemblePartList(parts, MainAspectType.AS_PLANNED))); if(resultsAsBuilt) { return resultsAsBuilt; diff --git a/pom.xml b/pom.xml index 97a0484000..4b608949c3 100644 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,9 @@ SPDX-License-Identifier: Apache-2.0 - 3.1.2 - 6.0.11 + 3.1.3 + 6.0.12 + 6.1.3 17 ${java.version} ${java.version} @@ -48,16 +49,15 @@ SPDX-License-Identifier: Apache-2.0 2.2.4 - 3.2.1 + 3.3.0 3.10.1 1.12.0 - 2.1.0 3.0.1 0.8.8 3.3.0 8.4.0 4.7.3.0 - 3.0.2 + 3.1.3 3.4.5 3.9.1.2184 3.1.2 @@ -74,32 +74,32 @@ SPDX-License-Identifier: Apache-2.0 3.1.0 9.4.3.0 2.1.0 - 5.5.0 + 5.7.0 2022.0.3 24.0.1 3.8.0 12.4 8.3.3 3.0.2 - 1.6.10 + 1.6.11 0.2.6 2.13.0 0.9.3 1.1.0 1.17.6 - 1.18.3 - 5.3.0 - 2.4-M1-groovy-4.0 + 5.3.2 + 2.0.4 3.24.2 1.2 1.18.28 2.38.0 7.12.1 - 2.1 - 2.15.1 + 2.2 + 2.15.2 5.9.3 3.0.0 + 1.2.1-SNAPSHOT jacoco reuseReports @@ -134,8 +134,8 @@ SPDX-License-Identifier: Apache-2.0 - + org.projectlombok lombok diff --git a/tx-backend/pom.xml b/tx-backend/pom.xml index 2a92b14797..d0da208e5f 100644 --- a/tx-backend/pom.xml +++ b/tx-backend/pom.xml @@ -54,7 +54,7 @@ SPDX-License-Identifier: Apache-2.0 org.eclipse.tractusx.irs irs-registry-client - 1.2.1-SNAPSHOT + ${irs-client-lib.version} @@ -77,6 +77,7 @@ SPDX-License-Identifier: Apache-2.0 org.springframework.boot spring-boot-starter-data-jpa + commons-fileupload @@ -142,7 +143,7 @@ SPDX-License-Identifier: Apache-2.0 org.springframework.security spring-security-config - 6.0.5 + ${spring-security-config.version} org.springframework.boot @@ -324,8 +325,9 @@ SPDX-License-Identifier: Apache-2.0 + - + net.minidev json-smart 2.4.10 @@ -351,19 +353,6 @@ SPDX-License-Identifier: Apache-2.0 test - - - org.spockframework - spock-core - ${spock-core.version} - test - - - org.spockframework - spock-spring - ${spock-core.version} - test - org.bitbucket.b_c jose4j @@ -388,12 +377,6 @@ SPDX-License-Identifier: Apache-2.0 ${testcontainer-postgresql.version} test - - org.testcontainers - spock - ${testcontainer-spock.version} - test - org.awaitility awaitility @@ -489,32 +472,6 @@ SPDX-License-Identifier: Apache-2.0 - - - org.codehaus.gmavenplus - gmavenplus-plugin - ${gmavenplus-plugin.version} - - - - tx-backend/src/integration - - **/*.groovy - - - - - - - - compile - compileTests - - - - - - org.apache.maven.plugins diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asbuilt/mapper/AssetAsBuiltResponseMapper.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asbuilt/mapper/AssetAsBuiltResponseMapper.java index 888a6dd9a4..ddbc218c33 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asbuilt/mapper/AssetAsBuiltResponseMapper.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asbuilt/mapper/AssetAsBuiltResponseMapper.java @@ -22,9 +22,12 @@ import org.eclipse.tractusx.traceability.assets.application.base.mapper.AssetBaseResponseMapper; import org.eclipse.tractusx.traceability.assets.domain.base.model.AssetBase; import org.eclipse.tractusx.traceability.common.model.PageResult; +import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotification; import java.util.List; +import static java.util.Objects.isNull; + public class AssetAsBuiltResponseMapper extends AssetBaseResponseMapper { public static AssetAsBuiltResponse from(final AssetBase asset) { @@ -54,6 +57,8 @@ public static AssetAsBuiltResponse from(final AssetBase asset) { .van(asset.getVan()) .semanticDataModel(from(asset.getSemanticDataModel())) .detailAspectModels(fromList(asset.getDetailAspectModels())) + .qualityAlertsInStatusActive(countNotificationsInActiveState(asset.getQualityAlerts())) + .qualityInvestigationsInStatusActive(countNotificationsInActiveState(asset.getQualityInvestigations())) .build(); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asplanned/mapper/AssetAsPlannedResponseMapper.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asplanned/mapper/AssetAsPlannedResponseMapper.java index a32eae2a41..c9758ccbd0 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asplanned/mapper/AssetAsPlannedResponseMapper.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/asplanned/mapper/AssetAsPlannedResponseMapper.java @@ -54,6 +54,8 @@ public static AssetAsPlannedResponse from(final AssetBase asset) { .van(asset.getVan()) .semanticDataModel(from(asset.getSemanticDataModel())) .detailAspectModels(fromList(asset.getDetailAspectModels())) + .qualityAlertsInStatusActive(countNotificationsInActiveState(asset.getQualityAlerts())) + .qualityInvestigationsInStatusActive(countNotificationsInActiveState(asset.getQualityInvestigations())) .build(); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/base/mapper/AssetBaseResponseMapper.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/base/mapper/AssetBaseResponseMapper.java index 7a24b4750f..de2ef9480e 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/base/mapper/AssetBaseResponseMapper.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/application/base/mapper/AssetBaseResponseMapper.java @@ -43,6 +43,7 @@ import org.eclipse.tractusx.traceability.assets.domain.base.model.aspect.DetailAspectData; import org.eclipse.tractusx.traceability.assets.domain.base.model.aspect.DetailAspectModel; import org.eclipse.tractusx.traceability.assets.domain.base.model.aspect.DetailAspectType; +import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotification; import java.util.List; @@ -124,4 +125,10 @@ public static SemanticDataModelResponse from(final SemanticDataModel semanticDat } return SemanticDataModelResponse.valueOf(semanticDataModel.name()); } + + protected static Integer countNotificationsInActiveState(List notifications) { + return emptyIfNull(notifications).stream() + .filter(QualityNotification::isActiveState) + .toList().size(); + } } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/model/AssetBase.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/model/AssetBase.java index 0708fa36f0..2454b047e8 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/model/AssetBase.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/model/AssetBase.java @@ -21,12 +21,14 @@ package org.eclipse.tractusx.traceability.assets.domain.base.model; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.Singular; import lombok.extern.slf4j.Slf4j; import org.eclipse.tractusx.traceability.assets.domain.base.model.aspect.DetailAspectModel; +import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotification; import java.util.List; @@ -54,4 +56,6 @@ public class AssetBase { private SemanticDataModel semanticDataModel; private String classification; private List detailAspectModels; + private List qualityAlerts; + private List qualityInvestigations; } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/service/AbstractAssetBaseService.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/service/AbstractAssetBaseService.java index 4e467a0547..b83ed700d0 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/service/AbstractAssetBaseService.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/domain/base/service/AbstractAssetBaseService.java @@ -70,6 +70,7 @@ public void synchronizeAssetsAsync(String globalAssetId) { if (!getUpwardAspects().isEmpty()) { + // TODO: change BomLifecycle.AS_BUILT to getBomLifecycle() List upwardAssets = getIrsRepository().findAssets(globalAssetId, Direction.UPWARD, Aspect.upwardAspectsForAssetsAsBuilt(), BomLifecycle.AS_BUILT); upwardAssets.forEach(asset -> { diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/model/AssetAsBuiltEntity.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/model/AssetAsBuiltEntity.java index 4d3a309fd5..2eca1bf972 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/model/AssetAsBuiltEntity.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asbuilt/model/AssetAsBuiltEntity.java @@ -33,20 +33,24 @@ import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import lombok.experimental.SuperBuilder; import org.eclipse.tractusx.traceability.assets.domain.base.model.AssetBase; import org.eclipse.tractusx.traceability.assets.domain.base.model.Descriptions; import org.eclipse.tractusx.traceability.assets.domain.base.model.aspect.DetailAspectModel; import org.eclipse.tractusx.traceability.assets.infrastructure.base.model.AssetBaseEntity; import org.eclipse.tractusx.traceability.assets.infrastructure.base.model.SemanticDataModelEntity; -import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.model.AlertNotificationEntity; +import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.model.AlertEntity; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.investigation.model.InvestigationEntity; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import static org.apache.commons.collections4.ListUtils.emptyIfNull; + @Getter +@Setter @NoArgsConstructor @Entity @SuperBuilder @@ -71,7 +75,7 @@ public class AssetAsBuiltEntity extends AssetBaseEntity { private List investigations = new ArrayList<>(); @ManyToMany(mappedBy = "assets") - private List alertNotificationEntities = new ArrayList<>(); + private List alerts = new ArrayList<>(); public static AssetAsBuiltEntity from(AssetBase asset) { ManufacturingInfo manufacturingInfo = ManufacturingInfo.from(asset.getDetailAspectModels()); @@ -127,6 +131,8 @@ public static AssetBase toDomain(AssetAsBuiltEntity entity) { .van(entity.getVan()) .classification(entity.getClassification()) .detailAspectModels(DetailAspectModel.from(entity)) + .qualityAlerts(emptyIfNull(entity.alerts).stream().map(AlertEntity::toDomain).toList()) + .qualityInvestigations(emptyIfNull(entity.investigations).stream().map(InvestigationEntity::toDomain).toList()) .build(); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/model/AssetAsPlannedEntity.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/model/AssetAsPlannedEntity.java index eb4dc7b293..66e71fce68 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/model/AssetAsPlannedEntity.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/asplanned/model/AssetAsPlannedEntity.java @@ -37,12 +37,14 @@ import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.ManufacturingInfo; import org.eclipse.tractusx.traceability.assets.infrastructure.base.model.AssetBaseEntity; import org.eclipse.tractusx.traceability.assets.infrastructure.base.model.SemanticDataModelEntity; -import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.model.AlertNotificationEntity; +import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.model.AlertEntity; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.investigation.model.InvestigationEntity; import java.util.ArrayList; import java.util.List; +import static org.apache.commons.collections4.ListUtils.emptyIfNull; + @Getter @NoArgsConstructor @Entity @@ -65,7 +67,7 @@ public class AssetAsPlannedEntity extends AssetBaseEntity { private List investigations = new ArrayList<>(); @ManyToMany(mappedBy = "assetsAsPlanned") - private List alertNotificationEntities = new ArrayList<>(); + private List alerts = new ArrayList<>(); @Builder @NoArgsConstructor @@ -126,6 +128,8 @@ public static AssetBase toDomain(AssetAsPlannedEntity entity) { .activeAlert(entity.isActiveAlert()) .qualityType(entity.getQualityType()) .detailAspectModels(DetailAspectModel.from(entity)) + .qualityAlerts(emptyIfNull(entity.alerts).stream().map(AlertEntity::toDomain).toList()) + .qualityInvestigations(emptyIfNull(entity.investigations).stream().map(InvestigationEntity::toDomain).toList()) .build(); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/Submodel.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/Submodel.java index 566958239c..84a0f6e8e2 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/Submodel.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/Submodel.java @@ -45,7 +45,8 @@ class Submodel { @Type(value = SemanticDataModel.class, names = { "urn:bamm:com.catenax.batch:1.0.0#Batch", "urn:bamm:io.catenax.batch:1.0.0#Batch", - "urn:bamm:io.catenax.batch:1.0.2#Batch" + "urn:bamm:io.catenax.batch:1.0.2#Batch", + "urn:samm:io.catenax.batch:2.0.0#Batch" }), @Type(value = SemanticDataModel.class, names = { "urn:bamm:io.catenax.part_as_planned:1.0.1#PartAsPlanned", diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationProfiles.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationProfiles.java index 871afaf3e5..3fe151ea28 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationProfiles.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationProfiles.java @@ -27,6 +27,7 @@ public class ApplicationProfiles { public static final String TESTS = "integration"; public static final String INTEGRATION_SPRING_BOOT = "integration-spring-boot"; public static final String NOT_TESTS = "!" + TESTS; + public static final String NOT_INTEGRATION_TESTS = "!" + INTEGRATION_SPRING_BOOT; public static final String DEV = "dev"; public static final String TEST = "test"; public static final String INT = "int"; diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfig.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfig.java index a9ff6b7624..bd88b93622 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfig.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfig.java @@ -34,10 +34,12 @@ import org.eclipse.tractusx.traceability.infrastructure.edc.notificationcontract.controller.model.NotificationType; import org.eclipse.tractusx.traceability.infrastructure.edc.notificationcontract.service.EdcNotificationContractService; +import static org.eclipse.tractusx.traceability.common.config.ApplicationProfiles.NOT_INTEGRATION_TESTS; + @Slf4j @Component -@Profile("!integration") +@Profile(NOT_INTEGRATION_TESTS) @RequiredArgsConstructor public class ApplicationStartupConfig { private final IrsRepository irsRepository; diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/SchedulerConfig.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/SchedulerConfig.java index 992b1b8d85..4e131fd500 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/SchedulerConfig.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/common/config/SchedulerConfig.java @@ -30,10 +30,11 @@ import javax.sql.DataSource; +import static org.eclipse.tractusx.traceability.common.config.ApplicationProfiles.NOT_INTEGRATION_TESTS; import static org.eclipse.tractusx.traceability.common.config.ApplicationProfiles.NOT_TESTS; @Configuration -@Profile(NOT_TESTS) +@Profile(NOT_INTEGRATION_TESTS) public class SchedulerConfig { @Bean diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/infrastructure/edc/properties/EdcProperties.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/infrastructure/edc/properties/EdcProperties.java index d847252695..feec852618 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/infrastructure/edc/properties/EdcProperties.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/infrastructure/edc/properties/EdcProperties.java @@ -26,9 +26,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; -import java.util.Map; -import java.util.Set; - @AllArgsConstructor @Getter @ConfigurationProperties("edc") @@ -42,18 +39,6 @@ public class EdcProperties { @Value("${edc.contractdefinitions}") private String contractDefinitionsPath; - @NotBlank - @Value("${edc.negotiation}") - private String negotiationPath; - - @NotBlank - @Value("${edc.transfer}") - private String transferPath; - - @NotBlank - @Value("${edc.catalog.path}") - private String catalogPath; - @NotBlank @Value("${edc.policydefinitions}") private String policyDefinitionsPath; @@ -67,10 +52,6 @@ public class EdcProperties { private String providerEdcUrl; @NotBlank + @Value("${edc.api-auth-key}") private String apiAuthKey; - - private Map bpnProviderUrlMappings; - - private Set callbackUrls; - } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/alert/request/StartQualityAlertRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/alert/request/StartQualityAlertRequest.java index c212b94590..243908186b 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/alert/request/StartQualityAlertRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/alert/request/StartQualityAlertRequest.java @@ -32,6 +32,7 @@ import java.time.Instant; import java.util.List; +// TODO move to tx-models @Data @Builder @NoArgsConstructor diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/CloseQualityNotificationRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/CloseQualityNotificationRequest.java index 6fbcd20074..f325607e0e 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/CloseQualityNotificationRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/CloseQualityNotificationRequest.java @@ -26,7 +26,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; - +// TODO move to tx-models @Setter @Getter @ToString diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationSeverityRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationSeverityRequest.java index a4679d3333..ebb8c79179 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationSeverityRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationSeverityRequest.java @@ -28,7 +28,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; import java.util.stream.Stream; - +// TODO move to tx-models @ApiModel(description = "Describes the criticality of a notification") public enum QualityNotificationSeverityRequest { MINOR("MINOR"), diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationStatusRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationStatusRequest.java index bc31d096ee..a04d28ca7b 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationStatusRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/QualityNotificationStatusRequest.java @@ -20,7 +20,7 @@ import io.swagger.annotations.ApiModel; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotificationStatus; - +// TODO move to tx-models @ApiModel(description = "Describes status for closed action") public enum QualityNotificationStatusRequest { CLOSED; diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/StartQualityNotificationRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/StartQualityNotificationRequest.java index 7c7cc479bf..c9a4515fc5 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/StartQualityNotificationRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/StartQualityNotificationRequest.java @@ -32,7 +32,7 @@ import java.time.Instant; import java.util.List; - +// TODO move to tx-models @Data @Builder @NoArgsConstructor diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationRequest.java index dfe0b24d83..db705e1dbc 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationRequest.java @@ -24,7 +24,7 @@ import io.swagger.annotations.ApiModelProperty; import jakarta.validation.constraints.NotNull; import lombok.Data; - +// TODO move to tx-models @Data public class UpdateQualityNotificationRequest { @NotNull(message = "status must be present") diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationStatusRequest.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationStatusRequest.java index ab7f9940fc..d4facdbcaa 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationStatusRequest.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/application/base/request/UpdateQualityNotificationStatusRequest.java @@ -26,7 +26,7 @@ import java.util.NoSuchElementException; import java.util.stream.Collectors; import java.util.stream.Stream; - +// TODO move to tx-models @Schema(description = "The UpdateInvestigationStatus") public enum UpdateQualityNotificationStatusRequest { ACKNOWLEDGED, diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotification.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotification.java index 2d7fe21a87..e322a9d2af 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotification.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotification.java @@ -200,6 +200,10 @@ public void addNotification(QualityNotificationMessage notification) { assetIds = Collections.unmodifiableList(newAssetIds); // } + public boolean isActiveState() { + return this.notificationStatus.isActiveState(); + } + public static class QualityNotificationBuilder { public QualityNotificationBuilder notifications(List notifications) { this.notifications = emptyIfNull(notifications).stream() diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java index ee80226c78..813b632ebb 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/domain/base/model/QualityNotificationStatus.java @@ -20,6 +20,7 @@ package org.eclipse.tractusx.traceability.qualitynotification.domain.base.model; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -42,6 +43,8 @@ public enum QualityNotificationStatus { private static final Set NO_TRANSITION_ALLOWED = emptySet(); private static final Map MAPPINGS; + private static final List ACTIVE_STATES = List.of(CREATED, SENT, RECEIVED, ACKNOWLEDGED, ACCEPTED, DECLINED); + static { STATE_MACHINE = Map.of( CREATED, of(SENT, CANCELED), @@ -88,4 +91,8 @@ public boolean transitionAllowed(QualityNotificationStatus to) { private boolean isSideEligibleForTransition(QualityNotificationStatus from, QualityNotificationStatus to) { return to.allowedTransitionFromSide.contains(from.qualityNotificationSide); } + + public boolean isActiveState() { + return ACTIVE_STATES.contains(this); + } } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/model/InvestigationEntity.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/model/InvestigationEntity.java index e627d79f3d..1232aad6e2 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/model/InvestigationEntity.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/qualitynotification/infrastructure/investigation/model/InvestigationEntity.java @@ -46,6 +46,8 @@ import java.util.List; +import static org.apache.commons.collections4.ListUtils.emptyIfNull; + @NoArgsConstructor @Getter @Setter @@ -74,28 +76,28 @@ public class InvestigationEntity extends NotificationBaseEntity { private List notifications; - public static QualityNotification toDomain(InvestigationEntity investigationNotificationEntity) { - List notifications = investigationNotificationEntity.getNotifications().stream() + public static QualityNotification toDomain(InvestigationEntity investigationEntity) { + List notifications = emptyIfNull(investigationEntity.getNotifications()).stream() .map(InvestigationNotificationEntity::toDomain) .toList(); - List assetIds = investigationNotificationEntity.getAssets().stream() + List assetIds = investigationEntity.getAssets().stream() .map(AssetAsBuiltEntity::getId) .toList(); return QualityNotification.builder() - .notificationId(new QualityNotificationId(investigationNotificationEntity.getId())) - .bpn(BPN.of(investigationNotificationEntity.getBpn())) - .notificationStatus(QualityNotificationStatus.fromStringValue(investigationNotificationEntity.getStatus().name())) - .notificationSide(QualityNotificationSide.valueOf(investigationNotificationEntity.getSide().name())) - .closeReason(investigationNotificationEntity.getCloseReason()) - .acceptReason(investigationNotificationEntity.getAcceptReason()) - .declineReason(investigationNotificationEntity.getDeclineReason()) - .createdAt(investigationNotificationEntity.getCreatedDate()) - .description(investigationNotificationEntity.getDescription()) + .notificationId(new QualityNotificationId(investigationEntity.getId())) + .bpn(BPN.of(investigationEntity.getBpn())) + .notificationStatus(QualityNotificationStatus.fromStringValue(investigationEntity.getStatus().name())) + .notificationSide(QualityNotificationSide.valueOf(investigationEntity.getSide().name())) + .closeReason(investigationEntity.getCloseReason()) + .acceptReason(investigationEntity.getAcceptReason()) + .declineReason(investigationEntity.getDeclineReason()) + .createdAt(investigationEntity.getCreatedDate()) + .description(investigationEntity.getDescription()) .assetIds(assetIds) .notifications(notifications) - .errorMessage(investigationNotificationEntity.getErrorMessage()) + .errorMessage(investigationEntity.getErrorMessage()) .build(); } diff --git a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/shelldescriptor/domain/service/DecentralRegistryServiceImpl.java b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/shelldescriptor/domain/service/DecentralRegistryServiceImpl.java index 898f8286f7..1dfe0331b4 100644 --- a/tx-backend/src/main/java/org/eclipse/tractusx/traceability/shelldescriptor/domain/service/DecentralRegistryServiceImpl.java +++ b/tx-backend/src/main/java/org/eclipse/tractusx/traceability/shelldescriptor/domain/service/DecentralRegistryServiceImpl.java @@ -55,6 +55,8 @@ public void updateShellDescriptorAndSynchronizeAssets() { updatedShellDescriptorList.stream() .map(ShellDescriptor::getGlobalAssetId) .forEach(globalAssetId -> { + //TODO: differentiate if this is either as-planned or as-built. Otherwise we have twice the load here. + // DT-Library offers methods to requests additional info to get the bomlifecycle assetAsPlannedService.synchronizeAssetsAsync(globalAssetId); assetAsBuiltService.synchronizeAssetsAsync(globalAssetId); }); diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/semanticdatamodel/SemanticDataModelTest.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/semanticdatamodel/SemanticDataModelTest.java index 207d161a3a..4a84c55d7c 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/semanticdatamodel/SemanticDataModelTest.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/base/irs/model/response/semanticdatamodel/SemanticDataModelTest.java @@ -47,6 +47,7 @@ void test_IsAsPlanned(String aspectType) { "urn:bamm:com.catenax.batch:1.0.0#Batch", "urn:bamm:io.catenax.batch:1.0.0#Batch", "urn:bamm:io.catenax.batch:1.0.2#Batch", + "urn:samm:io.catenax.batch:2.0.0#Batch", "urn:samm:io.catenax.serial_part:1.0.0#SerialPart", "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart", "urn:bamm:io.catenax.serial_part:1.1.0#SerialPart", diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/repository/rest/irs/model/JobDetailResponseTest.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/repository/rest/irs/model/JobDetailResponseTest.java index a0899f3a05..d13c591f4d 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/repository/rest/irs/model/JobDetailResponseTest.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/assets/infrastructure/repository/rest/irs/model/JobDetailResponseTest.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.tractusx.traceability.assets.domain.base.model.AssetBase; import org.eclipse.tractusx.traceability.assets.domain.base.model.Owner; import org.eclipse.tractusx.traceability.assets.infrastructure.base.irs.model.response.JobDetailResponse; @@ -30,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfigTest.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfigTest.java index b6233f2224..3f07466e45 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfigTest.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/common/config/ApplicationStartupConfigTest.java @@ -31,7 +31,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import static io.restassured.RestAssured.when; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/IntegrationTestSpecification.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/IntegrationTestSpecification.java index 6d0ed93eab..d91a6a8e73 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/IntegrationTestSpecification.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/IntegrationTestSpecification.java @@ -35,7 +35,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; -import org.testcontainers.spock.Testcontainers; import java.util.Map; import java.util.concurrent.Callable; @@ -46,7 +45,6 @@ @ActiveProfiles("integration-spring-boot") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @AutoConfigureMockMvc -@Testcontainers @ContextConfiguration(initializers = {PostgreSQLConfig.Initializer.class, RestitoConfig.Initializer.class}, classes = {RestAssuredConfig.class}) public class IntegrationTestSpecification { diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsBuiltControllerByIdIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsBuiltControllerByIdIT.java index a755612673..5bcdb4ecd3 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsBuiltControllerByIdIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsBuiltControllerByIdIT.java @@ -19,8 +19,12 @@ package org.eclipse.tractusx.traceability.integration.assets; import io.restassured.http.ContentType; +import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; +import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.repository.JpaAssetAsBuiltRepository; import org.eclipse.tractusx.traceability.integration.IntegrationTestSpecification; +import org.eclipse.tractusx.traceability.integration.common.support.AlertsSupport; import org.eclipse.tractusx.traceability.integration.common.support.AssetsSupport; +import org.eclipse.tractusx.traceability.integration.common.support.InvestigationsSupport; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity; import org.hamcrest.Matchers; import org.jose4j.lang.JoseException; @@ -30,12 +34,22 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; import java.util.Map; import java.util.stream.Stream; import static io.restassured.RestAssured.given; import static org.eclipse.tractusx.traceability.common.security.JwtRole.ADMIN; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.ACCEPTED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.ACKNOWLEDGED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.CANCELED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.CLOSED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.CREATED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.DECLINED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.RECEIVED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.SENT; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.params.provider.Arguments.arguments; class AssetAsBuiltControllerByIdIT extends IntegrationTestSpecification { @@ -43,6 +57,17 @@ class AssetAsBuiltControllerByIdIT extends IntegrationTestSpecification { @Autowired AssetsSupport assetsSupport; + + @Autowired + JpaAssetAsBuiltRepository jpaAssetAsBuiltRepository; + + @Autowired + AlertsSupport alertsSupport; + + @Autowired + InvestigationsSupport investigationsSupport; + + private static Stream requests() { return Stream.of( arguments(Map.of("qualityType", "NOT_EXISTING_QUALITY_TYPE"), "Failed to deserialize request body."), @@ -52,6 +77,58 @@ private static Stream requests() { ); } + @Test + void givenAlertsForAsset_whenCallAssetById_thenReturnProperCount() throws JoseException { + // Given + assetsSupport.defaultAssetsStored(); + AssetAsBuiltEntity assetAsBuilt = jpaAssetAsBuiltRepository.findById("urn:uuid:d387fa8e-603c-42bd-98c3-4d87fef8d2bb").orElseThrow(); + alertsSupport.storeAlertWithStatusAndAssets(CREATED, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(SENT, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(RECEIVED, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(ACKNOWLEDGED, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(ACCEPTED, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(DECLINED, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(CANCELED, List.of(assetAsBuilt), null); + alertsSupport.storeAlertWithStatusAndAssets(CLOSED, List.of(assetAsBuilt), null); + + // When + given() + .header(oAuth2Support.jwtAuthorization(ADMIN)) + .contentType(ContentType.JSON) + .when() + .get("/api/assets/as-built/urn:uuid:d387fa8e-603c-42bd-98c3-4d87fef8d2bb") + .then() + .statusCode(200) + .assertThat() + .body("qualityAlertsInStatusActive", is(6)); + } + + @Test + void givenInvestigationsForAsset_whenCallAssetById_thenReturnProperCount() throws JoseException { + // Given + assetsSupport.defaultAssetsStored(); + AssetAsBuiltEntity assetAsBuilt = jpaAssetAsBuiltRepository.findById("urn:uuid:d387fa8e-603c-42bd-98c3-4d87fef8d2bb").orElseThrow(); + investigationsSupport.storeInvestigationWithStatusAndAssets(CREATED, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(SENT, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(RECEIVED, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(ACKNOWLEDGED, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(ACCEPTED, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(DECLINED, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(CANCELED, List.of(assetAsBuilt), null); + investigationsSupport.storeInvestigationWithStatusAndAssets(CLOSED, List.of(assetAsBuilt), null); + + // When + given() + .header(oAuth2Support.jwtAuthorization(ADMIN)) + .contentType(ContentType.JSON) + .when() + .get("/api/assets/as-built/urn:uuid:d387fa8e-603c-42bd-98c3-4d87fef8d2bb") + .then() + .statusCode(200) + .assertThat() + .body("qualityInvestigationsInStatusActive", is(6)); + } + @Test void shouldReturnAssetsForAuthenticatedUserWithRole() throws JoseException { //GIVEN @@ -86,7 +163,7 @@ void shouldReturnAssetWithoutUnderInvestigationMark() throws JoseException { @Test void shouldReturnAssetWithUnderInvestigationMark() throws JoseException { //GIVEN - assetsSupport.defaultAssetsStoredWithOnGoingInvestigation(NotificationStatusBaseEntity.SENT, true); + assetsSupport.defaultAssetsStoredWithOnGoingInvestigation(SENT, true); //THEN given() diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsPlannedControllerByIdIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsPlannedControllerByIdIT.java index a67f562f21..5c0980b38a 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsPlannedControllerByIdIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/AssetAsPlannedControllerByIdIT.java @@ -19,8 +19,13 @@ package org.eclipse.tractusx.traceability.integration.assets; import io.restassured.http.ContentType; +import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; +import org.eclipse.tractusx.traceability.assets.infrastructure.asplanned.model.AssetAsPlannedEntity; +import org.eclipse.tractusx.traceability.assets.infrastructure.asplanned.repository.JpaAssetAsPlannedRepository; import org.eclipse.tractusx.traceability.integration.IntegrationTestSpecification; +import org.eclipse.tractusx.traceability.integration.common.support.AlertsSupport; import org.eclipse.tractusx.traceability.integration.common.support.AssetsSupport; +import org.eclipse.tractusx.traceability.integration.common.support.InvestigationsSupport; import org.hamcrest.Matchers; import org.jose4j.lang.JoseException; import org.junit.jupiter.api.Test; @@ -29,12 +34,22 @@ import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; import java.util.Map; import java.util.stream.Stream; import static io.restassured.RestAssured.given; import static org.eclipse.tractusx.traceability.common.security.JwtRole.ADMIN; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.ACCEPTED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.ACKNOWLEDGED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.CANCELED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.CLOSED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.CREATED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.DECLINED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.RECEIVED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.SENT; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.params.provider.Arguments.arguments; class AssetAsPlannedControllerByIdIT extends IntegrationTestSpecification { @@ -42,6 +57,15 @@ class AssetAsPlannedControllerByIdIT extends IntegrationTestSpecification { @Autowired AssetsSupport assetsSupport; + @Autowired + JpaAssetAsPlannedRepository jpaAssetAsPlannedRepository; + + @Autowired + AlertsSupport alertsSupport; + + @Autowired + InvestigationsSupport investigationsSupport; + private static Stream requests() { return Stream.of( arguments(Map.of("qualityType", "NOT_EXISTING_QUALITY_TYPE"), "Failed to deserialize request body."), @@ -51,6 +75,58 @@ private static Stream requests() { ); } + @Test + void givenAlertsForAsset_whenCallAssetById_thenReturnProperCount() throws JoseException { + // Given + assetsSupport.defaultAssetsAsPlannedStored(); + AssetAsPlannedEntity asset = jpaAssetAsPlannedRepository.findById("urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01").orElseThrow(); + alertsSupport.storeAlertWithStatusAndAssets(CREATED, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(SENT, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(RECEIVED, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(ACKNOWLEDGED, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(ACCEPTED, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(DECLINED, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(CANCELED, null, List.of(asset)); + alertsSupport.storeAlertWithStatusAndAssets(CLOSED, null, List.of(asset)); + + // When + given() + .header(oAuth2Support.jwtAuthorization(ADMIN)) + .contentType(ContentType.JSON) + .when() + .get("/api/assets/as-planned/urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01") + .then() + .statusCode(200) + .assertThat() + .body("qualityAlertsInStatusActive", is(6)); + } + + @Test + void givenInvestigationsForAsset_whenCallAssetById_thenReturnProperCount() throws JoseException { + // Given + assetsSupport.defaultAssetsAsPlannedStored(); + AssetAsPlannedEntity asset = jpaAssetAsPlannedRepository.findById("urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01").orElseThrow(); + investigationsSupport.storeInvestigationWithStatusAndAssets(CREATED, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(SENT, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(RECEIVED, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(ACKNOWLEDGED, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(ACCEPTED, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(DECLINED, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(CANCELED, null, List.of(asset)); + investigationsSupport.storeInvestigationWithStatusAndAssets(CLOSED, null, List.of(asset)); + + // When + given() + .header(oAuth2Support.jwtAuthorization(ADMIN)) + .contentType(ContentType.JSON) + .when() + .get("/api/assets/as-planned/urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01") + .then() + .statusCode(200) + .assertThat() + .body("qualityInvestigationsInStatusActive", is(6)); + } + @Test void shouldReturnAssetsForAuthenticatedUserWithRole() throws JoseException { //GIVEN diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java index 6b6ed0f055..6bdd59adef 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/assets/DashboardControllerIT.java @@ -30,6 +30,7 @@ import org.eclipse.tractusx.traceability.integration.common.support.InvestigationsSupport; import org.eclipse.tractusx.traceability.qualitynotification.application.base.request.QualityNotificationSeverityRequest; import org.eclipse.tractusx.traceability.qualitynotification.application.base.request.StartQualityNotificationRequest; +import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity; import org.jose4j.lang.JoseException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -47,6 +48,8 @@ import static org.eclipse.tractusx.traceability.common.security.JwtRole.ADMIN; import static org.eclipse.tractusx.traceability.common.security.JwtRole.SUPERVISOR; import static org.eclipse.tractusx.traceability.common.security.JwtRole.USER; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.RECEIVED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.SENT; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.params.provider.Arguments.arguments; @@ -104,8 +107,8 @@ void givenAlertsWithAssets_whenGetDashboard_thenReturnResponse() throws JoseExce List supplierAssets = assets.stream() .filter(asset -> asset.getOwner().equals(Owner.SUPPLIER)) .toList(); - alertsSupport.storeAlertWithStatusAndAssets(supplierAssets); - alertsSupport.defaultSentAlertStoredForAssets(ownAssets); + alertsSupport.storeAlertWithStatusAndAssets(RECEIVED, supplierAssets, null); + alertsSupport.storeAlertWithStatusAndAssets(SENT, ownAssets, null); // when/then given() diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/AlertsSupport.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/AlertsSupport.java index 185ff1a36b..a19cf40f2d 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/AlertsSupport.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/AlertsSupport.java @@ -20,6 +20,8 @@ package org.eclipse.tractusx.traceability.integration.common.support; import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; +import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.repository.JpaAssetAsBuiltRepository; +import org.eclipse.tractusx.traceability.assets.infrastructure.asplanned.model.AssetAsPlannedEntity; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotificationStatus; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.model.AlertEntity; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.repository.JpaAlertRepository; @@ -41,6 +43,9 @@ public class AlertsSupport { @Autowired JpaAlertRepository jpaAlertRepository; + @Autowired + JpaAssetAsBuiltRepository jpaAssetAsBuiltRepository; + public Long defaultReceivedAlertStored() { AlertEntity entity = AlertEntity.builder() .assets(Collections.emptyList()) @@ -54,15 +59,7 @@ public Long defaultReceivedAlertStored() { return storedAlert(entity); } - public Long storeAlertWithStatusAndAssets(List assets) { - return storeAlertWithStatusAndAssets(NotificationStatusBaseEntity.RECEIVED, assets); - } - - public Long defaultSentAlertStoredForAssets(List assets) { - return storeAlertWithStatusAndAssets(NotificationStatusBaseEntity.SENT, assets); - } - - private Long storeAlertWithStatusAndAssets(NotificationStatusBaseEntity status, List assets) { + public Long storeAlertWithStatusAndAssets(NotificationStatusBaseEntity status, List assetsAsBuilt, List assetsAsPlanned) { AlertEntity entity = AlertEntity.builder() .assets(Collections.emptyList()) .bpn("BPNL00000003AXS3") @@ -72,7 +69,8 @@ private Long storeAlertWithStatusAndAssets(NotificationStatusBaseEntity status, .build(); Long alertId = storedAlert(entity); AlertEntity savedAlert = jpaAlertRepository.findById(alertId).get(); - savedAlert.setAssets(assets); + savedAlert.setAssets(assetsAsBuilt); + savedAlert.setAssetsAsPlanned(assetsAsPlanned); jpaAlertRepository.save(savedAlert); return alertId; } diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/InvestigationsSupport.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/InvestigationsSupport.java index b7a065cc22..3b1fe051f6 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/InvestigationsSupport.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/common/support/InvestigationsSupport.java @@ -19,6 +19,8 @@ package org.eclipse.tractusx.traceability.integration.common.support; +import org.eclipse.tractusx.traceability.assets.infrastructure.asbuilt.model.AssetAsBuiltEntity; +import org.eclipse.tractusx.traceability.assets.infrastructure.asplanned.model.AssetAsPlannedEntity; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotificationStatus; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.investigation.model.InvestigationEntity; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.investigation.repository.JpaInvestigationRepository; @@ -53,6 +55,22 @@ public Long defaultReceivedInvestigationStored() { return jpaInvestigationRepository.save(entity).getId(); } + public Long storeInvestigationWithStatusAndAssets(NotificationStatusBaseEntity status, List assetsAsBuilt, List assetsAsPlanned) { + InvestigationEntity entity = InvestigationEntity.builder() + .assets(Collections.emptyList()) + .bpn("BPNL00000003AXS3") + .status(status) + .side(NotificationSideBaseEntity.RECEIVER) + .createdDate(Instant.now()) + .build(); + Long alertId = storedInvestigation(entity); + InvestigationEntity savedInvestigation = jpaInvestigationRepository.findById(alertId).get(); + savedInvestigation.setAssets(assetsAsBuilt); + savedInvestigation.setAssetsAsPlanned(assetsAsPlanned); + jpaInvestigationRepository.save(savedInvestigation); + return alertId; + } + public void assertInvestigationsSize(int size) { List investigations = jpaInvestigationRepository.findAll(); diff --git a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/qualitynotification/alert/AlertsRepositoryIT.java b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/qualitynotification/alert/AlertsRepositoryIT.java index 192d5b79f5..5fdd70e176 100644 --- a/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/qualitynotification/alert/AlertsRepositoryIT.java +++ b/tx-backend/src/test/java/org/eclipse/tractusx/traceability/integration/qualitynotification/alert/AlertsRepositoryIT.java @@ -28,12 +28,15 @@ import org.eclipse.tractusx.traceability.qualitynotification.domain.base.AlertRepository; import org.eclipse.tractusx.traceability.qualitynotification.domain.base.model.QualityNotificationStatus; import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.alert.repository.JpaAlertRepository; +import org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.RECEIVED; +import static org.eclipse.tractusx.traceability.qualitynotification.infrastructure.model.NotificationStatusBaseEntity.SENT; class AlertsRepositoryIT extends IntegrationTestSpecification { @@ -63,9 +66,9 @@ void givenAlertsWithAssets_whenCountDistinctAffectedSupplierPartsWithAlertReceiv List supplierAssets = assets.stream() .filter(asset -> asset.getOwner().equals(Owner.SUPPLIER)) .toList(); - alertsSupport.storeAlertWithStatusAndAssets(supplierAssets); - alertsSupport.storeAlertWithStatusAndAssets(supplierAssets); - alertsSupport.defaultSentAlertStoredForAssets(ownAssets); + alertsSupport.storeAlertWithStatusAndAssets(RECEIVED, supplierAssets, null); + alertsSupport.storeAlertWithStatusAndAssets(RECEIVED, supplierAssets, null); + alertsSupport.storeAlertWithStatusAndAssets(SENT, ownAssets, null); assertThat(jpaAlertRepository.findAll()).hasSize(3); // when @@ -86,9 +89,9 @@ void givenAlertsWithAssets_whenCountDistinctAffectedOwnPartsWithAlertSentStatus_ List supplierAssets = assets.stream() .filter(asset -> asset.getOwner().equals(Owner.SUPPLIER)) .toList(); - alertsSupport.storeAlertWithStatusAndAssets(supplierAssets); - alertsSupport.defaultSentAlertStoredForAssets(ownAssets); - alertsSupport.defaultSentAlertStoredForAssets(ownAssets); + alertsSupport.storeAlertWithStatusAndAssets(RECEIVED, supplierAssets, null); + alertsSupport.storeAlertWithStatusAndAssets(SENT, ownAssets, null); + alertsSupport.storeAlertWithStatusAndAssets(SENT, ownAssets, null); assertThat(jpaAlertRepository.findAll()).hasSize(3); // when diff --git a/tx-backend/testdata/CX_Testdata_MessagingTest_v0.0.9.json b/tx-backend/testdata/CX_Testdata_MessagingTest_v0.0.9.json new file mode 100644 index 0000000000..6be876e98b --- /dev/null +++ b/tx-backend/testdata/CX_Testdata_MessagingTest_v0.0.9.json @@ -0,0 +1,1208 @@ +{ + "policies" : { + "ID 3.0 Trace" : { + "@context" : { + "odrl" : "http://www.w3.org/ns/odrl/2/" + }, + "@type" : "PolicyDefinitionRequestDto", + "@id" : "id-3.0-trace", + "policy" : { + "@type" : "Policy", + "odrl:permission" : [ + { + "odrl:action" : "USE", + "odrl:constraint" : { + "@type" : "AtomicConstraint", + "odrl:or" : [ + { + "@type" : "Constraint", + "odrl:leftOperand" : "PURPOSE", + "odrl:operator" : { + "@id" : "odrl:eq" + }, + "odrl:rightOperand" : "ID 3.0 Trace" + } + ] + } + } + ] + } + } + }, + "https://catenax.io/schema/TestDataContainer/1.0.0" : [ + { + "catenaXId" : "urn:uuid:0fed587c-7ab4-4597-9841-1718e9693003", + "bpnl" : "BPNL00000003CML1", + "PlainObject" : [ + { + "BPN_OEM_C" : "BPNL00000003CML1", + "BPN_OEM_A" : "BPNL00000003CSGV", + "BPN_OEM_B" : "BPNL00000003AZQP", + "BPN_OEM_D" : "BPNL00000003CNKC", + "CREATION_DATE" : "2022-09-26T12:43:51.079Z" + } + ] + }, + { + "catenaXId" : "urn:uuid:6b2296cc-26c0-4f38-8a22-092338c36e22", + "bpnl" : "BPNL00000003CML1", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:6b2296cc-26c0-4f38-8a22-092338c36e22", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:7eeeac86-7b69-444d-81e6-655d0f1513bd", + "businessPartner" : "BPNL00000003CNKC" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "IF-53", + "key" : "manufacturerPartId" + }, + { + "value" : "OMAOYGBDTSRCMYSCX", + "key" : "partInstanceId" + }, + { + "value" : "OMAOYGBDTSRCMYSCX", + "key" : "van" + } + ], + "manufacturingInformation" : { + "date" : "2018-09-28T04:15:57.000Z", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:6b2296cc-26c0-4f38-8a22-092338c36e22", + "partTypeInformation" : { + "manufacturerPartId" : "IF-53", + "classification" : "product", + "nameAtManufacturer" : "Vehicle Hybrid" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:7eeeac86-7b69-444d-81e6-655d0f1513bd", + "bpnl" : "BPNL00000003CNKC", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:7eeeac86-7b69-444d-81e6-655d0f1513bd", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:1d2d8480-90a5-4a17-9ecb-2ff039d35fec", + "businessPartner" : "BPNL00000003CSGV" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "22782277-50", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-313869652971440618042264", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:7eeeac86-7b69-444d-81e6-655d0f1513bd", + "partTypeInformation" : { + "manufacturerPartId" : "22782277-50", + "customerPartId" : "22782277-50", + "classification" : "component", + "nameAtManufacturer" : "Door f-l", + "nameAtCustomer" : "Door front-left" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:6b2296cc-26c0-4f38-8a22-092338c36e22", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CML1" + } + ], + "catenaXId" : "urn:uuid:7eeeac86-7b69-444d-81e6-655d0f1513bd" + } + ] + }, + { + "catenaXId" : "urn:uuid:1d2d8480-90a5-4a17-9ecb-2ff039d35fec", + "bpnl" : "BPNL00000003CSGV", + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CSGV", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-584478761469608738361558", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1d2d8480-90a5-4a17-9ecb-2ff039d35fec", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "customerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key", + "nameAtCustomer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:7eeeac86-7b69-444d-81e6-655d0f1513bd", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CNKC" + } + ], + "catenaXId" : "urn:uuid:1d2d8480-90a5-4a17-9ecb-2ff039d35fec" + } + ] + }, + { + "catenaXId" : "urn:uuid:d8030bbf-a874-49fb-b2e1-7610f0ccad12", + "bpnl" : "BPNL00000003CML1", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:d8030bbf-a874-49fb-b2e1-7610f0ccad12", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:5205f736-8fc2-4585-b869-6bf36842369a", + "businessPartner" : "BPNL00000003CNKC" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "JQ-87", + "key" : "manufacturerPartId" + }, + { + "value" : "OMAYSKEITUGNVHKKX", + "key" : "partInstanceId" + }, + { + "value" : "OMAYSKEITUGNVHKKX", + "key" : "van" + } + ], + "manufacturingInformation" : { + "date" : "2015-03-07T19:38:12.000Z", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:d8030bbf-a874-49fb-b2e1-7610f0ccad12", + "partTypeInformation" : { + "manufacturerPartId" : "JQ-87", + "classification" : "product", + "nameAtManufacturer" : "Vehicle Hybrid" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:5205f736-8fc2-4585-b869-6bf36842369a", + "bpnl" : "BPNL00000003CNKC", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:5205f736-8fc2-4585-b869-6bf36842369a", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:4e390dab-707f-446e-bfbe-653f6f5b1f37", + "businessPartner" : "BPNL00000003AZQP" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "22782277-50", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-989134870198932317923938", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:5205f736-8fc2-4585-b869-6bf36842369a", + "partTypeInformation" : { + "manufacturerPartId" : "22782277-50", + "customerPartId" : "22782277-50", + "classification" : "component", + "nameAtManufacturer" : "Door f-l", + "nameAtCustomer" : "Door front-left" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:d8030bbf-a874-49fb-b2e1-7610f0ccad12", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CML1" + } + ], + "catenaXId" : "urn:uuid:5205f736-8fc2-4585-b869-6bf36842369a" + } + ] + }, + { + "catenaXId" : "urn:uuid:4e390dab-707f-446e-bfbe-653f6f5b1f37", + "bpnl" : "BPNL00000003AZQP", + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003AZQP", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-493575190274381019348907", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:4e390dab-707f-446e-bfbe-653f6f5b1f37", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "customerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key", + "nameAtCustomer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:5205f736-8fc2-4585-b869-6bf36842369a", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CNKC" + } + ], + "catenaXId" : "urn:uuid:4e390dab-707f-446e-bfbe-653f6f5b1f37" + } + ] + }, + { + "catenaXId" : "urn:uuid:7c7d5aec-b15d-491c-8fbd-c61c6c02c69a", + "bpnl" : "BPNL00000003AZQP", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:7c7d5aec-b15d-491c-8fbd-c61c6c02c69a", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:f11ddc62-3bd5-468f-b7b0-110fe13ed0cd", + "businessPartner" : "BPNL00000003CNKC" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003AZQP", + "key" : "manufacturerId" + }, + { + "value" : "WZ-95", + "key" : "manufacturerPartId" + }, + { + "value" : "OMAZRXWWMSPTQUEKI", + "key" : "partInstanceId" + }, + { + "value" : "OMAZRXWWMSPTQUEKI", + "key" : "van" + } + ], + "manufacturingInformation" : { + "date" : "2015-07-04T14:30:31.000Z", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:7c7d5aec-b15d-491c-8fbd-c61c6c02c69a", + "partTypeInformation" : { + "manufacturerPartId" : "WZ-95", + "classification" : "product", + "nameAtManufacturer" : "Vehicle Hybrid" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:f11ddc62-3bd5-468f-b7b0-110fe13ed0cd", + "bpnl" : "BPNL00000003CNKC", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:f11ddc62-3bd5-468f-b7b0-110fe13ed0cd", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:4a5e9ff6-2d5c-4510-a90e-d55af3ba502f", + "businessPartner" : "BPNL00000003CML1" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "22782277-50", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-004314332935115065980115", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:f11ddc62-3bd5-468f-b7b0-110fe13ed0cd", + "partTypeInformation" : { + "manufacturerPartId" : "22782277-50", + "customerPartId" : "22782277-50", + "classification" : "component", + "nameAtManufacturer" : "Door f-l", + "nameAtCustomer" : "Door front-left" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:7c7d5aec-b15d-491c-8fbd-c61c6c02c69a", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003AZQP" + } + ], + "catenaXId" : "urn:uuid:f11ddc62-3bd5-468f-b7b0-110fe13ed0cd" + } + ] + }, + { + "catenaXId" : "urn:uuid:4a5e9ff6-2d5c-4510-a90e-d55af3ba502f", + "bpnl" : "BPNL00000003CML1", + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-246880451848384868750731", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:4a5e9ff6-2d5c-4510-a90e-d55af3ba502f", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "customerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key", + "nameAtCustomer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:f11ddc62-3bd5-468f-b7b0-110fe13ed0cd", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CNKC" + } + ], + "catenaXId" : "urn:uuid:4a5e9ff6-2d5c-4510-a90e-d55af3ba502f" + } + ] + }, + { + "catenaXId" : "urn:uuid:4d33bfa6-0f1f-43a5-ad63-c3fe07a2d076", + "bpnl" : "BPNL00000003CSGV", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:4d33bfa6-0f1f-43a5-ad63-c3fe07a2d076", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:c47b9f8b-48d0-4ef4-8f0b-e965a225cb8d", + "businessPartner" : "BPNL00000003CNKC" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CSGV", + "key" : "manufacturerId" + }, + { + "value" : "KZ-19", + "key" : "manufacturerPartId" + }, + { + "value" : "OMAFIVCDHEBNXKNHH", + "key" : "partInstanceId" + }, + { + "value" : "OMAFIVCDHEBNXKNHH", + "key" : "van" + } + ], + "manufacturingInformation" : { + "date" : "2015-01-03T03:16:41.000Z", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:4d33bfa6-0f1f-43a5-ad63-c3fe07a2d076", + "partTypeInformation" : { + "manufacturerPartId" : "KZ-19", + "classification" : "product", + "nameAtManufacturer" : "Vehicle Hybrid" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:c47b9f8b-48d0-4ef4-8f0b-e965a225cb8d", + "bpnl" : "BPNL00000003CNKC", + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:c47b9f8b-48d0-4ef4-8f0b-e965a225cb8d", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:6ec3f1db-2798-454b-a73f-0d21a8966c74", + "businessPartner" : "BPNL00000003CML1" + } + ] + } + ], + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "22782277-50", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-477013846751358222215326", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:c47b9f8b-48d0-4ef4-8f0b-e965a225cb8d", + "partTypeInformation" : { + "manufacturerPartId" : "22782277-50", + "customerPartId" : "22782277-50", + "classification" : "component", + "nameAtManufacturer" : "Door f-l", + "nameAtCustomer" : "Door front-left" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:4d33bfa6-0f1f-43a5-ad63-c3fe07a2d076", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CSGV" + } + ], + "catenaXId" : "urn:uuid:c47b9f8b-48d0-4ef4-8f0b-e965a225cb8d" + } + ] + }, + { + "catenaXId" : "urn:uuid:6ec3f1db-2798-454b-a73f-0d21a8966c74", + "bpnl" : "BPNL00000003CML1", + "urn:bamm:io.catenax.serial_part:1.0.0#SerialPart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-613963493493659233961306", + "key" : "partInstanceId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:6ec3f1db-2798-454b-a73f-0d21a8966c74", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "customerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key", + "nameAtCustomer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:c47b9f8b-48d0-4ef4-8f0b-e965a225cb8d", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CNKC" + } + ], + "catenaXId" : "urn:uuid:6ec3f1db-2798-454b-a73f-0d21a8966c74" + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284b170", + "bpnl" : "BPNL00000003CSGV", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CSGV", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284b170", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:6ec3f1db-2798-454b-a73f-0d21a8966c74", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CML1" + } + ], + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284b170" + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa01", + "bpnl" : "BPNL00000003CML1", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa01", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa02", + "bpnl" : "BPNL00000003CML1", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa02", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa02", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb01", + "businessPartner" : "BPNL00000003CNKC" + } + ] + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb01", + "bpnl" : "BPNL00000003CNKC", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb01", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa02", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CML1" + } + ], + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb01" + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb02", + "bpnl" : "BPNL00000003CNKC", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb02", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_bom_as_built:1.0.0#SingleLevelBomAsBuilt" : [ + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb02", + "childItems" : [ + { + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa03", + "businessPartner" : "BPNL00000003CML1" + } + ] + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa03", + "bpnl" : "BPNL00000003CML1", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa03", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ], + "urn:bamm:io.catenax.single_level_usage_as_built:1.0.1#SingleLevelUsageAsBuilt" : [ + { + "parentParts" : [ + { + "parentCatenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb02", + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2023-02-03T14:48:54.709Z", + "lastModifiedOn" : "2023-02-03T14:48:54.709Z", + "businessPartner" : "BPNL00000003CNKC" + } + ], + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fa03" + } + ] + }, + { + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb03", + "bpnl" : "BPNL00000003CNKC", + "urn:samm:io.catenax.batch:2.0.0#Batch" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "95657762-59", + "key" : "manufacturerPartId" + }, + { + "value" : "NO-341449848714937445621543", + "key" : "batchId" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "DEU" + }, + "catenaXId" : "urn:uuid:1be6ec59-40fb-4993-9836-acb0e284fb03", + "partTypeInformation" : { + "manufacturerPartId" : "95657762-59", + "classification" : "component", + "nameAtManufacturer" : "Door Key" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01", + "bpnl" : "BPNL00000003CML1", + "urn:bamm:io.catenax.part_as_planned:1.0.1#PartAsPlanned" : [ + { + "validityPeriod" : { + "validFrom" : "2019-04-04T03:19:03.000Z", + "validTo" : "2024-12-29T10:25:12.000Z" + }, + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01", + "partTypeInformation" : { + "manufacturerPartId" : "ZX-55", + "classification" : "product", + "nameAtManufacturer" : "Vehicle Model A" + } + } + ], + "urn:bamm:io.catenax.part_site_information_as_planned:1.0.0#PartSiteInformationAsPlanned" : [ + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01", + "sites" : [ + { + "functionValidUntil" : "2025-02-08T04:30:48.000Z", + "function" : "production", + "functionValidFrom" : "2019-08-21T02:10:36.000Z", + "catenaXSiteId" : "BPNS000004711DMY" + } + ] + } + ], + "urn:bamm:io.catenax.single_level_bom_as_planned:2.0.0#SingleLevelBomAsPlanned" : [ + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01", + "childItems" : [ + { + "validityPeriod" : { + "validFrom" : "2023-03-21T08:17:29.187+01:00", + "validTo" : "2024-07-01T16:10:00.000+01:00" + }, + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4eb01", + "businessPartner" : "BPNL00000003CNKC" + } + ] + } + ] + }, + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4eb01", + "bpnl" : "BPNL00000003CNKC", + "urn:bamm:io.catenax.part_as_planned:1.0.1#PartAsPlanned" : [ + { + "validityPeriod" : { + "validFrom" : "2019-04-04T03:19:03.000Z", + "validTo" : "2024-12-29T10:25:12.000Z" + }, + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4eb01", + "partTypeInformation" : { + "manufacturerPartId" : "ZX-55", + "classification" : "product", + "nameAtManufacturer" : "Vehicle Model B" + } + } + ], + "urn:bamm:io.catenax.part_site_information_as_planned:1.0.0#PartSiteInformationAsPlanned" : [ + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4eb01", + "sites" : [ + { + "functionValidUntil" : "2025-02-08T04:30:48.000Z", + "function" : "production", + "functionValidFrom" : "2019-08-21T02:10:36.000Z", + "catenaXSiteId" : "BPNS000004711DMY" + } + ] + } + ], + "urn:bamm:io.catenax.single_level_bom_as_planned:2.0.0#SingleLevelBomAsPlanned" : [ + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4eb01", + "childItems" : [ + { + "validityPeriod" : { + "validFrom" : "2023-03-21T08:17:29.187+01:00", + "validTo" : "2024-07-01T16:10:00.000+01:00" + }, + "quantity" : { + "quantityNumber" : 1, + "measurementUnit" : "unit:piece" + }, + "createdOn" : "2022-02-03T14:48:54.709Z", + "lastModifiedOn" : "2022-02-03T14:48:54.709Z", + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e4da01", + "businessPartner" : "BPNL00000003CML1" + } + ] + } + ] + }, + { + "catenaXId" : "urn:uuid:580d3adf-1981-44a0-a214-13d6ceed6841", + "bpnl" : "BPNL00000003CNKC", + "urn:bamm:io.catenax.just_in_sequence_part:1.0.0#JustInSequencePart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CNKC", + "key" : "manufacturerId" + }, + { + "value" : "12345678ABC", + "key" : "jisNumber" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "HUR" + }, + "catenaXId" : "urn:uuid:580d3adf-1981-44a0-a214-13d6ceed6841", + "partTypeInformation" : { + "manufacturerPartId" : "123-0.740-3434-A", + "customerPartId" : "PRT-12345", + "classification" : "product", + "nameAtManufacturer" : "Mirror left", + "nameAtCustomer" : "side element A" + } + } + ] + }, + { + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e43842", + "bpnl" : "BPNL00000003CML1", + "urn:bamm:io.catenax.just_in_sequence_part:1.0.0#JustInSequencePart" : [ + { + "localIdentifiers" : [ + { + "value" : "BPNL00000003CML1", + "key" : "manufacturerId" + }, + { + "value" : "12345678ABC", + "key" : "jisNumber" + } + ], + "manufacturingInformation" : { + "date" : "2022-02-04T14:48:54", + "country" : "HUR" + }, + "catenaXId" : "urn:uuid:0733946c-59c6-41ae-9570-cb43a6e43842", + "partTypeInformation" : { + "manufacturerPartId" : "123-0.740-3434-A", + "customerPartId" : "PRT-12345", + "classification" : "product", + "nameAtManufacturer" : "Mirror left", + "nameAtCustomer" : "side element A" + } + } + ] + } + ] +} diff --git a/tx-cucumber-tests/src/test/java/org/eclipse/tractusx/traceability/test/TraceabilityTestStepDefinition.java b/tx-cucumber-tests/src/test/java/org/eclipse/tractusx/traceability/test/TraceabilityTestStepDefinition.java index f72d388c0a..99f3d29a93 100644 --- a/tx-cucumber-tests/src/test/java/org/eclipse/tractusx/traceability/test/TraceabilityTestStepDefinition.java +++ b/tx-cucumber-tests/src/test/java/org/eclipse/tractusx/traceability/test/TraceabilityTestStepDefinition.java @@ -201,7 +201,7 @@ public void iDeclineQualityInvestigation(DataTable dataTable) { public void iDeclineQualityAlert(DataTable dataTable) { String reason = normalize(dataTable.asMap()).get("reason"); System.out.println("reason: " + reason); - restProvider.updateNotification(INVESTIGATION, getNotificationIdBasedOnEnv(), DECLINED, reason); + restProvider.updateNotification(ALERT, getNotificationIdBasedOnEnv(), DECLINED, reason); } private Long getNotificationIdBasedOnEnv() { diff --git a/tx-models/src/main/java/assets/response/base/AssetBaseResponse.java b/tx-models/src/main/java/assets/response/base/AssetBaseResponse.java index 95d54fc2f4..f46b33aab8 100644 --- a/tx-models/src/main/java/assets/response/base/AssetBaseResponse.java +++ b/tx-models/src/main/java/assets/response/base/AssetBaseResponse.java @@ -75,4 +75,8 @@ public class AssetBaseResponse { @Size(max = 255) private String classification; private List detailAspectModels; + @ApiModelProperty(example = "1") + private Integer qualityAlertsInStatusActive; + @ApiModelProperty(example = "2") + private Integer qualityInvestigationsInStatusActive; }