From 2d92d9fa82d0564b4f10f9c88b6c51b9535f55ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 9 Aug 2022 12:49:12 +0200 Subject: [PATCH 01/43] Implement urn filter endpoint The changes of this commit add a new endpoint to the Semantic Hub that allows to filter for a list of URNs. Only these model entries will then be returned as part of the request. Pagination is included. --- .../semantics/hub/AspectModelService.java | 15 +++ .../hub/persistence/PersistenceLayer.java | 4 + .../triplestore/SparqlQueries.java | 95 +++++++++++++++++++ .../triplestore/TripleStorePersistence.java | 37 ++++++++ .../static/semantic-hub-openapi.yaml | 43 +++++++++ 5 files changed, 194 insertions(+) diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/AspectModelService.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/AspectModelService.java index c6c28f78..ec5f20c4 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/AspectModelService.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/AspectModelService.java @@ -6,6 +6,7 @@ import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.util.List; import org.eclipse.tractusx.semantics.hub.domain.ModelPackageStatus; import org.eclipse.tractusx.semantics.hub.domain.ModelPackageUrn; @@ -20,6 +21,7 @@ import org.springframework.http.ResponseEntity; import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Lists; import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel; import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn; @@ -169,6 +171,19 @@ public ResponseEntity getAasSubmodelTemplate(String urn, AasFormat aasFormat) { return new ResponseEntity( result.get(), responseHeaders, HttpStatus.OK ); } + @Override + public ResponseEntity getModelListByUrns(Integer pageSize, Integer page, List requestBody) { + List urnList = Lists.transform(requestBody, (String urn) -> AspectModelUrn.fromUrn(urn)); + + final SemanticModelList models = persistenceLayer.findModelListByUrns( urnList, page, pageSize ); + + if ( models == null ) { + return new ResponseEntity<>( HttpStatus.NOT_FOUND ); + } + + return new ResponseEntity( models, HttpStatus.OK ); + } + private Aspect getBamAspect( String urn ) { final Try aspect = bammHelper.getAspectFromVersionedModel( getVersionedModel( urn ) ); if ( aspect.isFailure() ) { diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/PersistenceLayer.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/PersistenceLayer.java index fd1a4268..483fd0ba 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/PersistenceLayer.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/PersistenceLayer.java @@ -20,6 +20,8 @@ package org.eclipse.tractusx.semantics.hub.persistence; +import java.util.List; + import javax.annotation.Nullable; import org.eclipse.tractusx.semantics.hub.domain.ModelPackageStatus; @@ -59,4 +61,6 @@ SemanticModelList getModels(String namespaceFilter, String nameFilter, void deleteModelsPackage( ModelPackageUrn urn ); boolean echo(); + + public SemanticModelList findModelListByUrns(List urns, int page, int pageSize); } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java index a935ed30..8dc5231c 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java @@ -21,11 +21,31 @@ import org.eclipse.tractusx.semantics.hub.domain.ModelPackageStatus; import org.eclipse.tractusx.semantics.hub.domain.ModelPackageUrn; + +import com.google.common.collect.Lists; + +import ch.qos.logback.core.pattern.LiteralConverter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + import org.apache.commons.lang3.StringUtils; +import org.apache.jena.datatypes.BaseDatatype; +import org.apache.jena.datatypes.RDFDatatype; +import org.apache.jena.datatypes.xsd.impl.XSDPlainType; +import org.apache.jena.graph.impl.AdhocDatatype; import org.apache.jena.query.ParameterizedSparqlString; import org.apache.jena.query.Query; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.RDFList; +import org.apache.jena.rdf.model.RDFNode; import org.apache.jena.rdf.model.ResourceFactory; +import org.apache.jena.rdf.model.impl.RDFListImpl; +import org.apache.jena.reasoner.rulesys.FunctorDatatype; import org.apache.jena.update.UpdateRequest; import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn; @@ -90,6 +110,23 @@ public class SparqlQueries { + " && (str(?s) = ?packageUrn) )\n" + " }"; + private static final String FIND_BY_MULTIPLE_URNS_QUERY = + "SELECT DISTINCT ?aspect (?status as ?statusResult)\n" + + "WHERE\n" + + " {\n" + + " VALUES (?urns) { ?urnParamList } \n" + + " VALUES (?packageUrns) { ?packageUrnParamList } \n" + + " bind( $bammAspectUrnParam as ?bammAspectUrn )\n" + + " ?aspect a ?bammAspect .\n" + + " ?s aux:status ?status .\n" + + " FILTER ( regex(str(?bammAspect), ?bammAspectUrn, \"\") \n" + + " && ( str(?aspect) IN (?urns) ) \n" + + " && ( str(?s) IN (?packageUrns)) ) \n" + + " }" + + "ORDER BY lcase(str(?aspect))\n" + + "OFFSET $offsetParam\n" + + "LIMIT $limitParam"; + private static final String FIND_BY_PACKAGE_URN_QUERY = "SELECT (?status as ?statusResult)\n" + "WHERE\n" @@ -154,6 +191,22 @@ public class SparqlQueries { + "FILTER ( !bound(?namespaceFilter) || contains(str(?aspect), ?namespaceFilter ) )\n" + "}\n"; + private static final String FILTER_QUERY_MINIMAL_WHERE_CLAUSE_SELECTIVE = "WHERE {\n" + + "BIND($bammAspectUrnRegexParam AS ?bammAspectUrnRegex)\n" + + "BIND(iri($bammFieldToSearchInParam) AS ?bammFieldToSearchIn)\n" + + "BIND($bammFieldSearchValueParam AS ?bammFieldSearchValue)\n" + + "BIND($statusFilterParam AS ?statusFilter)\n" + + "BIND($namespaceFilterParam AS ?namespaceFilter)\n" + + "VALUES (?urns) { ?urnParamList } \n" + + "?aspect a ?bammAspect .\n" + + "FILTER regex(str(?bammAspect), ?bammAspectUrnRegex, \"\")\n" + + "BIND(iri(concat(strbefore(str(?aspect ), \"#\"), \"#\")) AS ?package)\n" + + "?package aux:status ?status\n" + + "FILTER ( !bound(?statusFilter) || contains(str(?status), ?statusFilter) )\n" + + "FILTER ( !bound(?namespaceFilter) || contains(str(?aspect), ?namespaceFilter ) )\n" + + "FILTER ( str(?aspect) IN (?urns) ) " + + "}\n"; + private static final String FIND_ALL_MINIMAL_QUERY = "SELECT DISTINCT ?aspect (?status as ?statusResult)\n" + FILTER_QUERY_MINIMAL_WHERE_CLAUSE @@ -165,6 +218,10 @@ public class SparqlQueries { "SELECT (count(DISTINCT ?aspect) as ?aspectModelCount)\n" + FILTER_QUERY_MINIMAL_WHERE_CLAUSE; + private static final String COUNT_ASPECT_MODELS_MINIMAL_QUERY_SELECTIVE = + "SELECT (count(DISTINCT ?aspect) as ?aspectModelCount)\n" + + FILTER_QUERY_MINIMAL_WHERE_CLAUSE_SELECTIVE; + private SparqlQueries() { } @@ -176,6 +233,26 @@ public static Query buildFindByUrnQuery( final AspectModelUrn urn ) { return pss.asQuery(); } + public static Query buildFindListByUrns( final List urns, int page, int pageSize ) { + final ParameterizedSparqlString pss = create( FIND_BY_MULTIPLE_URNS_QUERY ); + + List urnList = new ArrayList<>(); + List modelPackageUrnList = new ArrayList<>(); + + urns.forEach((AspectModelUrn urn) -> { + urnList.add(ResourceFactory.createStringLiteral(urn.toString())); + modelPackageUrnList.add(ResourceFactory.createStringLiteral(ModelPackageUrn.fromUrn(urn).getUrn())); + }); + + pss.setValues("urnParamList", urnList); + pss.setLiteral( "$bammAspectUrnParam", BAMM_ASPECT_URN_REGEX ); + pss.setValues( "packageUrnParamList", modelPackageUrnList ); + pss.setLiteral("offsetParam", getOffset(page, pageSize)); + pss.setLiteral("limitParam", pageSize); + + return pss.asQuery(); + } + public static Query buildCountAspectModelsQuery( String namespaceFilter, String nameFilter, String nameType, ModelPackageStatus status ) { if(StringUtils.isNotBlank(nameType) && StringUtils.isNotBlank(nameFilter)){ @@ -185,6 +262,24 @@ public static Query buildCountAspectModelsQuery( String namespaceFilter, String return buildMinimalQuery(COUNT_ASPECT_MODELS_MINIMAL_QUERY, namespaceFilter, status).asQuery(); } + public static Query buildCountSelectiveAspectModelsQuery( String namespaceFilter, String nameFilter, String nameType, + ModelPackageStatus status, List urns ) { + ParameterizedSparqlString pss = buildMinimalQuery(COUNT_ASPECT_MODELS_MINIMAL_QUERY_SELECTIVE, namespaceFilter, status); + + List urnList = new ArrayList<>(); + List modelPackageUrnList = new ArrayList<>(); + + urns.forEach((AspectModelUrn urn) -> { + urnList.add(ResourceFactory.createStringLiteral(urn.toString())); + modelPackageUrnList.add(ResourceFactory.createStringLiteral(ModelPackageUrn.fromUrn(urn).getUrn())); + }); + + pss.setValues("urnParamList", urnList); + pss.setValues( "packageUrnParamList", modelPackageUrnList ); + + return pss.asQuery(); + } + public static Query buildFindByPackageQuery( final ModelPackageUrn modelsPackage ) { final ParameterizedSparqlString pss = create( FIND_BY_PACKAGE_URN_QUERY ); pss.setLiteral( "$urnParam", modelsPackage.getUrn() ); diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java index d0a424d6..df8b4536 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java @@ -172,6 +172,28 @@ public void deleteModelsPackage( final ModelPackageUrn urn ) { deleteByUrn( urn ); } + @Override + public SemanticModelList findModelListByUrns(List urns, int page, int pageSize) { + final Query query = SparqlQueries.buildFindListByUrns( urns, page, pageSize ); + final AtomicReference> aspectModels = new AtomicReference<>(); + try ( final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build() ) { + rdfConnection.queryResultSet( query, resultSet -> { + final List querySolutions = ResultSetFormatter.toList( resultSet ); + aspectModels.set( TripleStorePersistence.aspectModelFrom( querySolutions ) ); + } ); + } + int totalSemanticModelCount = getSelectiveItemsCount( null, null, null, null, urns ); + int totalPages = getTotalPages(totalSemanticModelCount, pageSize ); + SemanticModelList modelList = new SemanticModelList(); + List semanticModels = aspectModels.get(); + modelList.setCurrentPage( page ); + modelList.setItemCount( semanticModels.size() ); + modelList.setTotalPages( totalPages ); + modelList.setTotalItems( totalSemanticModelCount ); + modelList.setItems( aspectModels.get() ); + return modelList; + } + public boolean echo() { final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build(); @@ -214,6 +236,21 @@ private Integer getTotalItemsCount( @Nullable String namespaceFilter, @Nullable } } + private Integer getSelectiveItemsCount( @Nullable String namespaceFilter, @Nullable String nameFilter, + @Nullable String nameType, + @Nullable ModelPackageStatus status, List urns ) { + try ( final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build() ) { + AtomicReference count = new AtomicReference<>(); + rdfConnection.querySelect( + SparqlQueries.buildCountSelectiveAspectModelsQuery( namespaceFilter, nameFilter, nameType, status, urns ), + querySolution -> { + int countResult = querySolution.getLiteral( "aspectModelCount" ).getInt(); + count.set( countResult ); + } ); + return count.get(); + } + } + private void deleteByUrn( final ModelPackageUrn modelsPackage ) { final UpdateRequest deleteByUrn = SparqlQueries.buildDeleteByUrnRequest( modelsPackage ); try ( final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build() ) { diff --git a/backend/src/main/resources/static/semantic-hub-openapi.yaml b/backend/src/main/resources/static/semantic-hub-openapi.yaml index 3f13602e..1674cef5 100644 --- a/backend/src/main/resources/static/semantic-hub-openapi.yaml +++ b/backend/src/main/resources/static/semantic-hub-openapi.yaml @@ -128,6 +128,49 @@ paths: $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' + /models/bulk: + post: + tags: + - SemanticHub + operationId: getModelListByUrns + parameters: + - in: query + name: pageSize + required: true + schema: + default: 10 + type: integer + enum: + - 10 + - 50 + - 100 + description: Size of the pages that the results should be partitioned in + - in: query + name: page + required: true + schema: + default: 0 + type: integer + description: The page to return + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + type: string + responses: + '200': + $ref: '#/components/responses/SemanticModelList' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalServerError' '/models/{urn}': get: tags: From 37489cce4383e3df979663ea2f31d86cd869bcf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Mon, 15 Aug 2022 15:56:46 +0200 Subject: [PATCH 02/43] Tests for multiple URN based request --- .../tractusx/semantics/hub/ModelsApiTest.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java index a2b567b9..034d0e58 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java @@ -21,6 +21,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -36,7 +37,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.apache.jena.atlas.json.JSON; +import org.apache.poi.hpsf.Array; +import org.json.JSONArray; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -46,8 +52,11 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.internal.function.text.Length; +import jakarta.json.JsonArray; + @DirtiesContext( classMode = DirtiesContext.ClassMode.AFTER_CLASS ) public class ModelsApiTest extends AbstractModelsApiTest{ @@ -400,6 +409,69 @@ public void testDependentModelTransition() throws Exception { } } + @Test + public void testGetModelListByMultipleUrns() throws Exception { + String urnPrefixPattern = "urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_%s:1.0.0#"; + + List urnSearchArrayEvenNumbers = new ArrayList(); + List urnSearchArrayOddNumbers = new ArrayList(); + List urnSearchArrayNonExistingEntry = new ArrayList(); + + for(int i = 1; i <= 11; i++) { + String urnPrefix = String.format(urnPrefixPattern, i); + mvc.perform(post( TestUtils.createValidModelRequest(urnPrefix),"DRAFT") ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( status().isOk() ); + + if((i % 2) == 0) { + urnSearchArrayEvenNumbers.add(toMovementUrn(urnPrefix)); + } else { + urnSearchArrayOddNumbers.add(toMovementUrn(urnPrefix)); + } + } + + urnSearchArrayNonExistingEntry.add("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_50:1.0.0#Movement"); + + mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/bulk" ) + .param("pageSize", "2") + .param("page", "0") + .content(new JSONArray(urnSearchArrayEvenNumbers).toString()) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles())) + .andDo( MockMvcResultHandlers.print() ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.currentPage", equalTo(0))) + .andExpect(jsonPath("$.totalItems", equalTo(5))) + .andExpect(jsonPath("$.totalPages", equalTo(3))) + .andExpect(jsonPath("$.items[0].urn", equalTo("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_10:1.0.0#Movement"))) + .andExpect(jsonPath("$.items[1].urn", equalTo("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_2:1.0.0#Movement"))) + .andExpect(jsonPath("$.items.length()", equalTo(2))); + + mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/bulk") + .param("pageSize", "2") + .param("page", "1") + .content(new JSONArray(urnSearchArrayOddNumbers).toString()) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles())) + .andDo( MockMvcResultHandlers.print() ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.currentPage", equalTo(1))) + .andExpect(jsonPath("$.totalItems", equalTo(6))) + .andExpect(jsonPath("$.totalPages", equalTo(3))) + .andExpect(jsonPath("$.items[0].urn", equalTo("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_3:1.0.0#Movement"))) + .andExpect(jsonPath("$.items[1].urn", equalTo("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_5:1.0.0#Movement"))) + .andExpect(jsonPath("$.items.length()", equalTo(2))); + + mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/bulk") + .content(new JSONArray(urnSearchArrayNonExistingEntry).toString()) + .contentType(MediaType.APPLICATION_JSON) + .with(jwtTokenFactory.allRoles())) + .andDo( MockMvcResultHandlers.print() ) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.items.length()", equalTo(0))); + + } + private static String toMovementUrn(String urn){ return urn + "Movement"; } From c67d2611436c9a5cdfe10f3ddcb81d06c040df72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Wed, 31 Aug 2022 15:03:27 +0200 Subject: [PATCH 03/43] Add description for bulk endpoint --- backend/src/main/resources/static/semantic-hub-openapi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/resources/static/semantic-hub-openapi.yaml b/backend/src/main/resources/static/semantic-hub-openapi.yaml index 1674cef5..a1041751 100644 --- a/backend/src/main/resources/static/semantic-hub-openapi.yaml +++ b/backend/src/main/resources/static/semantic-hub-openapi.yaml @@ -133,6 +133,7 @@ paths: tags: - SemanticHub operationId: getModelListByUrns + description: This endpoint allows to filter for a specific set of URNs. The endpoint returns the metadata, such as the model status. For URNs that are not in the database there is no entry in the result set. Therefore, a search array with 10 URNs can result in a response containing 9 or less result entries. parameters: - in: query name: pageSize From 5e82d0877242671e71a67ad46e0d62072e04a62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Thu, 1 Sep 2022 14:26:45 +0200 Subject: [PATCH 04/43] Rename bulk to lookup --- backend/src/main/resources/static/semantic-hub-openapi.yaml | 2 +- .../org/eclipse/tractusx/semantics/hub/ModelsApiTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/resources/static/semantic-hub-openapi.yaml b/backend/src/main/resources/static/semantic-hub-openapi.yaml index a1041751..f4c78b89 100644 --- a/backend/src/main/resources/static/semantic-hub-openapi.yaml +++ b/backend/src/main/resources/static/semantic-hub-openapi.yaml @@ -128,7 +128,7 @@ paths: $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' - /models/bulk: + /models/lookup: post: tags: - SemanticHub diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java index 034d0e58..db65addb 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiTest.java @@ -432,7 +432,7 @@ public void testGetModelListByMultipleUrns() throws Exception { urnSearchArrayNonExistingEntry.add("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_50:1.0.0#Movement"); - mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/bulk" ) + mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/lookup" ) .param("pageSize", "2") .param("page", "0") .content(new JSONArray(urnSearchArrayEvenNumbers).toString()) @@ -447,7 +447,7 @@ public void testGetModelListByMultipleUrns() throws Exception { .andExpect(jsonPath("$.items[1].urn", equalTo("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_2:1.0.0#Movement"))) .andExpect(jsonPath("$.items.length()", equalTo(2))); - mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/bulk") + mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/lookup") .param("pageSize", "2") .param("page", "1") .content(new JSONArray(urnSearchArrayOddNumbers).toString()) @@ -462,7 +462,7 @@ public void testGetModelListByMultipleUrns() throws Exception { .andExpect(jsonPath("$.items[1].urn", equalTo("urn:bamm:org.eclipse.tractusx.test_model_list_by_urns_5:1.0.0#Movement"))) .andExpect(jsonPath("$.items.length()", equalTo(2))); - mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/bulk") + mvc.perform(MockMvcRequestBuilders.post("/api/v1/models/lookup") .content(new JSONArray(urnSearchArrayNonExistingEntry).toString()) .contentType(MediaType.APPLICATION_JSON) .with(jwtTokenFactory.allRoles())) From 42afc27154766c9714946f71716cd5d583ee3897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Thu, 1 Sep 2022 18:17:47 +0200 Subject: [PATCH 05/43] Adjust Helm Chart version --- backend/deployment/semantic-hub/Chart.yaml | 2 +- backend/deployment/semantic-hub/values.yaml | 6 +++--- backend/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/deployment/semantic-hub/Chart.yaml b/backend/deployment/semantic-hub/Chart.yaml index 8c98f1fb..b2a1cc1c 100644 --- a/backend/deployment/semantic-hub/Chart.yaml +++ b/backend/deployment/semantic-hub/Chart.yaml @@ -4,4 +4,4 @@ description: Helm Chart for the Catena-X Semantic Hub Application type: application version: 0.1.0 -appVersion: 1.3.0-SNAPSHOT \ No newline at end of file +appVersion: 0.1.0-M1 \ No newline at end of file diff --git a/backend/deployment/semantic-hub/values.yaml b/backend/deployment/semantic-hub/values.yaml index 023e2862..f5737a66 100644 --- a/backend/deployment/semantic-hub/values.yaml +++ b/backend/deployment/semantic-hub/values.yaml @@ -19,16 +19,16 @@ ############################################################### hub: - image: semantic-hub:latest + image: semantic-hub:0.1.0-M1 imagePullPolicy: IfNotPresent replicaCount: 1 containerPort: 4242 ## Use in-memory triple store that is not persistent embeddedTripleStore: false - host: minikube + host: host ## 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" + idpIssuerUri: https://idp-url ## Ignored if 'graphdb.enabled' is set to true graphdbBaseUrl: http://graphdb:3030 service: diff --git a/backend/pom.xml b/backend/pom.xml index 7e296314..cc5e7ff5 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -25,7 +25,7 @@ org.eclipse.tractusx semantic-hub - 1.3.0-SNAPSHOT + DEV-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 0f440b84..8fc5baab 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ org.eclipse.tractusx semantic-hub - 1.3.0-SNAPSHOT + DEV-SNAPSHOT Tractus-X Semantic Hub Root Module of the Tractus-X Semantic Hub pom From aec09641609c6df0dd7147b9bddf24a16a52e78b Mon Sep 17 00:00:00 2001 From: Fethullah Misir Date: Tue, 6 Sep 2022 13:41:47 +0200 Subject: [PATCH 06/43] Update spring version --- README.md | 13 ---- backend/pom.xml | 15 +++-- .../semantics/OAuthSecurityConfig.java | 10 ++-- backend/src/main/resources/application.yml | 3 + pom.xml | 60 +------------------ 5 files changed, 21 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 311ed582..6576c7a9 100644 --- a/README.md +++ b/README.md @@ -23,19 +23,6 @@ The Semantic Hub is a logical and architectural component of Tractus-X. The source code under this folder contains reference implementations of the SLDT Semantic Hub ## 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. diff --git a/backend/pom.xml b/backend/pom.xml index cc5e7ff5..a1fea91e 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -62,10 +62,6 @@ org.springframework.boot spring-boot-starter-jetty - - org.springframework.cloud - spring-cloud-starter-openfeign - org.springframework.boot spring-boot-starter-test @@ -108,6 +104,17 @@ io.openmanufacturing sds-aspect-model-starter + + + jackson-dataformat-xml + com.fasterxml.jackson.dataformat + 2.12.7 + io.openmanufacturing sds-aspect-model-aas-generator diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java b/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java index be2cd32c..0b163764 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/OAuthSecurityConfig.java @@ -33,11 +33,11 @@ public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(auth -> auth - .antMatchers(HttpMethod.OPTIONS).permitAll() - .mvcMatchers(HttpMethod.GET,"/**/models/**").access("@authorizationEvaluator.hasRoleViewSemanticModel()") - .mvcMatchers(HttpMethod.POST,"/**/models/**").access("@authorizationEvaluator.hasRoleAddSemanticModel()") - .mvcMatchers(HttpMethod.PUT,"/**/models/**").access("@authorizationEvaluator.hasRoleUpdateSemanticModel()") - .mvcMatchers(HttpMethod.DELETE,"/**/models/**").access("@authorizationEvaluator.hasRoleDeleteSemanticModel()")) + .mvcMatchers(HttpMethod.OPTIONS).permitAll() + .antMatchers(HttpMethod.GET,"/**/models/**").access("@authorizationEvaluator.hasRoleViewSemanticModel()") + .antMatchers(HttpMethod.POST,"/**/models/**").access("@authorizationEvaluator.hasRoleAddSemanticModel()") + .antMatchers(HttpMethod.PUT,"/**/models/**").access("@authorizationEvaluator.hasRoleUpdateSemanticModel()") + .antMatchers(HttpMethod.DELETE,"/**/models/**").access("@authorizationEvaluator.hasRoleDeleteSemanticModel()")) .csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .oauth2ResourceServer().jwt(); diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index cabe83f2..0f972cbb 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -55,6 +55,9 @@ http: spring: application: name: semantics-services + mvc: + pathmatch: + matching-strategy: ant-path-matcher banner: location: "classpath:banner.txt" servlet: diff --git a/pom.xml b/pom.xml index 8fc5baab..1823e3e8 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ org.springframework.boot spring-boot-starter-parent - 2.5.14 + 2.7.3 @@ -65,9 +65,7 @@ - 2.5.14 - 2020.0.3 - 5.3.20 + 2.7.3 3.1.3 1.6.6 2.9.2 @@ -165,13 +163,6 @@ ${google.findbugs.version} - - org.springframework.cloud - spring-cloud-dependencies - ${spring.cloud.version} - pom - import - org.springframework.boot spring-boot-starter-web @@ -213,22 +204,6 @@ - - org.springframework.cloud - spring-cloud-starter-openfeign - ${spring.feign.version} - - - org.springframework.cloud - spring-cloud-openfeign-core - ${spring.feign.version} - - - org.springframework - spring-test - ${spring.version} - test - org.springframework.boot spring-boot-starter-data-jpa @@ -240,12 +215,6 @@ - - org.springframework - spring-webmvc - ${spring.version} - - org.springdoc @@ -280,31 +249,6 @@ 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} - From cd18b6bbfbe0dca54e22237b2febb8156a948596 Mon Sep 17 00:00:00 2001 From: Fethullah Misir Date: Wed, 7 Sep 2022 10:26:40 +0200 Subject: [PATCH 07/43] Update docker base image to mitigate CVEs --- backend/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index ab32740c..d7da3236 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -18,7 +18,7 @@ # SPDX-License-Identifier: Apache-2.0 ############################################################### -FROM eclipse-temurin:11-jre +FROM eclipse-temurin:11.0.16.1_1-jre-jammy RUN apt-get -y upgrade \ && apt-get -y update \ From 50d4bd852da1d075116e78c7d67ccc691fd1cf01 Mon Sep 17 00:00:00 2001 From: Fethullah Misir Date: Wed, 7 Sep 2022 15:15:50 +0200 Subject: [PATCH 08/43] Update to jre-alpine --- backend/Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index d7da3236..71cd2c3c 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -18,14 +18,15 @@ # SPDX-License-Identifier: Apache-2.0 ############################################################### -FROM eclipse-temurin:11.0.16.1_1-jre-jammy +FROM eclipse-temurin:11-jre-alpine -RUN apt-get -y upgrade \ - && apt-get -y update \ - && apt-get -y install graphviz \ - && apt-get clean +RUN apk upgrade \ + && apk update \ + && apk add graphviz \ + && rm -rf /var/cache/apk/* -RUN adduser --system --group spring \ +RUN addgroup -S spring \ + && adduser -S spring -G spring \ && mkdir -p /service \ && chown spring:spring /service From d98e6a7ff021d2dc691aa0c81e2ecfd733955c5f Mon Sep 17 00:00:00 2001 From: Fethullah Misir Date: Thu, 8 Sep 2022 09:24:53 +0200 Subject: [PATCH 09/43] Exclude maven core --- backend/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/pom.xml b/backend/pom.xml index a1fea91e..8db1b077 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -123,6 +123,10 @@ junit junit + + maven-core + org.apache.maven + From 41d8c2f89dfb839ac39f309be443f5d500a46112 Mon Sep 17 00:00:00 2001 From: Fethullah Misir Date: Thu, 8 Sep 2022 10:31:52 +0200 Subject: [PATCH 10/43] Enable auth with swagger ui --- backend/pom.xml | 20 ++++---- backend/src/main/resources/application.yml | 2 +- .../static/semantic-hub-openapi.yaml | 12 ++++- pom.xml | 50 ------------------- 4 files changed, 22 insertions(+), 62 deletions(-) diff --git a/backend/pom.xml b/backend/pom.xml index 8db1b077..e4b7399e 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -62,10 +62,7 @@ org.springframework.boot spring-boot-starter-jetty - - org.springframework.boot - spring-boot-starter-test - + org.springframework.boot spring-boot-starter-oauth2-resource-server @@ -74,11 +71,6 @@ org.springframework.boot spring-boot-starter-actuator - - org.springframework.security - spring-security-test - test - org.mapstruct mapstruct @@ -175,7 +167,15 @@ org.slf4j slf4j-simple - + + org.springframework.boot + spring-boot-starter-test + + + org.springframework.security + spring-security-test + test + diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 0f972cbb..9f3e5a1e 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -84,7 +84,7 @@ springdoc: use-pkce-with-authorization-code-grant: true # the scopes and client id will be prefilled in the swagger ui scopes: openid profile - client-id: catenax-portal + client-id: ${hub.general.idm.public-client-id} title: '@project.name@' project_desc: '@project.description@' diff --git a/backend/src/main/resources/static/semantic-hub-openapi.yaml b/backend/src/main/resources/static/semantic-hub-openapi.yaml index 52432341..aea287c0 100644 --- a/backend/src/main/resources/static/semantic-hub-openapi.yaml +++ b/backend/src/main/resources/static/semantic-hub-openapi.yaml @@ -2,6 +2,11 @@ openapi: 3.0.3 info: title: Semantic Hub version: v1 + +security: + - OpenIdProfile: + - profile + servers: - url: ./api/{api-version} variables: @@ -577,4 +582,9 @@ components: NotFound: description: Not Found InternalServerError: - description: Internal Server Error \ No newline at end of file + description: Internal Server Error + + securitySchemes: + OpenIdProfile: + type: openIdConnect + openIdConnectUrl: ../.well-known/openid-configuration \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1823e3e8..2e4c49fb 100644 --- a/pom.xml +++ b/pom.xml @@ -83,21 +83,10 @@ 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 - 2.0.0 4.2.0 @@ -106,10 +95,6 @@ 1.4.2.Final 0.2.0 - 42.2.25 - 2.1.210 - 4.9.1 - 3.18.1 5.6.3 @@ -152,11 +137,6 @@ commons-io ${commons-io.version} - - javax.servlet - javax.servlet-api - ${javax.servlet} - com.google.code.findbugs jsr305 @@ -226,12 +206,6 @@ springfox-swagger2 ${springfox.version} - - io.springfox - springfox-swagger-ui - ${springfox.version} - runtime - @@ -257,36 +231,12 @@ ${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} - From 3fd3fa941cc515ed8a2c11666c41a097cb738e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 13 Sep 2022 16:37:54 +0200 Subject: [PATCH 11/43] Adjust snakeyaml version to 1.31 --- backend/pom.xml | 5 +++++ pom.xml | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/backend/pom.xml b/backend/pom.xml index e4b7399e..7b80b5b8 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -54,6 +54,11 @@ org.springframework.boot spring-boot-starter-validation + + + org.yaml + snakeyaml + org.springframework.boot spring-boot-starter-web diff --git a/pom.xml b/pom.xml index 2e4c49fb..f624d7fe 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,7 @@ 0.10.3 2.11.0 3.0.2 + 1.31 1.7.32 @@ -158,6 +159,23 @@ + + + org.springframework.boot + spring-boot-starter-validation + ${spring.boot.version} + + + org.yaml + snakeyaml + + + + + org.yaml + snakeyaml + ${snakeyaml.version} + org.springframework.boot spring-boot-starter-test From c578e50c104103083fd8adca51f3c9f51ea6f4ae Mon Sep 17 00:00:00 2001 From: Fethullah Misir Date: Tue, 20 Sep 2022 15:13:22 +0200 Subject: [PATCH 12/43] Add copy right headers --- .github/workflows/gitleaks.yml | 17 +++++++++++++++++ .github/workflows/release.yml | 17 +++++++++++++++++ .github/workflows/trivy-scan.yml | 17 +++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index f86851c9..1e8c2ba0 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -1,3 +1,20 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 name: gitleaks on: [push, pull_request, workflow_dispatch] jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f0a51f84..d0244066 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,20 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 --- name: "Create new version tag" diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 63c75688..eedc1420 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -1,3 +1,20 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 name: Trivy Scan on: From 560e84414911098538eafbb14ac1f2a7afd77d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Wed, 21 Sep 2022 14:26:24 +0200 Subject: [PATCH 13/43] Add KICS workflow --- .github/workflows/kics.yml | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 .github/workflows/kics.yml diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml new file mode 100644 index 00000000..66823274 --- /dev/null +++ b/.github/workflows/kics.yml @@ -0,0 +1,72 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 +--- + +name: "KICS" + +on: + push: + branches: [main, master] + # pull_request: + # The branches below must be a subset of the branches above + # branches: [main, master] + # paths-ignore: + # - "**/*.md" + # - "**/*.txt" + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - uses: actions/checkout@v2 + + - name: KICS scan + uses: checkmarx/kics-github-action@master + with: + # Scanning directory . + path: "." + # Exclude paths from scan by providing the paths as comma separated list + # exclude_paths: "postgres-init.yaml,templates/sharedidp.yaml" + # Exclude queries by providing the query / rule ID as comma separated list + # exclude_queries: "b9c83569-459b-4110-8f79-6305aa33cb37" + # Fail on HIGH severity results + fail_on: high + # Disable secrets detection - we use GitGuardian + disable_secrets: true + # When provided with a directory on output_path + # it will generate the specified reports file named 'results.{extension}' + # in this example it will generate: + # - results-dir/results.json and results-dir/results.sarif + output_path: kicsResults/ + output_formats: "json,sarif" + + # Upload findings to GitHub Advanced Security Dashboard + - name: Upload SARIF file for GitHub Advanced Security Dashboard + if: always() + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: kicsResults/results.sarif \ No newline at end of file From 80351359f86187248ac232f07ed9ee9520da2996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Wed, 21 Sep 2022 16:55:16 +0200 Subject: [PATCH 14/43] Fix syntax error in Helm Chart --- .../deployment/semantic-hub/templates/hub/hub-deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml b/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml index 901b856a..be88f854 100644 --- a/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml +++ b/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml @@ -66,14 +66,14 @@ spec: livenessProbe: httpGet: path: /actuator/health/liveness - port: { { .Values.hub.containerPort } } + port: {{ .Values.hub.containerPort }} initialDelaySeconds: 100 periodSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /actuator/health/readiness - port: { { .Values.hub.containerPort } } + port: {{ .Values.hub.containerPort }} initialDelaySeconds: 60 periodSeconds: 3 failureThreshold: 3 From 7aef15bdd4587580b90c19adcce1e7aa009a4152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Thu, 22 Sep 2022 09:28:43 +0200 Subject: [PATCH 15/43] Adjust appVersion in Helm Chart --- backend/deployment/semantic-hub/Chart.yaml | 4 ++-- backend/deployment/semantic-hub/values.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/deployment/semantic-hub/Chart.yaml b/backend/deployment/semantic-hub/Chart.yaml index b2a1cc1c..a5bacf99 100644 --- a/backend/deployment/semantic-hub/Chart.yaml +++ b/backend/deployment/semantic-hub/Chart.yaml @@ -3,5 +3,5 @@ name: semantic-hub description: Helm Chart for the Catena-X Semantic Hub Application type: application -version: 0.1.0 -appVersion: 0.1.0-M1 \ No newline at end of file +version: 0.1.1 +appVersion: 0.1.0-M2 \ No newline at end of file diff --git a/backend/deployment/semantic-hub/values.yaml b/backend/deployment/semantic-hub/values.yaml index f5737a66..a7487e61 100644 --- a/backend/deployment/semantic-hub/values.yaml +++ b/backend/deployment/semantic-hub/values.yaml @@ -19,7 +19,7 @@ ############################################################### hub: - image: semantic-hub:0.1.0-M1 + image: semantic-hub:0.1.0-M2 imagePullPolicy: IfNotPresent replicaCount: 1 containerPort: 4242 From 7b1c2545266f8dbe0373879f54e610e55ad2ddcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Thu, 6 Oct 2022 10:36:40 +0200 Subject: [PATCH 16/43] Add missing property in Helm Chart secret --- backend/deployment/semantic-hub/Chart.yaml | 2 +- backend/deployment/semantic-hub/templates/hub/hub-secret.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/deployment/semantic-hub/Chart.yaml b/backend/deployment/semantic-hub/Chart.yaml index a5bacf99..34cf7e1b 100644 --- a/backend/deployment/semantic-hub/Chart.yaml +++ b/backend/deployment/semantic-hub/Chart.yaml @@ -3,5 +3,5 @@ name: semantic-hub description: Helm Chart for the Catena-X Semantic Hub Application type: application -version: 0.1.1 +version: 0.1.2 appVersion: 0.1.0-M2 \ No newline at end of file diff --git a/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml b/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml index 3c30c433..bc595e14 100644 --- a/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml +++ b/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml @@ -29,6 +29,7 @@ metadata: type: Opaque data: SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: {{ .Values.hub.idpIssuerUri | b64enc }} + HUB_GENERAL_IDM_PUBLIC_CLIENT_ID: {{ .Values.hub.idpClientId | b64enc }} # the fuseki instance does not require authentication yet # this variables need to be provided because they are mandatory in the application HUB_TRIPLE_STORE_USERNAME: {{ .Values.graphdb.username | b64enc }} From 915e434a76f308d8b01e1a4f25d3c87389f922eb Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Mon, 17 Oct 2022 16:49:52 +0200 Subject: [PATCH 17/43] Update pom.xml --- pom.xml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f624d7fe..82ca6ab7 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ 2.13.1 + 2.13.4.2 20211205 @@ -240,6 +241,18 @@ org.openapitools jackson-databind-nullable 0.1.0 + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} @@ -266,7 +279,7 @@ io.openmanufacturing sds-aspect-model-aas-generator ${bamm.sdk.version} - + org.apache.jena jena-core From 52128a4ab6049db4af3c2f0561e72ab456f2bdd1 Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Mon, 17 Oct 2022 17:43:09 +0200 Subject: [PATCH 18/43] Exclude commons text and introduce it v 1.10.0 to prevent CVE https://avd.aquasec.com/nvd/2022/cve-2022-42889 --- pom.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 82ca6ab7..be2ee06b 100644 --- a/pom.xml +++ b/pom.xml @@ -270,11 +270,23 @@ - - io.openmanufacturing - sds-aspect-model-starter - ${bamm.sdk.version} - + + io.openmanufacturing + sds-aspect-model-starter + ${bamm.sdk.version} + + + + org.apache.commons + commons-text + + + + + org.apache.commons + commons-text + 1.10.0 + io.openmanufacturing sds-aspect-model-aas-generator From 480ac03d1dac867f3863120af54452defb1fd204 Mon Sep 17 00:00:00 2001 From: Sven Erik Jeroschewski Date: Tue, 18 Oct 2022 13:45:51 +0200 Subject: [PATCH 19/43] Updates snakeyaml dependency to 1.33 due to CVE in previous version (CVE-2022-38752). --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index be2ee06b..016691a8 100644 --- a/pom.xml +++ b/pom.xml @@ -78,7 +78,7 @@ 0.10.3 2.11.0 3.0.2 - 1.31 + 1.33 1.7.32 From 3be7df8282e1e4a91571b7fb58ffba270749eb55 Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Thu, 20 Oct 2022 18:04:50 +0200 Subject: [PATCH 20/43] Add version update of sds-sdk to v2.0.6 and adjust code --- .../semantics/hub/bamm/BammHelper.java | 181 +++++++++--------- .../hub/bamm/StaticResolutionStrategy.java | 70 +++++-- .../triplestore/TripleStorePersistence.java | 101 +++++----- pom.xml | 2 +- 4 files changed, 195 insertions(+), 159 deletions(-) diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java index 6429cfc5..a15d9507 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelper.java @@ -58,129 +58,122 @@ @Component public class BammHelper { - public Try loadBammModel(String ttl) { - InputStream targetStream = new ByteArrayInputStream(ttl.getBytes()); + public Try loadBammModel( String ttl ) { + InputStream targetStream = new ByteArrayInputStream( ttl.getBytes() ); - Try model = TurtleLoader.loadTurtle(targetStream); + Try model = TurtleLoader.loadTurtle( targetStream ); - StaticResolutionStrategy resolutionStrategy = new StaticResolutionStrategy(model); + StaticResolutionStrategy resolutionStrategy = new StaticResolutionStrategy( model ); + AspectModelResolver resolver = new AspectModelResolver(); - AspectModelUrn startUrn = AspectModelUrn - .fromUrn( "urn:bamm:org.eclipse.tractusx:1.0.0#Aspect" ); + Try versionedModel = resolver.resolveAspectModel( resolutionStrategy, resolutionStrategy.getAspectModelUrn() ); - - AspectModelResolver resolver = new AspectModelResolver(); + if ( resolutionStrategy.getResolvementCounter() > 1 ) { + return Try.failure( new ResolutionException( "The definition must be self contained!" ) ); + } - Try versionedModel = resolver.resolveAspectModel(resolutionStrategy, startUrn); + return versionedModel; + } - if(resolutionStrategy.getResolvementCounter() > 1) { - return Try.failure(new ResolutionException("The definition must be self contained!")); - } + public Try getAspectFromVersionedModel( VersionedModel versionedModel ) { - return versionedModel; - } + return AspectModelLoader.fromVersionedModel( versionedModel ); + } - public Try getAspectFromVersionedModel(VersionedModel versionedModel) { + public ValidationReport validateModel( Try model ) { + final AspectModelValidator validator = new AspectModelValidator(); + final ValidationReport validationReport = validator.validate( model ); - return AspectModelLoader.fromVersionedModel(versionedModel); - } + return validationReport; + } - public ValidationReport validateModel(Try model) { - final AspectModelValidator validator = new AspectModelValidator(); - final ValidationReport validationReport = validator.validate(model); + public byte[] generatePng( VersionedModel versionedModel ) { + final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( versionedModel ); - return validationReport; - } + try { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + generator.generateDiagram( Format.PNG, Locale.ENGLISH, output ); + final byte[] bytes = output.toByteArray(); - public byte[] generatePng(VersionedModel versionedModel) { - final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator(versionedModel); - - try { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - generator.generateDiagram(Format.PNG, Locale.ENGLISH, output); + return bytes; + } catch ( IOException e ) { + e.printStackTrace(); - final byte[] bytes = output.toByteArray(); - - return bytes; - } catch (IOException e) { - e.printStackTrace(); + return null; + } + } - return null; - } - } + public JsonNode getJsonSchema( Aspect aspect ) { + AspectModelJsonSchemaGenerator jsonSchemaGenerator = new AspectModelJsonSchemaGenerator(); - public JsonNode getJsonSchema(Aspect aspect) { - AspectModelJsonSchemaGenerator jsonSchemaGenerator = new AspectModelJsonSchemaGenerator(); + JsonNode json = jsonSchemaGenerator.apply( aspect ); - JsonNode json = jsonSchemaGenerator.apply(aspect); + return json; + } - return json; - } + public Try getHtmlDocu( VersionedModel versionedModel ) { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + AspectModelDocumentationGenerator documentationGenerator = new AspectModelDocumentationGenerator( versionedModel ); - public Try getHtmlDocu(VersionedModel versionedModel) { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - AspectModelDocumentationGenerator documentationGenerator = new AspectModelDocumentationGenerator(versionedModel); + Map options = new HashMap(); - Map options = new HashMap(); + try { + InputStream ompCSS = getClass().getResourceAsStream( "/catena-template.css" ); + String defaultCSS = CharStreams.toString( new InputStreamReader( ompCSS ) ); - try { - InputStream ompCSS = getClass().getResourceAsStream("/catena-template.css"); - String defaultCSS = CharStreams.toString(new InputStreamReader(ompCSS)); + options.put( HtmlGenerationOption.STYLESHEET, defaultCSS ); + } catch ( IOException e ) { + return Try.failure( e ); + } - options.put(HtmlGenerationOption.STYLESHEET, defaultCSS); - } catch (IOException e) { - return Try.failure(e); - } + try { + documentationGenerator.generate( ( String a ) -> { + return output; + }, options ); + return Try.success( output.toByteArray() ); + } catch ( IOException e ) { + return Try.failure( e ); + } + } - try { - documentationGenerator.generate((String a) -> { - return output; - }, options); + public String getOpenApiDefinitionJson( Aspect aspect, String baseUrl ) { + AspectModelOpenApiGenerator openApiGenerator = new AspectModelOpenApiGenerator(); - return Try.success(output.toByteArray()); - } catch (IOException e) { - return Try.failure(e); - } - } + JsonNode resultJson = openApiGenerator.applyForJson( aspect, true, baseUrl, Optional.empty(), Optional.empty(), false, Optional.empty() ); - public String getOpenApiDefinitionJson(Aspect aspect, String baseUrl) { - AspectModelOpenApiGenerator openApiGenerator = new AspectModelOpenApiGenerator(); + return resultJson.toString(); + } - JsonNode resultJson = openApiGenerator.applyForJson(aspect, true, baseUrl, Optional.empty(), Optional.empty(), false, Optional.empty()); + public Try getExamplePayloadJson( Aspect aspect ) { + AspectModelJsonPayloadGenerator payloadGenerator = new AspectModelJsonPayloadGenerator( aspect ); - return resultJson.toString(); - } + return Try.of( payloadGenerator::generateJson ); + } - public Try getExamplePayloadJson(Aspect aspect) { - AspectModelJsonPayloadGenerator payloadGenerator = new AspectModelJsonPayloadGenerator(aspect); + public Try getAasSubmodelTemplate( Aspect aspect, AasFormat aasFormat ) { + AspectModelAASGenerator aasGenerator = new AspectModelAASGenerator(); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); - return Try.of(payloadGenerator::generateJson); - } + try { + switch ( aasFormat ) { + case FILE: + aasGenerator.generateAASXFile( aspect, ( String s ) -> { + return stream; + } ); + return Try.of( stream::toByteArray ); + case XML: + aasGenerator.generateAasXmlFile( aspect, ( String s ) -> { + return stream; + } ); + return Try.of( stream::toString ); + default: + return Try.failure( new Exception( String.format( "Wrong AAS output format %s", aasFormat.toString() ) ) ); - public Try getAasSubmodelTemplate(Aspect aspect, AasFormat aasFormat) { - AspectModelAASGenerator aasGenerator = new AspectModelAASGenerator(); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - - try { - switch(aasFormat) { - case FILE: - aasGenerator.generateAASXFile(aspect, (String s) -> { - return stream; - }); - return Try.of(stream::toByteArray); - case XML: - aasGenerator.generateAasXmlFile(aspect, (String s) -> { - return stream; - }); - return Try.of(stream::toString); - default: - return Try.failure(new Exception(String.format("Wrong AAS output format %s", aasFormat.toString()))); - - } - } catch (IOException e) { - return Try.failure(e); - } - } + } + } catch ( IOException e ) { + return Try.failure( e ); + } + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java index c89cb5a4..6cd35aae 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java @@ -19,28 +19,60 @@ ********************************************************************************/ package org.eclipse.tractusx.semantics.hub.bamm; -import org.apache.jena.rdf.model.Model; - +import io.openmanufacturing.sds.aspectmetamodel.KnownVersion; import io.openmanufacturing.sds.aspectmodel.resolver.AbstractResolutionStrategy; import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn; +import io.openmanufacturing.sds.aspectmodel.vocabulary.BAMM; +import io.vavr.NotImplementedError; import io.vavr.control.Try; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.StmtIterator; +import org.apache.jena.vocabulary.RDF; + +import java.util.List; +import java.util.Optional; + public class StaticResolutionStrategy extends AbstractResolutionStrategy { - private int counter; - private final Try model; - - public StaticResolutionStrategy(Try model) { - this.model = model; - } - - @Override - public Try apply(AspectModelUrn t) { - counter++; - - return this.model; - } - - public int getResolvementCounter() { - return counter; - } + private int counter; + private final Try model; + + public StaticResolutionStrategy( Try model ) { + this.model = model; + } + + @Override + public Try apply( AspectModelUrn t ) { + counter++; + return this.model; + } + + public int getResolvementCounter() { + return counter; + } + + public AspectModelUrn getAspectModelUrn() { + final Optional stmtIterator = getStmtIterator(); + + final String aspectModelUrn = stmtIterator.orElseThrow( + () -> new NotImplementedError( "AspectModelUrn cannot be found." ) ) + .next().getSubject().getURI(); + + return AspectModelUrn.fromUrn( aspectModelUrn ); + } + + private Optional getStmtIterator() { + for ( final KnownVersion version : KnownVersion.getVersions() ) { + final BAMM bamm = new BAMM( version ); + final List resources = List.of( bamm.Aspect(), bamm.Property(), bamm.Entity(), + bamm.Characteristic() ); + + return resources.stream().filter( + resource -> model.get().listStatements( null, RDF.type, resource ).hasNext() ) + .map( resource -> model.get().listStatements( null, RDF.type, resource ) ).findFirst(); + } + + return Optional.empty(); + } } diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java index 5aa9b9b3..1d60bc22 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/TripleStorePersistence.java @@ -30,6 +30,9 @@ import javax.annotation.Nullable; +import io.openmanufacturing.sds.aspectmodel.urn.UrnSyntaxException; +import io.vavr.control.Try; + import org.eclipse.tractusx.semantics.hub.AspectModelNotFoundException; import org.eclipse.tractusx.semantics.hub.ModelPackageNotFoundException; import org.eclipse.tractusx.semantics.hub.domain.ModelPackage; @@ -52,6 +55,7 @@ import io.openmanufacturing.sds.aspectmodel.resolver.AspectModelResolver; import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn; + import org.eclipse.tractusx.semantics.hub.InvalidStateTransitionException; import org.eclipse.tractusx.semantics.hub.persistence.PersistenceLayer; @@ -67,8 +71,8 @@ public TripleStorePersistence( final RDFConnectionRemoteBuilder rdfConnectionRem } @Override - public SemanticModelList getModels(String namespaceFilter, - @Nullable ModelPackageStatus status, Integer page, Integer pageSize ) { + public SemanticModelList getModels( String namespaceFilter, + @Nullable ModelPackageStatus status, Integer page, Integer pageSize ) { final Query query = SparqlQueries.buildFindAllQuery( namespaceFilter, status, page, pageSize ); final AtomicReference> aspectModels = new AtomicReference<>(); @@ -79,7 +83,7 @@ public SemanticModelList getModels(String namespaceFilter, } ); } int totalSemanticModelCount = getTotalItemsCount( namespaceFilter, status ); - int totalPages = getTotalPages(totalSemanticModelCount, pageSize ); + int totalPages = getTotalPages( totalSemanticModelCount, pageSize ); SemanticModelList modelList = new SemanticModelList(); List semanticModels = aspectModels.get(); modelList.setCurrentPage( page ); @@ -90,11 +94,11 @@ public SemanticModelList getModels(String namespaceFilter, return modelList; } - private static int getTotalPages(int totalItemsCount, int pageSize){ - if(totalItemsCount == 0 || pageSize == 0){ + private static int getTotalPages( int totalItemsCount, int pageSize ) { + if ( totalItemsCount == 0 || pageSize == 0 ) { return 0; } - return (int) Math.ceil( ((double) totalItemsCount) / (double) pageSize); + return (int) Math.ceil( ((double) totalItemsCount) / (double) pageSize ); } @Override @@ -103,35 +107,35 @@ public SemanticModel getModel( final AspectModelUrn urn ) { } @Override - public SemanticModel save(SemanticModelType type, String newModel, SemanticModelStatus status ) { + public SemanticModel save( SemanticModelType type, String newModel, SemanticModelStatus status ) { final Model rdfModel = sdsSdk.load( newModel.getBytes( StandardCharsets.UTF_8 ) ); final AspectModelUrn modelUrn = sdsSdk.getAspectUrn( rdfModel ); Optional existsByPackage = findByPackageByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); if ( existsByPackage.isPresent() ) { ModelPackageStatus persistedModelStatus = existsByPackage.get().getStatus(); - final ModelPackageStatus desiredModelStatus = ModelPackageStatus.valueOf( status.name() ); + final ModelPackageStatus desiredModelStatus = ModelPackageStatus.valueOf( status.name() ); switch ( persistedModelStatus ) { - case DRAFT: - if(desiredModelStatus.equals(ModelPackageStatus.RELEASED) && !hasReferenceToDraftPackage(modelUrn, rdfModel)) { - throw new InvalidStateTransitionException("It is not allowed to release an aspect that has dependencies in DRAFT state."); - } + case DRAFT: + if ( desiredModelStatus.equals( ModelPackageStatus.RELEASED ) && !hasReferenceToDraftPackage( modelUrn, rdfModel ) ) { + throw new InvalidStateTransitionException( "It is not allowed to release an aspect that has dependencies in DRAFT state." ); + } + deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); + break; + case RELEASED: + // released models can only be updated when the new state is deprecated + if ( desiredModelStatus.equals( ModelPackageStatus.DEPRECATED ) ) { deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); - break; - case RELEASED: - // released models can only be updated when the new state is deprecated - if(desiredModelStatus.equals(ModelPackageStatus.DEPRECATED)){ - deleteByUrn( ModelPackageUrn.fromUrn( modelUrn ) ); - } else { - throw new IllegalArgumentException( - String.format( "The package %s is already in status %s and cannot be modified. Only a transition to DEPRECATED is possible.", - ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) ); - } - break; - case DEPRECATED: + } else { throw new IllegalArgumentException( - String.format( "The package %s is already in status %s and cannot be modified.", + String.format( "The package %s is already in status %s and cannot be modified. Only a transition to DEPRECATED is possible.", ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) ); + } + break; + case DEPRECATED: + throw new IllegalArgumentException( + String.format( "The package %s is already in status %s and cannot be modified.", + ModelPackageUrn.fromUrn( modelUrn ).getUrn(), persistedModelStatus.name() ) ); } } @@ -173,7 +177,7 @@ public void deleteModelsPackage( final ModelPackageUrn urn ) { } @Override - public SemanticModelList findModelListByUrns(List urns, int page, int pageSize) { + public SemanticModelList findModelListByUrns( List urns, int page, int pageSize ) { final Query query = SparqlQueries.buildFindListByUrns( urns, page, pageSize ); final AtomicReference> aspectModels = new AtomicReference<>(); try ( final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build() ) { @@ -183,7 +187,7 @@ public SemanticModelList findModelListByUrns(List urns, int page } ); } int totalSemanticModelCount = getSelectiveItemsCount( null, null, null, null, urns ); - int totalPages = getTotalPages(totalSemanticModelCount, pageSize ); + int totalPages = getTotalPages( totalSemanticModelCount, pageSize ); SemanticModelList modelList = new SemanticModelList(); List semanticModels = aspectModels.get(); modelList.setCurrentPage( page ); @@ -197,22 +201,21 @@ public SemanticModelList findModelListByUrns(List urns, int page public boolean echo() { final RDFConnection rdfConnection = rdfConnectionRemoteBuilder.build(); - return rdfConnection.queryAsk(SparqlQueries.echoQuery()); + return rdfConnection.queryAsk( SparqlQueries.echoQuery() ); } - private boolean hasReferenceToDraftPackage(AspectModelUrn modelUrn, Model model) { - Pattern pattern = Pattern.compile(SparqlQueries.ALL_BAMM_ASPECT_URN_PREFIX); - - List urns = AspectModelResolver.getAllUrnsInModel(model).stream().map((AspectModelUrn urn) -> { - return urn.getUrnPrefix(); - }) - .distinct() - .collect(Collectors.toList()); - - for(var entry : urns) { - Matcher matcher = pattern.matcher(entry); - if(!matcher.find() && !modelUrn.getUrnPrefix().equals(entry)) { - if(findByPackageByUrn(ModelPackageUrn.fromUrn(entry)).get().getStatus().equals(ModelPackageStatus.DRAFT)) { + private boolean hasReferenceToDraftPackage( AspectModelUrn modelUrn, Model model ) { + Pattern pattern = Pattern.compile( SparqlQueries.ALL_BAMM_ASPECT_URN_PREFIX ); + + List urns = AspectModelResolver.getAllUrnsInModel( model ).stream().filter( urn -> getAspectModelUrn( urn ).isSuccess() ) + .map( urn -> getAspectModelUrn( urn ).get().getUrnPrefix() ) + .distinct() + .collect( Collectors.toList() ); + + for ( var entry : urns ) { + Matcher matcher = pattern.matcher( entry ); + if ( !matcher.find() && !modelUrn.getUrnPrefix().equals( entry ) ) { + if ( findByPackageByUrn( ModelPackageUrn.fromUrn( entry ) ).get().getStatus().equals( ModelPackageStatus.DRAFT ) ) { return false; } } @@ -294,12 +297,20 @@ private Model findJenaModelByUrn( final AspectModelUrn urn ) { } } + private Try getAspectModelUrn( String urn ) { + try { + return Try.success( AspectModelUrn.fromUrn( urn ) ); + } catch ( UrnSyntaxException var2 ) { + return Try.failure( var2 ); + } + } + private static List aspectModelFrom( final List querySolutions ) { - return querySolutions - .stream() - .map( TripleStorePersistence::aspectModelFrom ) - .collect(Collectors.toList()); + return querySolutions + .stream() + .map( TripleStorePersistence::aspectModelFrom ) + .collect( Collectors.toList() ); } private static SemanticModel aspectModelFrom( final QuerySolution querySolution ) { diff --git a/pom.xml b/pom.xml index 016691a8..8e5e1fa2 100644 --- a/pom.xml +++ b/pom.xml @@ -90,7 +90,7 @@ 20211205 - 2.0.0 + 2.0.6 4.2.0 1.3.1 From 4c7ff324f04e03c9ca1798ee4244982863ab8882 Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Mon, 24 Oct 2022 09:47:45 +0200 Subject: [PATCH 21/43] Add test and fix sonarqube bug Signed-off-by: Johannes Kristan --- .../hub/bamm/StaticResolutionStrategy.java | 14 +++++------ .../semantics/hub/bamm/BammHelperTest.java | 25 ++++++++++++++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java index 6cd35aae..b2b188db 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/bamm/StaticResolutionStrategy.java @@ -65,14 +65,14 @@ public AspectModelUrn getAspectModelUrn() { private Optional getStmtIterator() { for ( final KnownVersion version : KnownVersion.getVersions() ) { final BAMM bamm = new BAMM( version ); - final List resources = List.of( bamm.Aspect(), bamm.Property(), bamm.Entity(), - bamm.Characteristic() ); - - return resources.stream().filter( - resource -> model.get().listStatements( null, RDF.type, resource ).hasNext() ) - .map( resource -> model.get().listStatements( null, RDF.type, resource ) ).findFirst(); + final List resources = List.of( bamm.Aspect(), bamm.Property(), bamm.Entity(), bamm.Characteristic() ); + final Optional stmtIterator = resources.stream().filter( + resource -> model.get().listStatements( null, RDF.type, resource ).hasNext() ).findFirst() + .map( resource -> model.get().listStatements( null, RDF.type, resource ) ); + if ( stmtIterator.isPresent() ) { + return stmtIterator; + } } - return Optional.empty(); } } diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java index c2d92860..f570a112 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/bamm/BammHelperTest.java @@ -19,13 +19,12 @@ ********************************************************************************/ package org.eclipse.tractusx.semantics.hub.bamm; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; -import org.eclipse.tractusx.semantics.hub.bamm.BammHelper; import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel; +import io.vavr.NotImplementedError; import io.vavr.control.Try; public class BammHelperTest { @@ -68,4 +67,24 @@ public void testBammValidationWithInvalidModel() { assertFalse(bammHelper.validateModel(model).conforms()); } + + @Test + public void testBammLoaderInVersion2_0_0_WithValidModelExpectSuccess() { + final String modelString = "@prefix bamm: .\n @prefix bamm-c: .\n @prefix bamm-e: .\n @prefix unit: .\n @prefix rdf: .\n @prefix rdfs: .\n @prefix xsd: .\n @prefix : .\n \n :Movement a bamm:Aspect;\n bamm:name \"Movement\";\n bamm:preferredName \"Movement\"@en;\n bamm:description \"Aspect for movement information\"@en;\n bamm:properties (:isMoving :speedLimitWarning :position);\n bamm:operations ().\n :isMoving a bamm:Property;\n bamm:name \"isMoving\";\n bamm:preferredName \"Moving\"@en;\n bamm:description \"Flag indicating whether the asset is currently moving\"@en;\n bamm:characteristic bamm-c:Boolean.\n :speedLimitWarning a bamm:Property;\n bamm:name \"speedLimitWarning\";\n bamm:preferredName \"Speed Limit Warning\"@en;\n bamm:description \"Indicates if the speed limit is adhered to.\"@en;\n bamm:characteristic :TrafficLight.\n :position a bamm:Property;\n bamm:name \"position\";\n bamm:preferredName \"Position\"@en;\n bamm:description \"Indicates a position\"@en;\n bamm:characteristic :SpatialPositionCharacteristic.\n :TrafficLight a bamm-c:Enumeration;\n bamm:name \"TrafficLight\";\n bamm:preferredName \"Warning Level\"@en;\n bamm:description \"Represents if speed of position change is within specification (green), within tolerance (yellow), or outside specification (red).\"@en;\n bamm:dataType xsd:string;\n bamm-c:values (\"green\" \"yellow\" \"red\").\n :SpatialPosition a bamm:Entity;\n bamm:name \"SpatialPosition\";\n bamm:preferredName \"Spatial Position\"@en;\n bamm:description \"Position in space, described along three axis, with the third axis optional, if all positions are in a plane.\"@en;\n bamm:properties (:x :y :z).\n :x a bamm:Property;\n bamm:name \"x\";\n bamm:preferredName \"x\"@en;\n bamm:description \"x coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :y a bamm:Property;\n bamm:name \"y\";\n bamm:preferredName \"y\"@en;\n bamm:description \"y coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :z a bamm:Property;\n bamm:name \"z\";\n bamm:preferredName \"z\"@en;\n bamm:description \"z coordinate in space\"@en;\n bamm:characteristic :Coordinate;\n bamm:optional \"true\"^^xsd:boolean.\n :Coordinate a bamm-c:Measurement;\n bamm:name \"Coordinate\";\n bamm:preferredName \"Coordinate\"@en;\n bamm:description \"Represents a coordinate along an axis in space.\"@en;\n bamm:dataType xsd:float;\n bamm-c:unit unit:metre.\n :SpatialPositionCharacteristic a bamm-c:SingleEntity;\n bamm:name \"SpatialPositionCharacteristic\";\n bamm:preferredName \"Spatial Position Characteristic\"@en;\n bamm:description \"Represents a single position in space with optional z coordinate.\"@en;\n bamm:dataType :SpatialPosition.\n"; + + BammHelper bammHelper = new BammHelper(); + + assertTrue(bammHelper.loadBammModel(modelString).isSuccess()); + } + + @Test + public void testBammLoaderInUnknownVersion0_0_0_WithInvalidModelExpectFailure() { + final String modelString = "@prefix bamm: .\n @prefix bamm-c: .\n @prefix bamm-e: .\n @prefix unit: .\n @prefix rdf: .\n @prefix rdfs: .\n @prefix xsd: .\n @prefix : .\n \n :Movement a bamm:Aspect;\n bamm:name \"Movement\";\n bamm:preferredName \"Movement\"@en;\n bamm:description \"Aspect for movement information\"@en;\n bamm:properties (:isMoving :speedLimitWarning :position);\n bamm:operations ().\n :isMoving a bamm:Property;\n bamm:name \"isMoving\";\n bamm:preferredName \"Moving\"@en;\n bamm:description \"Flag indicating whether the asset is currently moving\"@en;\n bamm:characteristic bamm-c:Boolean.\n :speedLimitWarning a bamm:Property;\n bamm:name \"speedLimitWarning\";\n bamm:preferredName \"Speed Limit Warning\"@en;\n bamm:description \"Indicates if the speed limit is adhered to.\"@en;\n bamm:characteristic :TrafficLight.\n :position a bamm:Property;\n bamm:name \"position\";\n bamm:preferredName \"Position\"@en;\n bamm:description \"Indicates a position\"@en;\n bamm:characteristic :SpatialPositionCharacteristic.\n :TrafficLight a bamm-c:Enumeration;\n bamm:name \"TrafficLight\";\n bamm:preferredName \"Warning Level\"@en;\n bamm:description \"Represents if speed of position change is within specification (green), within tolerance (yellow), or outside specification (red).\"@en;\n bamm:dataType xsd:string;\n bamm-c:values (\"green\" \"yellow\" \"red\").\n :SpatialPosition a bamm:Entity;\n bamm:name \"SpatialPosition\";\n bamm:preferredName \"Spatial Position\"@en;\n bamm:description \"Position in space, described along three axis, with the third axis optional, if all positions are in a plane.\"@en;\n bamm:properties (:x :y :z).\n :x a bamm:Property;\n bamm:name \"x\";\n bamm:preferredName \"x\"@en;\n bamm:description \"x coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :y a bamm:Property;\n bamm:name \"y\";\n bamm:preferredName \"y\"@en;\n bamm:description \"y coordinate in space\"@en;\n bamm:characteristic :Coordinate.\n :z a bamm:Property;\n bamm:name \"z\";\n bamm:preferredName \"z\"@en;\n bamm:description \"z coordinate in space\"@en;\n bamm:characteristic :Coordinate;\n bamm:optional \"true\"^^xsd:boolean.\n :Coordinate a bamm-c:Measurement;\n bamm:name \"Coordinate\";\n bamm:preferredName \"Coordinate\"@en;\n bamm:description \"Represents a coordinate along an axis in space.\"@en;\n bamm:dataType xsd:float;\n bamm-c:unit unit:metre.\n :SpatialPositionCharacteristic a bamm-c:SingleEntity;\n bamm:name \"SpatialPositionCharacteristic\";\n bamm:preferredName \"Spatial Position Characteristic\"@en;\n bamm:description \"Represents a single position in space with optional z coordinate.\"@en;\n bamm:dataType :SpatialPosition.\n"; + + BammHelper bammHelper = new BammHelper(); + + assertEquals( + "AspectModelUrn cannot be found.", + assertThrows( NotImplementedError.class, () -> bammHelper.loadBammModel( modelString )).getMessage() ); + } } From 65b8d05081856b5ea50b68a6ffb805fdc1fde7c2 Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Wed, 26 Oct 2022 17:38:04 +0200 Subject: [PATCH 22/43] Add changelog file Signed-off-by: Johannes Kristan --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..1f335d2d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,39 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0-M2 +- [ ] doublecheck the correct version + +### Added +- Swagger UI now integrated with Portal Authentication +- Filter endpoint for search of list of URNs +- Support of BAMM 2.0.0 +- Helm Charts available via helm Repository at Eclipse Foundation + + ### fixed +- Update jackson-databind to version 2.13.3 (A1SLDT-952) +- Update snakeyaml to version to 1.33 (A1SLDT-954) +- Update commons-text (apache-org.apache.commons) to 1.10.0 + +### Changed +- Update of Spring Boot version to 2.7.3 + +## 0.1.0-M1 +- [ ] doublecheck the correct version + +### Added +- The Semantic Hub allows the creation, update, deletion of Semantic models in the RDF based format BAMM Aspect Meta Model (short BAMM) +- Users can use the Semantic Hub API to view models and their meta information (such as version and current status, e.g. whether it is ready to be used) +- The Semantic Hub provides endpoints to generate artifacts from the BAMM models: + - example payload JSON + - detailed HTML documentation describing the model + - a diagram of the model + - an OpenAPI description + - an Asset Administration Shell representation of the model as a submodel template + - a JSON schema representation of the model +- The Semantic Hub allows the reuse of model components between different BAMM aspects and can resolve these dependencies +- The Semantic Hub prevents unauthenticated access by checking whether an access token is provided by a CX user +- The Semantic Hub enforces that only users with the correct role can read/create/update/delete semantic models \ No newline at end of file From 4291f64872b01bbc338dc3d83b9541deb415cbe0 Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Wed, 26 Oct 2022 17:40:11 +0200 Subject: [PATCH 23/43] Update CHANGELOG.md --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f335d2d..2bc57691 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.1.0-M2 -- [ ] doublecheck the correct version ### Added - Swagger UI now integrated with Portal Authentication @@ -36,4 +35,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - a JSON schema representation of the model - The Semantic Hub allows the reuse of model components between different BAMM aspects and can resolve these dependencies - The Semantic Hub prevents unauthenticated access by checking whether an access token is provided by a CX user -- The Semantic Hub enforces that only users with the correct role can read/create/update/delete semantic models \ No newline at end of file +- The Semantic Hub enforces that only users with the correct role can read/create/update/delete semantic models From ed84ae1b5d35fbf252da1005e3f587913f931d2b Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Wed, 26 Oct 2022 17:40:44 +0200 Subject: [PATCH 24/43] Update CHANGELOG.md --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc57691..c40e91b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update of Spring Boot version to 2.7.3 ## 0.1.0-M1 -- [ ] doublecheck the correct version ### Added - The Semantic Hub allows the creation, update, deletion of Semantic models in the RDF based format BAMM Aspect Meta Model (short BAMM) From c388d8a7ffa8f221deb7c546325642cba5566f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Thu, 27 Oct 2022 10:58:36 +0200 Subject: [PATCH 25/43] Amend changelog with newest version tags --- CHANGELOG.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c40e91b8..6e8c0e31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,23 +5,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.1.0-M2 - ### Added - Swagger UI now integrated with Portal Authentication - Filter endpoint for search of list of URNs -- Support of BAMM 2.0.0 - Helm Charts available via helm Repository at Eclipse Foundation - ### fixed -- Update jackson-databind to version 2.13.3 (A1SLDT-952) -- Update snakeyaml to version to 1.33 (A1SLDT-954) -- Update commons-text (apache-org.apache.commons) to 1.10.0 +### Fixed +- Update jackson-databind to version 2.13.3 +- Update snakeyaml to version to 1.31 ### Changed - Update of Spring Boot version to 2.7.3 ## 0.1.0-M1 +### Fixed +- Adjust tests to avoid duplicates +### Changed +- Deactivate name search + +## 0.0.1-M2 +### Added +- The Semantic Hub now allows the generation and export of AAS files +### Changed +- Switched to BAMM SDK version 2.0.0 +## 0.0.1-M1 ### Added - The Semantic Hub allows the creation, update, deletion of Semantic models in the RDF based format BAMM Aspect Meta Model (short BAMM) - Users can use the Semantic Hub API to view models and their meta information (such as version and current status, e.g. whether it is ready to be used) From c4fc03d7afefedda66a86e4719eb20cc109105bb Mon Sep 17 00:00:00 2001 From: Johannes Kristan Date: Fri, 28 Oct 2022 14:23:12 +0200 Subject: [PATCH 26/43] Add documentation Signed-off-by: Johannes Kristan --- doc/documentation.md | 84 +++++++++++++++++++++++++++++++++++++++++++ doc/img/image001.png | Bin 0 -> 99328 bytes doc/img/image002.png | Bin 0 -> 48954 bytes doc/img/image003.png | Bin 0 -> 28623 bytes 4 files changed, 84 insertions(+) create mode 100644 doc/documentation.md create mode 100644 doc/img/image001.png create mode 100644 doc/img/image002.png create mode 100644 doc/img/image003.png diff --git a/doc/documentation.md b/doc/documentation.md new file mode 100644 index 00000000..684cd547 --- /dev/null +++ b/doc/documentation.md @@ -0,0 +1,84 @@ +##Architectural Overview +The SLDT Semantic Hub stores Semantic Model definitions and allows the generation of several artifacts. It restricts access to the models by authentication via a token and authorization via roles in the token claims. Therefore, the Hub interacts with a Keycloak instance. The models are created in the Hub during our governance process as depicted below. +![](img/image001.png) + + + +## Implementation +The following section describes the use cases implemented for the semantic hub. + +### Upload of an aspect model +![](img/image002.png) + + +| Validation | Description Value | +|---|---| +| BAMM compliance | Checks if the model is compliant with the BAMM. The BAMM SDK does provide the validation logic. | +| Model Status check (RELEASE vs DRAFT) | Uploads will always accepted when there are no existing namespace:version combination in the TripleStore. For a model in DRAFT state, uploads will always be accepted. For a model in RELEASE state, uploads will be denied. RELEASED models are immutable. | +| External reference check | It will be checked if all exernal references are available in the TripleStore. The BAMM SDK does provide a mechanisim where the resolving against the TripleStore can be integrated. | + + + +## Example: +Example Aspect Model +``` +@prefix xsd: . +@prefix bamm: . +@prefix : . + + +:DocumentationSimple a bamm:Aspect; +bamm:name "ManufacturerDocumentationSimple"; +bamm:preferredName "ManufacturerDocumentation"@en; +bamm:description "The Submodel defines a simplified set of manufacturer documentation to bring about information from manufacturer to operator of industrial equipment."@en; + +:documents a bamm:Property; +bamm:name "documents"; +bamm:preferredName "documents"@en; +bamm:description "Set of documents"@en; +``` + +The semantic hub will add the release status as triple upon upload: +Release Status +``` +@prefix aux: + + aux:releaseStatus aux:DRAFT . +``` + +## Package +| No | Rule | Example | +|---|---|---| +| 1. | A package is defined by the urn prefix until "#". | net.catenax.semantics.product:1.2.0# | +| 2. | A package can contain one or multiple aspects. | Example 1: net.catenax.semantics.traceability:1.2.0#Traceability Example 2: net.catenax.semantics.product:1.2.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:1.2.0#ProductDetails| +| 3. | Multiple versions of a package can exists. | Possible: net.catenax.semantics.product:1.2.0 net.catenax.semantics.product:4.2.0 | +| 4. | The versioning applies to the package. All aspects and model elements scoped to a package have the same version.| Possible: net.catenax.semantics.product:1.2.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:1.2.0#ProductDetails Possible:net.catenax.semantics.product:4.3.0#ProductDescription net.catenax.semantics.product:4.3.0#ProductUsage net.catenax.semantics.product:4.3.0#ProductDetails Not Possible: net.catenax.semantics.product:1.3.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:3.2.0#ProductDetails | +| 5. | All aspect models and model elements scoped to a package have the same status. | Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE, net.catenax.semantics.product:1.2.0#ProductUsage → RELEASE net.catenax.semantics.product:1.2.0#ProductDetails → RELEASE Not Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE net.catenax.semantics.product:1.2. + + + +## Download of the documentation of an Aspect Model +![](img/image003.png) + +Example queries to resolve an aspect model with all references: +Construct Query +``` +@prefix ns: + +CONSTRUCT { +?s ?p ?o . +} WHERE { +bind( ns: as ?aspect) +?aspect (<>|!<>)* ?s . // resolves all references +?s ?p ?o . +} +Search for Aspect Models +The current search API can stay as is. Below is an example query for selecting bamm properties: +Search Queries +CONSTRUCT { +?s ?p ?o . +} WHERE { +FILTER ( $param == ?o ) // Custom filter can be added here. +?s ?p ?o . +} +``` diff --git a/doc/img/image001.png b/doc/img/image001.png new file mode 100644 index 0000000000000000000000000000000000000000..2180bbc77b49fea0475921c0ae89adee16c92543 GIT binary patch literal 99328 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%0Dqt*C}Jq}rv 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≻(pwA{leP*KnA5JaSnmG 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 literal 0 HcmV?d00001 diff --git a/doc/img/image002.png b/doc/img/image002.png new file mode 100644 index 0000000000000000000000000000000000000000..34b6d82d0d1e39abed5da0186f99bdee074e8591 GIT binary patch literal 48954 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 literal 0 HcmV?d00001 From 9030e4ef8584cf29a7fea7d285da6c994fa1bbb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 1 Nov 2022 10:19:02 +0100 Subject: [PATCH 27/43] Fix some errors in the documentation --- doc/documentation.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/documentation.md b/doc/documentation.md index 684cd547..cd1b229b 100644 --- a/doc/documentation.md +++ b/doc/documentation.md @@ -1,8 +1,7 @@ -##Architectural Overview +## Architectural Overview The SLDT Semantic Hub stores Semantic Model definitions and allows the generation of several artifacts. It restricts access to the models by authentication via a token and authorization via roles in the token claims. Therefore, the Hub interacts with a Keycloak instance. The models are created in the Hub during our governance process as depicted below. -![](img/image001.png) - +![](img/image001.png) ## Implementation The following section describes the use cases implemented for the semantic hub. @@ -38,7 +37,7 @@ bamm:preferredName "documents"@en; bamm:description "Set of documents"@en; ``` -The semantic hub will add the release status as triple upon upload: +The Semantic Hub will add the release status as triple upon upload: Release Status ``` @prefix aux: @@ -53,7 +52,7 @@ Release Status | 2. | A package can contain one or multiple aspects. | Example 1: net.catenax.semantics.traceability:1.2.0#Traceability Example 2: net.catenax.semantics.product:1.2.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:1.2.0#ProductDetails| | 3. | Multiple versions of a package can exists. | Possible: net.catenax.semantics.product:1.2.0 net.catenax.semantics.product:4.2.0 | | 4. | The versioning applies to the package. All aspects and model elements scoped to a package have the same version.| Possible: net.catenax.semantics.product:1.2.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:1.2.0#ProductDetails Possible:net.catenax.semantics.product:4.3.0#ProductDescription net.catenax.semantics.product:4.3.0#ProductUsage net.catenax.semantics.product:4.3.0#ProductDetails Not Possible: net.catenax.semantics.product:1.3.0#ProductDescription net.catenax.semantics.product:1.2.0#ProductUsage net.catenax.semantics.product:3.2.0#ProductDetails | -| 5. | All aspect models and model elements scoped to a package have the same status. | Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE, net.catenax.semantics.product:1.2.0#ProductUsage → RELEASE net.catenax.semantics.product:1.2.0#ProductDetails → RELEASE Not Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE net.catenax.semantics.product:1.2. +| 5. | All aspect models and model elements scoped to a package have the same status. | Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE, net.catenax.semantics.product:1.2.0#ProductUsage → RELEASE net.catenax.semantics.product:1.2.0#ProductDetails → RELEASE Not Possible: net.catenax.semantics.product:1.2.0#ProductDescription → RELEASE, net.catenax.semantics.product:1.2.0#ProductUsage → DRAFT From 3ddfe5c00d4558425f2e9789580ca349dd9ac6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 1 Nov 2022 15:31:59 +0100 Subject: [PATCH 28/43] Fix KICS findings This commit fixes the KICS scan findings (errors) that occur because of the missing array maxItems property. --- backend/deployment/.vscode/settings.json | 6 ++++++ backend/src/main/resources/static/semantic-hub-openapi.yaml | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 backend/deployment/.vscode/settings.json diff --git a/backend/deployment/.vscode/settings.json b/backend/deployment/.vscode/settings.json new file mode 100644 index 00000000..960918c0 --- /dev/null +++ b/backend/deployment/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "yaml.schemas": { + "https://json.schemastore.org/github-workflow.json": "/.github/workflows/*.yml", + "https://json.schemastore.org/helmfile.json": "file:///Users/ROL2BE/development/CATENA-X/sldt-semantic-hub-aas/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml" + } +} \ No newline at end of file diff --git a/backend/src/main/resources/static/semantic-hub-openapi.yaml b/backend/src/main/resources/static/semantic-hub-openapi.yaml index aea287c0..46310e16 100644 --- a/backend/src/main/resources/static/semantic-hub-openapi.yaml +++ b/backend/src/main/resources/static/semantic-hub-openapi.yaml @@ -152,6 +152,7 @@ paths: application/json: schema: type: array + maxItems: 10000 items: type: string responses: @@ -441,6 +442,7 @@ components: items: title: Items type: array + maxItems: 10000 items: $ref: '#/components/schemas/SemanticModel' totalItems: From da7d717238f308c7951124196daa4519f07f80f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 8 Nov 2022 11:08:26 +0100 Subject: [PATCH 29/43] Remove settings file from PR --- backend/deployment/.vscode/settings.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 backend/deployment/.vscode/settings.json diff --git a/backend/deployment/.vscode/settings.json b/backend/deployment/.vscode/settings.json deleted file mode 100644 index 960918c0..00000000 --- a/backend/deployment/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "yaml.schemas": { - "https://json.schemastore.org/github-workflow.json": "/.github/workflows/*.yml", - "https://json.schemastore.org/helmfile.json": "file:///Users/ROL2BE/development/CATENA-X/sldt-semantic-hub-aas/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml" - } -} \ No newline at end of file From 4beb0cb43e27f57f58a3eca5299f5254868f95e9 Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Mon, 14 Nov 2022 17:37:23 +0100 Subject: [PATCH 30/43] Increase security-core to 5.7.5 --- pom.xml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 8e5e1fa2..cf323535 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.3 + 2.7.5 @@ -65,10 +65,11 @@ - 2.7.3 + 2.7.5 3.1.3 1.6.6 2.9.2 + 5.7.5 4.4 1.18.22 1.3.2 @@ -160,6 +161,14 @@ + + + org.springframework.security + spring-security-core + ${spring.security.version} + org.springframework.boot From b3d13686ca053da56a40ea3657f935bf1d0da5aa Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Mon, 14 Nov 2022 17:46:17 +0100 Subject: [PATCH 31/43] Adjust sparqlquery to use case insensitive and add tests --- .../persistence/triplestore/SparqlQueries.java | 4 ++-- .../semantics/hub/ModelsApiFilterTest.java | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java index dbb6c8a7..6afa00e4 100644 --- a/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java +++ b/backend/src/main/java/org/eclipse/tractusx/semantics/hub/persistence/triplestore/SparqlQueries.java @@ -148,7 +148,7 @@ public class SparqlQueries { + "BIND(iri(concat(strbefore(str(?aspect ), \"#\"), \"#\")) AS ?package)\n" + "?package aux:status ?status\n" + "FILTER ( !bound(?statusFilter) || contains(str(?status), ?statusFilter) )\n" - + "FILTER ( !bound(?namespaceFilter) || contains(str(?aspect), ?namespaceFilter ) )\n" + + "FILTER ( !bound(?namespaceFilter) || contains(lcase(str(?aspect)), lcase(?namespaceFilter) ) )\n" + "}\n"; private static final String FILTER_QUERY_MINIMAL_WHERE_CLAUSE_SELECTIVE = "WHERE {\n" @@ -163,7 +163,7 @@ public class SparqlQueries { + "BIND(iri(concat(strbefore(str(?aspect ), \"#\"), \"#\")) AS ?package)\n" + "?package aux:status ?status\n" + "FILTER ( !bound(?statusFilter) || contains(str(?status), ?statusFilter) )\n" - + "FILTER ( !bound(?namespaceFilter) || contains(str(?aspect), ?namespaceFilter ) )\n" + + "FILTER ( !bound(?namespaceFilter) || contains(lcase(str(?aspect)), lcase(?namespaceFilter) ) )\n" + "FILTER ( str(?aspect) IN (?urns) ) " + "}\n"; diff --git a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java index f9a900cd..272a5a34 100644 --- a/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java +++ b/backend/src/test/java/org/eclipse/tractusx/semantics/hub/ModelsApiFilterTest.java @@ -76,6 +76,22 @@ public void testGetByNamespaceExpectResultsFound() throws Exception { .andExpect( MockMvcResultMatchers.status().isOk() ); } + @Test + public void testGetByNamespaceWithCaseInsensitiveExpectResultsFound() throws Exception { + mvc.perform( + MockMvcRequestBuilders.get( + "/api/v1/models?namespaceFilter=urn:bamm:org.eclipse.TRACtusx.TraceaBiliTy" ) + .accept( MediaType.APPLICATION_JSON ) + .with(jwtTokenFactory.allRoles()) + ) + .andDo( MockMvcResultHandlers.print() ) + .andExpect( jsonPath( "$.items" ).isArray() ) + .andExpect( jsonPath( "$.items.length()" ).value( 1 ) ) + .andExpect( jsonPath( "$.totalItems", equalTo( 1 ) ) ) + .andExpect( jsonPath( "$.itemCount", equalTo( 1 ) ) ) + .andExpect( MockMvcResultMatchers.status().isOk() ); + } + @Test public void testGetModelListByAvailablePropertyTypeExpectResultsFound() throws Exception { mvc.perform( From 2df65029729d2a6415069568810b6f72ff835619 Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Tue, 15 Nov 2022 11:11:58 +0100 Subject: [PATCH 32/43] fix cve-2022-40156 --- backend/pom.xml | 11 ++++++++++- pom.xml | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/pom.xml b/backend/pom.xml index 7b80b5b8..b603cf17 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -110,7 +110,16 @@ jackson-dataformat-xml com.fasterxml.jackson.dataformat - 2.12.7 + + + com.fasterxml.woodstox + woodstox-core + + + + + com.fasterxml.woodstox + woodstox-core io.openmanufacturing diff --git a/pom.xml b/pom.xml index 8e5e1fa2..0e005dd6 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,9 @@ 2.13.1 2.13.4.2 + 2.12.7 20211205 + 6.4.0 2.0.6 @@ -261,6 +263,16 @@ jackson-datatype-jsr310 ${jackson.version} + + jackson-dataformat-xml + com.fasterxml.jackson.dataformat + ${jackson.dataformat.version} + + + com.fasterxml.woodstox + woodstox-core + ${woodstoxcore.version} + From 9a1ac243f86025c09ea0805f74c60f0dbfc5e1de Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Mon, 14 Nov 2022 17:43:48 +0100 Subject: [PATCH 33/43] Increase jackson-databind version to 2.14.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e61ea9da..ad0386de 100644 --- a/pom.xml +++ b/pom.xml @@ -87,7 +87,7 @@ 2.13.1 - 2.13.4.2 + 2.14.0 2.12.7 20211205 6.4.0 From e7ce545e4dbeb0137b2ac5dea2725e9b5afff66a Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Thu, 17 Nov 2022 10:03:32 +0100 Subject: [PATCH 34/43] adjust readme + values.yaml --- README.md | 4 +++- backend/deployment/semantic-hub/values.yaml | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6576c7a9..2a790d90 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ The source code under this folder contains reference implementations of the SLDT Run `mvn install` to run unit tests, build and install the package. ## Run Package Locally -To check whether the build was successful, you can start the resulting JAR file from the build process by running `java -jar target/semantic-hub-{current-version}.jar`. +To check whether the build was successful, you can start the resulting JAR file from the build process by running `java -jar target/semantic-hub-backend-{current-version}.jar`. ## Build Docker Run `docker build -t semantic-hub .` @@ -47,6 +47,8 @@ Before deploying the Semantic Hub, enable a few add-ons in your minikube cluster `minikube addons enable ingress` +If you want to use the in-memory triple store that is not persistent (useful for local deployments) set `embeddedTripleStore: true`. + In order to deploy the helm chart, first create a new namespace "semantics": `kubectl create namespace semantics`. Then run `helm install hub -n semantics ./deployment/semantic-hub`. This will set up a new helm deployment in the semantics namespace. By default, the deployment contains the Semantic Hub instance itself, and a Fuseki Triplestore. diff --git a/backend/deployment/semantic-hub/values.yaml b/backend/deployment/semantic-hub/values.yaml index a7487e61..3b263ef1 100644 --- a/backend/deployment/semantic-hub/values.yaml +++ b/backend/deployment/semantic-hub/values.yaml @@ -25,10 +25,11 @@ hub: containerPort: 4242 ## Use in-memory triple store that is not persistent embeddedTripleStore: false - host: host + host: minikube ## If 'authentication' is set to false, no OAuth authentication is enforced authentication: false idpIssuerUri: https://idp-url + idpClientId: idpClientID ## Ignored if 'graphdb.enabled' is set to true graphdbBaseUrl: http://graphdb:3030 service: From acfd9d5c5a8baee01c4732fc5d3f0abce32afd50 Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Fri, 25 Nov 2022 10:57:16 +0100 Subject: [PATCH 35/43] increase chart version --- backend/deployment/semantic-hub/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/deployment/semantic-hub/Chart.yaml b/backend/deployment/semantic-hub/Chart.yaml index 34cf7e1b..b14564a6 100644 --- a/backend/deployment/semantic-hub/Chart.yaml +++ b/backend/deployment/semantic-hub/Chart.yaml @@ -3,5 +3,5 @@ name: semantic-hub description: Helm Chart for the Catena-X Semantic Hub Application type: application -version: 0.1.2 +version: 0.1.3 appVersion: 0.1.0-M2 \ No newline at end of file From 7e4e4ab4c0b040ea248a0bd122385060ade9678d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 29 Nov 2022 15:31:41 +0100 Subject: [PATCH 36/43] Fix gitleaks workflow These changes adjust the existing gitleaks workflow to fix a problem with the sarif upload --- .github/workflows/gitleaks.yml | 44 ++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index 1e8c2ba0..08cd4860 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -18,8 +18,11 @@ name: gitleaks on: [push, pull_request, workflow_dispatch] jobs: - gitleaks: + gitleaks-run: runs-on: ubuntu-latest + container: + image: zricethezav/gitleaks:latest + options: --user root steps: - name: Checkout uses: actions/checkout@v2 @@ -28,30 +31,29 @@ jobs: - name: Run Gitleaks id: gitleaks - uses: DariuszPorowski/github-action-gitleaks@v2 - with: - report_format: "sarif" - fail: true - # config: "/.gitleaks/GitleaksUdmCombo.toml" - - - name: Get the output from the gitleaks step run: | - echo "exitcode: ${{ steps.gitleaks.outputs.exitcode }}" - echo "result: ${{ steps.gitleaks.outputs.result }}" - echo "output: ${{ steps.gitleaks.outputs.output }}" - echo "command: ${{ steps.gitleaks.outputs.command }}" - echo "report: ${{ steps.gitleaks.outputs.report }}" - if: always() + git config --global --add safe.directory $PWD + gitleaks detect -f sarif -r ./gitleaks-report-semantic-hub.sarif --exit-code 0 - - name: Upload Gitleaks output as artifact - uses: actions/upload-artifact@v1 + - name: Upload artifact + uses: actions/upload-artifact@v3 with: - name: gitleaks.sarif - path: ${{ steps.gitleaks.outputs.report }} - if: always() + name: gitleaks-report + path: ./gitleaks-report-semantic-hub.sarif + + gitleaks-upload: + runs-on: ubuntu-latest + needs: gitleaks-run + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: gitleaks-report - name: Upload SARIF report if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: ${{ steps.gitleaks.outputs.report }} + sarif_file: ./gitleaks-report-semantic-hub.sarif From c6e3901462328cbc46a078fbbb9c614918311ca6 Mon Sep 17 00:00:00 2001 From: Sebastian Scherer <59142915+the-tatanka@users.noreply.github.com> Date: Tue, 29 Nov 2022 21:27:47 +0100 Subject: [PATCH 37/43] Update kics.yml --- .github/workflows/kics.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/kics.yml b/.github/workflows/kics.yml index 66823274..e9ae7138 100644 --- a/.github/workflows/kics.yml +++ b/.github/workflows/kics.yml @@ -42,7 +42,7 @@ jobs: security-events: write steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: KICS scan uses: checkmarx/kics-github-action@master @@ -67,6 +67,6 @@ jobs: # Upload findings to GitHub Advanced Security Dashboard - name: Upload SARIF file for GitHub Advanced Security Dashboard if: always() - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 with: - sarif_file: kicsResults/results.sarif \ No newline at end of file + sarif_file: kicsResults/results.sarif From 4cc14e8a552c51b7efc2d8afe30a803e5afbd93f Mon Sep 17 00:00:00 2001 From: Sebastian Scherer <59142915+the-tatanka@users.noreply.github.com> Date: Tue, 29 Nov 2022 21:31:16 +0100 Subject: [PATCH 38/43] Update and rename trivy-scan.yml to trivy.yml --- .../workflows/{trivy-scan.yml => trivy.yml} | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) rename .github/workflows/{trivy-scan.yml => trivy.yml} (66%) diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy.yml similarity index 66% rename from .github/workflows/trivy-scan.yml rename to .github/workflows/trivy.yml index eedc1420..174b1d98 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy.yml @@ -15,14 +15,42 @@ # under the License. # SPDX-License-Identifier: Apache-2.0 -name: Trivy Scan +name: Trivy on: schedule: - - cron: 0 2 * * 0 + - cron: 0 0 * * * workflow_dispatch: jobs: + analyze-config: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Run Trivy vulnerability scanner in repo mode + uses: aquasecurity/trivy-action@master + with: + scan-type: "config" + # ignore-unfixed: true + exit-code: "1" + hide-progress: false + format: "sarif" + output: "trivy-results1.sarif" + severity: "CRITICAL,HIGH" + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: "trivy-results1.sarif" + analyze-semantic-hub: runs-on: ubuntu-latest permissions: @@ -32,15 +60,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 + - uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '11' - name: Build JAR run: mvn clean package + - name: Build Image run: docker build -t semantic-hub ./backend + - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master with: @@ -53,7 +84,7 @@ jobs: severity: "CRITICAL,HIGH" - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@v1 + uses: github/codeql-action/upload-sarif@v2 if: always() with: sarif_file: "trivy-results-semantic-hub.sarif" From 2a1a1f6ccabaabcba716bf4c74bcaa3d673a88ee Mon Sep 17 00:00:00 2001 From: Tunahan Cicek Date: Thu, 1 Dec 2022 15:58:01 +0100 Subject: [PATCH 39/43] fix CVE-2022-31692 (spring-security-web) * update org.springframework.boot to version 2.7.6 * remove dependency spring-security-core since the version of spring-security-core is updated over the dependency org.springframework.boot --- pom.xml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index ad0386de..f5bb4629 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.5 + 2.7.6 @@ -65,11 +65,10 @@ - 2.7.5 + 2.7.6 3.1.3 1.6.6 2.9.2 - 5.7.5 4.4 1.18.22 1.3.2 @@ -163,14 +162,6 @@ - - - org.springframework.security - spring-security-core - ${spring.security.version} - org.springframework.boot From d2bf483eaf378d4c514ed9edfd44f6b1bbeddf88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 6 Dec 2022 17:21:08 +0100 Subject: [PATCH 40/43] Add workflow to create Helm chart releases These changes include a new GH Actions workflow that allows to create new Helm chart releases using GitHub Pages. --- .github/workflows/helm-release.yml | 36 +++++++++++++++++++ .../semantic-hub/.helmignore | 0 .../semantic-hub/Chart.lock | 0 .../semantic-hub/Chart.yaml | 0 .../semantic-hub/templates/_helpers.tpl | 0 .../templates/graphdb/graphdb-deployment.yaml | 0 .../templates/graphdb/graphdb-pvc.yaml | 0 .../templates/graphdb/graphdb-service.yaml | 0 .../templates/hub/hub-deployment.yaml | 0 .../templates/hub/hub-ingress.yaml | 0 .../templates/hub/hub-secret.yaml | 0 .../templates/hub/hub-service.yaml | 0 .../semantic-hub/values.yaml | 0 13 files changed, 36 insertions(+) create mode 100644 .github/workflows/helm-release.yml rename {backend/deployment => charts}/semantic-hub/.helmignore (100%) rename {backend/deployment => charts}/semantic-hub/Chart.lock (100%) rename {backend/deployment => charts}/semantic-hub/Chart.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/_helpers.tpl (100%) rename {backend/deployment => charts}/semantic-hub/templates/graphdb/graphdb-deployment.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/graphdb/graphdb-pvc.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/graphdb/graphdb-service.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/hub/hub-deployment.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/hub/hub-ingress.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/hub/hub-secret.yaml (100%) rename {backend/deployment => charts}/semantic-hub/templates/hub/hub-service.yaml (100%) rename {backend/deployment => charts}/semantic-hub/values.yaml (100%) diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml new file mode 100644 index 00000000..20eaaf92 --- /dev/null +++ b/.github/workflows/helm-release.yml @@ -0,0 +1,36 @@ +name: Release - Helm Charts + +on: + push: + paths: + - 'charts/**' + branches: + - main + workflow_dispatch: + +jobs: + release: + permissions: + contents: write + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Configure Git + run: | + git config user.name "$GITHUB_ACTOR" + git config user.email "$GITHUB_ACTOR@users.noreply.github.com" + + - name: Install Helm + uses: azure/setup-helm@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.4.1 + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/backend/deployment/semantic-hub/.helmignore b/charts/semantic-hub/.helmignore similarity index 100% rename from backend/deployment/semantic-hub/.helmignore rename to charts/semantic-hub/.helmignore diff --git a/backend/deployment/semantic-hub/Chart.lock b/charts/semantic-hub/Chart.lock similarity index 100% rename from backend/deployment/semantic-hub/Chart.lock rename to charts/semantic-hub/Chart.lock diff --git a/backend/deployment/semantic-hub/Chart.yaml b/charts/semantic-hub/Chart.yaml similarity index 100% rename from backend/deployment/semantic-hub/Chart.yaml rename to charts/semantic-hub/Chart.yaml diff --git a/backend/deployment/semantic-hub/templates/_helpers.tpl b/charts/semantic-hub/templates/_helpers.tpl similarity index 100% rename from backend/deployment/semantic-hub/templates/_helpers.tpl rename to charts/semantic-hub/templates/_helpers.tpl diff --git a/backend/deployment/semantic-hub/templates/graphdb/graphdb-deployment.yaml b/charts/semantic-hub/templates/graphdb/graphdb-deployment.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/graphdb/graphdb-deployment.yaml rename to charts/semantic-hub/templates/graphdb/graphdb-deployment.yaml diff --git a/backend/deployment/semantic-hub/templates/graphdb/graphdb-pvc.yaml b/charts/semantic-hub/templates/graphdb/graphdb-pvc.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/graphdb/graphdb-pvc.yaml rename to charts/semantic-hub/templates/graphdb/graphdb-pvc.yaml diff --git a/backend/deployment/semantic-hub/templates/graphdb/graphdb-service.yaml b/charts/semantic-hub/templates/graphdb/graphdb-service.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/graphdb/graphdb-service.yaml rename to charts/semantic-hub/templates/graphdb/graphdb-service.yaml diff --git a/backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml b/charts/semantic-hub/templates/hub/hub-deployment.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/hub/hub-deployment.yaml rename to charts/semantic-hub/templates/hub/hub-deployment.yaml diff --git a/backend/deployment/semantic-hub/templates/hub/hub-ingress.yaml b/charts/semantic-hub/templates/hub/hub-ingress.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/hub/hub-ingress.yaml rename to charts/semantic-hub/templates/hub/hub-ingress.yaml diff --git a/backend/deployment/semantic-hub/templates/hub/hub-secret.yaml b/charts/semantic-hub/templates/hub/hub-secret.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/hub/hub-secret.yaml rename to charts/semantic-hub/templates/hub/hub-secret.yaml diff --git a/backend/deployment/semantic-hub/templates/hub/hub-service.yaml b/charts/semantic-hub/templates/hub/hub-service.yaml similarity index 100% rename from backend/deployment/semantic-hub/templates/hub/hub-service.yaml rename to charts/semantic-hub/templates/hub/hub-service.yaml diff --git a/backend/deployment/semantic-hub/values.yaml b/charts/semantic-hub/values.yaml similarity index 100% rename from backend/deployment/semantic-hub/values.yaml rename to charts/semantic-hub/values.yaml From e6ba03b3d866320b384d114354ded78b930760a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Tue, 6 Dec 2022 17:30:27 +0100 Subject: [PATCH 41/43] Move content from doc folder to docs --- {doc => docs}/documentation.md | 0 {doc => docs}/img/image001.png | Bin {doc => docs}/img/image002.png | Bin {doc => docs}/img/image003.png | Bin 4 files changed, 0 insertions(+), 0 deletions(-) rename {doc => docs}/documentation.md (100%) rename {doc => docs}/img/image001.png (100%) rename {doc => docs}/img/image002.png (100%) rename {doc => docs}/img/image003.png (100%) diff --git a/doc/documentation.md b/docs/documentation.md similarity index 100% rename from doc/documentation.md rename to docs/documentation.md diff --git a/doc/img/image001.png b/docs/img/image001.png similarity index 100% rename from doc/img/image001.png rename to docs/img/image001.png diff --git a/doc/img/image002.png b/docs/img/image002.png similarity index 100% rename from doc/img/image002.png rename to docs/img/image002.png diff --git a/doc/img/image003.png b/docs/img/image003.png similarity index 100% rename from doc/img/image003.png rename to docs/img/image003.png From b68aaa385fb051e5df20d418211b719801ec1502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Wed, 7 Dec 2022 11:10:07 +0100 Subject: [PATCH 42/43] Add missing copyright header and adjust README These changes add the missing copyright header to the new helm chart release workflow and adjusts the README file to include changes to the documentation. --- .github/workflows/helm-release.yml | 19 +++++++++++++++++++ README.md | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml index 20eaaf92..3e64fdf2 100644 --- a/.github/workflows/helm-release.yml +++ b/.github/workflows/helm-release.yml @@ -1,3 +1,22 @@ +# Copyright (c) 2021-2022 Copyright (c) 2021-2022 Robert Bosch Manufacturing Solutions GmbH +# Copyright (c) 2021-2022 Contributors to the Eclipse Foundation + +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. + +# This program and the accompanying materials are made available under the +# terms of the Apache License, Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0. + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# SPDX-License-Identifier: Apache-2.0 +--- + name: Release - Helm Charts on: diff --git a/README.md b/README.md index 2a790d90..e3076380 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Run `docker build -t semantic-hub .` In case you want to publish your image into a remote container registry, apply the tag accordingly and `docker push` the image. ## Deploy using Helm and K8s -If you have a running Kubernetes cluster available, you can deploy the Semantic Hub using our Helm Chart, which is located under `./deployment/semantic-hub`. +If you have a running Kubernetes cluster available, you can deploy the Semantic Hub using our Helm Chart, which is located under `./charts/semantic-hub`. In case you don't have a running cluster, you can set up one by yourself locally, using [minikube](https://minikube.sigs.k8s.io/docs/start/). In the following, we will use a minikube cluster for reference. @@ -51,7 +51,7 @@ If you want to use the in-memory triple store that is not persistent (useful for In order to deploy the helm chart, first create a new namespace "semantics": `kubectl create namespace semantics`. -Then run `helm install hub -n semantics ./deployment/semantic-hub`. This will set up a new helm deployment in the semantics namespace. By default, the deployment contains the Semantic Hub instance itself, and a Fuseki Triplestore. +Then run `helm install hub -n semantics ./charts/semantic-hub`. This will set up a new helm deployment in the semantics namespace. By default, the deployment contains the Semantic Hub instance itself, and a Fuseki Triplestore. Check that the two containers are running by calling `kubectl get pod -n semantics`. From 4984ec3d22f14b6190daad65fa1ed17927a41f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20R=C3=B6mer?= Date: Wed, 7 Dec 2022 11:14:23 +0100 Subject: [PATCH 43/43] Remove Chart.lock file --- charts/semantic-hub/Chart.lock | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 charts/semantic-hub/Chart.lock diff --git a/charts/semantic-hub/Chart.lock b/charts/semantic-hub/Chart.lock deleted file mode 100644 index ef93b5ce..00000000 --- a/charts/semantic-hub/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: cert-manager - repository: https://charts.jetstack.io - version: v1.7.1 -digest: sha256:7996cd611e44d44b023a677c2ff4beb4104aedede18f5d4e182c11323b957a23 -generated: "2022-03-22T14:25:36.810497+01:00"