diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9f11b755 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000..ab8697a2 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,16 @@ +# Authors + +The following people have contributed to this repository: + +* Dr. Christoph "Schorsch" Jung, T-Systems International GmbH, https://github.com/drcgjung +* Fethuallah Misir, Robert Bosch GmbH, Fethullah.Misir@de.bosch.com +* Lukas Römer, Bosch.IO GmbH, Lukas.roemer@bosch.io +* Janine Semmler, T-Systems International GmbH, https://github.com/janemcbrain +* Birgit Boss, Robert Bosch GmbH +* Olaf Laser, T-Systems International GmbH, https://github.com/olaser +* Peter Vollmert, T-Systems International GmbH, https://github.com/pvollmert +* Artur Trzewik, T-Systems International GmbH, https://github.com/a-trzewik +* Sven Erik Jeroschewski, Bosch.IO GmbH +* Johannes Kristan, Bosch.IO GmbH, Johannes.Kristan@bosch.io + +Please add yourself to this list, if you contribute to the content. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..b7aae33f --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Community Code of Conduct + +**Version 1.2 +August 19, 2020** + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as community members, contributors, committers, and project leaders pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +With the support of the Eclipse Foundation staff (the “Staff”), project committers and leaders are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project committers and leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the Eclipse Foundation project or its community in public spaces. Examples of representing a project or community include posting via an official social media account, or acting as a project representative at an online or offline event. Representation of a project may be further defined and clarified by project committers, leaders, or the EMO. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the Staff at codeofconduct@eclipse.org. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The Staff is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project committers or leaders who do not follow the Code of Conduct in good faith may face temporary or permanent repercussions as determined by the Staff. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org) , version 1.4, available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..38fe8f8c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing to Eclipse Tractus-X + +Thanks for your interest in this project. + +## Project description + +The companies involved want to increase the automotive industry's +competitiveness, improve efficiency through industry-specific cooperation and +accelerate company processes through standardization and access to information +and data. A special focus is also on SMEs, whose active participation is of +central importance for the network’s success. That is why Catena-X has been +conceived from the outset as an open network with solutions ready for SMEs, +where these companies will be able to participate quickly and with little IT +infrastructure investment. Tractus-X is meant to be the PoC project of the +Catena-X alliance focusing on parts traceability. + +* https://projects.eclipse.org/projects/automotive.tractusx + +## Developer resources + +Information regarding source code management, builds, coding standards, and +more. + +* https://projects.eclipse.org/projects/automotive.tractusx/developer + +The project maintains the following source code repositories + +* https://github.com/eclipse/tractusx +* https://github.com/eclipse-tractusx/sldt-semantic-hub +* https://github.com/eclipse-tractusx/sldt-digital-twin-registry +* https://github.com/eclipse-tractusx/portal-frontend + +## Eclipse Development Process + +This Eclipse Foundation open project is governed by the Eclipse Foundation +Development Process and operates under the terms of the Eclipse IP Policy. + +* https://eclipse.org/projects/dev_process +* https://www.eclipse.org/org/documents/Eclipse_IP_Policy.pdf + +## Eclipse Contributor Agreement + +In order to be able to contribute to Eclipse Foundation projects you must +electronically sign the Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +The ECA provides the Eclipse Foundation with a permanent record that you agree +that each of your contributions will comply with the commitments documented in +the Developer Certificate of Origin (DCO). Having an ECA on file associated with +the email address matching the "Author" field of your contribution's Git commits +fulfills the DCO's requirement that you sign-off on your contributions. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Contact + +Contact the project developers via the project's "dev" list. + +* https://accounts.eclipse.org/mailing-list/tractusx-dev diff --git a/DEPENDENCIES b/DEPENDENCIES new file mode 100644 index 00000000..2d07b193 --- /dev/null +++ b/DEPENDENCIES @@ -0,0 +1,181 @@ +maven/mavencentral/ch.qos.logback/logback-classic/1.2.11, EPL-1.0, approved, CQ13636 +maven/mavencentral/ch.qos.logback/logback-core/1.2.11, EPL-1.0, approved, CQ13635 +maven/mavencentral/com.apicatalog/titanium-json-ld/1.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.core/jackson-annotations/2.12.6, Apache-2.0, approved, CQ23844 +maven/mavencentral/com.fasterxml.jackson.core/jackson-core/2.12.6, Apache-2.0, approved, CQ23845 +maven/mavencentral/com.fasterxml.jackson.core/jackson-databind/2.12.6.1, Apache-2.0, approved, CQ23725 +maven/mavencentral/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml/2.12.6, Apache-2.0, approved, CQ23726 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jdk8/2.12.6, Apache-2.0, approved, CQ23208 +maven/mavencentral/com.fasterxml.jackson.datatype/jackson-datatype-jsr310/2.13.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml.jackson.module/jackson-module-parameter-names/2.12.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.fasterxml/classmate/1.5.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.github.andrewoma.dexx/collection/0.7, MIT, approved, CQ22160 +maven/mavencentral/com.github.jsonld-java/jsonld-java/0.13.3, BSD-3-Clause, approved, CQ22136 +maven/mavencentral/com.github.stephenc.jcip/jcip-annotations/1.0-1, Apache-2.0, approved, CQ21949 +maven/mavencentral/com.google.code.findbugs/jsr305/3.0.2, Apache-2.0, approved, #20 +maven/mavencentral/com.google.errorprone/error_prone_annotations/2.7.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/failureaccess/1.0.1, Apache-2.0, approved, CQ22654 +maven/mavencentral/com.google.guava/guava/31.0.1-jre, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava, LicenseRef-NONE, approved, #803 +maven/mavencentral/com.google.j2objc/j2objc-annotations/1.3, Apache-2.0, approved, CQ21195 +maven/mavencentral/com.h2database/h2/2.1.210, MPL-2.0 OR EPL-1.0, approved, clearlydefined +maven/mavencentral/com.jayway.jsonpath/json-path/2.5.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.nimbusds/nimbus-jose-jwt/9.10.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/com.zaxxer/HikariCP/4.0.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/commons-cli/commons-cli/1.4, Apache-2.0, approved, CQ13132 +maven/mavencentral/commons-codec/commons-codec/1.15, Apache-2.0 AND BSD-3-Clause AND LicenseRef-Public-Domain, approved, CQ22641 +maven/mavencentral/commons-fileupload/commons-fileupload/1.4, Apache-2.0, approved, clearlydefined +maven/mavencentral/commons-io/commons-io/2.11.0, Apache-2.0, approved, CQ23745 +maven/mavencentral/io.github.classgraph/classgraph/4.8.138, MIT, approved, CQ22530 +maven/mavencentral/io.github.openfeign.form/feign-form-spring/3.8.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.github.openfeign.form/feign-form/2.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.github.openfeign/feign-core/11.7, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.github.openfeign/feign-slf4j/11.7, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.micrometer/micrometer-core/1.7.11, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger.core.v3/swagger-annotations/2.0.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger.core.v3/swagger-core/2.1.12, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger.core.v3/swagger-models/2.1.12, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.swagger/swagger-annotations/1.5.20, Apache-2.0, approved, CQ13287 +maven/mavencentral/io.vavr/vavr-match/0.10.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/io.vavr/vavr/0.10.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/jakarta.activation/jakarta.activation-api/1.2.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/1.3.5, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.ca +maven/mavencentral/jakarta.servlet/jakarta.servlet-api/4.0.4, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.servlet +maven/mavencentral/jakarta.validation/jakarta.validation-api/2.0.2, , approved, eclipse +maven/mavencentral/jakarta.websocket/jakarta.websocket-api/1.1.2, EPL-2.0 OR GPL-2.0-only with Classpath-exception-2.0, approved, ee4j.websocket +maven/mavencentral/jakarta.xml.bind/jakarta.xml.bind-api/2.3.3, BSD-3-Clause, approved, ee4j.jaxb +maven/mavencentral/javax.activation/javax.activation-api/1.2.0, (CDDL-1.1 OR GPL-2.0 WITH Classpath-exception-2.0) AND Apache-2.0, approved, CQ18740 +maven/mavencentral/javax.annotation/javax.annotation-api/1.3.2, CDDL-1.1 OR GPL-2.0-only WITH Classpath-exception-2.0, approved, CQ16910 +maven/mavencentral/javax.xml.bind/jaxb-api/2.3.1, CDDL-1.1 OR GPL-2.0, approved, CQ16911 +maven/mavencentral/net.bytebuddy/byte-buddy-agent/1.10.22, Apache-2.0, approved, clearlydefined +maven/mavencentral/net.bytebuddy/byte-buddy/1.10.22, , approved, CQ22491 +maven/mavencentral/net.minidev/accessors-smart/2.4.8, Apache-2.0, approved, clearlydefined +maven/mavencentral/net.minidev/json-smart/2.4.8, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.antlr/antlr4-runtime/4.5.3, BSD-2-Clause, approved, CQ9834 +maven/mavencentral/org.apache.commons/commons-compress/1.21, Apache-2.0 AND BSD-3-Clause AND bzip2-1.0.6 AND LicenseRef-Public-Domain, approved, CQ23710 +maven/mavencentral/org.apache.commons/commons-csv/1.9.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.commons/commons-lang3/3.12.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.commons/commons-text/1.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.httpcomponents/httpclient-cache/4.5.13, Apache-2.0, approved, CQ11714 +maven/mavencentral/org.apache.httpcomponents/httpclient/4.5.13, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ23527 +maven/mavencentral/org.apache.httpcomponents/httpcore/4.4.15, Apache-2.0, approved, CQ23528 +maven/mavencentral/org.apache.jena/jena-arq/4.2.0, Apache-2.0, approved, #2381 +maven/mavencentral/org.apache.jena/jena-base/4.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.jena/jena-core/4.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.jena/jena-iri/4.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.jena/jena-shaded-guava/4.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.logging.log4j/log4j-api/2.17.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.apache.logging.log4j/log4j-to-slf4j/2.17.2, Apache-2.0, approved, #2163 +maven/mavencentral/org.apache.thrift/libthrift/0.14.2, Apache-2.0, approved, #1923 +maven/mavencentral/org.apache.tomcat.embed/tomcat-embed-el/9.0.62, Apache-2.0, approved, CQ20193 +maven/mavencentral/org.apiguardian/apiguardian-api/1.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.aspectj/aspectjweaver/1.9.7, , approved, eclipse +maven/mavencentral/org.assertj/assertj-core/3.18.1, , approved, CQ22763 +maven/mavencentral/org.bouncycastle/bcpkix-jdk15on/1.68, MIT, approved, clearlydefined +maven/mavencentral/org.bouncycastle/bcprov-jdk15on/1.68, MIT, approved, CQ23714 +maven/mavencentral/org.checkerframework/checker-qual/3.5.0, MIT, approved, clearlydefined +maven/mavencentral/org.eclipse.jetty.websocket/javax-websocket-client-impl/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/javax-websocket-server-impl/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-api/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-client/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-common/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-server/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty.websocket/websocket-servlet/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-annotations/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-client/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-continuation/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-http/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-io/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-plus/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-security/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-server/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-servlet/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-servlets/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-util-ajax/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-util/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-webapp/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +maven/mavencentral/org.eclipse.jetty/jetty-xml/9.4.46.v20220331, EPL-2.0 OR Apache-2.0, approved, rt.jetty +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.hamcrest/hamcrest/2.2, BSD-3-Clause, approved, CQ23997 +maven/mavencentral/org.hdrhistogram/HdrHistogram/2.1.12, , approved, CQ13192 +maven/mavencentral/org.hibernate.validator/hibernate-validator/6.2.3.Final, Apache-2.0, approved, CQ23258 +maven/mavencentral/org.jboss.logging/jboss-logging/3.4.3.Final, Apache-2.0, approved, CQ21255 +maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.7.2, EPL-2.0, approved, CQ22640 +maven/mavencentral/org.junit.jupiter/junit-jupiter-engine/5.7.2, EPL-2.0, approved, clearlydefined +maven/mavencentral/org.junit.jupiter/junit-jupiter-params/5.7.2, EPL-2.0, approved, CQ22640 +maven/mavencentral/org.junit.jupiter/junit-jupiter/5.6.3, EPL-2.0, approved, CQ21545 +maven/mavencentral/org.junit.platform/junit-platform-commons/1.7.2, EPL-2.0, approved, CQ22640 +maven/mavencentral/org.junit.platform/junit-platform-engine/1.7.2, EPL-2.0, approved, clearlydefined +maven/mavencentral/org.latencyutils/LatencyUtils/2.0.3, BSD-2-Clause, approved, CQ17408 +maven/mavencentral/org.liquibase/liquibase-core/4.9.1, Apache-2.0, approved, #2484 +maven/mavencentral/org.mapstruct/mapstruct/1.4.2.Final, Apache-2.0, approved, #2483 +maven/mavencentral/org.mockito/mockito-core/3.9.0, Apache-2.0 AND MIT, approved, #138 +maven/mavencentral/org.mockito/mockito-junit-jupiter/3.9.0, MIT, approved, #137 +maven/mavencentral/org.objenesis/objenesis/3.2, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.openapitools/jackson-databind-nullable/0.1.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.opentest4j/opentest4j/1.2.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-analysis/9.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-commons/9.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm-tree/9.2, BSD-3-Clause, approved, clearlydefined +maven/mavencentral/org.ow2.asm/asm/9.2, BSD-3-Clause, approved, CQ23635 +maven/mavencentral/org.postgresql/postgresql/42.2.25, BSD-2-Clause, approved, #2380 +maven/mavencentral/org.projectlombok/lombok/1.18.22, MIT AND LicenseRef-Public-Domain, approved, CQ23907 +maven/mavencentral/org.skyscreamer/jsonassert/1.5.0, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.slf4j/jcl-over-slf4j/1.7.32, Apache-2.0, approved, CQ12843 +maven/mavencentral/org.slf4j/jul-to-slf4j/1.7.32, MIT, approved, CQ12842 +maven/mavencentral/org.slf4j/slf4j-api/1.7.32, MIT, approved, CQ13368 +maven/mavencentral/org.slf4j/slf4j-simple/1.7.32, MIT, approved, CQ7952 +maven/mavencentral/org.springdoc/springdoc-openapi-common/1.6.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-ui/1.6.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springdoc/springdoc-openapi-webmvc-core/1.6.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-actuator-autoconfigure/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-actuator/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-autoconfigure/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-actuator/2.5.13, Apache-2.0, approved, #2170 +maven/mavencentral/org.springframework.boot/spring-boot-starter-aop/2.5.13, Apache-2.0, approved, #2172 +maven/mavencentral/org.springframework.boot/spring-boot-starter-data-jdbc/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-jdbc/2.5.13, Apache-2.0, approved, #2161 +maven/mavencentral/org.springframework.boot/spring-boot-starter-jetty/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-json/2.5.13, Apache-2.0, approved, #1544 +maven/mavencentral/org.springframework.boot/spring-boot-starter-logging/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-oauth2-resource-server/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-test/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-validation/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter-web/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-starter/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-test-autoconfigure/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot-test/2.5.13, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.boot/spring-boot/2.5.13, Apache-2.0, approved, #2169 +maven/mavencentral/org.springframework.cloud/spring-cloud-commons/3.0.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.cloud/spring-cloud-context/3.0.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.cloud/spring-cloud-openfeign-core/3.0.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.cloud/spring-cloud-starter-openfeign/3.0.5, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.cloud/spring-cloud-starter/3.0.3, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.data/spring-data-commons/2.5.11, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.data/spring-data-jdbc/2.2.11, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.data/spring-data-relational/2.2.11, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-config/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-core/5.5.6, Apache-2.0, approved, #1539 +maven/mavencentral/org.springframework.security/spring-security-crypto/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-oauth2-core/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-oauth2-jose/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-oauth2-resource-server/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-rsa/1.0.10.RELEASE, Apache-2.0, approved, CQ20647 +maven/mavencentral/org.springframework.security/spring-security-test/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework.security/spring-security-web/5.5.6, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.springframework/spring-aop/5.3.19, Apache-2.0, approved, CQ24024 +maven/mavencentral/org.springframework/spring-beans/5.3.19, Apache-2.0, approved, CQ24025 +maven/mavencentral/org.springframework/spring-context/5.3.19, Apache-2.0, approved, CQ24027 +maven/mavencentral/org.springframework/spring-core/5.3.19, Apache-2.0 AND BSD-3-Clause, approved, CQ24026 +maven/mavencentral/org.springframework/spring-expression/5.3.19, Apache-2.0, approved, CQ23155 +maven/mavencentral/org.springframework/spring-jcl/5.3.19, Apache-2.0, approved, CQ23156 +maven/mavencentral/org.springframework/spring-jdbc/5.3.19, Apache-2.0, approved, #1545 +maven/mavencentral/org.springframework/spring-test/5.3.12, Apache-2.0, approved, CQ23054 +maven/mavencentral/org.springframework/spring-tx/5.3.19, Apache-2.0, approved, CQ23055 +maven/mavencentral/org.springframework/spring-web/5.3.19, Apache-2.0 AND LicenseRef-Public-Domain, approved, CQ24028 +maven/mavencentral/org.springframework/spring-webmvc/5.3.12, Apache-2.0, approved, CQ23158 +maven/mavencentral/org.topbraid/shacl/1.3.1, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.webjars/swagger-ui/4.5.0, Apache-2.0, approved, #2379 +maven/mavencentral/org.webjars/webjars-locator-core/0.46, MIT, approved, clearlydefined +maven/mavencentral/org.xmlunit/xmlunit-core/2.8.4, Apache-2.0, approved, clearlydefined +maven/mavencentral/org.yaml/snakeyaml/1.28, Apache-2.0 AND (EPL-1.0 OR LGPL-2.1-or-later OR GPL-2.0-or-later OR Apache-2.0 OR BSD-2-Clause), approved, CQ23168 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 00000000..348ec279 --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,49 @@ +# Notices for Eclipse Tractus-X + +This content is produced and maintained by the Eclipse Tractus-X project. + +* Project home: https://projects.eclipse.org/projects/automotive.tractusx + +See the AUTHORS file(s) distributed with this work for additional information regarding authorship. + +## Trademarks + +Eclipse Tractus-X is a trademark of the Eclipse Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Apache License, Version 2.0 which is available at +https://www.apache.org/licenses/LICENSE-2.0. + +SPDX-License-Identifier: Apache-2.0 + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse/tractusx +* https://github.com/eclipse-tractusx/sldt-semantic-hub +* https://github.com/eclipse-tractusx/sldt-digital-twin-registry +* https://github.com/eclipse-tractusx/portal-frontend + +## Third-party Content + +This project leverages the following third party content. + +See DEPENDENCIES file. + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted. diff --git a/README.md b/README.md index 09584c7f..bd27cced 100644 --- a/README.md +++ b/README.md @@ -1 +1,107 @@ -# sldt-digital-twin-registry + + + +# Digital Twin Registry +The Digital Twin Registry is a logical and architectural component of Catena-X. +The source code under this folder contains reference implementations of the SLDT Registry. + +## Build Packages +The project requires a private package from https://maven.pkg.github.com/eclipse-dataspaceconnector/DataSpaceConnector. +Add the following configuration to your `.m2/settings.xml`: + +```xml + + edc-github + oauth2 + $ADD_GITHUB_ACCESS_TOKEN_HERE + +``` + +You need to add your own GitHub Access Token. Navigate to https://github.com/settings/tokens and create a new token +with the permission `read:packages`. + +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/registry-{current-version}.jar`. + +## Build Docker +Run `docker build -t registry .` + +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 Registry using our Helm Chart, which is located under `./deployment/registry`. +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. + +Before deploying the Registry, enable a few add-ons in your minikube cluster by running the following commands: + +`minikube addons enable storage-provisioner` + +`minikube addons enable default-storageclass` + +`minikube addons enable ingress` + +Fetch all dependencies by running `helm dep up deployment/registry`. + +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 Registry instance itself, and a Fuseki Triplestore. + +Check that the two containers are running by calling `kubectl get pod -n semantics`. + +To access the Registry API from the host, you need to configure the `Ingress` resource. +By default, the Registry includes an `Ingress` that exposes the API on https://minikube/semantics/hub + +For that to work, you need to append `/etc/hosts` by running `echo "minikube $(minikube ip)" | sudo tee -a /etc/hosts`. + +For automated certificate generation, use and configure [cert-manager](https://cert-manager.io/). +By default, authentication is deactivated, please adjust `hub.authentication` if needed + +## Parameters +The Helm Chart can be configured using the following parameters (incomplete list). For a full overview, please see the [values.yaml](./deployment/semantic-hub/values.yaml). + +### Registry +| Parameter | Description | Default value | +| --- | --- | --- | +| `registry.image` | The registry and image of the Semantic Hub | `semantic-hub:latest` | +| `registry.host` | This value is used by the `Ingress` object (if enabled) to route traffic. | `minikube` | +| `registry.authentication` | Enables OAuth2 based authentication/authorization. | `false` | +| `registry.idpIssuerUri` | The issuer URI of the OAuth2 identity provider. | `http://localhost:8080/auth/realms/catenax` | +| `registry.dataSource.driverClassName` | The driver class name for the database connection. | `org.postgresql.Driver` | +| `registry.dataSource.url` | The url of the relational database (ignored if `enablePostgres` is set to `true`) | `jdbc:postgresql://database:5432` | +| `registry.dataSource.user` (ignored if `enablePostgres` is set to `true`) | The database user | `user` | +| `registry.dataSource.password` (ignored if `enablePostgres` is set to `true`) | The database password | `org.postgresql.Driver` | +| `registry.ingress.enabled` | Configures if an `Ingress` resource is created. | `true` | +| `registry.ingress.tls` | Configures whether the `Ingress` should include TLS configuration. In that case, a separate `Secret` (as defined by `registry.ingress.tlsSecretName`) needs to be provided manually or by using [cert-manager](https://cert-manager.io/) | `true` | +| `registry.ingress.tlsSecretName` | The `Secret` name that contains a `tls.crt` and `tls.key` entry. Subject Alternative Name must match the `registry.host` | `hub-certificate-secret` | +| `registry.ingress.urlPrefix` | The url prefix that is used by the `Ingress` resource to route traffic | `/semantics/hub` | +| `registry.ingress.className` | The `Ingress` class name | `nginx` | +| `registry.ingress.annotations` | Annotations to further configure the `Ingress` resource, e.g. for using with `cert-manager`. | | + +### PostgreSQL +| Parameter | Description | Default value | +| --- | --- | --- | +| `postgresql.primary.persistence.size` | Size of the `PersistentVolume` that persists the data | `50Gi` | +| `postgresql.auth.username` | Username that is used to authenticate at the database | `catenax` | +| `postgresql.auth.password` | Password for authentication at the database | `TFLIykCd4rUvSjbs` | +| `postgresql.auth.database` | Database name | `registry` | diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..1c3f1692 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,6 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report a found vulnerability here: +[https://www.eclipse.org/security/](https://www.eclipse.org/security/) diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 00000000..590c8819 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,37 @@ +############################################################### +# Copyright (c) 2021-2022 T-Systems International 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 +############################################################### + +# Docker buildfile to containerize the semantics layer +FROM openjdk:11-jre-buster + +RUN adduser --system --group spring \ + && mkdir -p /service \ + && chown spring:spring /service + +USER spring:spring + +WORKDIR /service + +COPY ./target/registry*.jar app.jar + +ENV JAVA_TOOL_OPTIONS "-Xms512m -Xmx2048m" +EXPOSE 4243 + +ENTRYPOINT [ "java","-jar","/service/app.jar" ] diff --git a/backend/deployment/registry/.helmignore b/backend/deployment/registry/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/backend/deployment/registry/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/backend/deployment/registry/Chart.lock b/backend/deployment/registry/Chart.lock new file mode 100644 index 00000000..a821e71b --- /dev/null +++ b/backend/deployment/registry/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 11.1.9 +digest: sha256:d94e937189226e296956741bf94853b3f484e582ce6b39ba23760ec682ad1073 +generated: "2022-04-07T16:25:17.18448+02:00" diff --git a/backend/deployment/registry/Chart.yaml b/backend/deployment/registry/Chart.yaml new file mode 100644 index 00000000..d70a2513 --- /dev/null +++ b/backend/deployment/registry/Chart.yaml @@ -0,0 +1,33 @@ +############################################################### +# 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 +############################################################### + +apiVersion: v2 +name: registry +description: A Helm chart for Kubernetes + +type: application +version: 0.1.0 +appVersion: 1.0.0 + +dependencies: + - repository: https://charts.bitnami.com/bitnami + name: postgresql + version: 11.1.9 + condition: enablePostgres diff --git a/backend/deployment/registry/templates/_helpers.tpl b/backend/deployment/registry/templates/_helpers.tpl new file mode 100644 index 00000000..e69de29b diff --git a/backend/deployment/registry/templates/registry/registry-deployment.yaml b/backend/deployment/registry/templates/registry/registry-deployment.yaml new file mode 100644 index 00000000..9078f156 --- /dev/null +++ b/backend/deployment/registry/templates/registry/registry-deployment.yaml @@ -0,0 +1,72 @@ +############################################################### +# 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 + ############################################################### + +{{- $deployment_name := printf "cx-%s-registry" .Release.Name }} +{{- $sec_name := printf "%s-sec" $deployment_name }} +{{- $svc_name := printf "%s-svc" $deployment_name }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $deployment_name }} +spec: + replicas: {{ .Values.registry.replicaCount }} + selector: + matchLabels: + app: {{ $deployment_name }} + template: + metadata: + labels: + app: {{ $deployment_name }} + spec: + containers: + - name: {{ $deployment_name }} + image: {{ .Values.registry.image }} + imagePullPolicy: {{ .Values.registry.imagePullPolicy }} + {{- if not .Values.registry.authentication }} + args: ["--spring.profiles.active=local"] + {{- end }} + ports: + - containerPort: {{ .Values.registry.containerPort }} + env: + - name: SPRING_DATASOURCE_DRIVERCLASSNAME + value: {{ .Values.registry.dataSource.driverClassName }} + - name: SPRING_SQL_INIT_PLATFORM + value: {{ .Values.registry.dataSource.sqlInitPlatform }} + livenessProbe: + httpGet: + path: /actuator/health/liveness + port: {{ .Values.registry.containerPort }} + initialDelaySeconds: 100 + periodSeconds: 3 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /actuator/health/readiness + port: {{ .Values.registry.containerPort }} + initialDelaySeconds: 60 + periodSeconds: 3 + failureThreshold: 3 + envFrom: + - secretRef: + name: {{ $sec_name }} + resources: +{{ .Values.registry.resources | toYaml | indent 12 }} + imagePullSecrets: +{{ .Values.registry.imagePullSecrets | toYaml | indent 8 }} diff --git a/backend/deployment/registry/templates/registry/registry-ingress.yaml b/backend/deployment/registry/templates/registry/registry-ingress.yaml new file mode 100644 index 00000000..835d967f --- /dev/null +++ b/backend/deployment/registry/templates/registry/registry-ingress.yaml @@ -0,0 +1,52 @@ +{{- if .Values.registry.ingress.enabled }} +############################################################### +# 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 +############################################################### + +{{- $deployment_name := printf "cx-%s-registry" .Release.Name }} +{{- $svc_name := printf "%s-svc" $deployment_name }} +{{- $ingr_name := printf "%s-ingr" $deployment_name }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $ingr_name }} + annotations: +{{ .Values.registry.ingress.annotations | toYaml | indent 4 }} + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +spec: + ingressClassName: {{ .Values.registry.ingress.className }} + {{- if .Values.registry.ingress.tls }} + tls: + - hosts: + - {{ .Values.registry.host }} + secretName: registry-certificate-secret + {{- end }} + rules: + - host: {{ .Values.registry.host }} + http: + paths: + - path: {{printf "%s(/|$)(.*)" .Values.registry.ingress.urlPrefix }} + pathType: Prefix + backend: + service: + name: {{ $svc_name }} + port: + number: {{ .Values.registry.service.port }} +{{- end}} diff --git a/backend/deployment/registry/templates/registry/registry-secret.yaml b/backend/deployment/registry/templates/registry/registry-secret.yaml new file mode 100644 index 00000000..639be664 --- /dev/null +++ b/backend/deployment/registry/templates/registry/registry-secret.yaml @@ -0,0 +1,38 @@ +############################################################### +# 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 +############################################################### + +{{- $deployment_name := printf "cx-%s-registry" .Release.Name }} +{{- $sec_name := printf "%s-sec" $deployment_name }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $sec_name }} +type: Opaque +data: + {{- if .Values.enablePostgres }} + SPRING_DATASOURCE_URL: {{ printf "jdbc:postgresql://%s-postgresql:%v/%s" .Release.Name .Values.postgresql.service.ports.postgresql .Values.postgresql.auth.database | b64enc }} + SPRING_DATASOURCE_USERNAME: {{ .Values.postgresql.auth.username | b64enc }} + SPRING_DATASOURCE_PASSWORD: {{ .Values.postgresql.auth.password | b64enc }} + {{- else }} + SPRING_DATASOURCE_URL: {{ .Values.registry.dataSource.url | b64enc }} + SPRING_DATASOURCE_USERNAME: {{ .Values.registry.dataSource.user | b64enc }} + SPRING_DATASOURCE_PASSWORD: {{ .Values.registry.dataSource.password | b64enc }} + {{- end }} + SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: {{ .Values.registry.idpIssuerUri | b64enc }} diff --git a/backend/deployment/registry/templates/registry/registry-service.yaml b/backend/deployment/registry/templates/registry/registry-service.yaml new file mode 100644 index 00000000..969dd927 --- /dev/null +++ b/backend/deployment/registry/templates/registry/registry-service.yaml @@ -0,0 +1,35 @@ +############################################################### +# 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 +############################################################### + +{{- $deployment_name := printf "cx-%s-registry" .Release.Name }} +{{- $svc_name := printf "%s-svc" $deployment_name }} +apiVersion: v1 +kind: Service +metadata: + name: {{ $svc_name }} + labels: + chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" +spec: + type: {{ .Values.registry.service.type }} + ports: + - port: {{ .Values.registry.service.port }} + targetPort: {{ .Values.registry.containerPort }} + selector: + app: {{ $deployment_name }} diff --git a/backend/deployment/registry/values.yaml b/backend/deployment/registry/values.yaml new file mode 100644 index 00000000..ad90debe --- /dev/null +++ b/backend/deployment/registry/values.yaml @@ -0,0 +1,72 @@ +############################################################### +# 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 +############################################################### + +enablePostgres: true + +registry: + image: registry:latest + replicaCount: 1 + imagePullPolicy: IfNotPresent + containerPort: 4242 + host: minikube + ## If 'authentication' is set to false, no OAuth authentication is enforced + authentication: false + idpIssuerUri: https://catenaxdev042akssrv.germanywestcentral.cloudapp.azure.com/iamcentralidp/auth/realms/CX-Central + service: + port: 8080 + type: ClusterIP + dataSource: + driverClassName: org.postgresql.Driver + sqlInitPlatform: pg + ## The url, user, and password parameter will be ignored if 'enablePostgres' is set to true. + ## In that case the postgresql auth parameters are used. + url: jdbc:postgresql://database:5432 + user: user + password: password + ingress: + enabled: true + tls: true + urlPrefix: /semantics/registry + className: nginx + annotations: + cert-manager.io/cluster-issuer: selfsigned-cluster-issuer + nginx.ingress.kubernetes.io/rewrite-target: /$2 + nginx.ingress.kubernetes.io/use-regex: "true" + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-credentials: "true" + nginx.ingress.kubernetes.io/x-forwarded-prefix: /semantics/registry + resources: + limits: + memory: "1024Mi" + requests: + memory: "512Mi" + +postgresql: + primary: + persistence: + enabled: true + size: 50Gi + service: + ports: + postgresql: 5432 + auth: + username: catenax + password: TFLIykCd4rUvSjbs + database: registry diff --git a/backend/loadtests/README.md b/backend/loadtests/README.md new file mode 100644 index 00000000..903a9aec --- /dev/null +++ b/backend/loadtests/README.md @@ -0,0 +1,63 @@ + + +# Introduction + +This folder contains the load tests for the AAS Registry. +The tool used for the load testing is `https://locust.io/`. + +# Load test + +The current implemented load test does the following: + + - Creates a shell + - Retrieves the created shell by id + - Lookups the shell by specific asset ids + +To reduce the complexity, authentication is disabled. + +# Executing the test + +The `docker-compose.yml` all relevant services to execute the load test. + + - AAS Registry (latest INT version) + - PostgreSQL Database as persistence for the AAS Registry + - Locust Master for the Webui + - Locust Worker for the load test execution + +# Run the load test + + 1. Execute `docker-compose up -d` + 2. Open the Locust WebUI `http://localhost:8090` + 3. In the opened form enter the following: + - Number of users = 100 (=> 10 req/s) + - Spawn rate = 5 + - Host = http://host.docker.internal:4242 + 4. Press Start. Locust will now execute the load test as long as you wish. + 5. You can stop the test at anytime through the UI and grab the statistics. + +# Local development + +The steps for local development of the load tests are: + + 1. Ensure python3 is installed + 2. Run `pip3 install -r requirements.txt` + 3. Modify the script + 4. Run `locust -f ./locust/locustfile.py --headless --users 1 --spawn-rate 1 -H http://host.docker.internal:4242` diff --git a/backend/loadtests/docker-compose.yml b/backend/loadtests/docker-compose.yml new file mode 100644 index 00000000..53ae7613 --- /dev/null +++ b/backend/loadtests/docker-compose.yml @@ -0,0 +1,68 @@ +############################################################### +# 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 +############################################################### + +version: '3' + +services: + + postgres: + image: postgres:13.6-alpine + container_name: postgres + ports: + - "5432:5432" + environment: + POSTGRES_PASSWORD: example + volumes: + - ./postgres-data:/var/lib/postgresql/data + + aas_registry: + image: catenaxacr.azurecr.io/semantics/registryint:latest + # build image locally with `docker build -f ./registry -t registry:latest .` + # and comment below in to test against local instance. + #image: registry:latest + container_name: aas_registry + ports: + - "4243:4243" + environment: + SPRING_DATASOURCE_URL: jdbc:postgresql://host.docker.internal:5432/postgres + SPRING_DATASOURCE_DRIVERCLASSNAME: org.postgresql.Driver + SPRING_DATASOURCE_USERNAME: postgres + SPRING_DATASOURCE_PASSWORD: example + SPRING_DATASOURCE_HIKARI_INITIALIZATION_FAIL_TIMEOUT: 0 + # disable security + SPRING_PROFILES_ACTIVE: local + IDP_ISSUER_URI: "" + + locust_master: + image: locustio/locust:2.8.2 + container_name: locust_master + ports: + - "8089:8089" + volumes: + - ./locust:/mnt/locust/ + command: -f /mnt/locust/locustfile.py --master -H http://host.docker.internal:4242 + + locust_worker: + image: locustio/locust:2.8.2 + container_name: locust_worker + volumes: + - ./locust:/mnt/locust/ + command: -f /mnt/locust/locustfile.py --worker --master-host locust_master + diff --git a/backend/loadtests/locust/locustfile.py b/backend/loadtests/locust/locustfile.py new file mode 100644 index 00000000..879bc57e --- /dev/null +++ b/backend/loadtests/locust/locustfile.py @@ -0,0 +1,139 @@ +############################################################### +# 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 +############################################################### + +import json +import uuid +import urllib.parse + + +from locust import HttpUser, task, constant_throughput +from locust.exception import RescheduleTask + +class AasRegistryTask(HttpUser): + + # If 100 Users are configured in the load test + # constant_throughput ensures that: 100 Users * 0.1 = 10 request/s + wait_time = constant_throughput(0.1) + + @task + def createAndQueryAasDescriptor(self): + shell = generate_shell() + headers = { 'Content-Type' : 'application/json'} + with self.client.post("/registry/shell-descriptors", data=json.dumps(shell), headers= headers, catch_response=True) as response: + if response.status_code != 201: + response.failure(f"Expected 201 but status code was {response.status_code}") + raise RescheduleTask() + + shell_id = shell['identification'] + + with self.client.get(f"/registry/shell-descriptors/{shell_id}", name = "/registry/shell-descriptors/{id}", catch_response=True) as response: + if response.status_code != 200: + response.failure(f"Expected 200 but status code was {response.status_code}") + raise RescheduleTask() + + specificAssetIds = shell['specificAssetIds'] + decodedAssetIds = urllib.parse.quote_plus(json.dumps(specificAssetIds)) + with self.client.get(f"/lookup/shells?assetIds={decodedAssetIds}", name = "/lookup/shells?assetIds={assetIds}", catch_response=True) as response: + if response.status_code != 200: + response.failure(f"Expected 200 but status code was {response.status_code}") + raise RescheduleTask() + +def generate_shell(): + aasId = uuid.uuid4() + globalAssetId = uuid.uuid4() + specificAssetId1 = uuid.uuid4() + specificAssetId2 = uuid.uuid4() + return { + "description": [ + { + "language": "en", + "text": "The shell for a vehicle" + } + ], + "globalAssetId": { + "value": [ + str(globalAssetId) + ] + }, + "idShort": "future concept x", + "identification": str(aasId), + "specificAssetIds": [ + { + "key": "MaterialId", + "value": str(specificAssetId1) + }, + { + "key": "PartId", + "value": str(specificAssetId2) + } + ], + "submodelDescriptors": [ + { + "description": [ + { + "language": "en", + "text": "Provides base vehicle information" + } + ], + "idShort": "vehicle base details", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax.vehicle:0.1.1" + ] + }, + "endpoints": [ + { + "interface": "HTTP", + "protocolInformation": { + "endpointAddress": "https://catena-x.net/vehicle/basedetails/", + "endpointProtocol": "HTTPS", + "endpointProtocolVersion": "1.0" + } + } + ] + }, + { + "description": [ + { + "language": "en", + "text": "Provides base vehicle information" + } + ], + "idShort": "vehicle part details", + "identification": "dae4d249-6d66-4818-b576-bf52f3b9ae90", + "semanticId": { + "value": [ + "urn:bamm:com.catenax.vehicle:0.1.1#PartDetails" + ] + }, + "endpoints": [ + { + "interface": "HTTP", + "protocolInformation": { + "endpointAddress": "https://catena-x.net/vehicle/partdetails/", + "endpointProtocol": "HTTPS", + "endpointProtocolVersion": "1.0" + } + } + ] + } + ] + } diff --git a/backend/loadtests/locust/requirements.txt b/backend/loadtests/locust/requirements.txt new file mode 100644 index 00000000..096abcb7 --- /dev/null +++ b/backend/loadtests/locust/requirements.txt @@ -0,0 +1 @@ +locust==2.8.2 \ No newline at end of file diff --git a/backend/pom.xml b/backend/pom.xml new file mode 100644 index 00000000..30551c34 --- /dev/null +++ b/backend/pom.xml @@ -0,0 +1,238 @@ + + + + + + 4.0.0 + + org.eclipse.tractusx + digital-twin-registry + 1.3.0-SNAPSHOT + ../pom.xml + + + org.eclipse.tractusx.digital_twin_registry + backend + Tractus-X Semantic Layer Digital Twin Registry Backend + Module contains the Semantic Layer Digital Twin Registry Service Backend + jar + + + ${organization} + ${url} + + + + + ${licence_name} + ${licence_url} + ${licence_distribution} + ${licence_comments} + + + + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-validation + + + org.liquibase + liquibase-core + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-jetty + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.boot + spring-boot-starter-test + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.security + spring-security-test + test + + + + + org.postgresql + postgresql + + + com.h2database + h2 + + + + org.mapstruct + mapstruct + + + org.projectlombok + lombok + + + io.swagger.core.v3 + swagger-annotations + + + io.swagger + swagger-annotations + + + org.springdoc + springdoc-openapi-ui + + + org.apache.commons + commons-text + 1.6 + + + com.google.guava + guava + + + org.topbraid + shacl + + + io.vavr + vavr + + + org.openapitools + jackson-databind-nullable + + + + + org.assertj + assertj-core + + + org.junit.jupiter + junit-jupiter + + + org.slf4j + slf4j-simple + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + io.github.git-commit-id + git-commit-id-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + + + generate-aas-registry-api-stubs + + generate + + + ${project.basedir}/src/main/resources/static/aas-registry-openapi.yaml + spring + true + org.eclipse.tractusx.semantics.aas.registry + org.eclipse.tractusx.semantics.aas.registry.model + org.eclipse.tractusx.semantics.aas.registry.api + + ApiUtil.java + + true + true + org.eclipse.tractusx + true + false + + org.eclipse.tractusx.semantics + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + org.apache.maven.plugins + maven-deploy-plugin + + + + + + src/main/resources + true + + **/*.yml + + + + src/main/resources + false + + **/*.yml + + + + + + diff --git a/backend/run_local.sh b/backend/run_local.sh new file mode 100644 index 00000000..687aeb58 --- /dev/null +++ b/backend/run_local.sh @@ -0,0 +1,81 @@ +############################################################### +# Copyright (c) 2021-2022 T-Systems International 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 +############################################################### + +# +# Shell script to build and run a local registry for testing purposes. +# +# Prerequisites: +# Windows, (git)-bash shell, java 11 (java) and maven (mvn) in the $PATH. +# +# Synposis: +# ./build_run_local.sh (-build)? (clean)? (-suspend)? (-debug)? (-proxy)? +# +# Comments: +# + +DEBUG_PORT=8887 +DEBUG_SUSPEND=n +DEBUG_OPTIONS= +DB_FILE="./target/db_semantics;CASE_INSENSITIVE_IDENTIFIERS=TRUE" +CLEAN_DB=n +PROXY= + +for var in "$@" +do + if [ "$var" == "-debug" ]; then + DEBUG_OPTIONS="-agentlib:jdwp=transport=dt_socket,address=${DEBUG_PORT},server=y,suspend=${DEBUG_SUSPEND}" + else + if [ "$var" == "-build" ]; then + mvn install -DskipTests -Dmaven.javadoc.skip=true + else + if [ "$var" == "-suspend" ]; then + DEBUG_SUSPEND=y + else + if [ "$var" == "-clean" ]; then + CLEAN_DB=y + mvn clean + else + if [ "$var" == "-proxy" ]; then + PROXY="-Dhttp.proxyHost=${HTTP_PROXY_HOST} -Dhttp.proxyPort=${HTTP_PROXY_PORT} -Dhttps.proxyHost=${HTTP_PROXY_HOST} -Dhttps.proxyPort=${HTTP_PROXY_PORT}" + if [ "${HTTP_NONPROXY_HOSTS}" != "" ]; then + PROXY="${PROXY} -Dhttp.nonProxyHosts=${HTTP_NONPROXY_HOSTS} -Dhttps.nonProxyHosts=${HTTP_NONPROXY_HOSTS}" + fi + fi + fi + fi + fi + fi +done + +H2_URL=jdbc:h2:file:${DB_FILE} + +if [ "$CLEAN_DB" == "y" ]; then + rm -f ${DB_FILE}* +fi + +CALL_ARGS="-classpath target/registry-1.3.0-SNAPSHOT.jar \ + -Dspring.profiles.active=local \ + -Dspring.datasource.url=$H2_URL\ + -Dserver.ssl.enabled=false $PROXY $DEBUG_OPTIONS\ + org.springframework.boot.loader.JarLauncher" + +java ${CALL_ARGS} + + diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/ApiExceptionHandler.java b/backend/src/main/java/org/eclipse/tractusx/semantics/ApiExceptionHandler.java new file mode 100644 index 00000000..e9a52b4f --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/ApiExceptionHandler.java @@ -0,0 +1,110 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.tractusx.semantics.registry.model.support.DatabaseExceptionTranslation; +import org.eclipse.tractusx.semantics.registry.service.EntityNotFoundException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + +import org.eclipse.tractusx.semantics.aas.registry.model.Error; +import org.eclipse.tractusx.semantics.aas.registry.model.ErrorResponse; + + +@ControllerAdvice +public class ApiExceptionHandler extends ResponseEntityExceptionHandler { + + @Override + protected ResponseEntity handleMethodArgumentNotValid( final MethodArgumentNotValidException ex, + final HttpHeaders headers, + final HttpStatus status, final WebRequest request ) { + final String path = ((ServletWebRequest) request).getRequest().getRequestURI(); + final Map errors = ex.getBindingResult() + .getFieldErrors() + .stream() + .collect( Collectors.toMap( FieldError::getField, e -> { + if ( null == e.getDefaultMessage() ) { + return "null"; + } + return e.getDefaultMessage(); + } ) ); + // TODO: the ErrorResponse classes are currently in the AAS api definition + // we should move that out to a general api definition. Error response should be identical for all semantic layer + // services. + return new ResponseEntity<>( new ErrorResponse() + .error( new Error() + .message( "Validation failed." ) + .details( errors ) + .path( path ) ), HttpStatus.BAD_REQUEST ); + } + + @ExceptionHandler( { EntityNotFoundException.class } ) + public ResponseEntity handleNotFoundException( final HttpServletRequest request, + final RuntimeException exception ) { + return new ResponseEntity<>( new ErrorResponse() + .error( new Error() + .message( exception.getMessage() ) + .path( request.getRequestURI() ) ), HttpStatus.NOT_FOUND ); + } + + @ExceptionHandler( {IllegalArgumentException.class}) + public ResponseEntity handleIllegalArgumentException( final HttpServletRequest request, + final IllegalArgumentException exception ) { + return new ResponseEntity<>( new ErrorResponse() + .error( new Error() + .message( exception.getMessage() ) + .path( request.getRequestURI() ) ), HttpStatus.BAD_REQUEST ); + } + + @ExceptionHandler( {MethodArgumentConversionNotSupportedException.class}) + public ResponseEntity handleMethodArgumentNotSupportedException( final HttpServletRequest request ) { + String queryString = request.getQueryString(); + return new ResponseEntity<>( new ErrorResponse() + .error( new Error() + .message( String.format("The provided parameters are invalid. %s", URLDecoder.decode(queryString, StandardCharsets.UTF_8)) ) + .path( request.getRequestURI() ) ), HttpStatus.BAD_REQUEST ); + } + + @ExceptionHandler( {DuplicateKeyException.class}) + public ResponseEntity handleDuplicateKeyException( final HttpServletRequest request, DuplicateKeyException e ) { + return new ResponseEntity<>( new ErrorResponse() + .error( new Error() + .message(DatabaseExceptionTranslation.translate( e ) ) + .path( request.getRequestURI() ) ), HttpStatus.BAD_REQUEST ); + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/AuthorizationEvaluator.java b/backend/src/main/java/org/eclipse/tractusx/semantics/AuthorizationEvaluator.java new file mode 100644 index 00000000..c889a0bd --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/AuthorizationEvaluator.java @@ -0,0 +1,113 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +import java.util.Collection; +import java.util.Map; + +import static org.eclipse.tractusx.semantics.AuthorizationEvaluator.Roles.*; + +/** + * This class contains methods validating JWT tokens for correctness and ensuring that the JWT token contains a desired role. + * The methods are meant to be used in Spring Security expressions for RBAC on API operations. + * + * The Catena-X JWT Tokens are configured as in the example below: + * + * resource_access: + * catenax-portal: + * roles: + * - add_digitial_twin + * - delete_digitial_twin + * - ... .. .. + * + * Before checking for an existing role, the token is validated first. If any attributes are not set as the expected structure, + * the token will be considered invalid. Invalid tokens result in 403. + * + */ +public class AuthorizationEvaluator { + + private final String clientId; + + public AuthorizationEvaluator(String clientId) { + this.clientId = clientId; + } + + public boolean hasRoleViewDigitalTwin() { + return containsRole(ROLE_VIEW_DIGITAL_TWIN); + } + + public boolean hasRoleAddDigitalTwin() { + return containsRole(ROLE_ADD_DIGITAL_TWIN); + } + + public boolean hasRoleUpdateDigitalTwin() { + return containsRole(ROLE_UPDATE_DIGITAL_TWIN); + } + + public boolean hasRoleDeleteDigitalTwin() { + return containsRole(ROLE_DELETE_DIGITAL_TWIN); + } + + + private boolean containsRole(String role){ + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if(!(authentication instanceof JwtAuthenticationToken)){ + return false; + } + + JwtAuthenticationToken jwtAuthenticationToken = (JwtAuthenticationToken) (authentication); + Map claims = jwtAuthenticationToken.getToken().getClaims(); + + Object resourceAccess = claims.get("resource_access"); + if (!(resourceAccess instanceof Map)) { + return false; + } + + Object resource = ((Map) resourceAccess).get(clientId); + if(!(resource instanceof Map)){ + return false; + } + + Object roles = ((Map)resource).get("roles"); + if(!(roles instanceof Collection)){ + return false; + } + + Collection rolesList = (Collection ) roles; + return rolesList.contains(role); + } + + /** + * Represents the roles defined for the registry. + */ + public static final class Roles { + public static final String ROLE_VIEW_DIGITAL_TWIN = "view_digital_twin"; + public static final String ROLE_UPDATE_DIGITAL_TWIN = "update_digital_twin"; + public static final String ROLE_ADD_DIGITAL_TWIN = "add_digital_twin"; + public static final String ROLE_DELETE_DIGITAL_TWIN = "delete_digital_twin"; + } + +} + diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/LocalOauthSecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/semantics/LocalOauthSecurityConfig.java new file mode 100644 index 00000000..41277ff3 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/LocalOauthSecurityConfig.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Profile("local") +@Configuration +public class LocalOauthSecurityConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests(auth -> auth + .anyRequest().permitAll()) + .csrf().disable(); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java new file mode 100644 index 00000000..22a1d802 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java @@ -0,0 +1,74 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.convert.converter.Converter; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; + +import java.util.Collection; + +@Profile("!local") +@Configuration +public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter { + + /** + * Applies the jwt token based security configuration. + * + * The OpenAPI generator does not support roles. + * API Paths are authorized in this method with path and method based matchers. + */ + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests(auth -> auth + .antMatchers(HttpMethod.OPTIONS).permitAll() + // fetch endpoint is allowed for reader + .mvcMatchers(HttpMethod.POST,"/**/registry/**/fetch").access("@authorizationEvaluator.hasRoleViewDigitalTwin()") + // others are HTTP method based + .antMatchers(HttpMethod.GET,"/**/registry/**").access("@authorizationEvaluator.hasRoleViewDigitalTwin()") + .antMatchers(HttpMethod.POST,"/**/registry/**").access("@authorizationEvaluator.hasRoleAddDigitalTwin()") + .antMatchers(HttpMethod.PUT,"/**/registry/**").access("@authorizationEvaluator.hasRoleUpdateDigitalTwin()") + .antMatchers(HttpMethod.DELETE,"/**/registry/**").access("@authorizationEvaluator.hasRoleDeleteDigitalTwin()") + // lookup + // query endpoint is allowed for reader + .antMatchers(HttpMethod.POST,"/**/lookup/**/query/**").access("@authorizationEvaluator.hasRoleViewDigitalTwin()") + // others are HTTP method based + .antMatchers(HttpMethod.GET,"/**/lookup/**").access("@authorizationEvaluator.hasRoleViewDigitalTwin()") + .antMatchers(HttpMethod.POST,"/**/lookup/**").access("@authorizationEvaluator.hasRoleAddDigitalTwin()") + .antMatchers(HttpMethod.PUT,"/**/lookup/**").access("@authorizationEvaluator.hasRoleUpdateDigitalTwin()") + .antMatchers(HttpMethod.DELETE,"/**/lookup/**").access("@authorizationEvaluator.hasRoleDeleteDigitalTwin()") + ) + .csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .oauth2ResourceServer() + .jwt(); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/RegistryApplication.java b/backend/src/main/java/org/eclipse/tractusx/semantics/RegistryApplication.java new file mode 100644 index 00000000..d3e761f2 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/RegistryApplication.java @@ -0,0 +1,95 @@ +/******************************************************************************** + * Copyright (c) 2021-2022 T-Systems International 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics; + +import org.springdoc.core.SpringDocConfigProperties; +import org.springdoc.core.SpringDocConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jdbc.repository.config.EnableJdbcAuditing; +import org.springframework.security.web.firewall.HttpFirewall; +import org.springframework.security.web.firewall.StrictHttpFirewall; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Main Adapter Application + */ +@SpringBootApplication +@EnableJdbcAuditing +@EnableConfigurationProperties(RegistryProperties.class) +@ComponentScan(basePackages = {"org.eclipse.tractusx.semantics", "org.openapitools.configuration"}) +public class RegistryApplication { + + private static final String OPEN_ID_CONNECT_DISCOVERY_PATH = "/.well-known/openid-configuration"; + + @Bean + public WebMvcConfigurer configurer(OAuth2ResourceServerProperties securityProperties) { + return new WebMvcConfigurer(){ + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*").allowedMethods("*"); + } + @Override + public void addViewControllers(ViewControllerRegistry registry){ + // this redirect ensures that the SwaggerUI can get the open id discovery data + String fullDiscoveryPath = securityProperties.getJwt().getIssuerUri() + OPEN_ID_CONNECT_DISCOVERY_PATH; + registry.addRedirectViewController(OPEN_ID_CONNECT_DISCOVERY_PATH, fullDiscoveryPath); + } + }; + } + + @Bean + SpringDocConfiguration springDocConfiguration(){ + return new SpringDocConfiguration(); + } + + @Bean + public SpringDocConfigProperties springDocConfigProperties() { + return new SpringDocConfigProperties(); + } + + @Bean + public HttpFirewall allowUrlEncodedSlashHttpFirewall() { + StrictHttpFirewall firewall = new StrictHttpFirewall(); + firewall.setAllowUrlEncodedSlash(true); + return firewall; + } + + @Bean + public AuthorizationEvaluator authorizationEvaluator(RegistryProperties registryProperties){ + return new AuthorizationEvaluator(registryProperties.getIdm().getPublicClientId()); + } + + /** + * entry point if started as an app + * @param args command line + */ + public static void main(String[] args) { + new SpringApplication(RegistryApplication.class).run(args); + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/RegistryProperties.java b/backend/src/main/java/org/eclipse/tractusx/semantics/RegistryProperties.java new file mode 100644 index 00000000..ec3ab70b --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/RegistryProperties.java @@ -0,0 +1,50 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + + +@Data +@Validated +@ConfigurationProperties(prefix = "registry") +public class RegistryProperties { + + private final Idm idm = new Idm(); + + /** + * Properties for Identity Management system + */ + @Data + @NotNull + public static class Idm { + /** + * The public client id used for the redirect urls. + */ + @NotEmpty(message = "public client id must not be empty") + private String publicClientId; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/AssetAdministrationShellApiDelegate.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/AssetAdministrationShellApiDelegate.java new file mode 100644 index 00000000..3affed0f --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/AssetAdministrationShellApiDelegate.java @@ -0,0 +1,179 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.controller; + +import org.eclipse.tractusx.semantics.registry.service.ShellService; +import org.eclipse.tractusx.semantics.aas.registry.api.LookupApiDelegate; +import org.eclipse.tractusx.semantics.aas.registry.api.RegistryApiDelegate; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptorCollection; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptorCollectionBase; +import org.eclipse.tractusx.semantics.aas.registry.model.BatchResult; +import org.eclipse.tractusx.semantics.aas.registry.model.IdentifierKeyValuePair; +import org.eclipse.tractusx.semantics.aas.registry.model.ShellLookup; +import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelDescriptor; +import org.eclipse.tractusx.semantics.registry.dto.BatchResultDto; +import org.eclipse.tractusx.semantics.registry.mapper.ShellMapper; +import org.eclipse.tractusx.semantics.registry.mapper.SubmodelMapper; +import org.eclipse.tractusx.semantics.registry.model.Shell; +import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier; +import org.eclipse.tractusx.semantics.registry.model.Submodel; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.NativeWebRequest; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +@Service +public class AssetAdministrationShellApiDelegate implements RegistryApiDelegate, LookupApiDelegate { + + private final ShellService shellService; + private final ShellMapper shellMapper; + private final SubmodelMapper submodelMapper; + + public AssetAdministrationShellApiDelegate(final ShellService shellService, final ShellMapper shellMapper, SubmodelMapper submodelMapper) { + this.shellService = shellService; + this.shellMapper = shellMapper; + this.submodelMapper = submodelMapper; + } + + + @Override + public Optional getRequest() { + return RegistryApiDelegate.super.getRequest(); + } + + @Override + public ResponseEntity getAllAssetAdministrationShellDescriptors(Integer page, Integer pageSize) { + return new ResponseEntity<>(shellMapper.toApiDto(shellService.findAllShells(page, pageSize)), HttpStatus.OK); + } + + @Override + public ResponseEntity postFetchAssetAdministrationShellDescriptor(List shellIdentifications) { + List shellsByExternalShellIds = shellService.findShellsByExternalShellIds(new HashSet<>(shellIdentifications)); + return new ResponseEntity<>(new AssetAdministrationShellDescriptorCollectionBase() + .items(shellMapper.toApiDto(shellsByExternalShellIds)), HttpStatus.OK); + } + + @Override + public ResponseEntity deleteAssetAdministrationShellDescriptorById(String aasIdentifier) { + shellService.deleteShell(aasIdentifier); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public ResponseEntity deleteSubmodelDescriptorById(String aasIdentifier, String submodelIdentifier) { + shellService.deleteSubmodel(aasIdentifier, submodelIdentifier); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public ResponseEntity> getAllSubmodelDescriptors(String aasIdentifier) { + Shell savedShell = shellService.findShellByExternalId(aasIdentifier); + return new ResponseEntity<>(submodelMapper.toApiDto(savedShell.getSubmodels()), HttpStatus.OK); + } + + @Override + public ResponseEntity getAssetAdministrationShellDescriptorById(String aasIdentifier) { + Shell saved = shellService.findShellByExternalId(aasIdentifier); + return new ResponseEntity<>(shellMapper.toApiDto(saved), HttpStatus.OK); + } + + @Override + public ResponseEntity getSubmodelDescriptorById(String aasIdentifier, String submodelIdentifier) { + Submodel submodel = shellService.findSubmodelByExternalId(aasIdentifier, submodelIdentifier); + return new ResponseEntity<>(submodelMapper.toApiDto(submodel), HttpStatus.OK); + } + + @Override + public ResponseEntity postAssetAdministrationShellDescriptor(AssetAdministrationShellDescriptor assetAdministrationShellDescriptor) { + Shell saved = shellService.save(shellMapper.fromApiDto(assetAdministrationShellDescriptor)); + return new ResponseEntity<>(shellMapper.toApiDto(saved), HttpStatus.CREATED); + } + + @Override + public ResponseEntity postSubmodelDescriptor(String aasIdentifier, SubmodelDescriptor submodelDescriptor) { + Submodel savedSubModel = shellService.save(aasIdentifier, submodelMapper.fromApiDto(submodelDescriptor)); + return new ResponseEntity<>(submodelMapper.toApiDto(savedSubModel), HttpStatus.CREATED); + } + + @Override + public ResponseEntity putAssetAdministrationShellDescriptorById(String aasIdentifier, AssetAdministrationShellDescriptor assetAdministrationShellDescriptor) { + shellService.update(aasIdentifier, shellMapper.fromApiDto(assetAdministrationShellDescriptor) + // the external id in the payload must not differ from the path parameter and will be overridden + .withIdExternal(aasIdentifier)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public ResponseEntity putSubmodelDescriptorById(String aasIdentifier, String submodelIdentifier, SubmodelDescriptor submodelDescriptor) { + shellService.update(aasIdentifier, submodelIdentifier, submodelMapper.fromApiDto(submodelDescriptor) + // the external id in the payload must not differ from the path parameter and will be overridden + .withIdExternal(submodelIdentifier)); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public ResponseEntity deleteAllAssetLinksById(String aasIdentifier) { + shellService.deleteAllIdentifiers(aasIdentifier); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @Override + public ResponseEntity> getAllAssetAdministrationShellIdsByAssetLink(List assetIds) { + if( assetIds == null || assetIds.isEmpty()){ + return new ResponseEntity<>(Collections.emptyList(), HttpStatus.OK); + } + List externalIds = shellService.findExternalShellIdsByIdentifiersByExactMatch(shellMapper.fromApiDto(assetIds)); + return new ResponseEntity<>(externalIds, HttpStatus.OK); + } + + @Override + public ResponseEntity> getAllAssetLinksById(String aasIdentifier) { + Set identifiers = shellService.findShellIdentifiersByExternalShellId(aasIdentifier); + return new ResponseEntity<>(shellMapper.toApiDto(identifiers), HttpStatus.OK); + } + + @Override + public ResponseEntity> postAllAssetLinksById(String aasIdentifier, List identifierKeyValuePair) { + Set shellIdentifiers = shellService.save(aasIdentifier, shellMapper.fromApiDto(identifierKeyValuePair)); + return new ResponseEntity<>(shellMapper.toApiDto(shellIdentifiers), HttpStatus.CREATED); + } + + @Override + public ResponseEntity> postBatchAssetAdministrationShellDescriptor(List assetAdministrationShellDescriptor) { + List shells = shellMapper.fromListApiDto(assetAdministrationShellDescriptor); + List batchResults = shellService.saveBatch(shells); + return new ResponseEntity<>(shellMapper.toListApiDto(batchResults), HttpStatus.CREATED); + } + + @Override + public ResponseEntity> postQueryAllAssetAdministrationShellIds(ShellLookup shellLookup) { + List assetIds = shellLookup.getQuery().getAssetIds(); + List externalIds = shellService.findExternalShellIdsByIdentifiersByAnyMatch(shellMapper.fromApiDto(assetIds)); + return new ResponseEntity<>(externalIds, HttpStatus.OK); + } +} + diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/IdentifierKeyValuePairArrayConverter.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/IdentifierKeyValuePairArrayConverter.java new file mode 100644 index 00000000..80b88312 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/IdentifierKeyValuePairArrayConverter.java @@ -0,0 +1,47 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.controller; + +import org.springframework.core.convert.converter.Converter; +import org.eclipse.tractusx.semantics.aas.registry.model.IdentifierKeyValuePair; +import org.springframework.stereotype.Component; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * This converter is required so that Spring is able to convert array-style query parameters to custom objects. + */ +@Component +@RequiredArgsConstructor +public class IdentifierKeyValuePairArrayConverter implements Converter> { + + private final IdentifierKeyValuePairConverter singleConverter; + + @Override + public List convert(String[] source) { + List result = new ArrayList<>(source.length); + for (int count = 0; count < source.length; count++) { + result.addAll(singleConverter.convert(source[count])); + } + return result; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/IdentifierKeyValuePairConverter.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/IdentifierKeyValuePairConverter.java new file mode 100644 index 00000000..89c142e8 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/controller/IdentifierKeyValuePairConverter.java @@ -0,0 +1,69 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.controller; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.eclipse.tractusx.semantics.aas.registry.model.IdentifierKeyValuePair; +import org.apache.commons.text.StringEscapeUtils; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * This converter is required so that Spring is able to convert single query parameters to custom objects. + */ +@Component +public class IdentifierKeyValuePairConverter implements Converter> { + + private final ObjectMapper objectMapper; + + IdentifierKeyValuePairConverter(ObjectMapper objectMapper){ + this.objectMapper = objectMapper; + } + + @Override + public List convert(String source) { + try { + String processedSource = removeLineBreaks(source); + if(processedSource.startsWith("{")) { + return List.of(objectMapper.readValue(processedSource,IdentifierKeyValuePair.class)); + } else { + return objectMapper.readValue(processedSource, new TypeReference<>() { + }); + } + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * SwaggerUI does weired encoding for user added items. + * This method that SwaggerUI requests for lookups work. + */ + private static String removeLineBreaks(String source){ + return StringEscapeUtils + .unescapeJava(source).replace("\n", "").replace("\r", "") + .replace("\"{", "{") + .replace("}\"", "}"); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/dto/BatchResultDto.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/dto/BatchResultDto.java new file mode 100644 index 00000000..1bee4106 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/dto/BatchResultDto.java @@ -0,0 +1,30 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics.registry.dto; + +import lombok.Value; + +@Value +public class BatchResultDto { + String message; + String idExternal; + Integer status; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/dto/ShellCollectionDto.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/dto/ShellCollectionDto.java new file mode 100644 index 00000000..0dd21cdc --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/dto/ShellCollectionDto.java @@ -0,0 +1,36 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.dto; + +import org.eclipse.tractusx.semantics.registry.model.Shell; +import lombok.Builder; +import lombok.Value; + +import java.util.List; + +@Value +@Builder +public class ShellCollectionDto { + List items; + Integer totalItems; + Integer currentPage; + Integer totalPages; + Integer itemCount; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapper.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapper.java new file mode 100644 index 00000000..9e7fb60a --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapper.java @@ -0,0 +1,82 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.mapper; + +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptorCollection; +import org.eclipse.tractusx.semantics.aas.registry.model.BatchResult; +import org.eclipse.tractusx.semantics.aas.registry.model.IdentifierKeyValuePair; +import org.eclipse.tractusx.semantics.registry.dto.BatchResultDto; +import org.eclipse.tractusx.semantics.registry.dto.ShellCollectionDto; +import org.eclipse.tractusx.semantics.registry.model.Shell; +import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier; +import org.mapstruct.*; + +import java.util.List; +import java.util.Set; + +@Mapper(uses = {SubmodelMapper.class}, componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR) +public interface ShellMapper { + @Mappings({ + @Mapping(target = "idExternal", source = "identification"), + @Mapping(target = "identifiers", source = "specificAssetIds"), + @Mapping(target = "descriptions", source = "description"), + @Mapping(target = "submodels", source = "submodelDescriptors"), + }) + Shell fromApiDto(AssetAdministrationShellDescriptor apiDto); + + List fromListApiDto(List apiDto); + + ShellIdentifier fromApiDto(IdentifierKeyValuePair apiDto); + + Set fromApiDto(List apiDto); + + AssetAdministrationShellDescriptorCollection toApiDto( ShellCollectionDto shell); + + @Mappings({ + @Mapping(target = "identification", source = "idExternal"), + }) + BatchResult toApiDto( BatchResultDto batchResult); + + List toListApiDto(List batchResults); + + @InheritInverseConfiguration + AssetAdministrationShellDescriptor toApiDto(Shell shell); + + List toApiDto(List shell); + + List toApiDto(Set shell); + + @AfterMapping + default Shell convertGlobalAssetIdToShellIdentifier(AssetAdministrationShellDescriptor apiDto, @MappingTarget Shell shell){ + return ShellMapperCustomization.globalAssetIdToShellIdentifier(apiDto, shell); + } + + @AfterMapping + default void convertShellIdentifierToGlobalAssetId(Shell shell, @MappingTarget AssetAdministrationShellDescriptor apiDto){ + ShellMapperCustomization.shellIdentifierToGlobalAssetId(shell, apiDto); + } + + @AfterMapping + default void removeGlobalAssetIdFromIdentifiers(@MappingTarget List apiDto){ + ShellMapperCustomization.removeGlobalAssetIdIdentifier(apiDto); + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapperCustomization.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapperCustomization.java new file mode 100644 index 00000000..e4d61ce2 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapperCustomization.java @@ -0,0 +1,98 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.mapper; + +import com.google.common.base.Strings; +import org.eclipse.tractusx.semantics.aas.registry.model.AssetAdministrationShellDescriptor; +import org.eclipse.tractusx.semantics.aas.registry.model.IdentifierKeyValuePair; +import org.eclipse.tractusx.semantics.aas.registry.model.Reference; +import org.eclipse.tractusx.semantics.registry.model.Shell; +import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier; + +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +/** + * The globalAssetId of a AssetAdministrationShellDescriptor is the same as specificAssetIds from persistence point of view. + * This class is responsible to map the globalAssetId to {@code ShellIdentifier} and in reverse order back to the API object. + * + */ +public class ShellMapperCustomization { + + public static Shell globalAssetIdToShellIdentifier(AssetAdministrationShellDescriptor apiDto, Shell shell){ + Optional shellIdentifierOptional = extractGlobalAssetId(apiDto.getGlobalAssetId()); + if(shellIdentifierOptional.isEmpty()) { + return shell; + } + ShellIdentifier shellIdentifier = shellIdentifierOptional.get(); + if(shell.getIdentifiers() == null){ + return shell.withIdentifiers(Set.of(shellIdentifier)); + } + return shell.withIdentifiers( new HashSet<>(){{ + addAll( shell.getIdentifiers()); + add(shellIdentifier); + }}); + } + + public static void shellIdentifierToGlobalAssetId(Shell shell, AssetAdministrationShellDescriptor apiDto) { + Optional globalAssetId = extractGlobalAssetId(shell.getIdentifiers()); + // there are no immutable objects for the generated ones, mapping to api objects is done in mutable way + globalAssetId.ifPresent(apiDto::setGlobalAssetId); + } + + public static void removeGlobalAssetIdIdentifier(List specificAssetIds){ + if(specificAssetIds == null || specificAssetIds.isEmpty()){ + return; + } + specificAssetIds.removeIf(identifierKeyValuePair -> ShellIdentifier.GLOBAL_ASSET_ID_KEY.equals(identifierKeyValuePair.getKey()) ); + } + + private static Optional extractGlobalAssetId(Set shellIdentifiers){ + if(shellIdentifiers == null || shellIdentifiers.isEmpty()){ + return Optional.empty(); + } + Optional globalAssetId = shellIdentifiers + .stream() + .filter(shellIdentifier -> ShellIdentifier.GLOBAL_ASSET_ID_KEY.equals(shellIdentifier.getKey())) + .findFirst(); + return globalAssetId.map(value -> { + Reference reference = new Reference(); + reference.setValue(List.of(globalAssetId.get().getValue())); + return reference; + }); + } + + private static Optional extractGlobalAssetId(Reference globalAssetIdReference){ + if (globalAssetIdReference == null) { + return Optional.empty(); + } + List value = globalAssetIdReference.getValue(); + if(value == null || value.isEmpty()){ + return Optional.empty(); + } + String globalAssetId = value.get(0); + if(Strings.isNullOrEmpty(globalAssetId)){ + return Optional.empty(); + } + return Optional.of(new ShellIdentifier(null, ShellIdentifier.GLOBAL_ASSET_ID_KEY, globalAssetId, null)); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/SubmodelMapper.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/SubmodelMapper.java new file mode 100644 index 00000000..4fde9341 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/mapper/SubmodelMapper.java @@ -0,0 +1,77 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.mapper; + +import org.eclipse.tractusx.semantics.aas.registry.model.Endpoint; +import org.eclipse.tractusx.semantics.aas.registry.model.Reference; +import org.eclipse.tractusx.semantics.aas.registry.model.SubmodelDescriptor; +import org.eclipse.tractusx.semantics.registry.model.Submodel; +import org.eclipse.tractusx.semantics.registry.model.SubmodelEndpoint; +import org.eclipse.tractusx.semantics.aas.registry.model.*; +import org.eclipse.tractusx.semantics.registry.model.*; + +import org.mapstruct.*; + +import java.util.List; +import java.util.Set; + +@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR) +public interface SubmodelMapper { + @Mappings({ + @Mapping(target="idExternal", source="identification"), + @Mapping(target="descriptions", source="description"), + @Mapping(target="semanticId", source = "semanticId") + }) + Submodel fromApiDto(SubmodelDescriptor apiDto); + + @Mappings({ + @Mapping(target="interfaceName", source = "interface"), + @Mapping(target="endpointAddress", source = "protocolInformation.endpointAddress"), + @Mapping(target="endpointProtocol", source = "protocolInformation.endpointProtocol"), + @Mapping(target="endpointProtocolVersion", source = "protocolInformation.endpointProtocolVersion"), + @Mapping(target="subProtocol", source = "protocolInformation.subprotocol"), + @Mapping(target="subProtocolBody", source = "protocolInformation.subprotocolBody"), + @Mapping(target="subProtocolBodyEncoding", source = "protocolInformation.subprotocolBodyEncoding"), + }) + SubmodelEndpoint fromApiDto(Endpoint apiDto); + + @InheritInverseConfiguration + List toApiDto(Set shell); + + @InheritInverseConfiguration + SubmodelDescriptor toApiDto(Submodel shell); + + @InheritInverseConfiguration + Endpoint toApiDto(SubmodelEndpoint apiDto); + + default String map(Reference reference){ + return reference != null && reference.getValue() != null && !reference.getValue().isEmpty() ? reference.getValue().get(0) : null; + } + + default Reference map(String semanticId){ + if(semanticId == null || semanticId.isBlank()) { + return null; + } + Reference reference = new Reference(); + reference.setValue(List.of(semanticId)); + return reference; + } + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/Shell.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/Shell.java new file mode 100644 index 00000000..517a4c14 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/Shell.java @@ -0,0 +1,57 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model; + + +import lombok.Value; +import lombok.With; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.relational.core.mapping.MappedCollection; + +import java.time.Instant; +import java.util.Set; +import java.util.UUID; + +@Value +@With +public class Shell { + @Id + UUID id; + String idExternal; + String idShort; + + @MappedCollection(idColumn = "fk_shell_id") + Set identifiers; + + @MappedCollection(idColumn = "fk_shell_id") + Set descriptions; + + @MappedCollection(idColumn = "fk_shell_id") + Set submodels; + + @CreatedDate + Instant createdDate; + + @LastModifiedDate + Instant lastModifiedDate; + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/ShellDescription.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/ShellDescription.java new file mode 100644 index 00000000..5cc0881a --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/ShellDescription.java @@ -0,0 +1,34 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model; + +import lombok.Value; +import org.springframework.data.annotation.Id; + +import java.util.UUID; + +@Value +public class ShellDescription { + @Id + UUID id; + String language; + String text; + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/ShellIdentifier.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/ShellIdentifier.java new file mode 100644 index 00000000..942a665b --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/ShellIdentifier.java @@ -0,0 +1,45 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model; + + + +import lombok.Value; +import lombok.With; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; + +import java.util.UUID; + +@Value +@With +public class ShellIdentifier { + + public static final String GLOBAL_ASSET_ID_KEY = "globalAssetId"; + + @Id + UUID id; + @Column("namespace") + String key; + @Column("identifier") + String value; + @Column( "fk_shell_id") + UUID shellId; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/Submodel.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/Submodel.java new file mode 100644 index 00000000..a72bee3a --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/Submodel.java @@ -0,0 +1,50 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model; + + +import lombok.Value; +import lombok.With; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.MappedCollection; + +import java.util.Set; +import java.util.UUID; + +@Value +@With +public class Submodel { + @Id + UUID id; + + String idExternal; + String idShort; + String semanticId; + + @MappedCollection(idColumn = "fk_submodel_id") + Set descriptions; + + @MappedCollection(idColumn = "fk_submodel_id") + Set endpoints; + + @Column( "fk_shell_id") + UUID shellId; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/SubmodelDescription.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/SubmodelDescription.java new file mode 100644 index 00000000..315eebd1 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/SubmodelDescription.java @@ -0,0 +1,33 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model; + +import lombok.Value; +import org.springframework.data.annotation.Id; + +import java.util.UUID; + +@Value +public class SubmodelDescription { + @Id + UUID id; + String language; + String text; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/SubmodelEndpoint.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/SubmodelEndpoint.java new file mode 100644 index 00000000..0cb26e76 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/SubmodelEndpoint.java @@ -0,0 +1,39 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model; + +import lombok.Value; +import org.springframework.data.annotation.Id; + +import java.util.UUID; + +@Value +public class SubmodelEndpoint { + @Id + UUID id; + String interfaceName; + String endpointAddress; + String endpointProtocol; + String endpointProtocolVersion; + String subProtocol; + String subProtocolBody; + String subProtocolBodyEncoding; + +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/projection/ShellMinimal.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/projection/ShellMinimal.java new file mode 100644 index 00000000..e8d25317 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/projection/ShellMinimal.java @@ -0,0 +1,32 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model.projection; + +import lombok.Value; + +import java.time.Instant; +import java.util.UUID; + +@Value +public class ShellMinimal { + UUID id; + String idExternal; + Instant createdDate; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/projection/SubmodelMinimal.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/projection/SubmodelMinimal.java new file mode 100644 index 00000000..7311b0d1 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/projection/SubmodelMinimal.java @@ -0,0 +1,30 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model.projection; + +import lombok.Value; + +import java.time.Instant; +import java.util.UUID; + +@Value +public class SubmodelMinimal { + UUID id; +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/support/DatabaseExceptionTranslation.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/support/DatabaseExceptionTranslation.java new file mode 100644 index 00000000..941494ce --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/model/support/DatabaseExceptionTranslation.java @@ -0,0 +1,52 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.model.support; + +import org.springframework.dao.DuplicateKeyException; + +import java.util.Locale; +import java.util.regex.Pattern; + +public class DatabaseExceptionTranslation { + + private static final String DEFAULT_DUPLICATE_KEY_MESSAGE = "An entity for the given id does already exist."; + + private static final Pattern SUBMODEL = Pattern.compile("(SUBMODEL_AK_01)|(ON.*SUBMODEL)"); + private static final Pattern SHELL = Pattern.compile("(SHELL_AK_01)|(ON.*SHELL)"); + + public static String translate(DuplicateKeyException exception){ + String message = exception.getMessage(); + if(message == null ){ + return DEFAULT_DUPLICATE_KEY_MESSAGE; + } + + String upperCaseMessage=message.toUpperCase(); + + if(SUBMODEL.matcher(upperCaseMessage).find()) { + return "A SubmodelDescriptor with the given identification does already exists."; + } + + if(SHELL.matcher(upperCaseMessage).find()) { + return "An AssetAdministrationShell for the given identification does already exists."; + } + + return DEFAULT_DUPLICATE_KEY_MESSAGE; + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellIdentifierRepository.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellIdentifierRepository.java new file mode 100644 index 00000000..e4368c5e --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellIdentifierRepository.java @@ -0,0 +1,37 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.repository; + +import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier; +import org.springframework.data.jdbc.repository.query.Modifying; +import org.springframework.data.jdbc.repository.query.Query; +import org.springframework.data.repository.CrudRepository; + +import java.util.Set; +import java.util.UUID; + +public interface ShellIdentifierRepository extends CrudRepository { + + @Modifying + @Query("delete from shell_identifier si where si.fk_shell_id = :shellId and si.namespace != :keyToIgnore") + void deleteShellIdentifiersByShellId(UUID shellId, String keyToIgnore); + + Set findByShellId(UUID shellId); +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java new file mode 100644 index 00000000..65756d0d --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/ShellRepository.java @@ -0,0 +1,82 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.repository; + +import org.eclipse.tractusx.semantics.registry.model.projection.ShellMinimal; +import org.eclipse.tractusx.semantics.registry.model.Shell; +import org.springframework.data.jdbc.repository.query.Query; +import org.springframework.data.repository.PagingAndSortingRepository; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.*; + +@Repository +public interface ShellRepository extends PagingAndSortingRepository{ + Optional findByIdExternal(String idExternal); + + @Query("select s.id, s.created_date from shell s where s.id_external = :idExternal") + Optional findMinimalRepresentationByIdExternal(String idExternal); + + List findShellsByIdExternalIsIn(Set idExternals); + + /** + * Returns external shell ids for the given keyValueCombinations. + * Only external shell ids that match all keyValueCombinations are returned. + * + * To be able to properly index the key and value conditions, the query does not use any functions. + * Computed indexes cannot be created for mutable functions like CONCAT in Postgres. + * + * @param keyValueCombinations the keys values to search for as tuples + * @param keyValueCombinationsSize the size of the key value combinations + * @return external shell ids for the given key value combinations + */ + @Query( + "select s.id_external from shell s where s.id in (" + + "select si.fk_shell_id from shell_identifier si " + + "join (values :keyValueCombinations ) as t (input_key,input_value) " + + "ON si.namespace = input_key AND si.identifier = input_value " + + "group by si.fk_shell_id " + + "having count(*) = :keyValueCombinationsSize " + + ")" + ) + List findExternalShellIdsByIdentifiersByExactMatch(@Param("keyValueCombinations") List keyValueCombinations, + @Param("keyValueCombinationsSize") int keyValueCombinationsSize); + + /** + * Returns external shell ids for the given keyValueCombinations. + * External shell ids that match any keyValueCombinations are returned. + * + * To be able to properly index the key and value conditions, the query does not use any functions. + * Computed indexes cannot be created for mutable functions like CONCAT in Postgres. + * + * @param keyValueCombinations the keys values to search for as tuples + * @return external shell ids for the given key value combinations + */ + @Query( + "select distinct s.id_external from shell s where s.id in (" + + "select si.fk_shell_id from shell_identifier si " + + "join (values :keyValueCombinations ) as t (input_key,input_value) " + + "ON si.namespace = input_key AND si.identifier = input_value " + + "group by si.fk_shell_id " + + ")" + ) + List findExternalShellIdsByIdentifiersByAnyMatch(@Param("keyValueCombinations") List keyValueCombinations); +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/SubmodelRepository.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/SubmodelRepository.java new file mode 100644 index 00000000..3b5a97ab --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/repository/SubmodelRepository.java @@ -0,0 +1,38 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.repository; + +import org.eclipse.tractusx.semantics.registry.model.projection.SubmodelMinimal; +import org.eclipse.tractusx.semantics.registry.model.Submodel; +import org.springframework.data.jdbc.repository.query.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; +import java.util.UUID; + +@Repository +public interface SubmodelRepository extends CrudRepository { + + Optional findByShellIdAndIdExternal(UUID shellId, String externalId); + + @Query("select s.id from submodel s where s.fk_shell_id = :shellId and s.id_external = :externalId") + Optional findMinimalRepresentationByShellIdAndIdExternal(UUID shellId, String externalId); +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/EntityNotFoundException.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/EntityNotFoundException.java new file mode 100644 index 00000000..de9cc6f0 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/EntityNotFoundException.java @@ -0,0 +1,26 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.service; + +public class EntityNotFoundException extends RuntimeException { + public EntityNotFoundException(String message){ + super(message); + } +} diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java new file mode 100644 index 00000000..c9aa7125 --- /dev/null +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/registry/service/ShellService.java @@ -0,0 +1,205 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry.service; + +import com.google.common.collect.ImmutableSet; +import org.eclipse.tractusx.semantics.registry.model.projection.ShellMinimal; +import org.eclipse.tractusx.semantics.registry.model.projection.SubmodelMinimal; +import org.eclipse.tractusx.semantics.registry.model.support.DatabaseExceptionTranslation; +import org.eclipse.tractusx.semantics.registry.repository.ShellIdentifierRepository; +import org.eclipse.tractusx.semantics.registry.repository.ShellRepository; +import org.eclipse.tractusx.semantics.registry.repository.SubmodelRepository; +import org.eclipse.tractusx.semantics.registry.dto.BatchResultDto; +import org.eclipse.tractusx.semantics.registry.dto.ShellCollectionDto; +import org.eclipse.tractusx.semantics.registry.model.Shell; +import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier; +import org.eclipse.tractusx.semantics.registry.model.Submodel; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +public class ShellService { + + private final ShellRepository shellRepository; + private final ShellIdentifierRepository shellIdentifierRepository; + private final SubmodelRepository submodelRepository; + + public ShellService(ShellRepository shellRepository, ShellIdentifierRepository shellIdentifierRepository, + SubmodelRepository submodelRepository) { + this.shellRepository = shellRepository; + this.shellIdentifierRepository = shellIdentifierRepository; + this.submodelRepository = submodelRepository; + } + + @Transactional + public Shell save(Shell shell) { + return shellRepository.save(shell); + } + + @Transactional(readOnly = true) + public Shell findShellByExternalId(String externalShellId){ + return shellRepository.findByIdExternal(externalShellId) + .orElseThrow(() -> new EntityNotFoundException(String.format("Shell for identifier %s not found", externalShellId))); + } + + @Transactional(readOnly = true) + public ShellCollectionDto findAllShells(int page, int pageSize){ + Pageable pageable = PageRequest.of(page, pageSize, Sort.Direction.ASC, "createdDate"); + Page shellsPage = shellRepository.findAll(pageable); + return ShellCollectionDto.builder() + .currentPage(pageable.getPageNumber()) + .totalItems((int)shellsPage.getTotalElements()) + .totalPages(shellsPage.getTotalPages()) + .itemCount(shellsPage.getNumberOfElements()) + .items(shellsPage.getContent()) + .build(); + } + + @Transactional(readOnly = true) + public List findExternalShellIdsByIdentifiersByExactMatch(Set shellIdentifiers){ + List keyValueCombinations = shellIdentifiers.stream().map(shellIdentifier -> new String[]{shellIdentifier.getKey(), shellIdentifier.getValue()}).collect(Collectors.toList()); + return shellRepository.findExternalShellIdsByIdentifiersByExactMatch(keyValueCombinations, keyValueCombinations.size()); + } + + @Transactional(readOnly = true) + public List findExternalShellIdsByIdentifiersByAnyMatch(Set shellIdentifiers){ + List keyValueCombinations = shellIdentifiers.stream().map(shellIdentifier -> new String[]{shellIdentifier.getKey(), shellIdentifier.getValue()}).collect(Collectors.toList()); + return shellRepository.findExternalShellIdsByIdentifiersByAnyMatch(keyValueCombinations); + } + + @Transactional(readOnly = true) + public List findShellsByExternalShellIds(Set externalShellIds){ + return shellRepository.findShellsByIdExternalIsIn(externalShellIds); + } + + @Transactional + public Shell update(String externalShellId, Shell shell){ + ShellMinimal shellFromDb = findShellMinimalByExternalId(externalShellId); + return shellRepository.save( + shell.withId(shellFromDb.getId()).withCreatedDate(shellFromDb.getCreatedDate()) + ); + } + + @Transactional + public void deleteShell(String externalShellId) { + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + shellRepository.deleteById(shellId.getId()); + } + + @Transactional(readOnly = true) + public Set findShellIdentifiersByExternalShellId(String externalShellId){ + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + return shellIdentifierRepository.findByShellId(shellId.getId()); + } + + @Transactional + public void deleteAllIdentifiers(String externalShellId){ + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + shellIdentifierRepository.deleteShellIdentifiersByShellId(shellId.getId(), ShellIdentifier.GLOBAL_ASSET_ID_KEY); + } + + @Transactional + public Set save(String externalShellId, Set shellIdentifiers){ + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + shellIdentifierRepository.deleteShellIdentifiersByShellId(shellId.getId(), ShellIdentifier.GLOBAL_ASSET_ID_KEY); + + List identifiersToUpdate = shellIdentifiers.stream().map(identifier -> identifier.withShellId(shellId.getId())) + .collect(Collectors.toList()); + return ImmutableSet.copyOf(shellIdentifierRepository.saveAll(identifiersToUpdate)); + } + + @Transactional + public Submodel save(String externalShellId, Submodel submodel){ + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + return submodelRepository.save(submodel.withShellId(shellId.getId())); + } + + @Transactional + public Submodel update(String externalShellId, String externalSubmodelId, Submodel submodel){ + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + SubmodelMinimal subModelId = findSubmodelMinimalByExternalId(shellId.getId(), externalSubmodelId); + return submodelRepository.save(submodel + .withId(subModelId.getId()) + .withShellId(shellId.getId()) + ); + } + + @Transactional + public void deleteSubmodel(String externalShellId, String externalSubModelId) { + ShellMinimal shellId = findShellMinimalByExternalId(externalShellId); + SubmodelMinimal submodelId = findSubmodelMinimalByExternalId(shellId.getId(), externalSubModelId); + submodelRepository.deleteById(submodelId.getId()); + } + + @Transactional(readOnly = true) + public Submodel findSubmodelByExternalId(String externalShellId, String externalSubModelId){ + ShellMinimal shellIdByExternalId = findShellMinimalByExternalId(externalShellId); + return submodelRepository + .findByShellIdAndIdExternal(shellIdByExternalId.getId(), externalSubModelId) + .orElseThrow(() -> new EntityNotFoundException(String.format("Submodel for identifier %s not found.", externalSubModelId))); + } + + private SubmodelMinimal findSubmodelMinimalByExternalId(UUID shellId, String externalSubModelId ){ + return submodelRepository + .findMinimalRepresentationByShellIdAndIdExternal(shellId, externalSubModelId) + .orElseThrow(() -> new EntityNotFoundException(String.format("Submodel for identifier %s not found.", externalSubModelId))); + } + + private ShellMinimal findShellMinimalByExternalId(String externalShellId){ + return shellRepository.findMinimalRepresentationByIdExternal(externalShellId) + .orElseThrow(() -> new EntityNotFoundException(String.format("Shell for identifier %s not found", externalShellId))); + } + + /** + * Saves the provided shells. The transaction is scoped per shell. If saving of one shell fails others may succeed. + * @param shells the shells to save + * @return the result of each save operation + */ + public List saveBatch(List shells) { + return shells.stream().map(shell -> { + try { + shellRepository.save(shell); + return new BatchResultDto("AssetAdministrationShell successfully created.", + shell.getIdExternal(), HttpStatus.OK.value()); + } catch (Exception e){ + if(e.getCause() instanceof DuplicateKeyException){ + DuplicateKeyException duplicateKeyException = (DuplicateKeyException) e.getCause(); + return new BatchResultDto( DatabaseExceptionTranslation.translate(duplicateKeyException), + shell.getIdExternal(), + HttpStatus.BAD_REQUEST.value()); + } + return new BatchResultDto(String.format("Failed to create AssetAdministrationShell %s", + e.getMessage()), shell.getIdExternal(), HttpStatus.BAD_REQUEST.value()); + } + }).collect(Collectors.toList()); + } + +} diff --git a/backend/src/main/resources/application-local.yml b/backend/src/main/resources/application-local.yml new file mode 100644 index 00000000..5eba0e1a --- /dev/null +++ b/backend/src/main/resources/application-local.yml @@ -0,0 +1,42 @@ +############################################################### +# 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 +############################################################### + +########################################################### +# Configuration of the Semantic Layer +########################################################## +spring: + security: + oauth2: + resourceserver: + jwt: + issuer-uri: + + datasource: + driverClassName: org.h2.Driver + password: tractusx + username: tractusx + url: jdbc:h2:file:./persistence/registrydb;CASE_INSENSITIVE_IDENTIFIERS=TRUE + + h2: + console: + path: /admin/database + enabled: true + settings: + web-allow-others: true diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml new file mode 100644 index 00000000..c58f32f4 --- /dev/null +++ b/backend/src/main/resources/application.yml @@ -0,0 +1,91 @@ +############################################################### +# Copyright (c) 2021-2022 T-Systems International 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 +############################################################### + +server: + port: 4243 + ssl: + key-store-password: __KEYSTOREPASSWORD__ + key-store: classpath:conf/__KEYSTOREFILENAME__.p12 + key-store-type: PKCS12 + key-alias: 1 + enabled: false + forward-headers-strategy: framework + +http: + timeout: + write: 10000 + connect: 10000 + call: 10000 + read: 10000 + +spring: + application: + name: semantics-services + banner: + location: "classpath:banner.txt" + servlet: + multipart: + enabled: true + max-file-size: 200MB + max-request-size: 215MB + file-size-threshold: 2KB + jackson: + default-property-inclusion: non_null + +registry: + idm: + public-client-id: catenax-portal + +springdoc: + cache: + disabled: true + api-docs: + enabled: false + swagger-ui: + path: / + urls: + - name: AAS Registry API + url: /aas-registry-openapi.yaml + oauth: + use-pkce-with-authorization-code-grant: true + # the scopes and client id will be prefilled in the swagger ui + scopes: openid profile + client-id: ${registry.idm.public-client-id} + +title: '@project.name@' +project_desc: '@project.description@' +contact_email: '@email@' +licence: '@licence_name@' +contact_url: '@project.url@' +licence_url: '@licence_url@' +organization_name: '@project.organization.name@' +version: '@project.version@' + +management: + endpoint: + health: + probes: + enabled: true + info: + enabled: true + endpoints: + web: + exposure: + include: health,info diff --git a/backend/src/main/resources/banner.txt b/backend/src/main/resources/banner.txt new file mode 100644 index 00000000..6ace9aaa --- /dev/null +++ b/backend/src/main/resources/banner.txt @@ -0,0 +1,12 @@ + ______ __ _ __ + /_ __/________ ______/ /___ _______ | |/ / + / / / ___/ __ `/ ___/ __/ / / / ___/_____| / + / / / / / /_/ / /__/ /_/ /_/ (__ )_____/ | +/_/ /_/ \__,_/\___/\__/\__,_/____/ /_/|_| + +________ _____ ________ _____ _____ +___ __/__ ____(_)______ ___ __ \___________ ___(_)________ /____________ __ +__ / __ | /| / /_ /__ __ \ __ /_/ / _ \_ __ `/_ /__ ___/ __/_ ___/_ / / / +_ / __ |/ |/ /_ / _ / / / _ _, _// __/ /_/ /_ / _(__ )/ /_ _ / _ /_/ / +/_/ ____/|__/ /_/ /_/ /_/ /_/ |_| \___/_\__, / /_/ /____/ \__/ /_/ _\__, / + /____/ /____/ diff --git a/backend/src/main/resources/catena-template.css b/backend/src/main/resources/catena-template.css new file mode 100644 index 00000000..323182e1 --- /dev/null +++ b/backend/src/main/resources/catena-template.css @@ -0,0 +1,68 @@ +h1 { + color: black; + font-size: 2rem; +} + +h2 { + color: #B3CB2D; + font-size: 1.5rem; +} + +h3 { + font-size: 1.5rem; + padding-top: 15px; + padding-bottom: 15px; +} + +h4 { + font-size: 1.2rem; + font-weight: bold; + padding-top: 5px; + padding-bottom: 5px; +} + +h5 { + font-size: 1.2rem; +} + +.heading { + padding-top: 45px; +} + +#content-title { + margin-top: 15px; +} + +a.toc-link { + padding-left: 15px; +} + +.is-position-fixed { + top: 9rem; + position: unset; +} + +#documentation-toc { + padding-top: 15px; +} + +#documentation-toc > ol { + margin-top: 15px; +} + +.text-gray { + color: #B3CB2D; +} + +.page-top { + background-color: #B3CB2D; + height: 14px; +} + +.logo-header { + background: left no-repeat url('data:image/svg+xml;base64,PHN2ZyBpZD0iRWJlbmVfMSIgZGF0YS1uYW1lPSJFYmVuZSAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyODIuOTQgNjYuMDYiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDojZmZhNjAwO30uY2xzLTJ7ZmlsbDojYjNjYjJkO30uY2xzLTN7ZmlsbDojMTkxNzE1O308L3N0eWxlPjwvZGVmcz48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik00Ny40NiwzNFYyNi44OWExLjE3LDEuMTcsMCwwLDAtMS44My0xTDE3LjQxLDQ1LjQxYTQuODcsNC44NywwLDAsMS03LjY0LTRWMzJBOS43NSw5Ljc1LDAsMCwxLDE0LDI0bDguMzktNS44YTQuOCw0LjgsMCwwLDAsMi4wOC00VjcuNTZsMC02LjM5YTEuMTcsMS4xNywwLDAsMC0xLjg0LTFMMTYuNzUsNC4yN2E0Ljg0LDQuODQsMCwwLDAtMi4wOCw0djMuNDJsLTcuMiw1QTE3LjMsMTcuMywwLDAsMCwwLDMwLjg1VjQxLjM5QTE0LjY1LDE0LjY1LDAsMCwwLDIzLDUzLjQ1TDQ1LjM4LDM4QTQuODQsNC44NCwwLDAsMCw0Ny40NiwzNFoiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik0zOS4xMywxMi42LDE2LjczLDI4LjA2YTQuODMsNC44MywwLDAsMC0yLjA5LDR2Ny4xMWExLjE4LDEuMTgsMCwwLDAsMS44NCwxTDQ0LjcsMjAuNjRhNC44Nyw0Ljg3LDAsMCwxLDcuNjQsNFYzNGE5Ljc1LDkuNzUsMCwwLDEtNC4yMSw4bC03LjUsNS4xOC0uODYuNTloMGE0LjgyLDQuODIsMCwwLDAtMi4wOCw0djEuODFsMCwxMS4yNGExLjE3LDEuMTcsMCwwLDAsMS44NCwxbDUuODktNC4wNmE0LjgzLDQuODMsMCwwLDAsMi4wOC00VjU0LjM4bDcuMTgtNWExNy4yOSwxNy4yOSwwLDAsMCw3LjQ3LTE0LjIzVjI0LjY1QTE0LjY1LDE0LjY1LDAsMCwwLDM5LjEzLDEyLjZaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNOTAuMjYsNjQuMjVoLTRsLS42NSwxLjY2SDg0LjMzbDMuMi04SDg5bDMuMiw4SDkwLjkxWm0tMy42LTEuMDhoMy4yNWwtMS42My00LjI2WiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTk3LjU3LDY1LjEyYTIuODksMi44OSwwLDAsMS0yLjA4Ljk0LDEuNjQsMS42NCwwLDAsMS0xLjg1LTEuODZWNjAuMDhoMS4wOHYzLjc2YzAsMSwuNDgsMS4yNSwxLjIzLDEuMjVhMi4xLDIuMSwwLDAsMCwxLjYyLS44MlY2MC4wOGgxLjA5djUuODNIOTcuNTdaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTAxLjIsNjQuNjJWNjFoLTF2LTFoMVY1OC40OWgxLjA5djEuNTloMS4xOHYxaC0xLjE4djMuMzRjMCwuNDEuMTkuNzEuNTcuNzFhLjg0Ljg0LDAsMCwwLC41OS0uMjNsLjI4LjgyYTEuNTUsMS41NSwwLDAsMS0xLjE0LjM4QTEuMjcsMS4yNywwLDAsMSwxMDEuMiw2NC42MloiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xMDQuOTIsNjNhMi45NSwyLjk1LDAsMSwxLDMsMy4wN0EyLjksMi45LDAsMCwxLDEwNC45Miw2M1ptNC43NiwwYTEuODMsMS44MywwLDEsMC0xLjgxLDIuMUExLjg5LDEuODksMCwwLDAsMTA5LjY4LDYzWiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTExOS44OCw2MmMwLS42OC0uMzEtMS4xMy0xLTEuMTNhMS45LDEuOSwwLDAsMC0xLjQ1Ljg0djQuMTdoLTEuMDhWNjJjMC0uNjgtLjI5LTEuMTMtMS0xLjEzYTEuOTEsMS45MSwwLDAsMC0xLjQyLjg1djQuMTZoLTEuMDlWNjAuMDhoMS4wOXYuODJhMi41MiwyLjUyLDAsMCwxLDEuODgtMUExLjUsMS41LDAsMCwxLDExNy4zNCw2MWEyLjQzLDIuNDMsMCwwLDEsMi0xLjA5YzEuMDcsMCwxLjY1LjU5LDEuNjUsMS43NnY0LjIxaC0xLjA4WiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTEyMi45Myw2M2EyLjk1LDIuOTUsMCwxLDEsMi45NCwzLjA3QTIuODksMi44OSwwLDAsMSwxMjIuOTMsNjNabTQuNzUsMGExLjgzLDEuODMsMCwxLDAtMS44MSwyLjFBMS44OSwxLjg5LDAsMCwwLDEyNy42OCw2M1oiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xMzEsNjQuNjJWNjFoLTF2LTFoMVY1OC40OWgxLjA4djEuNTloMS4xOHYxSDEzMi4xdjMuMzRjMCwuNDEuMi43MS41Ny43MWEuODQuODQsMCwwLDAsLjU5LS4yM2wuMjguODJhMS41NSwxLjU1LDAsMCwxLTEuMTQuMzhBMS4yNywxLjI3LDAsMCwxLDEzMSw2NC42MloiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xMzQuOTMsNTguNDJhLjcuNywwLDEsMSwuNy42OUEuNy43LDAsMCwxLDEzNC45Myw1OC40MlptLjE3LDEuNjZoMS4wOHY1LjgzSDEzNS4xWiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTEzNy42NSw2MC4wOGgxLjE2bDEuODMsNC41OCwxLjgyLTQuNThoMS4xN2wtMi40LDUuODNoLTEuMTdaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTQ3LjY3LDU5Ljk0YTIuODcsMi44NywwLDAsMSwyLjg0LDMuMTV2LjI3SDE0NS45YTEuOTIsMS45MiwwLDAsMCwyLDEuODEsMi41LDIuNSwwLDAsMCwxLjcyLS42OWwuNTEuNzFhMy4yNywzLjI3LDAsMCwxLTIuMzQuODcsMy4wNiwzLjA2LDAsMCwxLS4xLTYuMTJabS0xLjc5LDIuNjNoMy41N2ExLjc0LDEuNzQsMCwwLDAtMS43OC0xLjc0QTEuOCwxLjgsMCwwLDAsMTQ1Ljg4LDYyLjU3WiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTE1Ny40Miw1OS43M3Y2LjE4aC0xLjJ2LThoMS4yNGw0LjM4LDZ2LTZoMS4yMXY4aC0xLjE3WiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTE2OCw1OS45NGEyLjg3LDIuODcsMCwwLDEsMi44NCwzLjE1di4yN0gxNjYuMmExLjkyLDEuOTIsMCwwLDAsMiwxLjgxLDIuNTQsMi41NCwwLDAsMCwxLjczLS42OWwuNS43MWEzLjI3LDMuMjcsMCwwLDEtMi4zNC44NywzLjA2LDMuMDYsMCwwLDEtLjEtNi4xMlptLTEuNzgsMi42M2gzLjU2QTEuNzQsMS43NCwwLDAsMCwxNjgsNjAuODMsMS43OCwxLjc4LDAsMCwwLDE2Ni4xOSw2Mi41N1oiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xNzMsNjQuNjJWNjFoLTF2LTFoMVY1OC40OWgxLjA5djEuNTloMS4xOHYxaC0xLjE4djMuMzRjMCwuNDEuMTkuNzEuNTcuNzFhLjg0Ljg0LDAsMCwwLC41OS0uMjNsLjI4LjgyYTEuNTUsMS41NSwwLDAsMS0xLjE0LjM4QTEuMjcsMS4yNywwLDAsMSwxNzMsNjQuNjJaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTgwLjcsNjEuNDRsLTEuNDQsNC40N2gtMS4xMmwtMS44NC01LjgzaDEuMTRsMS4zMiw0LjQ1LDEuNDYtNC40NWguOTVsMS40Niw0LjQ1TDE4NCw2MC4wOGgxLjE0bC0xLjg0LDUuODNoLTEuMTJaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTg2LjI5LDYzYTMsMywwLDEsMSwzLDMuMDdBMi45LDIuOSwwLDAsMSwxODYuMjksNjNabTQuNzYsMGExLjgzLDEuODMsMCwxLDAtMS44MSwyLjFBMS44OSwxLjg5LDAsMCwwLDE5MS4wNSw2M1oiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xOTQuMTcsNjAuMDhoMS4wOFY2MWEyLjQ5LDIuNDksMCwwLDEsMS45MS0xdjEuMWEyLDIsMCwwLDAtLjM4LDAsMi4wNiwyLjA2LDAsMCwwLTEuNTMuODV2NGgtMS4wOFoiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0yMDAuODIsNjMuNGwtLjg5Ljl2MS42MWgtMS4wOHYtOGgxLjA4VjYzbDIuODItM2gxLjM2bC0yLjUxLDIuNjUsMi41NCwzLjE4aC0xLjM4WiIvPjxwYXRoIGNsYXNzPSJjbHMtMyIgZD0iTTgyLjIyLDMzLjA1YzAtMTAuMTMsNy41Ni0xNywxNy0xN2ExNSwxNSwwLDAsMSwxMy4xNyw2LjkzLjUxLjUxLDAsMCwxLS4yLjc1bC00LDJhLjUyLjUyLDAsMCwxLS42Ny0uMTgsOS45Myw5LjkzLDAsMCwwLTguMzEtNC40Yy02LjI4LDAtMTEuMTIsNS0xMS4xMiwxMS45MVM5Myw0NSw5OS4yNyw0NWE5LjgzLDkuODMsMCwwLDAsOC4zMS00LjM5LjUxLjUxLDAsMCwxLC42Ny0uMThsNCwyYS41My41MywwLDAsMSwuMi43NkExNSwxNSwwLDAsMSw5OS4yNyw1MC4xQzg5Ljc4LDUwLjEsODIuMjIsNDMuMTgsODIuMjIsMzMuMDVaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMTMxLjg0LDQ2Ljk0YTEwLjEsMTAuMSwwLDAsMS03LjcxLDMuMTZjLTMuOTEsMC04LjMtMi42Mi04LjMtNy45MSwwLTUuNDgsNC4zOS03Ljc2LDguMy03Ljc2YTEwLDEwLDAsMCwxLDcuNiwyLjk0LjA2LjA2LDAsMCwwLC4xMSwwVjMzLjk0YzAtMi43Ny0yLjI4LTQuNDUtNS41OS00LjQ1YTkuNjgsOS42OCwwLDAsMC02Ljc0LDIuNzEuMzYuMzYsMCwwLDEtLjU1LS4wOGwtMS43NS0zYS4zNS4zNSwwLDAsMSwuMDctLjQ1QTE0LjIyLDE0LjIyLDAsMCwxLDEyNy4wOSwyNWM1LjI5LDAsOS45NCwyLjIzLDkuOTQsOC42NXYxNS41YS4zMy4zMywwLDAsMS0uMzMuMzJoLTQuNTRhLjMyLjMyLDAsMCwxLS4zMi0uMzJabTAtNS4yMWExLjMxLDEuMzEsMCwwLDAtLjE0LS41OUE2LjQ3LDYuNDcsMCwwLDAsMTI2LjE1LDM4Yy0yLjkxLDAtNS4wOSwxLjY4LTUuMDksNC4zczIuMTgsNC4yNSw1LjA5LDQuMjVhNyw3LDAsMCwwLDUuNDctMi40MiwxLjMzLDEuMzMsMCwwLDAsLjIyLS43M1oiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xNDQuNzgsNDRWMzAuMThoLTMuNmEuMzUuMzUsMCwwLDEtLjM1LS4zNFYyNmEuMzUuMzUsMCwwLDEsLjM1LS4zNWgzLjZWMTkuNDFhLjMuMywwLDAsMSwuMy0uM2g0LjZhLjI5LjI5LDAsMCwxLC4yOS4zdjYuMjNoNC41YS4zNS4zNSwwLDAsMSwuMzUuMzV2My44NWEuMzUuMzUsMCwwLDEtLjM1LjM0SDE1MFY0Mi42OWMwLDEuNjMuNzksMi44MSwyLjI4LDIuODFhMy42NywzLjY3LDAsMCwwLDIuMDgtLjYyLjIyLjIyLDAsMCwxLC4zNC4xbDEuMDksMy40NmEuMTkuMTksMCwwLDEtLjA2LjIyLDcsNywwLDAsMS00Ljc5LDEuNDRDMTQ2LjkxLDUwLjEsMTQ0Ljc4LDQ3LjkzLDE0NC43OCw0NFoiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0xNzAuMTQsMjVjNy4xNiwwLDExLjc2LDUuNDksMTEuNzYsMTN2LjgzYS40Mi40MiwwLDAsMS0uNDEuNDFoLTE3LjhhLjIxLjIxLDAsMCwwLS4yLjIzLDcuMjMsNy4yMywwLDAsMCw3LjU5LDYuMzQsMTAuMTksMTAuMTksMCwwLDAsNi41Ni0yLjQzLjI1LjI1LDAsMCwxLC4zNywwbDIuMDksM2EuMjUuMjUsMCwwLDEsMCwuMzIsMTQsMTQsMCwwLDEtOS41MywzLjMxYy03LjE2LDAtMTIuNS01LTEyLjUtMTIuNTVDMTU4LDMwLjYzLDE2My4wNywyNSwxNzAuMTQsMjVabS02LjM4LDEwLjU4aDEyLjc3YS4zMS4zMSwwLDAsMCwuMzEtLjMyLDYuMzcsNi4zNywwLDAsMC02LjctNiw2LjU1LDYuNTUsMCwwLDAtNi42OSw2QS4zMS4zMSwwLDAsMCwxNjMuNzYsMzUuNjJaIi8+PHBhdGggY2xhc3M9ImNscy0zIiBkPSJNMjAzLjE1LDM0LjU4YzAtMy43LTEuODgtNC45NC00LjgtNC45NGE3LjgzLDcuODMsMCwwLDAtNiwzLjA5LjQ3LjQ3LDAsMCwwLS4wOS4yOFY0OS4yMmEuMjkuMjksMCwwLDEtLjI5LjI5aC00LjYyYS4yOS4yOSwwLDAsMS0uMjgtLjI5VjI1LjkyYS4yOS4yOSwwLDAsMSwuMjgtLjI4aDQuNjJhLjI5LjI5LDAsMCwxLC4yOS4yOHYyLjc5YS4wNy4wNywwLDAsMCwuMTIsMEExMS4zNCwxMS4zNCwwLDAsMSwyMDAuNjMsMjVjNS4xNCwwLDcuNzEsMi43Nyw3LjcxLDcuNjZWNDkuMjJhLjI5LjI5LDAsMCwxLS4yOS4yOWgtNC42MmEuMjkuMjksMCwwLDEtLjI4LS4yOVoiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0yNDAuMjQsMzUuMzJIMjUxYS41OC41OCwwLDAsMSwuNTguNTh2My4zYS41OC41OCwwLDAsMS0uNTguNTdIMjQwLjI0YS41OC41OCwwLDAsMS0uNTctLjU3VjM1LjlBLjU4LjU4LDAsMCwxLDI0MC4yNCwzNS4zMloiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0yNjcsMzYuNzhsLTksMTIuNTlhLjMxLjMxLDAsMCwxLS4yNi4xNGgtNi4xMWEuMzMuMzMsMCwwLDEtLjI3LS41MkwyNjMuMjIsMzIuN2EuMTcuMTcsMCwwLDAsMC0uMTlMMjUyLjA1LDE3LjA2YS4zMi4zMiwwLDAsMSwuMjctLjUxaDYuMTFhLjMzLjMzLDAsMCwxLC4yNi4xM0wyNjcsMjguNDdhLjE1LjE1LDAsMCwwLC4yNSwwbDguMjMtMTEuNzlhLjM0LjM0LDAsMCwxLC4yNy0uMTNoNi4xNmEuMzIuMzIsMCwwLDEsLjI2LjUxTDI3MSwzMi40N2EuMTUuMTUsMCwwLDAsMCwuMThMMjgyLjg4LDQ5YS4zMy4zMywwLDAsMS0uMjYuNTJIMjc2LjVhLjMxLjMxLDAsMCwxLS4yNi0uMTRsLTktMTIuNTlBLjE2LjE2LDAsMCwwLDI2NywzNi43OFoiLz48cGF0aCBjbGFzcz0iY2xzLTMiIGQ9Ik0yMjkuNjksNDYuOTFBMTAuMDgsMTAuMDgsMCwwLDEsMjIyLDUwLjA4Yy0zLjkxLDAtOC4zMS0yLjYyLTguMzEtNy45MSwwLTUuNDksNC40LTcuNzYsOC4zMS03Ljc2YTkuODksOS44OSwwLDAsMSw3LjcxLDMuMDZWMzMuOTJjMC0yLjc3LTIuMjgtNC40NS01LjU5LTQuNDVhOS42OSw5LjY5LDAsMCwwLTYuNzQsMi43LjM1LjM1LDAsMCwxLS41NS0uMDhsLTEuNzUtM2EuMzcuMzcsMCwwLDEsLjA3LS40NkExNC4yLDE0LjIsMCwwLDEsMjI0Ljk0LDI1YzUuMjksMCw5LjkzLDIuMjIsOS45Myw4LjY1VjQ5LjE2YS4zMi4zMiwwLDAsMS0uMzIuMzJIMjMwYS4zMi4zMiwwLDAsMS0uMzItLjMyWm0wLTUuMjFhMS4zLDEuMywwLDAsMC0uMTQtLjU4QTYuNDcsNi40NywwLDAsMCwyMjQsMzhjLTIuOTEsMC01LjA5LDEuNjgtNS4wOSw0LjNzMi4xOCw0LjI1LDUuMDksNC4yNWE3LDcsMCwwLDAsNS40Ni0yLjQzLDEuMjYsMS4yNiwwLDAsMCwuMjMtLjczWiIvPjwvc3ZnPg=='); + background-size: contain; + height: 35px; + width: 250px; + right: 75px; +} diff --git a/backend/src/main/resources/db/changelog/db.changelog-extensions.yaml b/backend/src/main/resources/db/changelog/db.changelog-extensions.yaml new file mode 100644 index 00000000..303d8b1c --- /dev/null +++ b/backend/src/main/resources/db/changelog/db.changelog-extensions.yaml @@ -0,0 +1,31 @@ +############################################################### +# 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 +############################################################### + +databaseChangeLog: + - changeSet: + id: 01012022-01 + author: fmisir + changes: + - sql: + dbms: postgresql + endDelimiter: \nGO + splitStatements: true + sql: CREATE EXTENSION IF NOT EXISTS "uuid-ossp" + stripComments: true diff --git a/backend/src/main/resources/db/changelog/db.changelog-master.yaml b/backend/src/main/resources/db/changelog/db.changelog-master.yaml new file mode 100644 index 00000000..32933e13 --- /dev/null +++ b/backend/src/main/resources/db/changelog/db.changelog-master.yaml @@ -0,0 +1,40 @@ +############################################################### +# 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 +############################################################### + +databaseChangeLog: + - property: + name: uuid_type + value: uuid + dbms: postgresql, h2 + - property: + name: uuid_function + value: uuid_generate_v4() + dbms: postgresql + - property: + name: uuid_function + value: random_uuid() + dbms: h2 + + - include: + file: db.changelog-extensions.yaml + relativeToChangelogFile: true + - include: + file: db.changelog-v1.yaml + relativeToChangelogFile: true diff --git a/backend/src/main/resources/db/changelog/db.changelog-v1.yaml b/backend/src/main/resources/db/changelog/db.changelog-v1.yaml new file mode 100644 index 00000000..faaddc38 --- /dev/null +++ b/backend/src/main/resources/db/changelog/db.changelog-v1.yaml @@ -0,0 +1,366 @@ +############################################################### +# 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 +############################################################### + +databaseChangeLog: + - changeSet: + id: 27012022-01 + author: fmisir + changes: + - createTable: + tableName: SHELL + columns: + - column: + name: ID + type: ${uuid_type} + defaultValueComputed: ${uuid_function} + constraints: + primaryKey: true + nullable: false + - column: + name: ID_EXTERNAL + type: nvarchar(200) + constraints: + nullable: false + - column: + name: ID_SHORT + type: nvarchar(100) + constraints: + nullable: false + - column: + name: CREATED_DATE + type: timestamp + constraints: + nullable: false + - column: + name: LAST_MODIFIED_DATE + type: timestamp + constraints: + nullable: false + - addUniqueConstraint: + columnNames: ID_EXTERNAL + constraintName: SHELL_AK_01 + tableName: SHELL + validate: true + - changeSet: + id: 27012022-02 + author: fmisir + changes: + - createTable: + tableName: SHELL_IDENTIFIER + columns: + - column: + name: ID + type: ${uuid_type} + defaultValueComputed: ${uuid_function} + constraints: + primaryKey: true + nullable: false + - column: + name: KEY + type: nvarchar(200) + constraints: + nullable: false + - column: + name: VALUE + type: nvarchar(200) + constraints: + nullable: false + - column: + name: FK_SHELL_ID + type: ${uuid_type} + constraints: + nullable: false + - addUniqueConstraint: + columnNames: KEY, FK_SHELL_ID + constraintName: SHELL_IDENTIFIER_AK_01 + tableName: SHELL_IDENTIFIER + validate: true + - addForeignKeyConstraint: + baseTableName: SHELL_IDENTIFIER + baseColumnNames: FK_SHELL_ID + constraintName: SHELL_IDENTIFIER_FK_SHELL + onDelete: CASCADE + onUpdate: RESTRICT + referencedColumnNames: ID + referencedTableName: SHELL + validate: true + - changeSet: + id: 27012022-03 + author: fmisir + changes: + - createTable: + tableName: SHELL_DESCRIPTION + columns: + - column: + name: ID + type: ${uuid_type} + defaultValueComputed: ${uuid_function} + constraints: + primaryKey: true + nullable: false + - column: + name: LANGUAGE + type: nvarchar(10) + constraints: + nullable: false + - column: + name: TEXT + type: nvarchar(500) + constraints: + nullable: false + - column: + name: FK_SHELL_ID + type: ${uuid_type} + constraints: + nullable: false + - addUniqueConstraint: + columnNames: LANGUAGE, FK_SHELL_ID + constraintName: SHELL_DESCRIPTION_AK_01 + tableName: SHELL_DESCRIPTION + validate: true + - addForeignKeyConstraint: + baseTableName: SHELL_DESCRIPTION + baseColumnNames: FK_SHELL_ID + constraintName: SHELL_DESCRIPTION_FK_SHELL + onDelete: CASCADE + onUpdate: RESTRICT + referencedColumnNames: ID + referencedTableName: SHELL + validate: true + - changeSet: + id: 27012022-04 + author: fmisir + changes: + - createTable: + tableName: SUBMODEL + columns: + - column: + name: ID + type: ${uuid_type} + defaultValueComputed: ${uuid_function} + constraints: + primaryKey: true + nullable: false + - column: + name: ID_EXTERNAL + type: nvarchar(200) + constraints: + nullable: false + - column: + name: ID_SHORT + type: nvarchar(100) + constraints: + nullable: false + - column: + name: SEMANTIC_ID + type: nvarchar(200) + constraints: + nullable: false + - column: + name: FK_SHELL_ID + type: ${uuid_type} + constraints: + nullable: false + - addUniqueConstraint: + columnNames: ID_EXTERNAL, FK_SHELL_ID + constraintName: SUBMODEL_SHELL_AK_01 + tableName: SUBMODEL + validate: true + - addForeignKeyConstraint: + baseTableName: SUBMODEL + baseColumnNames: FK_SHELL_ID + constraintName: SUBMODEL_FK_SHELL_ID + onDelete: CASCADE + onUpdate: RESTRICT + referencedColumnNames: ID + referencedTableName: SHELL + validate: true + - changeSet: + id: 27012022-05 + author: fmisir + changes: + - createTable: + tableName: SUBMODEL_DESCRIPTION + columns: + - column: + name: ID + type: ${uuid_type} + defaultValueComputed: ${uuid_function} + constraints: + primaryKey: true + nullable: false + - column: + name: LANGUAGE + type: nvarchar(10) + constraints: + nullable: false + - column: + name: TEXT + type: nvarchar(500) + constraints: + nullable: false + - column: + name: FK_SUBMODEL_ID + type: ${uuid_type} + constraints: + nullable: false + - addUniqueConstraint: + columnNames: LANGUAGE, FK_SUBMODEL_ID + constraintName: SM_DESCRIPTION_AK_01 + tableName: SUBMODEL_DESCRIPTION + validate: true + - addForeignKeyConstraint: + baseTableName: SUBMODEL_DESCRIPTION + baseColumnNames: FK_SUBMODEL_ID + constraintName: SM_DESCRIPTION_FK_SM + onDelete: CASCADE + onUpdate: RESTRICT + referencedColumnNames: ID + referencedTableName: SUBMODEL + validate: true + - changeSet: + id: 27012022-06 + author: fmisir + changes: + - createTable: + tableName: SUBMODEL_ENDPOINT + columns: + - column: + name: ID + type: ${uuid_type} + defaultValueComputed: ${uuid_function} + constraints: + primaryKey: true + nullable: false + - column: + name: INTERFACE_NAME + type: nvarchar(50) + constraints: + nullable: false + - column: + name: ENDPOINT_ADDRESS + type: nvarchar(300) + constraints: + nullable: false + - column: + name: ENDPOINT_PROTOCOL + type: nvarchar(50) + - column: + name: ENDPOINT_PROTOCOL_VERSION + type: nvarchar(10) + - column: + name: SUB_PROTOCOL + type: nvarchar(50) + - column: + name: SUB_PROTOCOL_BODY + type: nvarchar(50) + - column: + name: SUB_PROTOCOL_BODY_ENCODING + type: nvarchar(50) + - column: + name: FK_SUBMODEL_ID + type: ${uuid_type} + constraints: + nullable: false + - addForeignKeyConstraint: + baseTableName: SUBMODEL_ENDPOINT + baseColumnNames: FK_SUBMODEL_ID + constraintName: SM_ENDPOINT_FK_SM + onDelete: CASCADE + onUpdate: RESTRICT + referencedColumnNames: ID + referencedTableName: SUBMODEL + validate: true + - changeSet: + id: 21022022-01 + author: cjung7 + comment: 'Adapt to H2 latest version (which does not allow "KEY" and "VALUE" as column names) and introduce multiple global asset ids' + changes: + - modifyDataType: + tableName: SUBMODEL_ENDPOINT + columnName: ENDPOINT_ADDRESS + newDataType: VARCHAR(512) + - renameColumn: + tableName: SHELL_IDENTIFIER + newColumnName: NAMESPACE + oldColumnName: KEY + - renameColumn: + tableName: SHELL_IDENTIFIER + newColumnName: IDENTIFIER + oldColumnName: VALUE + - addColumn: + tableName: SHELL_IDENTIFIER + columns: + - column: + name: IS_UNIQUE + type: BOOLEAN + defaultValue: false + - changeSet: + id: 22022022-01 + author: fmisir + changes: + - createIndex: + tableName: SHELL_IDENTIFIER + indexName: SHELL_IDENTIFIER_IX01 + columns: + - column: + name: NAMESPACE + - column: + name: IDENTIFIER + - changeSet: + id: 02032022-01 + author: fmisir + changes: + - dropUniqueConstraint: + tableName: SHELL_IDENTIFIER + constraintName: SHELL_IDENTIFIER_AK_01 + uniqueColumns: KEY, FK_SHELL_ID + - changeSet: + id: 10032022-01 + author: cjung7 + changes: + - modifyDataType: + tableName: SUBMODEL_ENDPOINT + columnName: INTERFACE_NAME + newDataType: NVARCHAR(512) + - modifyDataType: + tableName: SUBMODEL_ENDPOINT + columnName: ENDPOINT_PROTOCOL_VERSION + newDataType: nvarchar(24) + - changeSet: + id: 12042022-01 + author: fmisir + changes: + - dropUniqueConstraint: + tableName: SUBMODEL + uniqueColumns: ID_EXTERNAL, FK_SHELL_ID + constraintName: SUBMODEL_SHELL_AK_01 + - addUniqueConstraint: + columnNames: ID_EXTERNAL + constraintName: SUBMODEL_AK_01 + tableName: SUBMODEL + validate: true + - changeSet: + id: 20042022-01 + author: fmisir + changes: + - dropColumn: + tableName: SHELL_IDENTIFIER + columnName: IS_UNIQUE diff --git a/backend/src/main/resources/static/aas-registry-openapi.yaml b/backend/src/main/resources/static/aas-registry-openapi.yaml new file mode 100644 index 00000000..9cb564dd --- /dev/null +++ b/backend/src/main/resources/static/aas-registry-openapi.yaml @@ -0,0 +1,1294 @@ +############################################################### +# 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 +############################################################### + +# Plattform_i40-Registry-and-Discovery-Final-Draft-resolved.yaml +# https://app.swaggerhub.com/apis/Plattform_i40/Registry-and-Discovery/Final-Draft#/info +--- +openapi: 3.0.3 +info: + title: DotAAS Part 2 | HTTP/REST | Registry and Discovery + description: The registry and discovery interface as part of Details of the Asset Administration Shell Part 2 + termsOfService: https://github.com/admin-shell-io/aas-specs + contact: + name: Michael Hoffmeister, Torben Miny, Andreas Orzelski, Manuel Sauer, Constantin Ziesche + version: Final-Draft + +servers: +- url: ./ + +security: + - CatenaXOpenId: + - profile + +paths: + /registry/shell-descriptors: + get: + tags: + - Registry and Discovery Interface + summary: Returns all Asset Administration Shell Descriptors + operationId: GetAllAssetAdministrationShellDescriptors + parameters: + - in: query + name: page + required: false + schema: + type: integer + description: The page to return + default: 0 + - in: query + name: pageSize + required: false + schema: + type: integer + enum: + - 10 + - 50 + - 100 + default: 10 + description: Size of the pages that the results should be partitioned in + responses: + "200": + description: Requested Asset Administration Shell Descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/AssetAdministrationShellDescriptorCollection' + examples: + complete: + $ref: '#/components/examples/complete-asset-administration-shell-collection' + x-semanticIds: + - https://admin-shell.io/aas/API/GetAllAssetAdministrationShellDescriptors/1/0/RC02 + post: + tags: + - Registry and Discovery Interface + summary: Creates a new Asset Administration Shell Descriptor, i.e. registers an AAS + operationId: PostAssetAdministrationShellDescriptor + requestBody: + description: Asset Administration Shell Descriptor object + content: + application/json: + schema: + $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-asset-administration-shell-descriptor' + minimal: + $ref: '#/components/examples/minimal-asset-administration-shell-descriptor' + required: true + responses: + "201": + description: Asset Administration Shell Descriptor created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-asset-administration-shell-descriptor' + minimal: + $ref: '#/components/examples/minimal-asset-administration-shell-descriptor' + x-semanticIds: + - https://admin-shell.io/aas/API/PostAssetAdministrationShellDescriptor/1/0/RC02 + /registry/shell-descriptors/batch: + post: + tags: + - Registry and Discovery Interface + summary: Creates multiple Asset Administration Shell Descriptors. + operationId: PostBatchAssetAdministrationShellDescriptor + requestBody: + description: Asset Administration Shell Descriptor object + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + examples: + complete: + $ref: '#/components/examples/asset-administration-shell-descriptor-batch' + required: true + responses: + "201": + description: Asset Administration Shells in batch created successfully + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/BatchResult' + examples: + complete: + $ref: '#/components/examples/asset-administration-shell-descriptor-batch-result' + x-semanticIds: + - https://admin-shell.io/aas/API/PostAssetAdministrationShellDescriptor/1/0/RC02 + /registry/shell-descriptors/fetch: + post: + tags: + - Registry and Discovery Interface + summary: Returns shell descriptors for the given identifications. + operationId: PostFetchAssetAdministrationShellDescriptor + requestBody: + description: Asset Administration Shell Descriptor object + content: + application/json: + schema: + type: array + minLength: 1 + items: + $ref: '#/components/schemas/Identifier' + examples: + complete: + $ref: '#/components/examples/asset-administration-shell-descriptor-fetch-request' + required: true + responses: + "201": + description: Asset Administration Shell Descriptors + content: + application/json: + schema: + $ref: '#/components/schemas/AssetAdministrationShellDescriptorCollectionBase' + examples: + complete: + $ref: '#/components/examples/asset-administration-shell-descriptor-fetch-result' + x-semanticIds: + - https://admin-shell.io/aas/API/PostAssetAdministrationShellDescriptor/1/0/RC02 + /registry/shell-descriptors/{aasIdentifier}: + get: + tags: + - Registry and Discovery Interface + summary: Returns a specific Asset Administration Shell Descriptor + operationId: GetAssetAdministrationShellDescriptorById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Requested Asset Administration Shell Descriptor + content: + application/json: + schema: + $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-submodel-descriptor' + x-semanticIds: + - https://admin-shell.io/aas/API/GetAssetAdministrationShellDescriptorById/1/0/RC02 + put: + tags: + - Registry and Discovery Interface + summary: Updates an existing Asset Administration Shell Descriptor + operationId: PutAssetAdministrationShellDescriptorById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + requestBody: + description: Asset Administration Shell Descriptor object + content: + application/json: + schema: + $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-submodel-descriptor' + minimal: + $ref: '#/components/examples/minimal-submodel-descriptor' + required: true + responses: + "204": + description: Asset Administration Shell Descriptor updated successfully + x-semanticIds: + - https://admin-shell.io/aas/API/PutAssetAdministrationShellDescriptorById/1/0/RC02 + delete: + tags: + - Registry and Discovery Interface + summary: Deletes an Asset Administration Shell Descriptor, i.e. de-registers an AAS + operationId: DeleteAssetAdministrationShellDescriptorById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "204": + description: Asset Administration Shell Descriptor deleted successfully + x-semanticIds: + - https://admin-shell.io/aas/API/DeleteAssetAdministrationShellDescriptorById/1/0/RC02 + /registry/shell-descriptors/{aasIdentifier}/submodel-descriptors: + get: + tags: + - Registry and Discovery Interface + summary: Returns all Submodel Descriptors + operationId: GetAllSubmodelDescriptors + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Requested Submodel Descriptors + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SubmodelDescriptor' + examples: + collection: + $ref: '#/components/examples/submodel-descriptor-collection' + x-semanticIds: + - https://admin-shell.io/aas/API/GetAllSubmodelDescriptors/1/0/RC02 + post: + tags: + - Registry and Discovery Interface + summary: Creates a new Submodel Descriptor, i.e. registers a submodel + operationId: PostSubmodelDescriptor + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + requestBody: + description: Submodel Descriptor object + content: + application/json: + schema: + $ref: '#/components/schemas/SubmodelDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-submodel-descriptor' + minimal: + $ref: '#/components/examples/minimal-submodel-descriptor' + required: true + responses: + "201": + description: Submodel Descriptor created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SubmodelDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-asset-administration-shell-descriptor' + minimal: + $ref: '#/components/examples/minimal-asset-administration-shell-descriptor' + x-semanticIds: + - https://admin-shell.io/aas/API/PostSubmodelDescriptor/1/0/RC02 + /registry/shell-descriptors/{aasIdentifier}/submodel-descriptors/{submodelIdentifier}: + get: + tags: + - Registry and Discovery Interface + summary: Returns a specific Submodel Descriptor + operationId: GetSubmodelDescriptorById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + - name: submodelIdentifier + in: path + description: The Submodel’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Requested Submodel Descriptor + content: + application/json: + schema: + $ref: '#/components/schemas/SubmodelDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-submodel-descriptor' + minimal: + $ref: '#/components/examples/minimal-submodel-descriptor' + x-semanticIds: + - https://admin-shell.io/aas/API/GetSubmodelDescriptorById/1/0/RC02 + put: + tags: + - Registry and Discovery Interface + summary: Updates an existing Submodel Descriptor + operationId: PutSubmodelDescriptorById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + - name: submodelIdentifier + in: path + description: The Submodel’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + requestBody: + description: Submodel Descriptor object + content: + application/json: + schema: + $ref: '#/components/schemas/SubmodelDescriptor' + examples: + complete: + $ref: '#/components/examples/complete-submodel-descriptor' + minimal: + $ref: '#/components/examples/minimal-submodel-descriptor' + required: true + responses: + "204": + description: Submodel Descriptor updated successfully + x-semanticIds: + - https://admin-shell.io/aas/API/PutSubmodelDescriptorById/1/0/RC02 + delete: + tags: + - Registry and Discovery Interface + summary: Deletes a Submodel Descriptor, i.e. de-registers a submodel + operationId: DeleteSubmodelDescriptorById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + - name: submodelIdentifier + in: path + description: The Submodel’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "204": + description: Submodel Descriptor deleted successfully + x-semanticIds: + - https://admin-shell.io/aas/API/DeleteSubmodelDescriptorById/1/0/RC02 + /lookup/shells: + get: + tags: + - Registry and Discovery Interface + summary: Returns a list of Asset Administration Shell ids based on Asset identifier key-value-pairs. Only the Shell ids are returned when all provided key-value pairs match. + operationId: GetAllAssetAdministrationShellIdsByAssetLink + parameters: + - name: assetIds + in: query + description: The key-value-pair of an Asset identifier + required: false + # The form style defined in the AAS API does not match with the provided example. + # the "content" is the correct way to accept json encoded query parameters + # style: form + # explode: true + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IdentifierKeyValuePair' + examples: + complete: + $ref: '#/components/examples/lookup-shells-by-aas-identifier-query' + responses: + "200": + description: Requested Asset Administration Shell ids + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Identifier' + examples: + complete: + $ref: '#/components/examples/lookup-shells-by-aas-identifier-response' + x-semanticIds: + - https://admin-shell.io/aas/API/GetAllAssetAdministrationShellIdsByAssetLink/1/0/RC02 + /lookup/shells/{aasIdentifier}: + get: + tags: + - Registry and Discovery Interface + summary: Returns a list of Asset identifier key-value-pairs based on an Asset Administration Shell id to edit discoverable content + operationId: GetAllAssetLinksById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Requested Asset identifier key-value-pairs + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IdentifierKeyValuePair' + examples: + complete: + $ref: '#/components/examples/lookup-specific-asset-ids' + x-semanticIds: + - https://admin-shell.io/aas/API/GetAllAssetLinksById/1/0/RC02 + post: + tags: + - Registry and Discovery Interface + summary: Creates all Asset identifier key-value-pair linked to an Asset Administration Shell to edit discoverable content + operationId: PostAllAssetLinksById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + requestBody: + description: Asset identifier key-value-pairs + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IdentifierKeyValuePair' + examples: + complete: + $ref: '#/components/examples/lookup-specific-asset-ids' + required: true + responses: + "201": + description: Asset identifier key-value-pairs created successfully + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IdentifierKeyValuePair' + examples: + complete: + $ref: '#/components/examples/lookup-specific-asset-ids' + x-semanticIds: + - https://admin-shell.io/aas/API/PostAllAssetLinksById/1/0/RC02 + delete: + tags: + - Registry and Discovery Interface + summary: Deletes all Asset identifier key-value-pair linked to an Asset Administration Shell to edit discoverable content + operationId: DeleteAllAssetLinksById + parameters: + - name: aasIdentifier + in: path + description: The Asset Administration Shell’s unique id (BASE64-URL-encoded) + required: true + style: simple + explode: false + schema: + type: string + responses: + "204": + description: Asset identifier key-value-pairs deleted successfully + x-semanticIds: + - https://admin-shell.io/aas/API/DeleteAllAssetLinksById/1/0/RC02 + /lookup/shells/query: + post: + tags: + - Registry and Discovery Interface + summary: Returns a list of Asset Administration Shell ids based on Asset identifier key-value-pairs. + operationId: PostQueryAllAssetAdministrationShellIds + requestBody: + description: Asset Administration Shell Descriptor object + content: + application/json: + schema: + $ref: '#/components/schemas/ShellLookup' + required: true + responses: + "200": + description: Requested Asset Administration Shell ids + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Identifier' + examples: + complete: + $ref: '#/components/examples/lookup-shells-by-aas-identifier-response' +components: + schemas: + # The official spec does not support pagination right now. This is an addition. + AssetAdministrationShellDescriptorCollectionBase: + title: AssetAdministrationShellDescriptorCollectionBase + required: + - items + type: object + properties: + items: + title: Items + type: array + items: + $ref: '#/components/schemas/AssetAdministrationShellDescriptor' + AssetAdministrationShellDescriptorCollection: + title: AssetAdministrationShellDescriptorCollection + allOf: + - $ref: '#/components/schemas/AssetAdministrationShellDescriptorCollectionBase' + required: + - totalItems + - currentPage + - totalPages + - itemCount + type: object + properties: + totalItems: + title: Totalitems + type: integer + currentPage: + title: Currentpage + type: integer + totalPages: + title: Totalpages + type: integer + itemCount: + title: Itemcount + type: integer + AssetAdministrationShellDescriptor: + required: + - identification + - idShort + type: object + properties: + administration: + $ref: '#/components/schemas/AdministrativeInformation' + description: + type: array + items: + $ref: '#/components/schemas/LangString' + globalAssetId: + $ref: '#/components/schemas/Reference' + idShort: + type: string + minLength: 1 + maxLength: 100 + identification: + $ref: '#/components/schemas/Identifier' + specificAssetIds: + type: array + items: + $ref: '#/components/schemas/IdentifierKeyValuePair' + submodelDescriptors: + type: array + items: + $ref: '#/components/schemas/SubmodelDescriptor' + allOf: + - $ref: '#/components/schemas/Descriptor' + Descriptor: + type: object + properties: + endpoints: + type: array + items: + $ref: '#/components/schemas/Endpoint' + example: '{ "endpoints": [{ "protocolInformation": { "endpointAddress": "https://localhost:1234", "endpointProtocolVersion: "1.1" }, "interface": "AAS-1.0" }, { "protocolInformation": { "endpointAddress": "opc.tcp://localhost:4840" }, "interface": "AAS-1.0" }, { "protocolInformation": { "endpointAddress": "https://localhost:5678", "endpointProtocolVersion: "1.1", "subprotocol": "OPC UA Basic SOAP", "subprotocolBody": "ns=2;s=MyAAS", "subprotocolBodyEncoding": "plain" }, "interface": "AAS-1.0" }] }' + Endpoint: + required: + - interface + - protocolInformation + type: object + properties: + interface: + type: string + protocolInformation: + $ref: '#/components/schemas/ProtocolInformation' + ProtocolInformation: + required: + - endpointAddress + type: object + properties: + endpointAddress: + type: string + minLength: 1 + maxLength: 512 + endpointProtocol: + type: string + minLength: 1 + maxLength: 50 + endpointProtocolVersion: + type: string + minLength: 1 + maxLength: 24 + subprotocol: + type: string + minLength: 1 + maxLength: 50 + subprotocolBody: + type: string + minLength: 1 + maxLength: 50 + subprotocolBodyEncoding: + type: string + minLength: 1 + maxLength: 50 + AdministrativeInformation: + type: object + properties: + revision: + type: string + version: + type: string + LangString: + required: + - language + - text + type: object + properties: + language: + type: string + minLength: 1 + maxLength: 10 + text: + type: string + minLength: 1 + maxLength: 500 + Reference: + type: object + oneOf: + - $ref: '#/components/schemas/GlobalReference' + #- $ref: '#/components/schemas/ModelReference' + GlobalReference: + required: + - value + properties: + value: + type: array + minItems: 1 + maxItems: 1 + items: + $ref: '#/components/schemas/Identifier' + # The code generator gets in issues, because it's somehow recursive. Reference has a reference ModelReference .. + # allOf: + # - $ref: '#/components/schemas/Reference' + Identifier: + type: string + minLength: 1 + maxLength: 200 + ModelReference: + required: + - keys + properties: + # not supported for Catena-X + # referredSemanticId: + # $ref: '#/components/schemas/Reference' + keys: + type: array + items: + $ref: '#/components/schemas/Key' + # The code generator gets in issues, because it's somehow recursive. Reference has a reference ModelReference .. + # allOf: + # - $ref: '#/components/schemas/Reference' + Key: + required: + - type + - value + type: object + properties: + type: + $ref: '#/components/schemas/KeyElements' + value: + type: string + KeyElements: + type: string + enum: + - AssetAdministrationShell + - AccessPermissionRule + - ConceptDescription + - Submodel + - AnnotatedRelationshipElement + - BasicEvent + - Blob + - Capability + - DataElement + - File + - Entity + - Event + - MultiLanguageProperty + - Operation + - Property + - Range + - ReferenceElement + - RelationshipElement + - SubmodelElement + - SubmodelElementList + - SubmodelElementStruct + - View + - FragmentReference + IdentifierKeyValuePair: + allOf: + - $ref: '#/components/schemas/HasSemantics' + - required: + - key + - value + properties: + key: + type: string + minLength: 1 + maxLength: 200 + # not supported for Catena-X + # subjectId: + # $ref: '#/components/schemas/Reference' + value: + type: string + minLength: 1 + maxLength: 200 + HasSemantics: + type: object + properties: + semanticId: + $ref: '#/components/schemas/Reference' + SubmodelDescriptor: + required: + - endpoints + - identification + # in the AAS Spec the semanticId is not required for submodels + # this does not make sense as it's the only way for consumers to know what kind of data an endpoint returns + - semanticId + type: object + properties: + administration: + $ref: '#/components/schemas/AdministrativeInformation' + description: + type: array + items: + $ref: '#/components/schemas/LangString' + idShort: + type: string + minLength: 1 + maxLength: 100 + identification: + $ref: '#/components/schemas/Identifier' + semanticId: + $ref: '#/components/schemas/Reference' + allOf: + - $ref: '#/components/schemas/Descriptor' + ErrorResponse: + type: object + required: + - error + properties: + error: + $ref: '#/components/schemas/Error' + Error: + type: object + required: + - details + properties: + message: + type: string + example: size must be between {min} and {max} + description: The detailed error message for the exception which occurred. + minLength: 1 + path: + type: string + description: The requested path. + minLength: 1 + details: + type: object + additionalProperties: + type: object + description: An object with key/value pairs containing additional information about the error. + BatchResult: + type: object + required: + - message + - identification + - status + properties: + message: + type: string + description: The detailed error message for the exception which occurred. + identification: + type: string + description: The requested path. + status: + type: integer + description: The status code + ShellLookup: + type: object + required: + - query + properties: + query: + type: object + properties: + assetIds: + type: array + items: + $ref: '#/components/schemas/IdentifierKeyValuePair' + securitySchemes: + CatenaXOpenId: + type: openIdConnect + openIdConnectUrl: ../.well-known/openid-configuration + + examples: + minimal-asset-administration-shell-descriptor: + value: + { + "idShort": "future concept x", + "identification": "882fc530-b69b-4707-95f6-5dbc5e9baaa8" + } + complete-asset-administration-shell-descriptor: + value: + { + "description": [ + { + "language": "en", + "text": "The shell for a vehicle" + } + ], + "globalAssetId": { + "value": [ + "urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8" + ] + }, + "idShort": "future concept x", + "identification": "882fc530-b69b-4707-95f6-5dbc5e9baaa8", + "specificAssetIds": [ + { + "key": "PartInstanceID", + "value": "24975539203421" + } + ], + "submodelDescriptors": [ + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + }, + { + "description": [ + { + "language": "en", + "text": "Provides base vehicle information" + } + ], + "idShort": "vehicle part details", + "identification": "dae4d249-6d66-4818-b576-bf52f3b9ae90", + "semanticId": { + "value": [ + "urn:bamm:com.catenax.vehicle:0.1.1#PartDetails" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-details/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/dae4d249-6d66-4818-b576-bf52f3b9ae90/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + ] + } + complete-asset-administration-shell-collection: + value: + { + "items": [ + { + "description": [ + { + "language": "en", + "text": "The shell for a vehicle" + } + ], + "globalAssetId": { + "value": [ + "urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8" + ] + }, + "idShort": "future concept x", + "identification": "882fc530-b69b-4707-95f6-5dbc5e9baaa8", + "specificAssetIds": [ + { + "key": "PartInstanceID", + "value": "24975539203421" + } + ], + "submodelDescriptors": [ + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + ] + }, + { + "idShort": "future concept x", + "identification": "459842bf-3466-4eb6-8d95-ef0557e64883" + } + ], + "totalItems": 2, + "currentPage": 0, + "totalPages": 1, + "itemCount": 2 + } + asset-administration-shell-descriptor-fetch-result: + value: + { + "items": [ + { + "description": [ + { + "language": "en", + "text": "The shell for a vehicle" + } + ], + "globalAssetId": { + "value": [ + "urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8" + ] + }, + "idShort": "future concept x", + "identification": "882fc530-b69b-4707-95f6-5dbc5e9baaa8", + "specificAssetIds": [ + { + "key": "PartInstanceID", + "value": "24975539203421" + } + ], + "submodelDescriptors": [ + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + ] + }, + { + "idShort": "future concept x", + "identification": "459842bf-3466-4eb6-8d95-ef0557e64883" + } + ] + } + asset-administration-shell-descriptor-fetch-request: + value: + [ + "882fc530-b69b-4707-95f6-5dbc5e9baaa8", + "459842bf-3466-4eb6-8d95-ef0557e64883" + ] + asset-administration-shell-descriptor-batch: + value: + [ + { + "identification": "5bed6107-24b7-4f47-a5e9-25a6d1acc836", + "idShort": "exampleShellShortId", + "description": [ + { + "language": "en", + "text": "this is an example description" + }, + { + "language": "de", + "text": "das ist ein beispiel" + } + ], + "globalAssetId": { + "value": [ + "urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8" + ] + }, + "specificAssetIds": [ + { + "key": "VAN", + "value": "WEBFC102u30912" + } + ], + "submodelDescriptors": [ + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + }, + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + ] + }, + { + "identification": "5bed6107-24b7-4f47-a5e9-25a6d1acc836", + "idShort": "exampleShellShortId" + } + ] + asset-administration-shell-descriptor-batch-result: + value: + [ + { + "message": "AssetAdministrationShell successfully created.", + "identification": "5bed6107-24b7-4f47-a5e9-25a6d1acc836", + "status": 200 + }, + { + "message": "An AssetAdministrationShell for the given identification does already exists.", + "identification": "5bed6107-24b7-4f47-a5e9-25a6d1acc836", + "status": 400 + } + ] + minimal-submodel-descriptor: + value: + { + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + complete-submodel-descriptor: + value: + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + submodel-descriptor-collection: + value: + [ + { + "description": [ + { + "language": "en", + "text": "Provides traceability information" + } + ], + "idShort": "traceability-info", + "identification": "4a738a24-b7d8-4989-9cd6-387772f40565", + "semanticId": { + "value": [ + "urn:bamm:com.catenax:0.0.1#Traceability" + ] + }, + "endpoints": [ + { + "interface": "SUBMODEL-1.0RC02", + "protocolInformation": { + "endpointAddress": "edc://provider.connector:9191/offer-windchill/shells/882fc530-b69b-4707-95f6-5dbc5e9baaa8/aas/4a738a24-b7d8-4989-9cd6-387772f40565/submodel", + "endpointProtocol": "IDS/ECLIPSE DATASPACE CONNECTOR", + "endpointProtocolVersion": "0.0.1" + } + } + ] + } + ] + lookup-shells-by-aas-identifier-query: + value: + [ + { + "key" : "globalAssetId", + "value" : "urn:uuid:882fc530-b69b-4707-95f6-5dbc5e9baaa8" + }, + { + "key": "PartInstanceID", + "value": "24975539203421" + } + ] + lookup-shells-by-aas-identifier-response: + value: + [ + "882fc530-b69b-4707-95f6-5dbc5e9baaa8" + ] + lookup-specific-asset-ids: + value: + [ + { + "key": "PartInstanceID", + "value": "24975539203421" + } + ] diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/ApplicationTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/ApplicationTest.java new file mode 100644 index 00000000..7939aeb3 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/ApplicationTest.java @@ -0,0 +1,32 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ + +package org.eclipse.tractusx.semantics; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +public class ApplicationTest { + + @Test + public void contextLoads() { + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/SwaggerUITest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/SwaggerUITest.java new file mode 100644 index 00000000..eb89015d --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/SwaggerUITest.java @@ -0,0 +1,54 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics; + +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +public class SwaggerUITest { + @Autowired + private MockMvc mockMvc; + + @Test + public void testGetSwaggerUiExpect200() throws Exception { + this.mockMvc.perform( get( "/swagger-ui/index.html" ) ) + .andDo( print() ) + .andExpect( status().isOk() ) + .andExpect( content().string( containsString( "
" ) ) ); + } + + @Test + public void testGetRootExpectRedirectedToSwaggerUI() throws Exception { + this.mockMvc.perform( get( "/" ) ) + .andDo( print() ) + .andExpect( status().isFound() ) + .andExpect( redirectedUrl( "/swagger-ui/index.html" ) ); + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/TestOAuthSecurityConfig.java b/backend/src/test/java/org/eclipse/tractusx/semantics/TestOAuthSecurityConfig.java new file mode 100644 index 00000000..c56acd43 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/TestOAuthSecurityConfig.java @@ -0,0 +1,47 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics; + +import org.eclipse.tractusx.semantics.registry.JwtTokenFactory; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.security.oauth2.jwt.JwtDecoder; + +@TestConfiguration +public class TestOAuthSecurityConfig { + + /** + * In tests the OAuth2 flow is mocked by Spring. The Spring Security test support directly creates the + * authentication object in the SecurityContextHolder. + * + * This decoder is only required for being present in the application context due to Spring autoconfiguration. + */ + @Bean + public JwtDecoder jwtDecoder(){ + return token -> { + throw new UnsupportedOperationException("The JwtDecoder must not be called in tests by Spring."); + }; + } + + @Bean + public JwtTokenFactory jwtTokenFactory(RegistryProperties registryProperties){ + return new JwtTokenFactory(registryProperties.getIdm().getPublicClientId()); + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AbstractAssetAdministrationShellApi.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AbstractAssetAdministrationShellApi.java new file mode 100644 index 00000000..dd38323d --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AbstractAssetAdministrationShellApi.java @@ -0,0 +1,213 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.eclipse.tractusx.semantics.RegistryProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +import java.util.UUID; + +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + + +@SpringBootTest +@AutoConfigureMockMvc +@EnableConfigurationProperties( RegistryProperties.class) +public abstract class AbstractAssetAdministrationShellApi { + + + protected static final String SHELL_BASE_PATH = "/registry/shell-descriptors"; + protected static final String SINGLE_SHELL_BASE_PATH = "/registry/shell-descriptors/{shellIdentifier}"; + protected static final String LOOKUP_SHELL_BASE_PATH = "/lookup/shells"; + protected static final String SINGLE_LOOKUP_SHELL_BASE_PATH = "/lookup/shells/{shellIdentifier}"; + protected static final String SUB_MODEL_BASE_PATH = "/registry/shell-descriptors/{shellIdentifier}/submodel-descriptors"; + protected static final String SINGLE_SUB_MODEL_BASE_PATH = "/registry/shell-descriptors/{shellIdentifier}/submodel-descriptors/{submodelIdentifier}"; + + + + @Autowired + protected MockMvc mvc; + + @Autowired + protected ObjectMapper mapper; + + @Autowired + protected JwtTokenFactory jwtTokenFactory; + + protected String getId(ObjectNode payload) { + return payload.get("identification").textValue(); + } + + protected void performSubmodelCreateRequest(String payload, String shellIdentifier) throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post(SUB_MODEL_BASE_PATH, shellIdentifier) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(payload) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(content().json(payload)); + } + + /** + * calls create and checks result for identity + * @param payload + * @throws Exception + */ + protected void performShellCreateRequest(String payload) throws Exception { + performShellCreateRequest(payload,payload); + } + + /** + * performs create and checks result for expections + * @param payload + * @param expectation + * @throws Exception + */ + protected void performShellCreateRequest(String payload, String expectation) throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(payload) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(content().json(expectation)); + } + + + protected ObjectNode createShell() throws JsonProcessingException { + ObjectNode shellPayload = createBaseIdPayload("exampleShellIdPrefix", "exampleShellShortId"); + shellPayload.set("description", emptyArrayNode() + .add(createDescription("en", "this is an example description")) + .add(createDescription("de", "das ist ein beispiel"))); + + String globalId="exampleGlobalAssetId"; + + shellPayload.set("globalAssetId", mapper.createObjectNode() + .set("value", emptyArrayNode().add(globalId) )); + + shellPayload.set("specificAssetIds", emptyArrayNode() + .add(specificAssetId("vin1", "valueforvin1")) + .add(specificAssetId("enginenumber1", "enginenumber1"))); + + shellPayload.set("submodelDescriptors", emptyArrayNode() + .add(createSubmodel("submodel_external1")) + .add(createSubmodel("submodel_external2"))); + return shellPayload; + } + + protected ObjectNode createSubmodel(String submodelIdPrefix) throws JsonProcessingException { + ObjectNode submodelPayload = createBaseIdPayload(submodelIdPrefix, "exampleSubModelShortId"); + submodelPayload.set("description", emptyArrayNode() + .add(createDescription("en", "this is an example submodel description")) + .add(createDescription("de", "das ist ein Beispiel submodel"))); + submodelPayload.set("endpoints", emptyArrayNode() + .add(createEndpoint())); + submodelPayload.set("semanticId", createSemanticId()); + return submodelPayload; + } + + protected static String uuid(String prefix) { + return prefix + "#" + UUID.randomUUID(); + } + + + protected ArrayNode emptyArrayNode() { + return mapper.createArrayNode(); + } + + protected ObjectNode createBaseIdPayload(String idPrefix, String idShort) throws JsonProcessingException { + ObjectNode objectNode = mapper.createObjectNode(); + objectNode.put("identification", uuid(idPrefix)); + objectNode.put("idShort", idShort); + return objectNode; + } + + protected ObjectNode createDescription(String language, String text) { + ObjectNode description = mapper.createObjectNode(); + description.put("language", language); + description.put("text", text); + return description; + } + + protected ObjectNode createGlobalAssetId(String value) { + ObjectNode semanticId = mapper.createObjectNode(); + semanticId.set("value", emptyArrayNode().add(value) ); + return semanticId; + } + + protected ObjectNode specificAssetId(String key, String value) { + ObjectNode specificAssetId = mapper.createObjectNode(); + specificAssetId.put("key", key); + specificAssetId.put("value", value); + return specificAssetId; + } + + protected ObjectNode createSemanticId() { + ObjectNode semanticId = mapper.createObjectNode(); + semanticId.set("value", emptyArrayNode().add("urn:net.catenax.vehicle:1.0.0#Parts")); + return semanticId; + } + + protected ObjectNode createEndpoint() { + ObjectNode endpoint = mapper.createObjectNode(); + endpoint.put("interface", "interfaceName"); + endpoint.set("protocolInformation", mapper.createObjectNode() + .put("endpointAddress", "https://catena-xsubmodel-vechile.net/path") + .put("endpointProtocol", "https") + .put("subprotocol", "Mca1uf1") + .put("subprotocolBody", "Mafz1") + .put("subprotocolBodyEncoding", "Fj1092ufj") + ); + return endpoint; + } + + protected String toJson(JsonNode jsonNode) throws JsonProcessingException { + return mapper.writeValueAsString(jsonNode); + } + + protected String toJson(ObjectNode objectNode) throws JsonProcessingException { + return mapper.writeValueAsString(objectNode); + } + + protected String toJson(ArrayNode objectNode) throws JsonProcessingException { + return mapper.writeValueAsString(objectNode); + } + +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AssetAdministrationShellApiSecurityTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AssetAdministrationShellApiSecurityTest.java new file mode 100644 index 00000000..545ae514 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AssetAdministrationShellApiSecurityTest.java @@ -0,0 +1,556 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +import java.util.UUID; + +import static org.hamcrest.Matchers.hasSize; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * This class contains test to verify Authentication and RBAC based Authorization for all API endpoints. + * Every API endpoint is tested explicitly. + */ +public class AssetAdministrationShellApiSecurityTest extends AbstractAssetAdministrationShellApi { + + @Nested + @DisplayName("Authentication Tests") + class SecurityTests { + @Test + public void testWithoutAuthenticationTokenProvidedExpectUnauthorized() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, UUID.randomUUID()) + .accept(MediaType.APPLICATION_JSON) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isUnauthorized()); + } + + @Test + public void testWithAuthenticationTokenProvidedExpectUnauthorized() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, UUID.randomUUID()) + .accept(MediaType.APPLICATION_JSON) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isUnauthorized()); + } + + @Test + public void testWithInvalidAuthenticationTokenConfigurationExpectUnauthorized() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, UUID.randomUUID()) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.withoutResourceAccess()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, UUID.randomUUID()) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.withoutRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + } + + } + + @Nested + @DisplayName("Shell Authorization Test") + class ShellCrudTest { + String shellId; + + @BeforeEach + public void before() throws Exception{ + ObjectNode shell = createShell(); + performShellCreateRequest(toJson(shell)); + shellId = getId(shell); + } + + @Test + public void testRbacForGetAll() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SHELL_BASE_PATH) + .accept(MediaType.APPLICATION_JSON) + // test with wrong role + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(SHELL_BASE_PATH) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + @Test + public void testRbacForGetById() throws Exception { + // get shell by id + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId ) + .accept(MediaType.APPLICATION_JSON) + // test with wrong role + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId ) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + @Test + public void testRbacForCreate() throws Exception { + ObjectNode shellPayloadForPost = createShell(); + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(shellPayloadForPost)) + // test with wrong role + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH, toJson(shellPayloadForPost) ) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(shellPayloadForPost)) + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()); + } + + @Test + public void testRbacForUpdate() throws Exception { + ObjectNode shellPayloadForUpdate = createShell() + .put("identification", shellId); + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SHELL_BASE_PATH, shellId) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(shellPayloadForUpdate)) + // test with wrong role + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SHELL_BASE_PATH, shellId ) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(shellPayloadForUpdate)) + .with(jwtTokenFactory.updateTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + + } + + @Test + public void testRbacForDelete() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SHELL_BASE_PATH, shellId ) + // test with wrong role + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SHELL_BASE_PATH, shellId ) + // test with wrong role + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + } + + } + + @Nested + @DisplayName("Submodel Descriptor Authorization Test") + class SubmodelDescriptorCrudTests { + private String shellId; + private String submodelId; + + @BeforeEach + public void before() throws Exception{ + ObjectNode shell = createShell(); + performShellCreateRequest(toJson(shell)); + + ObjectNode submodel = createSubmodel("submodelIdPrefix"); + performSubmodelCreateRequest(toJson(submodel), getId(shell)); + + shellId = getId(shell); + submodelId = getId(submodel); + } + + + @Test + public void testRbacForGetAll() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SUB_MODEL_BASE_PATH, shellId ) + .accept(MediaType.APPLICATION_JSON) + // test with wrong role + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(SUB_MODEL_BASE_PATH, shellId ) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + @Test + public void testRbacForGetById() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId ) + .accept(MediaType.APPLICATION_JSON) + // test with wrong role + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId ) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + @Test + public void testRbacForCreate() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post(SUB_MODEL_BASE_PATH, shellId ) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(createSubmodel("exampleSubmodel"))) + // test with wrong role + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .post(SUB_MODEL_BASE_PATH, shellId) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(createSubmodel("exampleSubmodel"))) + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()); + } + + @Test + public void testRbacForUpdate() throws Exception { + ObjectNode submodelToUpdate = createSubmodel("1231") + .put("identification", submodelId); + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId ) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(submodelToUpdate)) + // test with wrong role + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(submodelToUpdate)) + .with(jwtTokenFactory.updateTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + } + + @Test + public void testRbacForDelete() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .contentType(MediaType.APPLICATION_JSON) + // test with wrong role + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + } + + } + + @Nested + @DisplayName("SpecificAssetIds Crud Test") + class SpecificAssetIdsCrudTest { + String shellId; + + @BeforeEach + public void before() throws Exception{ + ObjectNode shell = createShell(); + performShellCreateRequest(toJson(shell)); + shellId = getId(shell); + } + + @Test + public void testRbacForGet() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + @Test + public void testRbacForCreate() throws Exception { + ArrayNode specificAssetIds = emptyArrayNode() + .add(specificAssetId("key1", "value1")) + .add(specificAssetId("key2", "value2")); + + mvc.perform( + MockMvcRequestBuilders + .post(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(specificAssetIds)) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .post(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(specificAssetIds)) + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()); + } + + @Test + public void testRbacForDelete() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + } + } + + @Nested + @DisplayName("Lookup Authorization Test") + class LookupTest { + + @Test + public void testRbacForLookupByAssetIds() throws Exception { + ArrayNode specificAssetIds = emptyArrayNode().add(specificAssetId("abc", "123")); + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", toJson(specificAssetIds)) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", toJson(specificAssetIds)) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + } + + @Nested + @DisplayName("Custom AAS API Authorization Tests") + class CustomAASApiTest { + + @Test + public void testRbacCreateShellInBatch() throws Exception { + ObjectNode shell = createShell(); + ArrayNode batchShellBody = emptyArrayNode().add(shell); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/batch") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(batchShellBody)) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/batch") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(batchShellBody)) + .with(jwtTokenFactory.addTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()); + } + + @Test + public void testRbacForFindShellsWithAnyMatch() throws Exception { + JsonNode anyMatchLookupPayload = mapper.createObjectNode().set("query", mapper.createObjectNode() + .set("assetIds", emptyArrayNode().add(specificAssetId("abc", "123"))) + ); + mvc.perform( + MockMvcRequestBuilders + .post(LOOKUP_SHELL_BASE_PATH + "/query") + .content(toJson(anyMatchLookupPayload)) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .post(LOOKUP_SHELL_BASE_PATH + "/query") + .content(toJson(anyMatchLookupPayload)) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()); + } + + @Test + public void testRbacForFetchShellsByIds() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/fetch") + .content(toJson(emptyArrayNode())) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.deleteTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isForbidden()); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/fetch") + .content(toJson(emptyArrayNode())) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.readTwin()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items", hasSize(0))); + } + + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AssetAdministrationShellApiTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AssetAdministrationShellApiTest.java new file mode 100644 index 00000000..de3f15d1 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/AssetAdministrationShellApiTest.java @@ -0,0 +1,901 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; + +import java.util.UUID; + +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +public class AssetAdministrationShellApiTest extends AbstractAssetAdministrationShellApi { + + @Nested + @DisplayName("Shell CRUD API") + class ShellAPITests { + + + @Test + public void testCreateShellExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + + ObjectNode onlyRequiredFieldsShell = createBaseIdPayload("exampleId", "exampleShortId"); + performShellCreateRequest(toJson(onlyRequiredFieldsShell)); + } + + @Test + public void testCreateShellWithExistingIdExpectBadRequest() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(shellPayload)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error.message", is("An AssetAdministrationShell for the given identification does already exists."))); + } + + @Test + public void testGetShellExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(shellPayload))); + } + + @Test + public void testGetShellExpectNotFound() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, "NotExistingShellId") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testGetAllShellsExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + mvc.perform( + MockMvcRequestBuilders + .get(SHELL_BASE_PATH) + .queryParam("pageSize", "100") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items").exists()) + .andExpect(jsonPath("$.items[*].identification", hasItem(getId(shellPayload)))) + .andExpect(jsonPath("$.totalItems", is(greaterThan(0)))) + .andExpect(jsonPath("$.currentPage", is(0))) + .andExpect(jsonPath("$.totalPages", is(greaterThan(0)))) + .andExpect(jsonPath("$.itemCount", is(greaterThan(0)))); + } + + @Test + public void testUpdateShellExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + + ObjectNode updateDescription = shellPayload.deepCopy(); + updateDescription.set("description", emptyArrayNode() + .add(createDescription("fr", "exampleFrtext"))); + String shellId = updateDescription.get("identification").textValue(); + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(updateDescription)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(updateDescription))); + } + + + @Test + public void testUpdateShellExpectNotFound() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SHELL_BASE_PATH, "shellIdthatdoesnotexists") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(createShell())) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Shell for identifier shellIdthatdoesnotexists not found"))); + } + + @Test + public void testUpdateShellWithDifferentIdInPayloadExpectPathIdIsTaken() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + // assigning a new identification to an existing shell must not be possible in an update + ObjectNode updatedShell = shellPayload.deepCopy() + .put("identification", "newIdInUpdateRequest") + .put("idShort", "newIdShortInUpdateRequest"); + + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(updatedShell)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + + // verify that anything expect the identification can be updated + ObjectNode expectedShellAfterUpdate = updatedShell + .deepCopy() + .put("identification", shellId); + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(expectedShellAfterUpdate))); + } + + @Test + public void testDeleteShellExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + } + + @Test + public void testDeleteShellExpectNotFound() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + } + + /** + * It must be possible to create multiple specificAssetIds for the same key. + */ + @Test + public void testCreateShellWithSameSpecificAssetIdKeyButDifferentValuesExpectSuccess() throws Exception{ + ObjectNode shellPayload = createBaseIdPayload("example", "example"); + shellPayload.set("specificAssetIds", emptyArrayNode() + .add(specificAssetId("WMI", "1234123")) + .add(specificAssetId("WMI", "fug01")) + ); + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(shellPayload)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(content().json(toJson(shellPayload))); + } + } + + @Nested + @DisplayName("Shell SpecificAssetId CRUD API") + class SpecificAssetIdAPITests { + + @Test + public void testCreateSpecificAssetIdsExpectSuccess() throws Exception { + ObjectNode shellPayload = createBaseIdPayload("exampleShellId", "exampleIdShort"); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + ArrayNode specificAssetIds = emptyArrayNode() + .add(specificAssetId("key1", "value1")) + .add(specificAssetId("key2", "value2")); + + mvc.perform( + MockMvcRequestBuilders + .post(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(specificAssetIds)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(content().json(toJson(specificAssetIds))); + } + + + /** + * The API method for creation of specificAssetIds accepts an array of objects. + * Invoking the API removes all existing specificAssetIds and adds the new ones. + */ + @Test + public void testCreateSpecificAssetIdsReplacesAllExistingSpecificAssetIdsExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + ArrayNode specificAssetIds = emptyArrayNode() + .add(specificAssetId("key1", "value1")) + .add(specificAssetId("key2", "value2")); + + mvc.perform( + MockMvcRequestBuilders + .post(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(specificAssetIds)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(content().json(toJson(specificAssetIds))); + + // verify that the shell payload does no longer contain the initial specificAssetIds that were provided at creation time + ObjectNode expectedShellPayload = shellPayload.deepCopy().set("specificAssetIds", specificAssetIds); + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(expectedShellPayload))); + } + + @Test + public void testCreateSpecificIdsExpectNotFound() throws Exception { + ArrayNode specificAssetIds = emptyArrayNode() + .add(specificAssetId("key1", "value1")); + mvc.perform( + MockMvcRequestBuilders + .post(SINGLE_LOOKUP_SHELL_BASE_PATH, "notexistingshell") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(specificAssetIds)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Shell for identifier notexistingshell not found"))); + } + + @Test + public void testGetSpecificAssetIdsExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_LOOKUP_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(shellPayload.get("specificAssetIds")))); + } + + @Test + public void testGetSpecificIdsExpectNotFound() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_LOOKUP_SHELL_BASE_PATH, "notexistingshell", "notexistingsubmodel") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Shell for identifier notexistingshell not found"))); + } + } + + + @Nested + @DisplayName("Submodel CRUD API") + class SubmodelApiTest { + + @Test + public void testCreateSubmodelExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + ObjectNode submodel = createSubmodel(uuid("submodelExample")); + performSubmodelCreateRequest(toJson(submodel), shellId); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SHELL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.submodelDescriptors", hasSize(3))) + .andExpect(jsonPath("$.submodelDescriptors[*].identification", hasItem(getId(submodel)))); + } + + @Test + public void testCreateSubmodelWithExistingIdExpectBadRequest() throws Exception { + ObjectNode shellPayload1 = createShell(); + performShellCreateRequest(toJson(shellPayload1)); + + ObjectNode shellPayload2 = createShell(); + performShellCreateRequest(toJson(shellPayload2)); + + // assign submodel with existing id to shellPayload1 to ensure global uniqueness + String shellId = getId(shellPayload1); + JsonNode existingSubmodel = shellPayload2.get("submodelDescriptors").get(0); + mvc.perform( + MockMvcRequestBuilders + .post(SUB_MODEL_BASE_PATH, shellId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(existingSubmodel)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error.message", is("A SubmodelDescriptor with the given identification does already exists."))); + } + + @Test + public void testUpdateSubModelExpectSuccess() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + ObjectNode submodel = createSubmodel(uuid("submodelExample")); + performSubmodelCreateRequest(toJson(submodel), shellId); + String submodelId = getId(submodel); + + ObjectNode updatedSubmodel = submodel.deepCopy() + .put("idShort", "updatedSubmodelId").set("description", emptyArrayNode() + .add(createDescription("es", "spanish description"))); + + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(updatedSubmodel)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(updatedSubmodel))); + } + + @Test + public void testUpdateSubmodelExpectNotFound() throws Exception { + // verify shell is missing + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, "notexistingshell", "notexistingsubmodel") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Shell for identifier notexistingshell not found"))); + + + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + // verify submodel is missing + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, shellId, "notexistingsubmodel") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Submodel for identifier notexistingsubmodel not found."))); + } + + @Test + public void testUpdateSubmodelWithDifferentIdInPayloadExpectPathIdIsTaken() throws Exception { + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + ObjectNode submodel = createSubmodel(uuid("submodelExample")); + performSubmodelCreateRequest(toJson(submodel), shellId); + String submodelId = getId(submodel); + + // assigning a new identification to an existing submodel must not be possible in an update + ObjectNode updatedSubmodel = submodel.deepCopy() + .put("identification", "newIdInUpdateRequest") + .put("idShort", "newIdShortInUpdateRequest"); + + mvc.perform( + MockMvcRequestBuilders + .put(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(updatedSubmodel)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + + // verify that anything expect the identification can be updated + ObjectNode expectedShellAfterUpdate = updatedSubmodel + .deepCopy() + .put("identification", submodelId); + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(content().json(toJson(expectedShellAfterUpdate))); + } + + @Test + public void testDeleteSubmodelExpectSuccess() throws Exception { + + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + + ObjectNode submodel = createSubmodel(uuid("submodelExample")); + performSubmodelCreateRequest(toJson(submodel), shellId); + String submodelId = getId(submodel); + + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNoContent()); + + mvc.perform( + MockMvcRequestBuilders + .get(SINGLE_SUB_MODEL_BASE_PATH, shellId, submodelId) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()); + } + + @Test + public void testDeleteSubmodelExpectNotFound() throws Exception { + // verify shell is missing + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SUB_MODEL_BASE_PATH, "notexistingshell", "notexistingsubmodel") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Shell for identifier notexistingshell not found"))); + + + ObjectNode shellPayload = createShell(); + performShellCreateRequest(toJson(shellPayload)); + String shellId = getId(shellPayload); + // verify submodel is missing + mvc.perform( + MockMvcRequestBuilders + .delete(SINGLE_SUB_MODEL_BASE_PATH, shellId, "notexistingsubmodel") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.error.message", is("Submodel for identifier notexistingsubmodel not found."))); + } + } + + @Nested + @DisplayName("Shell Lookup Query API") + class ShellLookupQueryAPI { + + @Test + public void testLookUpApiWithInvalidQueryParameterExpectFailure() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", "{ invalid }") + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.error.message", is("The provided parameters are invalid. assetIds={ invalid }"))); + } + + @Test + public void testLookUpApiWithSwaggerUIEscapedQueryParameterExpectSuccess() throws Exception { + String swaggerUIEscapedAssetIds = "[\"{\\n \\\"key\\\": \\\"brakenumber\\\",\\n \\\"value\\\": \\\"123f092\\\"\\n}\",{\"key\":\"globalAssetId\",\"value\":\"12397f2kf97df\"}]"; + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", swaggerUIEscapedAssetIds) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$" ).isArray()); + } + + @Test + public void testLookUpApiWithMultiParamIds() throws Exception { + String assetId1 = "{\"key\": \"brakenumber\",\"value\": \"123f092\"}"; + String assetId2 = "{\"key\":\"globalAssetId\",\"value\":\"12397f2kf97df\"}"; + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", assetId1) + .queryParam("assetIds", assetId2) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$" ).isArray()); + } + + @Test + public void testFindExternalShellIdsBySpecificAssetIdsExpectSuccess() throws Exception { + // the keyPrefix ensures that this test can run against a persistent database multiple times + String keyPrefix = UUID.randomUUID().toString(); + ObjectNode commonAssetId = specificAssetId(keyPrefix + "commonAssetIdKey", "commonAssetIdValue"); + // first shell + ObjectNode firstShellPayload = createBaseIdPayload("sampleForQuery", "idShortSampleForQuery"); + firstShellPayload.set("specificAssetIds", emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2", "value_2")) + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2_1", "value_2_1")) + .add(commonAssetId)); + performShellCreateRequest(toJson(firstShellPayload)); + + // second shell + ObjectNode secondShellPayload = createBaseIdPayload("sampleForQuery", "idShortSampleForQuery"); + secondShellPayload.set("specificAssetIds", emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_3", "value_3")) + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_3_1", "value_3_1")) + .add(commonAssetId)); + performShellCreateRequest(toJson(secondShellPayload)); + + // Test first shell match with all specific assetIds + ArrayNode allSpecificAssetIdsForFirstShell = emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2", "value_2")) + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2_1", "value_2_1")) + .add(commonAssetId); + + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", toJson(allSpecificAssetIdsForFirstShell)) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + // ensure that only three results match + .andExpect(jsonPath("$", contains(getId(firstShellPayload)))); + + // Test first shell match with single assetId + ArrayNode oneAssetIdForFirstShell = emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2", "value_2")); + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", toJson(oneAssetIdForFirstShell)) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + // ensure that only three results match + .andExpect(jsonPath("$", contains(getId(firstShellPayload)))); + + // Test first and second shell match with common asssetId + ArrayNode commonAssetIdBothShells = emptyArrayNode() + .add(commonAssetId); + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", toJson(commonAssetIdBothShells)) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + // ensure that only three results match + .andExpect(jsonPath("$", containsInAnyOrder(getId(firstShellPayload), getId(secondShellPayload)))); + } + + @Test + public void testFindExternalShellIdByGlobalAssetIdExpectSuccess() throws Exception { + ObjectNode shellPayload = createBaseIdPayload("sampleForQuery", "idShortSampleForQuery"); + + String globalAssetId = UUID.randomUUID().toString(); + shellPayload.set("globalAssetId", createGlobalAssetId(globalAssetId)); + performShellCreateRequest(toJson(shellPayload)); + + // for lookup global asset id is handled as specificAssetIds + ArrayNode globalAssetIdForSampleQuery = emptyArrayNode().add( + specificAssetId("globalAssetId", globalAssetId) + ); + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .queryParam("assetIds", toJson(globalAssetIdForSampleQuery)) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + // ensure that only three results match + .andExpect(jsonPath("$", contains(getId(shellPayload)))); + } + + @Test + public void testFindExternalShellIdsWithoutProvidingQueryParametersExpectEmptyResult() throws Exception { + // prepare the data set + mvc.perform( + MockMvcRequestBuilders + .get(LOOKUP_SHELL_BASE_PATH) + .accept(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(0))); + } + } + + @Nested + @DisplayName("Custom AAS API Tests") + class CustomAASApiTest { + + @Test + public void testCreateShellInBatchWithOneDuplicateExpectSuccess() throws Exception { + ObjectNode shell = createShell(); + + JsonNode identification = shell.get("identification"); + ArrayNode batchShellBody = emptyArrayNode().add(shell).add(createShell() + // create duplicate + .set("identification", identification)); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/batch") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(batchShellBody)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$[0].message", equalTo("AssetAdministrationShell successfully created."))) + .andExpect(jsonPath("$[0].identification", equalTo(identification.textValue()))) + .andExpect(jsonPath("$[0].status", equalTo(200))) + .andExpect(jsonPath("$[1].message", equalTo("An AssetAdministrationShell for the given identification does already exists."))) + .andExpect(jsonPath("$[1].identification", equalTo(identification.textValue()))) + .andExpect(jsonPath("$[1].status", equalTo(400))); + } + + @Test + public void testCreateShellInBatchExpectSuccess() throws Exception { + ArrayNode batchShellBody = emptyArrayNode().add(createShell()) + .add(createShell()) + .add(createShell()) + .add(createShell()) + .add(createShell()); + + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/batch") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(toJson(batchShellBody)) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$", hasSize(5))); + } + + @Test + public void testFindExternalShellIdsBySpecificAssetIdsWithAnyMatchExpectSuccess() throws Exception { + // the keyPrefix ensures that this test can run against a persistent database multiple times + String keyPrefix = UUID.randomUUID().toString(); + ObjectNode commonAssetId = specificAssetId(keyPrefix + "commonAssetIdKey", "commonAssetIdValue"); + // first shell + ObjectNode firstShellPayload = createBaseIdPayload("sampleForQuery", "idShortSampleForQuery"); + firstShellPayload.set("specificAssetIds", emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_1", "value_1"))); + performShellCreateRequest(toJson(firstShellPayload)); + + // second shell + ObjectNode secondShellPayload = createBaseIdPayload("sampleForQuery", "idShortSampleForQuery"); + secondShellPayload.set("specificAssetIds", emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2", "value_2"))); + performShellCreateRequest(toJson(secondShellPayload)); + + // query to retrieve any match + JsonNode anyMatchAueryByAssetIds = mapper.createObjectNode().set("query", mapper.createObjectNode() + .set("assetIds", emptyArrayNode() + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_1", "value_1")) + .add(specificAssetId(keyPrefix + "findExternalShellIdQueryKey_2", "value_2")) + .add(commonAssetId)) + ); + + mvc.perform( + MockMvcRequestBuilders + .post(LOOKUP_SHELL_BASE_PATH + "/query") + .content(toJson(anyMatchAueryByAssetIds)) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(2))) + .andExpect(jsonPath("$", containsInAnyOrder(getId(firstShellPayload), getId(secondShellPayload)))); + } + + @Test + public void testFetchShellsByNoIdentificationsExpectEmptyResult() throws Exception { + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/fetch") + .content(toJson(emptyArrayNode())) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items", hasSize(0))); + } + + @Test + public void testFetchShellsByMultipleIdentificationsExpectSuccessExpectSuccess() throws Exception { + + ObjectNode shellPayload1 = createShell(); + performShellCreateRequest(toJson(shellPayload1)); + + ObjectNode shellPayload2 = createShell(); + performShellCreateRequest(toJson(shellPayload2)); + + ArrayNode fetchOneShellsById = emptyArrayNode().add(getId(shellPayload1)); + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/fetch") + .content(toJson(fetchOneShellsById)) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items", hasSize(1))) + // ensure that only three results match + .andExpect(jsonPath("$.items[*].identification", hasItem(getId(shellPayload1)))); + + + ArrayNode fetchTwoShellsById = emptyArrayNode() + .add(getId(shellPayload1)) + .add(getId(shellPayload2)); + mvc.perform( + MockMvcRequestBuilders + .post(SHELL_BASE_PATH + "/fetch") + .content(toJson(fetchTwoShellsById)) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles()) + ) + .andDo(MockMvcResultHandlers.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items", hasSize(2))) + // ensure that only three results match + .andExpect(jsonPath("$.items[*].identification", + hasItems(getId(shellPayload1), getId(shellPayload2)) )); + } + } + +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/HealthCheckTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/HealthCheckTest.java new file mode 100644 index 00000000..74c32ace --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/HealthCheckTest.java @@ -0,0 +1,52 @@ +package org.eclipse.tractusx.semantics.registry; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +@SpringBootTest +@AutoConfigureMockMvc +public class HealthCheckTest { + private static final String HEALTH_ENDPOINT = "/actuator/health"; + private static final String LIVENESS_ENDPOINT = HEALTH_ENDPOINT + "/liveness"; + private static final String READINESS_ENDPOINT = HEALTH_ENDPOINT + "/readiness"; + + @Autowired + private MockMvc mvc; + + @Test + public void testHealthEndpoint() throws Exception { + mvc.perform(MockMvcRequestBuilders.get(HEALTH_ENDPOINT)) + .andExpect(jsonPath("$.status", is("UP"))) + .andExpect(status().is2xxSuccessful()); + } + + @Test + public void testLivenessEndpoint() throws Exception { + mvc.perform(MockMvcRequestBuilders.get(LIVENESS_ENDPOINT)) + .andExpect(jsonPath("$.status", is("UP"))) + .andExpect(status().is2xxSuccessful()); + } + + @Test + public void testReadinessEndpoint() throws Exception { + mvc.perform(MockMvcRequestBuilders.get(READINESS_ENDPOINT)) + .andExpect(jsonPath("$.status", is("UP"))) + .andExpect(status().is2xxSuccessful()); + } + + @Test + public void testInfoEndpoint() throws Exception { + mvc.perform(MockMvcRequestBuilders.get("/actuator/info")) + .andExpect(jsonPath("$.git.commit.id", notNullValue())) + .andExpect(status().is2xxSuccessful()); + } +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java new file mode 100644 index 00000000..d795e4c8 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/JwtTokenFactory.java @@ -0,0 +1,108 @@ +/******************************************************************************** + * 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 + ********************************************************************************/ +package org.eclipse.tractusx.semantics.registry; + +import com.nimbusds.jose.shaded.json.JSONArray; +import org.eclipse.tractusx.semantics.AuthorizationEvaluator; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.test.web.servlet.request.RequestPostProcessor; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; + +public class JwtTokenFactory { + + private String publicClientId; + + public JwtTokenFactory(String publicClientId){ + this.publicClientId = publicClientId; + } + + private RequestPostProcessor authenticationWithRoles(String ... roles){ + Jwt jwt = Jwt.withTokenValue("token") + .header("alg", "none") + .claim("sub", "user") + .claim("resource_access", Map.of(publicClientId, Map.of("roles", toJsonArray(roles) ))) + .build(); + Collection authorities = Collections.emptyList(); + return authentication(new JwtAuthenticationToken(jwt, authorities)); + } + + private static JSONArray toJsonArray(String ... elements){ + JSONArray jsonArray = new JSONArray(); + for (String element : elements){ + jsonArray.appendElement(element); + } + return jsonArray; + } + + + + public RequestPostProcessor allRoles(){ + return authenticationWithRoles( + AuthorizationEvaluator.Roles.ROLE_VIEW_DIGITAL_TWIN, + AuthorizationEvaluator.Roles.ROLE_ADD_DIGITAL_TWIN, + AuthorizationEvaluator.Roles.ROLE_UPDATE_DIGITAL_TWIN, + AuthorizationEvaluator.Roles.ROLE_DELETE_DIGITAL_TWIN + ); + } + + public RequestPostProcessor readTwin(){ + return authenticationWithRoles(AuthorizationEvaluator.Roles.ROLE_VIEW_DIGITAL_TWIN); + } + + public RequestPostProcessor addTwin(){ + return authenticationWithRoles(AuthorizationEvaluator.Roles.ROLE_ADD_DIGITAL_TWIN); + } + + public RequestPostProcessor updateTwin(){ + return authenticationWithRoles(AuthorizationEvaluator.Roles.ROLE_UPDATE_DIGITAL_TWIN); + } + + public RequestPostProcessor deleteTwin(){ + return authenticationWithRoles(AuthorizationEvaluator.Roles.ROLE_DELETE_DIGITAL_TWIN); + } + + public RequestPostProcessor withoutResourceAccess(){ + Jwt jwt = Jwt.withTokenValue("token") + .header("alg", "none") + .claim("sub", "user") + .build(); + Collection authorities = Collections.emptyList(); + return authentication(new JwtAuthenticationToken(jwt, authorities)); + } + + public RequestPostProcessor withoutRoles(){ + Jwt jwt = Jwt.withTokenValue("token") + .header("alg", "none") + .claim("sub", "user") + .claim("resource_access", Map.of(publicClientId, new HashMap())) + .build(); + Collection authorities = Collections.emptyList(); + return authentication(new JwtAuthenticationToken(jwt, authorities)); + } + +} diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapperTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapperTest.java new file mode 100644 index 00000000..0d618683 --- /dev/null +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/registry/mapper/ShellMapperTest.java @@ -0,0 +1,231 @@ +package org.eclipse.tractusx.semantics.registry.mapper; + +import org.eclipse.tractusx.semantics.aas.registry.model.*; +import org.eclipse.tractusx.semantics.registry.model.*; +import org.assertj.core.groups.Tuple; +import org.eclipse.tractusx.semantics.registry.model.Shell; +import org.eclipse.tractusx.semantics.registry.model.ShellDescription; +import org.eclipse.tractusx.semantics.registry.model.ShellIdentifier; +import org.eclipse.tractusx.semantics.registry.model.Submodel; +import org.eclipse.tractusx.semantics.registry.model.SubmodelDescription; +import org.eclipse.tractusx.semantics.registry.model.SubmodelEndpoint; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.tuple; + +public class ShellMapperTest { + private final ShellMapper mapper = new ShellMapperImpl(new SubmodelMapperImpl()); + + @Test + public void testMapFromApiExpectSuccess() { + AssetAdministrationShellDescriptor aas = createCompleteAasDescriptor(); + + Shell shell = mapper.fromApiDto(aas); + assertThat(shell.getIdExternal()).isEqualTo(aas.getIdentification()); + assertThat(shell.getIdShort()).isEqualTo(aas.getIdShort()); + + List expectedIdentifiers = new ArrayList<>(List.of(toIdentifierTuples(aas.getSpecificAssetIds()))); + expectedIdentifiers.add(tuple( ShellIdentifier.GLOBAL_ASSET_ID_KEY, aas.getGlobalAssetId().getValue().get(0))); + assertThat(shell.getIdentifiers()) + .extracting("key", "value") + .containsExactlyInAnyOrder(expectedIdentifiers.toArray(new Tuple[0])); + + assertThat(shell.getDescriptions()) + .extracting("language", "text") + .contains(toDescriptionTuples(aas.getDescription())); + + + assertThat(shell.getSubmodels()).hasSize(1); + + SubmodelDescriptor submodelDescriptor = aas.getSubmodelDescriptors().stream().findFirst().get(); + Endpoint endpoint = submodelDescriptor.getEndpoints().stream().findFirst().get(); + String semanticId = submodelDescriptor.getSemanticId().getValue().stream().findFirst().get(); + ProtocolInformation protocolInformation = endpoint.getProtocolInformation(); + + Submodel submodel = shell.getSubmodels().stream().findFirst().get(); + SubmodelEndpoint submodelEndpoint = submodel.getEndpoints().stream().findFirst().get(); + + + assertThat(submodel.getIdExternal()).isEqualTo(submodelDescriptor.getIdentification()); + assertThat(submodel.getIdShort()).isEqualTo(submodelDescriptor.getIdShort()); + assertThat(submodel.getSemanticId()).isEqualTo(semanticId); + + assertThat(submodelDescriptor.getSemanticId().getValue().stream().findFirst().get()).isEqualTo(submodel.getSemanticId()); + + assertThat(submodelEndpoint.getInterfaceName()).isEqualTo(endpoint.getInterface()); + + + assertThat(submodelEndpoint.getInterfaceName()).isEqualTo(endpoint.getInterface()); + assertThat(submodelEndpoint.getEndpointProtocol()).isEqualTo(protocolInformation.getEndpointProtocol()); + assertThat(submodelEndpoint.getEndpointProtocolVersion()).isEqualTo(protocolInformation.getEndpointProtocolVersion()); + assertThat(submodelEndpoint.getSubProtocol()).isEqualTo(protocolInformation.getSubprotocol()); + assertThat(submodelEndpoint.getSubProtocolBody()).isEqualTo(protocolInformation.getSubprotocolBody()); + assertThat(submodelEndpoint.getSubProtocolBodyEncoding()).isEqualTo(protocolInformation.getSubprotocolBodyEncoding()); + } + + @Test + public void testMapToApiExpectSuccess() { + Shell shell = createCompleteShell(); + AssetAdministrationShellDescriptor aas = mapper.toApiDto(shell); + assertThat(aas.getIdentification()).isEqualTo(shell.getIdExternal()); + assertThat(aas.getIdShort()).isEqualTo(shell.getIdShort()); + + String expectedGlobalAssetId = shell.getIdentifiers().stream() + .filter(shellIdentifier -> ShellIdentifier.GLOBAL_ASSET_ID_KEY.equals(shellIdentifier.getKey())) + .map(ShellIdentifier::getValue).findFirst().get(); + assertThat(aas.getGlobalAssetId().getValue().get(0)).isEqualTo(expectedGlobalAssetId); + + Set expectedIdentifiersSet = shell.getIdentifiers().stream() + .filter(shellIdentifier -> !ShellIdentifier.GLOBAL_ASSET_ID_KEY.equals(shellIdentifier.getKey())) + .collect(Collectors.toSet()); + + assertThat(aas.getSpecificAssetIds()) + .extracting("key", "value") + .containsExactly(createTuplesForShellIdentifier(expectedIdentifiersSet)); + + assertThat(aas.getDescription()) + .extracting("language", "text") + .contains(createTuplesForShellDescriptionTuples(shell.getDescriptions())); + + assertThat(aas.getSubmodelDescriptors()).hasSize(1); + SubmodelDescriptor apiSubmodelDescriptor = aas.getSubmodelDescriptors().get(0); + + // submodel mappings + Submodel submodel = shell.getSubmodels().stream().findFirst().get(); + SubmodelEndpoint submodelEndpoint = submodel.getEndpoints().stream().findFirst().get(); + assertThat(apiSubmodelDescriptor.getIdentification()).isEqualTo(submodel.getIdExternal()); + assertThat(apiSubmodelDescriptor.getIdShort()).isEqualTo(submodel.getIdShort()); + + assertThat(apiSubmodelDescriptor.getDescription()) + .extracting("language", "text") + .contains(createTuplesForSubmodelDescriptionTuples(submodel.getDescriptions())); + + assertThat(apiSubmodelDescriptor.getEndpoints()).hasSize(1); + Endpoint apiSubmodelEndpoint = apiSubmodelDescriptor.getEndpoints().stream().findFirst().get(); + + ProtocolInformation apiProtocolInformation = apiSubmodelEndpoint.getProtocolInformation(); + assertThat(apiSubmodelEndpoint.getInterface()).isEqualTo(submodelEndpoint.getInterfaceName()); + assertThat(apiProtocolInformation.getEndpointProtocol()).isEqualTo(submodelEndpoint.getEndpointProtocol()); + assertThat(apiProtocolInformation.getEndpointProtocolVersion()).isEqualTo(submodelEndpoint.getEndpointProtocolVersion()); + assertThat(apiProtocolInformation.getSubprotocol()).isEqualTo(submodelEndpoint.getSubProtocol()); + assertThat(apiProtocolInformation.getSubprotocolBody()).isEqualTo(submodelEndpoint.getSubProtocolBody()); + assertThat(apiProtocolInformation.getSubprotocolBodyEncoding()).isEqualTo(submodelEndpoint.getSubProtocolBodyEncoding()); + } + + private Shell createCompleteShell() { + ShellIdentifier shellIdentifier1 = new ShellIdentifier(UUID.randomUUID(), "key1", "value1", null); + ShellIdentifier shellIdentifier2 = new ShellIdentifier(UUID.randomUUID(), "key1", "value1", null); + ShellIdentifier shellIdentifier3 = new ShellIdentifier(UUID.randomUUID(), ShellIdentifier.GLOBAL_ASSET_ID_KEY, "exampleGlobalAssetId", null); + Set shellIdentifiers = Set.of(shellIdentifier1, shellIdentifier2, shellIdentifier3); + + ShellDescription shellDescription1 = new ShellDescription(UUID.randomUUID(), "en", "example description1"); + ShellDescription shellDescription2 = new ShellDescription(UUID.randomUUID(), "de", "exampleDescription2"); + + Set shellDescriptions = Set.of(shellDescription1, shellDescription2); + + + Submodel submodel = new Submodel(UUID.randomUUID(), + "submodelIdExternal", + "submodelIdShort", "submodelSemanticId", + Set.of(new SubmodelDescription(UUID.randomUUID(), "en", "example submodel description")), + Set.of(new SubmodelEndpoint(UUID.randomUUID(), "interfaceExample", + "endpointAddressExample", "endpointProtocolExample", + "endpointProtocolVersionExample", "subProtocolExample" + , "subProtocolBodyExample", "subProtocolEncodingExample" + )), + null + ); + + return new Shell(UUID.randomUUID(), "idExternalExample", "idShortExample", + shellIdentifiers, shellDescriptions, Set.of(submodel), null, null); + } + + + private AssetAdministrationShellDescriptor createCompleteAasDescriptor() { + AssetAdministrationShellDescriptor aas = new AssetAdministrationShellDescriptor(); + aas.setIdentification("identificationExample"); + aas.setIdShort("idShortExample"); + + Reference globalAssetId = new Reference(); + globalAssetId.setValue(List.of("globalAssetIdExample")); + aas.setGlobalAssetId(globalAssetId); + + IdentifierKeyValuePair identifier1 = new IdentifierKeyValuePair(); + identifier1.setKey("identifier1KeyExample"); + identifier1.setValue("identifier1ValueExample"); + + IdentifierKeyValuePair identifier2 = new IdentifierKeyValuePair(); + identifier2.setKey("identifier2KeyExample"); + identifier2.setValue("identifier2ValueExample"); + aas.setSpecificAssetIds(List.of(identifier1, identifier2)); + + LangString description1 = new LangString(); + description1.setLanguage("de"); + description1.setText("this is an example description1"); + + LangString description2 = new LangString(); + description2.setLanguage("en"); + description2.setText("this is an example for description2"); + aas.setDescription(List.of(description1, description2)); + + + ProtocolInformation protocolInformation = new ProtocolInformation(); + protocolInformation.setEndpointProtocol("endpointProtocolExample"); + protocolInformation.setEndpointAddress("endpointAddressExample"); + protocolInformation.setEndpointProtocolVersion("endpointProtocolVersionExample"); + protocolInformation.setSubprotocol("subprotocolExample"); + protocolInformation.setSubprotocolBody("subprotocolBodyExample"); + protocolInformation.setSubprotocolBodyEncoding("subprotocolBodyExample"); + Endpoint endpoint = new Endpoint(); + endpoint.setInterface("interfaceNameExample"); + endpoint.setProtocolInformation(protocolInformation); + + Reference reference = new Reference(); + reference.setValue(List.of("semanticIdExample")); + SubmodelDescriptor submodelDescriptor = new SubmodelDescriptor(); + submodelDescriptor.setIdentification("identificationExample"); + submodelDescriptor.setIdShort("idShortExample"); + submodelDescriptor.setSemanticId(reference); + submodelDescriptor.setDescription(List.of(description1, description2)); + submodelDescriptor.setEndpoints(List.of(endpoint)); + aas.setSubmodelDescriptors(List.of(submodelDescriptor)); + return aas; + } + + private Tuple[] createTuplesForShellIdentifier(Set identifiers) { + return identifiers.stream() + .map(identifier -> tuple(identifier.getKey(), identifier.getValue())) + .toArray(Tuple[]::new); + } + + private Tuple[] createTuplesForShellDescriptionTuples(Set descriptions) { + return descriptions.stream() + .map(description -> tuple(description.getLanguage(), description.getText())) + .toArray(Tuple[]::new); + } + + private Tuple[] createTuplesForSubmodelDescriptionTuples(Set descriptions) { + return descriptions.stream() + .map(description -> tuple(description.getLanguage(), description.getText())) + .toArray(Tuple[]::new); + } + + private Tuple[] toIdentifierTuples(List identifiers) { + return identifiers.stream() + .map(identifier -> tuple(identifier.getKey(), identifier.getValue())) + .toArray(Tuple[]::new); + } + + private Tuple[] toDescriptionTuples(List descriptions) { + return descriptions.stream() + .map(description -> tuple(description.getLanguage(), description.getText())) + .toArray(Tuple[]::new); + } +} diff --git a/backend/src/test/resources/application-test.yml b/backend/src/test/resources/application-test.yml new file mode 100644 index 00000000..1c17d7fc --- /dev/null +++ b/backend/src/test/resources/application-test.yml @@ -0,0 +1,30 @@ +############################################################### +# Copyright (c) 2021-2022 T-Systems International 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 +############################################################### + +spring: + security: + oauth2: + resourceserver: + jwt: + issuer-uri: + + datasource: + driverClassName: org.h2.Driver + url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;CASE_INSENSITIVE_IDENTIFIERS=TRUE diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties new file mode 100644 index 00000000..ec846693 --- /dev/null +++ b/backend/src/test/resources/application.properties @@ -0,0 +1,25 @@ +############################################################### +# 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 +############################################################### + +# Active profiles can only be set in non-profile specific files. +# To avoid the activation of the profile in each test, we activate the test profile here. +# The file must be named application.properties file. For whatever reason application.yml does not work. + +spring.profiles.active=test diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..7057668f --- /dev/null +++ b/pom.xml @@ -0,0 +1,503 @@ + + + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.13 + + + + org.eclipse.tractusx + digital-twin-registry + 1.3.0-SNAPSHOT + Tractus-X Semantic Layer Digital Twin Registry + Root Module of the Tractus-X Semantic Layer Digital Twin Registry + pom + + + ${organization} + ${url} + + + + + ${licence_name} + ${licence_url} + ${licence_distribution} + ${licence_comments} + + + + + + Tractus-X project + https://projects.eclipse.org/projects/automotive.tractusx + tractusx-dev@eclipse.org + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + An Eclipse Project + + 11 + 3.3.9 + + + + 2.5.13 + 2020.0.3 + 5.3.12 + 3.0.5 + 1.6.6 + 2.9.2 + 4.4 + 1.18.22 + 1.3.2 + 1.5.20 + 2.0.0 + 31.0.1-jre + 0.10.3 + 2.11.0 + 3.0.2 + + + 1.7.32 + 1.2.11 + + + 4.5.13 + 11.7 + 2.1.0 + 4.0.3 + 0.0.1-SNAPSHOT + 4.0.1 + + + 2.13.1 + 20211205 + + 1.0.2 + + + 1.0.0 + 1.0.2 + 4.2.0 + 1.3.1 + + + 1.4.2.Final + 0.2.0 + 42.2.25 + 2.1.210 + 4.9.1 + + + 3.18.1 + 5.6.3 + + + 3.8.1 + + + + backend + + + + + + + + + javax.annotation + javax.annotation-api + ${javax-annotation-api.version} + + + org.projectlombok + lombok + ${lombok.version} + + + com.google.guava + guava + ${guava.version} + + + io.vavr + vavr + ${vavr.version} + + + commons-io + commons-io + ${commons-io.version} + + + javax.servlet + javax.servlet-api + ${javax.servlet} + + + com.google.code.findbugs + jsr305 + ${google.findbugs.version} + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-logging + + + org.springframework.boot + spring-boot-starter-tomcat + + + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + test + + + org.springframework.boot + spring-boot-starter-logging + + + + + + + org.slf4j + slf4j-simple + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + ${spring.feign.version} + + + org.springframework + spring-test + ${spring.version} + test + + + org.springframework.boot + spring-boot-starter-data-jpa + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework + spring-webmvc + ${spring.version} + + + + + org.springdoc + springdoc-openapi-ui + ${springdoc.version} + + + io.springfox + springfox-swagger2 + ${springfox.version} + + + io.springfox + springfox-swagger-ui + ${springfox.version} + runtime + + + + + io.swagger.core.v3 + swagger-annotations + ${swagger-core-version} + + + io.swagger + swagger-annotations + ${swagger-annotations.version} + + + org.openapitools + jackson-databind-nullable + 0.1.0 + + + io.github.openfeign + feign-core + ${feign-version} + + + io.github.openfeign + feign-jackson + ${feign-version} + + + io.github.openfeign + feign-slf4j + ${feign-version} + + + io.github.openfeign.form + feign-form + ${feign-form-version} + + + org.apache.httpcomponents + httpclient + ${httpcomponents.version} + + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + ${jackson.version} + + + + + org.apache.oltu.oauth2 + org.apache.oltu.oauth2.client + ${oltu-version} + + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + org.liquibase + liquibase-core + ${liquibase.version} + + + + + org.postgresql + postgresql + ${postgresql.version} + + + com.h2database + h2 + ${h2.version} + + + + + io.openmanufacturing + sds-aspect-meta-model + ${bamm.version} + + + io.openmanufacturing + sds-aspect-model-starter + ${bamm.sdk.version} + + + org.apache.jena + jena-core + ${jena.version} + + + org.apache.jena + jena-arq + ${jena.version} + + + org.apache.jena + jena-fuseki-main + ${jena.version} + + + org.apache.jena + jena-querybuilder + ${jena.version} + + + org.topbraid + shacl + ${shacl.version} + + + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + ${java.version} + ${java.version} + + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + org.projectlombok + lombok-mapstruct-binding + ${mapstruct.lombok.version} + + + + + + io.github.git-commit-id + git-commit-id-maven-plugin + 5.0.0 + + + get-the-git-infos + + revision + + initialize + + + + true + ${project.build.outputDirectory}/git.properties + + ^git.build.(time|version)$ + ^git.commit.id.(abbrev|full)$ + + full + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + org.asciidoctor + asciidoctorj + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + licence_type + licence_url + licence_distribution + ${project.build.directory} + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + org.openapitools + openapi-generator-maven-plugin + 5.4.0 + + + + + + + + Maven Central + maven-central + https://repo1.maven.org/maven2/ + + + ids-fraunhofer + ids fraunhofer repository + https://maven.iais.fraunhofer.de/artifactory/eis-ids-public/ + + +