diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala index 0506b80113..e5c7277a14 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/OntologyResponderV2.scala @@ -1403,12 +1403,15 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon currentTime: Instant = Instant.now + cardinalitiesToAdd: Map[SmartIri, KnoraCardinalityInfo] = + newInternalClassDefWithLinkValueProps.directCardinalities -- existingClassDef.directCardinalities.keySet + updateSparql = org.knora.webapi.messages.twirl.queries.sparql.v2.txt .addCardinalitiesToClass( ontologyNamedGraphIri = internalOntologyIri, ontologyIri = internalOntologyIri, classIri = internalClassIri, - cardinalitiesToAdd = newInternalClassDefWithLinkValueProps.directCardinalities, + cardinalitiesToAdd = cardinalitiesToAdd, lastModificationDate = addCardinalitiesRequest.lastModificationDate, currentTime = currentTime ) diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/AddCardinalitiesToClassSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/AddCardinalitiesToClassSpec.scala new file mode 100644 index 0000000000..20994ef135 --- /dev/null +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ontology/AddCardinalitiesToClassSpec.scala @@ -0,0 +1,111 @@ +/* + * Copyright © 2021 - 2022 Swiss National Data and Service Center for the Humanities and/or DaSCH Service Platform contributors. + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.knora.webapi.responders.v2.ontology + +import akka.util.Timeout +import org.knora.webapi.ApiV2Complex +import org.knora.webapi.CoreSpec +import org.knora.webapi.messages.IriConversions._ +import org.knora.webapi.messages.OntologyConstants +import org.knora.webapi.messages.SmartIri +import org.knora.webapi.messages.StringFormatter +import org.knora.webapi.messages.store.triplestoremessages.RdfDataObject +import org.knora.webapi.messages.store.triplestoremessages.SmartIriLiteralV2 +import org.knora.webapi.messages.store.triplestoremessages.SparqlSelectRequest +import org.knora.webapi.messages.util.rdf.SparqlSelectResult +import org.knora.webapi.messages.v2.responder.ontologymessages.AddCardinalitiesToClassRequestV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.Cardinality +import org.knora.webapi.messages.v2.responder.ontologymessages.ClassInfoContentV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.ClassesGetRequestV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.OntologyMetadataGetByIriRequestV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.PredicateInfoV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.ReadOntologyMetadataV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.ReadOntologyV2 +import org.knora.webapi.sharedtestdata.SharedTestDataADM + +import java.time.Instant +import java.util.UUID +import scala.concurrent.duration._ + +/** + * This spec is used to test [[org.knora.webapi.responders.v2.ontology.Cardinalities]]. + */ +class AddCardinalitiesToClassSpec extends CoreSpec { + + private implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance + private implicit val timeout = 10.seconds + private val freeTestOntology = RdfDataObject( + path = "test_data/ontologies/freetest-onto.ttl", + name = "http://www.knora.org/ontology/0001/freetest" + ) + override lazy val rdfDataObjects: List[RdfDataObject] = List(freeTestOntology) + + private def getCardinalityCountFromTriplestore(classIri: SmartIri, propertyIri: SmartIri) = { + val sparqlCountQuery = + s"""|PREFIX owl: + |PREFIX rdfs: + | + |SELECT (count(?blanknode) as ?count) + |WHERE { + | <$classIri> rdfs:subClassOf ?blanknode + | FILTER isBlank(?blanknode) + | ?blanknode owl:onProperty <$propertyIri> + |} + |""".stripMargin + appActor ! SparqlSelectRequest(sparqlCountQuery) + val sparqlCountResponseInitial = expectMsgType[SparqlSelectResult] + sparqlCountResponseInitial.results.bindings.head.rowMap.values.head + } + + val freetestOntologyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/freetest/v2".toSmartIri + + "The ontology responder" should { + "add a cardinality to a class without duplicating all existing cardinalities in the triplestore" in { + + val user = SharedTestDataADM.anythingAdminUser + val classIri = "http://www.knora.org/ontology/0001/freetest#Book".toSmartIri + val propertyIri = "http://www.knora.org/ontology/0001/freetest#hasAuthor".toSmartIri + val newPropertyIri = "http://www.knora.org/ontology/0001/freetest#hasName".toSmartIri + + // check ontology first + appActor ! OntologyMetadataGetByIriRequestV2(ontologyIris = Set(freetestOntologyIri), requestingUser = user) + val ontologyMetadata = expectMsgType[ReadOntologyMetadataV2] + val ontologyLastModificationDate = ontologyMetadata.ontologies.head.lastModificationDate + .getOrElse(throw new AssertionError("Ontology does not have a LastModificationDate")) + + // assert that the cardinality for `:hasAuthor` is only once in the triplestore + val countInitial = getCardinalityCountFromTriplestore(classIri, propertyIri) + assert(countInitial == "1") + + // add an additional cardinality to the class + val addCardinalitiesRequest = AddCardinalitiesToClassRequestV2( + classInfoContent = ClassInfoContentV2( + classIri = classIri.toOntologySchema(ApiV2Complex), + ontologySchema = ApiV2Complex, + predicates = Map( + OntologyConstants.Rdf.Type.toSmartIri -> PredicateInfoV2( + predicateIri = OntologyConstants.Rdf.Type.toSmartIri, + objects = Vector(SmartIriLiteralV2(value = OntologyConstants.Owl.Class.toSmartIri)) + ) + ), + directCardinalities = + Map(newPropertyIri -> Cardinality.KnoraCardinalityInfo(cardinality = Cardinality.MayHaveOne)) + ), + lastModificationDate = ontologyLastModificationDate, + apiRequestID = UUID.randomUUID, + requestingUser = user + ) + appActor ! addCardinalitiesRequest + val addCardinalitiesResponse = expectMsgType[ReadOntologyV2] + + // assert that the cardinalities for `:hasAuthor` and `:hasName` is still only once in the triplestore + val countAfterwards = getCardinalityCountFromTriplestore(classIri, propertyIri) + assert(countAfterwards == "1") + val countNewProperty = getCardinalityCountFromTriplestore(classIri, newPropertyIri) + assert(countNewProperty == "1") + } + } +}