diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index f86851c9..08cd4860 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -1,8 +1,28 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 name: gitleaks on: [push, pull_request, workflow_dispatch] jobs: - gitleaks: + gitleaks-run: runs-on: ubuntu-latest + container: + image: zricethezav/gitleaks:latest + options: --user root steps: - name: Checkout uses: actions/checkout@v2 @@ -11,30 +31,29 @@ jobs: - name: Run Gitleaks id: gitleaks - uses: DariuszPorowski/github-action-gitleaks@v2 - with: - report_format: "sarif" - fail: true - # config: "/.gitleaks/GitleaksUdmCombo.toml" - - - name: Get the output from the gitleaks step run: | - echo "exitcode: ${{ steps.gitleaks.outputs.exitcode }}" - echo "result: ${{ steps.gitleaks.outputs.result }}" - echo "output: ${{ steps.gitleaks.outputs.output }}" - echo "command: ${{ steps.gitleaks.outputs.command }}" - echo "report: ${{ steps.gitleaks.outputs.report }}" - if: always() + git config --global --add safe.directory $PWD + gitleaks detect -f sarif -r ./gitleaks-report-semantic-hub.sarif --exit-code 0 - - name: Upload Gitleaks output as artifact - uses: actions/upload-artifact@v1 + - name: Upload artifact + uses: actions/upload-artifact@v3 with: - name: gitleaks.sarif - path: ${{ steps.gitleaks.outputs.report }} - if: always() + name: gitleaks-report + path: ./gitleaks-report-semantic-hub.sarif + + gitleaks-upload: + runs-on: ubuntu-latest + needs: gitleaks-run + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: gitleaks-report - name: Upload SARIF report if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: ${{ steps.gitleaks.outputs.report }} + sarif_file: ./gitleaks-report-semantic-hub.sarif diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml new file mode 100644 index 00000000..3e64fdf2 --- /dev/null +++ b/.github/workflows/helm-release.yml @@ -0,0 +1,55 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 +--- + +name: Release - Helm Charts + +on: + push: + paths: + - 'charts/**' + branches: + - main + workflow_dispatch: + +jobs: + release: + permissions: + contents: write + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.4.1 + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml new file mode 100644 index 00000000..e9ae7138 --- /dev/null +++ b/.github/workflows/kics.yml @@ -0,0 +1,72 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 +--- + +name: "KICS" + +on: + push: + branches: [main, master] + # pull_request: + # The branches below must be a subset of the branches above + # branches: [main, master] + # paths-ignore: + # - "**/*.md" + # - "**/*.txt" + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - uses: actions/checkout@v3 + + - name: KICS scan + uses: checkmarx/kics-github-action@master + with: + # Scanning directory . + path: "." + # Exclude paths from scan by providing the paths as comma separated list + # exclude_paths: "postgres-init.yaml,templates/sharedidp.yaml" + # Exclude queries by providing the query / rule ID as comma separated list + # exclude_queries: "b9c83569-459b-4110-8f79-6305aa33cb37" + # Fail on HIGH severity results + fail_on: high + # Disable secrets detection - we use GitGuardian + disable_secrets: true + # When provided with a directory on output_path + # it will generate the specified reports file named 'results.{extension}' + # in this example it will generate: + # - results-dir/results.json and results-dir/results.sarif + output_path: kicsResults/ + output_formats: "json,sarif" + + # Upload findings to GitHub Advanced Security Dashboard + - name: Upload SARIF file for GitHub Advanced Security Dashboard + if: always() + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: kicsResults/results.sarif diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f0a51f84..d0244066 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,20 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 --- name: "Create new version tag" diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml deleted file mode 100644 index 63c75688..00000000 --- a/.github/workflows/trivy-scan.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Trivy Scan - -on: - schedule: - - cron: 0 2 * * 0 - workflow_dispatch: - -jobs: - analyze-semantic-hub: - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - uses: actions/setup-java@v3 - with: - distribution: 'temurin' - java-version: '11' - - name: Build JAR - run: mvn clean package - - name: Build Image - run: docker build -t semantic-hub ./backend - - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@master - with: - image-ref: semantic-hub - # ignore-unfixed: true - exit-code: "1" - hide-progress: false - format: "sarif" - output: "trivy-results-semantic-hub.sarif" - severity: "CRITICAL,HIGH" - - - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v1 - if: always() - with: - sarif_file: "trivy-results-semantic-hub.sarif" diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml new file mode 100644 index 00000000..174b1d98 --- /dev/null +++ b/.github/workflows/trivy.yml @@ -0,0 +1,90 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 +name: Trivy + +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: + +jobs: + analyze-config: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Run Trivy vulnerability scanner in repo mode + uses: aquasecurity/trivy-action@master + with: + scan-type: "config" + # ignore-unfixed: true + exit-code: "1" + hide-progress: false + format: "sarif" + output: "trivy-results1.sarif" + severity: "CRITICAL,HIGH" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: "trivy-results1.sarif" + + analyze-semantic-hub: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + - name: Build JAR + run: mvn clean package + + - name: Build Image + run: docker build -t semantic-hub ./backend + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: semantic-hub + # ignore-unfixed: true + exit-code: "1" + hide-progress: false + format: "sarif" + output: "trivy-results-semantic-hub.sarif" + severity: "CRITICAL,HIGH" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: "trivy-results-semantic-hub.sarif" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..6e8c0e31 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,45 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0-M2 +### Added +- Swagger UI now integrated with Portal Authentication +- Filter endpoint for search of list of URNs +- Helm Charts available via helm Repository at Eclipse Foundation + +### Fixed +- Update jackson-databind to version 2.13.3 +- Update snakeyaml to version to 1.31 + +### Changed +- Update of Spring Boot version to 2.7.3 + +## 0.1.0-M1 +### Fixed +- Adjust tests to avoid duplicates +### Changed +- Deactivate name search + +## 0.0.1-M2 +### Added +- The Semantic Hub now allows the generation and export of AAS files +### Changed +- Switched to BAMM SDK version 2.0.0 + +## 0.0.1-M1 +### Added +- The Semantic Hub allows the creation, update, deletion of Semantic models in the RDF based format BAMM Aspect Meta Model (short BAMM) +- Users can use the Semantic Hub API to view models and their meta information (such as version and current status, e.g. whether it is ready to be used) +- The Semantic Hub provides endpoints to generate artifacts from the BAMM models: + - example payload JSON + - detailed HTML documentation describing the model + - a diagram of the model + - an OpenAPI description + - an Asset Administration Shell representation of the model as a submodel template + - a JSON schema representation of the model +- The Semantic Hub allows the reuse of model components between different BAMM aspects and can resolve these dependencies +- The Semantic Hub prevents unauthenticated access by checking whether an access token is provided by a CX user +- The Semantic Hub enforces that only users with the correct role can read/create/update/delete semantic models diff --git a/README.md b/README.md index 6576c7a9..e3076380 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ The source code under this folder contains reference implementations of the SLDT Run `mvn install` to run unit tests, build and install the package. ## Run Package Locally -To check whether the build was successful, you can start the resulting JAR file from the build process by running `java -jar target/semantic-hub-{current-version}.jar`. +To check whether the build was successful, you can start the resulting JAR file from the build process by running `java -jar target/semantic-hub-backend-{current-version}.jar`. ## Build Docker Run `docker build -t semantic-hub .` @@ -35,7 +35,7 @@ Run `docker build -t semantic-hub .` In case you want to publish your image into a remote container registry, apply the tag accordingly and `docker push` the image. ## Deploy using Helm and K8s -If you have a running Kubernetes cluster available, you can deploy the Semantic Hub using our Helm Chart, which is located under `./deployment/semantic-hub`. +If you have a running Kubernetes cluster available, you can deploy the Semantic Hub using our Helm Chart, which is located under `./charts/semantic-hub`. In case you don't have a running cluster, you can set up one by yourself locally, using [minikube](https://minikube.sigs.k8s.io/docs/start/). In the following, we will use a minikube cluster for reference. @@ -47,9 +47,11 @@ Before deploying the Semantic Hub, enable a few add-ons in your minikube cluster `minikube addons enable ingress` +If you want to use the in-memory triple store that is not persistent (useful for local deployments) set `embeddedTripleStore: true`. + In order to deploy the helm chart, first create a new namespace "semantics": `kubectl create namespace semantics`. -Then run `helm install hub -n semantics ./deployment/semantic-hub`. This will set up a new helm deployment in the semantics namespace. By default, the deployment contains the Semantic Hub instance itself, and a Fuseki Triplestore. +Then run `helm install hub -n semantics ./charts/semantic-hub`. This will set up a new helm deployment in the semantics namespace. By default, the deployment contains the Semantic Hub instance itself, and a Fuseki Triplestore. Check that the two containers are running by calling `kubectl get pod -n semantics`. diff --git a/backend/deployment/semantic-hub/Chart.lock b/backend/deployment/semantic-hub/Chart.lock deleted file mode 100644 index ef93b5ce..00000000 --- a/backend/deployment/semantic-hub/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: cert-manager - repository: https://charts.jetstack.io - version: v1.7.1 -digest: sha256:7996cd611e44d44b023a677c2ff4beb4104aedede18f5d4e182c11323b957a23 -generated: "2022-03-22T14:25:36.810497+01:00" diff --git a/backend/pom.xml b/backend/pom.xml index 17f3416f..b603cf17 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -25,7 +25,7 @@ org.eclipse.tractusx semantic-hub - 0.1.0-M2 + DEV-SNAPSHOT ../pom.xml @@ -110,7 +110,16 @@ jackson-dataformat-xml com.fasterxml.jackson.dataformat - 2.12.7 + + + com.fasterxml.woodstox + woodstox-core + + + + + com.fasterxml.woodstox + woodstox-core io.openmanufacturing diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java index 6429cfc5..a15d9507 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java @@ -58,129 +58,122 @@ @Component public class BammHelper { - public Try loadBammModel(String ttl) { - InputStream targetStream = new ByteArrayInputStream(ttl.getBytes()); + public Try loadBammModel( String ttl ) { + InputStream targetStream = new ByteArrayInputStream( ttl.getBytes() ); - Try model = TurtleLoader.loadTurtle(targetStream); + Try model = TurtleLoader.loadTurtle( targetStream ); - StaticResolutionStrategy resolutionStrategy = new StaticResolutionStrategy(model); + StaticResolutionStrategy resolutionStrategy = new StaticResolutionStrategy( model ); + AspectModelResolver resolver = new AspectModelResolver(); - AspectModelUrn startUrn = AspectModelUrn - .fromUrn( "urn:bamm:org.eclipse.tractusx:1.0.0#Aspect" ); + Try versionedModel = resolver.resolveAspectModel( resolutionStrategy, resolutionStrategy.getAspectModelUrn() ); - - AspectModelResolver resolver = new AspectModelResolver(); + if ( resolutionStrategy.getResolvementCounter() > 1 ) { + return Try.failure( new ResolutionException( "The definition must be self contained!" ) ); + } - Try versionedModel = resolver.resolveAspectModel(resolutionStrategy, startUrn); + return versionedModel; + } - if(resolutionStrategy.getResolvementCounter() > 1) { - return Try.failure(new ResolutionException("The definition must be self contained!")); - } + public Try getAspectFromVersionedModel( VersionedModel versionedModel ) { - return versionedModel; - } + return AspectModelLoader.fromVersionedModel( versionedModel ); + } - public Try getAspectFromVersionedModel(VersionedModel versionedModel) { + public ValidationReport validateModel( Try model ) { + final AspectModelValidator validator = new AspectModelValidator(); + final ValidationReport validationReport = validator.validate( model ); - return AspectModelLoader.fromVersionedModel(versionedModel); - } + return validationReport; + } - public ValidationReport validateModel(Try model) { - final AspectModelValidator validator = new AspectModelValidator(); - final ValidationReport validationReport = validator.validate(model); + public byte[] generatePng( VersionedModel versionedModel ) { + final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( versionedModel ); - return validationReport; - } + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + generator.generateDiagram( Format.PNG, Locale.ENGLISH, output ); + final byte[] bytes = output.toByteArray(); - public byte[] generatePng(VersionedModel versionedModel) { - final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator(versionedModel); - - try { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - generator.generateDiagram(Format.PNG, Locale.ENGLISH, output); + return bytes; + } catch ( IOException e ) { + e.printStackTrace(); - final byte[] bytes = output.toByteArray(); - - return bytes; - } catch (IOException e) { - e.printStackTrace(); + return null; + } + } - return null; - } - } + public JsonNode getJsonSchema( Aspect aspect ) { + AspectModelJsonSchemaGenerator jsonSchemaGenerator = new AspectModelJsonSchemaGenerator(); - public JsonNode getJsonSchema(Aspect aspect) { - AspectModelJsonSchemaGenerator jsonSchemaGenerator = new AspectModelJsonSchemaGenerator(); + JsonNode json = jsonSchemaGenerator.apply( aspect ); - JsonNode json = jsonSchemaGenerator.apply(aspect); + return json; + } - return json; - } + public Try getHtmlDocu( VersionedModel versionedModel ) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + AspectModelDocumentationGenerator documentationGenerator = new AspectModelDocumentationGenerator( versionedModel ); - public Try getHtmlDocu(VersionedModel versionedModel) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - AspectModelDocumentationGenerator documentationGenerator = new AspectModelDocumentationGenerator(versionedModel); + Map options = new HashMap(); - Map options = new HashMap(); + try { + InputStream ompCSS = getClass().getResourceAsStream( "/catena-template.css" ); + String defaultCSS = CharStreams.toString( new InputStreamReader( ompCSS ) ); - try { - InputStream ompCSS = getClass().getResourceAsStream("/catena-template.css"); - String defaultCSS = CharStreams.toString(new InputStreamReader(ompCSS)); + options.put( HtmlGenerationOption.STYLESHEET, defaultCSS ); + } catch ( IOException e ) { + return Try.failure( e ); + } - options.put(HtmlGenerationOption.STYLESHEET, defaultCSS); - } catch (IOException e) { - return Try.failure(e); - } + try { + documentationGenerator.generate( ( String a ) -> { + return output; + }, options ); + return Try.success( output.toByteArray() ); + } catch ( IOException e ) { + return Try.failure( e ); + } + } - try { - documentationGenerator.generate((String a) -> { - return output; - }, options); + public String getOpenApiDefinitionJson( Aspect aspect, String baseUrl ) { + AspectModelOpenApiGenerator openApiGenerator = new AspectModelOpenApiGenerator(); - return Try.success(output.toByteArray()); - } catch (IOException e) { - return Try.failure(e); - } - } + JsonNode resultJson = openApiGenerator.applyForJson( aspect, true, baseUrl, Optional.empty(), Optional.empty(), false, Optional.empty() ); - public String getOpenApiDefinitionJson(Aspect aspect, String baseUrl) { - AspectModelOpenApiGenerator openApiGenerator = new AspectModelOpenApiGenerator(); + return resultJson.toString(); + } - JsonNode resultJson = openApiGenerator.applyForJson(aspect, true, baseUrl, Optional.empty(), Optional.empty(), false, Optional.empty()); + public Try getExamplePayloadJson( Aspect aspect ) { + AspectModelJsonPayloadGenerator payloadGenerator = new AspectModelJsonPayloadGenerator( aspect ); - return resultJson.toString(); - } + return Try.of( payloadGenerator::generateJson ); + } - public Try getExamplePayloadJson(Aspect aspect) { - AspectModelJsonPayloadGenerator payloadGenerator = new AspectModelJsonPayloadGenerator(aspect); + public Try getAasSubmodelTemplate( Aspect aspect, AasFormat aasFormat ) { + AspectModelAASGenerator aasGenerator = new AspectModelAASGenerator(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); - return Try.of(payloadGenerator::generateJson); - } + try { + switch ( aasFormat ) { + case FILE: + aasGenerator.generateAASXFile( aspect, ( String s ) -> { + return stream; + } ); + return Try.of( stream::toByteArray ); + case XML: + aasGenerator.generateAasXmlFile( aspect, ( String s ) -> { + return stream; + } ); + return Try.of( stream::toString ); + default: + return Try.failure( new Exception( String.format( "Wrong AAS output format %s", aasFormat.toString() ) ) ); - public Try getAasSubmodelTemplate(Aspect aspect, AasFormat aasFormat) { - AspectModelAASGenerator aasGenerator = new AspectModelAASGenerator(); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - - try { - switch(aasFormat) { - case FILE: - aasGenerator.generateAASXFile(aspect, (String s) -> { - return stream; - }); - return Try.of(stream::toByteArray); - case XML: - aasGenerator.generateAasXmlFile(aspect, (String s) -> { - return stream; - }); - return Try.of(stream::toString); - default: - return Try.failure(new Exception(String.format("Wrong AAS output format %s", aasFormat.toString()))); - - } - } catch (IOException e) { - return Try.failure(e); - } - } + } + } catch ( IOException e ) { + return Try.failure( e ); + } + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java index c89cb5a4..b2b188db 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java @@ -19,28 +19,60 @@ ********************************************************************************/ package org.eclipse.tractusx.semantics.hub.bamm; -import org.apache.jena.rdf.model.Model; - +import io.openmanufacturing.sds.aspectmetamodel.KnownVersion; import io.openmanufacturing.sds.aspectmodel.resolver.AbstractResolutionStrategy; import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn; +import io.openmanufacturing.sds.aspectmodel.vocabulary.BAMM; +import io.vavr.NotImplementedError; import io.vavr.control.Try; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.vocabulary.RDF; + +import java.util.List; +import java.util.Optional; + public class StaticResolutionStrategy extends AbstractResolutionStrategy { - private int counter; - private final Try model; - - public StaticResolutionStrategy(Try model) { - this.model = model; - } - - @Override - public Try apply(AspectModelUrn t) { - counter++; - - return this.model; - } - - public int getResolvementCounter() { - return counter; - } + private int counter; + private final Try model; + + public StaticResolutionStrategy( Try model ) { + this.model = model; + } + + @Override + public Try apply( AspectModelUrn t ) { + counter++; + return this.model; + } + + public int getResolvementCounter() { + return counter; + } + + public AspectModelUrn getAspectModelUrn() { + final Optional stmtIterator = getStmtIterator(); + + final String aspectModelUrn = stmtIterator.orElseThrow( + () -> new NotImplementedError( "AspectModelUrn cannot be found." ) ) + .next().getSubject().getURI(); + + return AspectModelUrn.fromUrn( aspectModelUrn ); + } + + private Optional getStmtIterator() { + for ( final KnownVersion version : KnownVersion.getVersions() ) { + final BAMM bamm = new BAMM( version ); + final List resources = List.of( bamm.Aspect(), bamm.Property(), bamm.Entity(), bamm.Characteristic() ); + final Optional stmtIterator = resources.stream().filter( + resource -> model.get().listStatements( null, RDF.type, resource ).hasNext() ).findFirst() + .map( resource -> model.get().listStatements( null, RDF.type, resource ) ); + if ( stmtIterator.isPresent() ) { + return stmtIterator; + } + } + return Optional.empty(); + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java index dbb6c8a7..100b843b 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java @@ -148,7 +148,23 @@ public class SparqlQueries { + "BIND(iri(concat(strbefore(str(?aspect ), \"#\"), \"#\")) AS ?package)\n" + "?package aux:status ?status\n" + "FILTER ( !bound(?statusFilter) || contains(str(?status), ?statusFilter) )\n" - + "FILTER ( !bound(?namespaceFilter) || contains(str(?aspect), ?namespaceFilter ) )\n" + + "FILTER ( !bound(?namespaceFilter) || contains(lcase(str(?aspect)), lcase(?namespaceFilter) ) )\n" + + "}\n"; + + private static final String FILTER_QUERY_MINIMAL_WHERE_CLAUSE_SELECTIVE = "WHERE {\n" + + "BIND($bammAspectUrnRegexParam AS ?bammAspectUrnRegex)\n" + + "BIND(iri($bammFieldToSearchInParam) AS ?bammFieldToSearchIn)\n" + + "BIND($bammFieldSearchValueParam AS ?bammFieldSearchValue)\n" + + "BIND($statusFilterParam AS ?statusFilter)\n" + + "BIND($namespaceFilterParam AS ?namespaceFilter)\n" + + "VALUES (?urns) { ?urnParamList } \n" + + "?aspect a ?bammAspect .\n" + + "FILTER regex(str(?bammAspect), ?bammAspectUrnRegex, \"\")\n" + + "BIND(iri(concat(strbefore(str(?aspect ), \"#\"), \"#\")) AS ?package)\n" + + "?package aux:status ?status\n" + + "FILTER ( !bound(?statusFilter) || contains(str(?status), ?statusFilter) )\n" + + "FILTER ( !bound(?namespaceFilter) || contains(lcase(str(?aspect)), lcase(?namespaceFilter) ) )\n" + + "FILTER ( str(?aspect) IN (?urns) ) " + "}\n"; private static final String FILTER_QUERY_MINIMAL_WHERE_CLAUSE_SELECTIVE = "WHERE {\n" diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java index 5aa9b9b3..1d60bc22 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java @@ -30,6 +30,9 @@ import javax.annotation.Nullable; +import io.openmanufacturing.sds.aspectmodel.urn.UrnSyntaxException; +import io.vavr.control.Try; + import org.eclipse.tractusx.semantics.hub.AspectModelNotFoundException; import org.eclipse.tractusx.semantics.hub.ModelPackageNotFoundException; import org.eclipse.tractusx.semantics.hub.domain.ModelPackage; @@ -52,6 +55,7 @@ import io.openmanufacturing.sds.aspectmodel.resolver.AspectModelResolver; import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn; + import org.eclipse.tractusx.semantics.hub.InvalidStateTransitionException; import org.eclipse.tractusx.semantics.hub.persistence.PersistenceLayer; @@ -67,8 +71,8 @@ public TripleStorePersistence( final RDFConnectionRemoteBuilder rdfConnectionRem } @Override - public SemanticModelList getModels(String namespaceFilter, - @Nullable ModelPackageStatus status, Integer page, Integer pageSize ) { + public SemanticModelList getModels( String namespaceFilter, + @Nullable ModelPackageStatus status, Integer page, Integer pageSize ) { final Query query = SparqlQueries.buildFindAllQuery( namespaceFilter, status, page, pageSize ); final AtomicReference> aspectModels = new AtomicReference<>(); @@ -79,7 +83,7 @@ public SemanticModelList getModels(String namespaceFilter, } ); } int totalSemanticModelCount = getTotalItemsCount( namespaceFilter, status ); - int totalPages = getTotalPages(totalSemanticModelCount, pageSize ); + int totalPages = getTotalPages( totalSemanticModelCount, pageSize ); SemanticModelList modelList = new SemanticModelList(); List semanticModels = aspectModels.get(); modelList.setCurrentPage( page ); @@ -90,11 +94,11 @@ public SemanticModelList getModels(String namespaceFilter, return modelList; } - private static int getTotalPages(int totalItemsCount, int pageSize){ - if(totalItemsCount == 0 || pageSize == 0){ + private static int getTotalPages( int totalItemsCount, int pageSize ) { + if ( totalItemsCount == 0 || pageSize == 0 ) { return 0; } - return (int) Math.ceil( ((double) totalItemsCount) / (double) pageSize); + return (int) Math.ceil( ((double) totalItemsCount) / (double) pageSize ); } @Override @@ -103,35 +107,35 @@ public SemanticModel getModel( final AspectModelUrn urn ) { } @Override - public SemanticModel save(SemanticModelType type, String newModel, SemanticModelStatus status ) { + public SemanticModel save( SemanticModelType type, String newModel, SemanticModelStatus status ) { final Model rdfModel = sdsSdk.load( newModel.getBytes( StandardCharsets.UTF_8 ) ); final AspectModelUrn modelUrn = sdsSdk.getAspectUrn( rdfModel ); Optional existsByPackage = findByPackageByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); if ( existsByPackage.isPresent() ) { ModelPackageStatus persistedModelStatus = existsByPackage.get().getStatus(); - final ModelPackageStatus desiredModelStatus = ModelPackageStatus.valueOf( status.name() ); + final ModelPackageStatus desiredModelStatus = ModelPackageStatus.valueOf( status.name() ); switch ( persistedModelStatus ) { - case DRAFT: - if(desiredModelStatus.equals(ModelPackageStatus.RELEASED) && !hasReferenceToDraftPackage(modelUrn, rdfModel)) { - throw new InvalidStateTransitionException("It is not allowed to release an aspect that has dependencies in DRAFT state."); - } + case DRAFT: + if ( desiredModelStatus.equals( ModelPackageStatus.RELEASED ) && !hasReferenceToDraftPackage( modelUrn, rdfModel ) ) { + throw new InvalidStateTransitionException( "It is not allowed to release an aspect that has dependencies in DRAFT state." ); + } + deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); + break; + case RELEASED: + // released models can only be updated when the new state is deprecated + if ( desiredModelStatus.equals( ModelPackageStatus.DEPRECATED ) ) { deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); - break; - case RELEASED: - // released models can only be updated when the new state is deprecated - if(desiredModelStatus.equals(ModelPackageStatus.DEPRECATED)){ - deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); - } else { - throw new IllegalArgumentException( - String.format( "The package %s is already in status %s and cannot be modified. Only a transition to DEPRECATED is possible.", - ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) ); - } - break; - case DEPRECATED: + } else { throw new IllegalArgumentException( - String.format( "The package %s is already in status %s and cannot be modified.", + String.format( "The package %s is already in status %s and cannot be modified. Only a transition to DEPRECATED is possible.", ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) ); + } + break; + case DEPRECATED: + throw new IllegalArgumentException( + String.format( "The package %s is already in status %s and cannot be modified.", + ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) ); } } @@ -173,7 +177,7 @@ public void deleteModelsPackage( final ModelPackageUrn urn ) { } @Override - public SemanticModelList findModelListByUrns(List urns, int page, int pageSize) { + public SemanticModelList findModelListByUrns( List urns, int page, int pageSize ) { final Query query = SparqlQueries.buildFindListByUrns( urns, page, pageSize ); final AtomicReference> aspectModels = new AtomicReference<>(); try ( final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build() ) { @@ -183,7 +187,7 @@ public SemanticModelList findModelListByUrns(List urns, int page } ); } int totalSemanticModelCount = getSelectiveItemsCount( null, null, null, null, urns ); - int totalPages = getTotalPages(totalSemanticModelCount, pageSize ); + int totalPages = getTotalPages( totalSemanticModelCount, pageSize ); SemanticModelList modelList = new SemanticModelList(); List semanticModels = aspectModels.get(); modelList.setCurrentPage( page ); @@ -197,22 +201,21 @@ public SemanticModelList findModelListByUrns(List urns, int page public boolean echo() { final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build(); - return rdfConnection.queryAsk(SparqlQueries.echoQuery()); + return rdfConnection.queryAsk( SparqlQueries.echoQuery() ); } - private boolean hasReferenceToDraftPackage(AspectModelUrn modelUrn, Model model) { - Pattern pattern = Pattern.compile(SparqlQueries.ALL_BAMM_ASPECT_URN_PREFIX); - - List urns = AspectModelResolver.getAllUrnsInModel(model).stream().map((AspectModelUrn urn) -> { - return urn.getUrnPrefix(); - }) - .distinct() - .collect(Collectors.toList()); - - for(var entry : urns) { - Matcher matcher = pattern.matcher(entry); - if(!matcher.find() && !modelUrn.getUrnPrefix().equals(entry)) { - if(findByPackageByUrn(ModelPackageUrn.fromUrn(entry)).get().getStatus().equals(ModelPackageStatus.DRAFT)) { + private boolean hasReferenceToDraftPackage( AspectModelUrn modelUrn, Model model ) { + Pattern pattern = Pattern.compile( SparqlQueries.ALL_BAMM_ASPECT_URN_PREFIX ); + + List urns = AspectModelResolver.getAllUrnsInModel( model ).stream().filter( urn -> getAspectModelUrn( urn ).isSuccess() ) + .map( urn -> getAspectModelUrn( urn ).get().getUrnPrefix() ) + .distinct() + .collect( Collectors.toList() ); + + for ( var entry : urns ) { + Matcher matcher = pattern.matcher( entry ); + if ( !matcher.find() && !modelUrn.getUrnPrefix().equals( entry ) ) { + if ( findByPackageByUrn( ModelPackageUrn.fromUrn( entry ) ).get().getStatus().equals( ModelPackageStatus.DRAFT ) ) { return false; } } @@ -294,12 +297,20 @@ private Model findJenaModelByUrn( final AspectModelUrn urn ) { } } + private Try getAspectModelUrn( String urn ) { + try { + return Try.success( AspectModelUrn.fromUrn( urn ) ); + } catch ( UrnSyntaxException var2 ) { + return Try.failure( var2 ); + } + } + private static List aspectModelFrom( final List querySolutions ) { - return querySolutions - .stream() - .map( TripleStorePersistence::aspectModelFrom ) - .collect(Collectors.toList()); + return querySolutions + .stream() + .map( TripleStorePersistence::aspectModelFrom ) + .collect( Collectors.toList() ); } private static SemanticModel aspectModelFrom( final QuerySolution querySolution ) { diff --git a/backend/src/main/resources/static/semantic-hub-openapi.yaml b/backend/src/main/resources/static/semantic-hub-openapi.yaml index aea287c0..46310e16 100644 --- a/backend/src/main/resources/static/semantic-hub-openapi.yaml +++ b/backend/src/main/resources/static/semantic-hub-openapi.yaml @@ -152,6 +152,7 @@ paths: application/json: schema: type: array + maxItems: 10000 items: type: string responses: @@ -441,6 +442,7 @@ components: items: title: Items type: array + maxItems: 10000 items: $ref: '#/components/schemas/SemanticModel' totalItems: diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java index f9a900cd..272a5a34 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java @@ -76,6 +76,22 @@ public void testGetByNamespaceExpectResultsFound() throws Exception { .andExpect( MockMvcResultMatchers.status().isOk() ); } + @Test + public void testGetByNamespaceWithCaseInsensitiveExpectResultsFound() throws Exception { + mvc.perform( + MockMvcRequestBuilders.get( + "/api/v1/models?namespaceFilter=urn:bamm:org.eclipse.TRACtusx.TraceaBiliTy" ) + .accept( MediaType.APPLICATION_JSON ) + .with(jwtTokenFactory.allRoles()) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( jsonPath( "$.items" ).isArray() ) + .andExpect( jsonPath( "$.items.length()" ).value( 1 ) ) + .andExpect( jsonPath( "$.totalItems", equalTo( 1 ) ) ) + .andExpect( jsonPath( "$.itemCount", equalTo( 1 ) ) ) + .andExpect( MockMvcResultMatchers.status().isOk() ); + } + @Test public void testGetModelListByAvailablePropertyTypeExpectResultsFound() throws Exception { mvc.perform( diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java index c2d92860..f570a112 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java @@ -19,13 +19,12 @@ ********************************************************************************/ package org.eclipse.tractusx.semantics.hub.bamm; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; -import org.eclipse.tractusx.semantics.hub.bamm.BammHelper; import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel; +import io.vavr.NotImplementedError; import io.vavr.control.Try; public class BammHelperTest { @@ -68,4 +67,24 @@ public void testBammValidationWithInvalidModel() { assertFalse(bammHelper.validateModel(model).conforms()); } + + @Test + public void testBammLoaderInVersion2_0_0_WithValidModelExpectSuccess() { + final String modelString = "@prefix bamm: .\n @prefix bamm-c: .\n @prefix bamm-e: .\n @prefix unit: .\n @prefix rdf: .\n @prefix rdfs: .\n @prefix xsd: .\n @prefix : .\n \n :Movement a bamm:Aspect;\n bamm:name \"Movement\";\n bamm:preferredName \"Movement\"@en;\n bamm:description \"Aspect for movement information\"@en;\n bamm:properties (:isMoving :speedLimitWarning :position);\n bamm:operations ().\n :isMoving a bamm:Property;\n bamm:name \"isMoving\";\n bamm:preferredName \"Moving\"@en;\n bamm:description \"Flag indicating whether the asset is currently moving\"@en;\n bamm:characteristic bamm-c:Boolean.\n :speedLimitWarning a bamm:Property;\n bamm:name \"speedLimitWarning\";\n bamm:preferredName \"Speed Limit Warning\"@en;\n bamm:description \"Indicates if the speed limit is adhered to.\"@en;\n bamm:characteristic :TrafficLight.\n :position a bamm:Property;\n bamm:name \"position\";\n bamm:preferredName \"Position\"@en;\n bamm:description \"Indicates a position\"@en;\n bamm:characteristic :SpatialPositionCharacteristic.\n :TrafficLight a bamm-c:Enumeration;\n bamm:name \"TrafficLight\";\n bamm:preferredName \"Warning Level\"@en;\n bamm:description \"Represents if speed of position change is within specification (green), within tolerance (yellow), or outside specification (red).\"@en;\n bamm:dataType xsd:string;\n bamm-c:values (\"green\" \"yellow\" \"red\").\n :SpatialPosition a bamm:Entity;\n bamm:name \"SpatialPosition\";\n bamm:preferredName \"Spatial Position\"@en;\n bamm:description \"Position in space, described along three axis, with the third axis optional, if all positions are in a plane.\"@en;\n bamm:properties (:x :y :z).\n :x a bamm:Property;\n bamm:name \"x\";\n bamm:preferredName \"x\"@en;\n bamm:description \"x coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :y a bamm:Property;\n bamm:name \"y\";\n bamm:preferredName \"y\"@en;\n bamm:description \"y coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :z a bamm:Property;\n bamm:name \"z\";\n bamm:preferredName \"z\"@en;\n bamm:description \"z coordinate in space\"@en;\n bamm:characteristic :Coordinate;\n bamm:optional \"true\"^^xsd:boolean.\n :Coordinate a bamm-c:Measurement;\n bamm:name \"Coordinate\";\n bamm:preferredName \"Coordinate\"@en;\n bamm:description \"Represents a coordinate along an axis in space.\"@en;\n bamm:dataType xsd:float;\n bamm-c:unit unit:metre.\n :SpatialPositionCharacteristic a bamm-c:SingleEntity;\n bamm:name \"SpatialPositionCharacteristic\";\n bamm:preferredName \"Spatial Position Characteristic\"@en;\n bamm:description \"Represents a single position in space with optional z coordinate.\"@en;\n bamm:dataType :SpatialPosition.\n"; + + BammHelper bammHelper = new BammHelper(); + + assertTrue(bammHelper.loadBammModel(modelString).isSuccess()); + } + + @Test + public void testBammLoaderInUnknownVersion0_0_0_WithInvalidModelExpectFailure() { + final String modelString = "@prefix bamm: .\n @prefix bamm-c: .\n @prefix bamm-e: .\n @prefix unit: .\n @prefix rdf: .\n @prefix rdfs: .\n @prefix xsd: .\n @prefix : .\n \n :Movement a bamm:Aspect;\n bamm:name \"Movement\";\n bamm:preferredName \"Movement\"@en;\n bamm:description \"Aspect for movement information\"@en;\n bamm:properties (:isMoving :speedLimitWarning :position);\n bamm:operations ().\n :isMoving a bamm:Property;\n bamm:name \"isMoving\";\n bamm:preferredName \"Moving\"@en;\n bamm:description \"Flag indicating whether the asset is currently moving\"@en;\n bamm:characteristic bamm-c:Boolean.\n :speedLimitWarning a bamm:Property;\n bamm:name \"speedLimitWarning\";\n bamm:preferredName \"Speed Limit Warning\"@en;\n bamm:description \"Indicates if the speed limit is adhered to.\"@en;\n bamm:characteristic :TrafficLight.\n :position a bamm:Property;\n bamm:name \"position\";\n bamm:preferredName \"Position\"@en;\n bamm:description \"Indicates a position\"@en;\n bamm:characteristic :SpatialPositionCharacteristic.\n :TrafficLight a bamm-c:Enumeration;\n bamm:name \"TrafficLight\";\n bamm:preferredName \"Warning Level\"@en;\n bamm:description \"Represents if speed of position change is within specification (green), within tolerance (yellow), or outside specification (red).\"@en;\n bamm:dataType xsd:string;\n bamm-c:values (\"green\" \"yellow\" \"red\").\n :SpatialPosition a bamm:Entity;\n bamm:name \"SpatialPosition\";\n bamm:preferredName \"Spatial Position\"@en;\n bamm:description \"Position in space, described along three axis, with the third axis optional, if all positions are in a plane.\"@en;\n bamm:properties (:x :y :z).\n :x a bamm:Property;\n bamm:name \"x\";\n bamm:preferredName \"x\"@en;\n bamm:description \"x coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :y a bamm:Property;\n bamm:name \"y\";\n bamm:preferredName \"y\"@en;\n bamm:description \"y coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :z a bamm:Property;\n bamm:name \"z\";\n bamm:preferredName \"z\"@en;\n bamm:description \"z coordinate in space\"@en;\n bamm:characteristic :Coordinate;\n bamm:optional \"true\"^^xsd:boolean.\n :Coordinate a bamm-c:Measurement;\n bamm:name \"Coordinate\";\n bamm:preferredName \"Coordinate\"@en;\n bamm:description \"Represents a coordinate along an axis in space.\"@en;\n bamm:dataType xsd:float;\n bamm-c:unit unit:metre.\n :SpatialPositionCharacteristic a bamm-c:SingleEntity;\n bamm:name \"SpatialPositionCharacteristic\";\n bamm:preferredName \"Spatial Position Characteristic\"@en;\n bamm:description \"Represents a single position in space with optional z coordinate.\"@en;\n bamm:dataType :SpatialPosition.\n"; + + BammHelper bammHelper = new BammHelper(); + + assertEquals( + "AspectModelUrn cannot be found.", + assertThrows( NotImplementedError.class, () -> bammHelper.loadBammModel( modelString )).getMessage() ); + } } diff --git a/backend/deployment/semantic-hub/.helmignore b/charts/semantic-hub/.helmignore similarity index 100% rename from backend/deployment/semantic-hub/.helmignore rename to charts/semantic-hub/.helmignore diff --git a/backend/deployment/semantic-hub/Chart.yaml b/charts/semantic-hub/Chart.yaml similarity index 76% rename from backend/deployment/semantic-hub/Chart.yaml rename to charts/semantic-hub/Chart.yaml index b2a1cc1c..4462fb05 100644 --- a/backend/deployment/semantic-hub/Chart.yaml +++ b/charts/semantic-hub/Chart.yaml @@ -3,5 +3,5 @@ name: semantic-hub description: Helm Chart for the Catena-X Semantic Hub Application type: application -version: 0.1.0 -appVersion: 0.1.0-M1 \ No newline at end of file +version: 0.1.3 +appVersion: 0.1.0-M2 diff --git a/backend/deployment/semantic-hub/templates/_helpers.tpl b/charts/semantic-hub/templates/_helpers.tpl similarity index 100% rename from backend/deployment/semantic-hub/templates/_helpers.tpl rename to charts/semantic-hub/templates/_helpers.tpl diff --git a/backend/deployment/semantic-hub/templates/graphdb/graphdb-deployment.yaml b/charts/semantic-hub/templates/graphdb/graphdb-deployment.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/graphdb/graphdb-deployment.yaml rename to charts/semantic-hub/templates/graphdb/graphdb-deployment.yaml diff --git a/backend/deployment/semantic-hub/templates/graphdb/graphdb-pvc.yaml b/charts/semantic-hub/templates/graphdb/graphdb-pvc.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/graphdb/graphdb-pvc.yaml rename to charts/semantic-hub/templates/graphdb/graphdb-pvc.yaml diff --git a/backend/deployment/semantic-hub/templates/graphdb/graphdb-service.yaml b/charts/semantic-hub/templates/graphdb/graphdb-service.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/graphdb/graphdb-service.yaml rename to charts/semantic-hub/templates/graphdb/graphdb-service.yaml diff --git a/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml b/charts/semantic-hub/templates/hub/hub-deployment.yaml similarity index 96% rename from backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml rename to charts/semantic-hub/templates/hub/hub-deployment.yaml index 901b856a..be88f854 100644 --- a/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml +++ b/charts/semantic-hub/templates/hub/hub-deployment.yaml @@ -66,14 +66,14 @@ spec: livenessProbe: httpGet: path: /actuator/health/liveness - port: { { .Values.hub.containerPort } } + port: {{ .Values.hub.containerPort }} initialDelaySeconds: 100 periodSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /actuator/health/readiness - port: { { .Values.hub.containerPort } } + port: {{ .Values.hub.containerPort }} initialDelaySeconds: 60 periodSeconds: 3 failureThreshold: 3 diff --git a/backend/deployment/semantic-hub/templates/hub/hub-ingress.yaml b/charts/semantic-hub/templates/hub/hub-ingress.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/hub/hub-ingress.yaml rename to charts/semantic-hub/templates/hub/hub-ingress.yaml diff --git a/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml b/charts/semantic-hub/templates/hub/hub-secret.yaml similarity index 95% rename from backend/deployment/semantic-hub/templates/hub/hub-secret.yaml rename to charts/semantic-hub/templates/hub/hub-secret.yaml index 3c30c433..bc595e14 100644 --- a/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml +++ b/charts/semantic-hub/templates/hub/hub-secret.yaml @@ -29,6 +29,7 @@ metadata: type: Opaque data: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: {{ .Values.hub.idpIssuerUri | b64enc }} + HUB_GENERAL_IDM_PUBLIC_CLIENT_ID: {{ .Values.hub.idpClientId | b64enc }} # the fuseki instance does not require authentication yet # this variables need to be provided because they are mandatory in the application HUB_TRIPLE_STORE_USERNAME: {{ .Values.graphdb.username | b64enc }} diff --git a/backend/deployment/semantic-hub/templates/hub/hub-service.yaml b/charts/semantic-hub/templates/hub/hub-service.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/hub/hub-service.yaml rename to charts/semantic-hub/templates/hub/hub-service.yaml diff --git a/backend/deployment/semantic-hub/values.yaml b/charts/semantic-hub/values.yaml similarity index 97% rename from backend/deployment/semantic-hub/values.yaml rename to charts/semantic-hub/values.yaml index f5737a66..087ea5be 100644 --- a/backend/deployment/semantic-hub/values.yaml +++ b/charts/semantic-hub/values.yaml @@ -19,7 +19,7 @@ ############################################################### hub: - image: semantic-hub:0.1.0-M1 + image: semantic-hub:0.1.0-M2 imagePullPolicy: IfNotPresent replicaCount: 1 containerPort: 4242 @@ -29,6 +29,7 @@ hub: ## If 'authentication' is set to false, no OAuth authentication is enforced authentication: false idpIssuerUri: https://idp-url + idpClientId: idpClientID ## Ignored if 'graphdb.enabled' is set to true graphdbBaseUrl: http://graphdb:3030 service: diff --git a/docs/documentation.md b/docs/documentation.md new file mode 100644 index 00000000..cd1b229b --- /dev/null +++ b/docs/documentation.md @@ -0,0 +1,83 @@ +## Architectural Overview +The SLDT Semantic Hub stores Semantic Model definitions and allows the generation of several artifacts. It restricts access to the models by authentication via a token and authorization via roles in the token claims. Therefore, the Hub interacts with a Keycloak instance. The models are created in the Hub during our governance process as depicted below. + +![](img/image001.png) + +## Implementation +The following section describes the use cases implemented for the semantic hub. + +### Upload of an aspect model +![](img/image002.png) + + +| Validation | Description Value | +|---|---| +| BAMM compliance | Checks if the model is compliant with the BAMM. The BAMM SDK does provide the validation logic. | +| Model Status check (RELEASE vs DRAFT) | Uploads will always accepted when there are no existing namespace:version combination in the TripleStore. For a model in DRAFT state, uploads will always be accepted. For a model in RELEASE state, uploads will be denied. RELEASED models are immutable. | +| External reference check | It will be checked if all exernal references are available in the TripleStore. The BAMM SDK does provide a mechanisim where the resolving against the TripleStore can be integrated. | + + + +## Example: +Example Aspect Model +``` +@prefix xsd: . +@prefix bamm: . +@prefix : . + + +:DocumentationSimple a bamm:Aspect; +bamm:name "ManufacturerDocumentationSimple"; +bamm:preferredName "ManufacturerDocumentation"@en; +bamm:description "The Submodel defines a simplified set of manufacturer documentation to bring about information from manufacturer to operator of industrial equipment."@en; + +:documents a bamm:Property; +bamm:name "documents"; +bamm:preferredName "documents"@en; +bamm:description "Set of documents"@en; +``` + +The Semantic Hub will add the release status as triple upon upload: +Release Status +``` +@prefix aux: + + aux:releaseStatus aux:DRAFT . +``` + +## Package +| No | Rule | Example | +|---|---|---| +| 1. | A package is defined by the urn prefix until "#". | net.catenax.semantics.product:1.2.0# | +| 2. | A package can contain one or multiple aspects. | Example 1: net.catenax.semantics.traceability:1.2.0#Traceability Example 2: net.catenax.semantics.product:1.2.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:1.2.0#ProductDetails| +| 3. | Multiple versions of a package can exists. | Possible: net.catenax.semantics.product:1.2.0 net.catenax.semantics.product:4.2.0 | +| 4. | The versioning applies to the package. All aspects and model elements scoped to a package have the same version.| Possible: net.catenax.semantics.product:1.2.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:1.2.0#ProductDetails Possible:net.catenax.semantics.product:4.3.0#ProductDescription net.catenax.semantics.product:4.3.0#ProductUsage net.catenax.semantics.product:4.3.0#ProductDetails Not Possible: net.catenax.semantics.product:1.3.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:3.2.0#ProductDetails | +| 5. | All aspect models and model elements scoped to a package have the same status. | Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE, net.catenax.semantics.product:1.2.0#ProductUsage → RELEASE net.catenax.semantics.product:1.2.0#ProductDetails → RELEASE Not Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE, net.catenax.semantics.product:1.2.0#ProductUsage → DRAFT + + + +## Download of the documentation of an Aspect Model +![](img/image003.png) + +Example queries to resolve an aspect model with all references: +Construct Query +``` +@prefix ns: + +CONSTRUCT { +?s ?p ?o . +} WHERE { +bind( ns: as ?aspect) +?aspect (<>|!<>)* ?s . // resolves all references +?s ?p ?o . +} +Search for Aspect Models +The current search API can stay as is. Below is an example query for selecting bamm properties: +Search Queries +CONSTRUCT { +?s ?p ?o . +} WHERE { +FILTER ( $param == ?o ) // Custom filter can be added here. +?s ?p ?o . +} +``` diff --git a/docs/img/image001.png b/docs/img/image001.png new file mode 100644 index 00000000..2180bbc7 Binary files /dev/null and b/docs/img/image001.png differ diff --git a/docs/img/image002.png b/docs/img/image002.png new file mode 100644 index 00000000..34b6d82d Binary files /dev/null and b/docs/img/image002.png differ diff --git a/docs/img/image003.png b/docs/img/image003.png new file mode 100644 index 00000000..13a6bb32 Binary files /dev/null and b/docs/img/image003.png differ diff --git a/pom.xml b/pom.xml index 08d22820..f5bb4629 100644 --- a/pom.xml +++ b/pom.xml @@ -25,13 +25,13 @@ org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.6 org.eclipse.tractusx semantic-hub - 0.1.0-M2 + DEV-SNAPSHOT Tractus-X Semantic Hub Root Module of the Tractus-X Semantic Hub pom @@ -65,7 +65,7 @@ - 2.7.3 + 2.7.6 3.1.3 1.6.6 2.9.2 @@ -78,7 +78,7 @@ 0.10.3 2.11.0 3.0.2 - 1.31 + 1.33 1.7.32 @@ -86,10 +86,13 @@ 2.13.1 + 2.14.0 + 2.12.7 20211205 + 6.4.0 - 2.0.0 + 2.0.6 4.2.0 1.3.1 @@ -240,6 +243,18 @@ org.openapitools jackson-databind-nullable 0.1.0 + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} @@ -248,6 +263,16 @@ jackson-datatype-jsr310 ${jackson.version} + + jackson-dataformat-xml + com.fasterxml.jackson.dataformat + ${jackson.dataformat.version} + + + com.fasterxml.woodstox + woodstox-core + ${woodstoxcore.version} + @@ -257,16 +282,28 @@ - - io.openmanufacturing - sds-aspect-model-starter - ${bamm.sdk.version} - + + io.openmanufacturing + sds-aspect-model-starter + ${bamm.sdk.version} + + + + org.apache.commons + commons-text + + + + + org.apache.commons + commons-text + 1.10.0 + io.openmanufacturing sds-aspect-model-aas-generator ${bamm.sdk.version} - + org.apache.jena jena-core