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 91c165cd80..dd6465d557 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 @@ -3744,19 +3744,6 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon featureFactoryConfig = changePropertyLabelsOrCommentsRequest.featureFactoryConfig ) - // Check that the new labels/comments are different from the current ones. - - currentLabelsOrComments: Seq[OntologyLiteralV2] = currentReadPropertyInfo.entityInfoContent.predicates - .get(changePropertyLabelsOrCommentsRequest.predicateToUpdate) match { - case Some(pred) => pred.objects - case None => Seq.empty[OntologyLiteralV2] - } - - _ = if (currentLabelsOrComments == changePropertyLabelsOrCommentsRequest.newObjects) { - throw BadRequestException( - s"The submitted objects of ${changePropertyLabelsOrCommentsRequest.propertyIri} are the same as the current ones in property ${changePropertyLabelsOrCommentsRequest.propertyIri}") - } - // If this is a link property, also change the labels/comments of the corresponding link value property. maybeCurrentLinkValueReadPropertyInfo: Option[ReadPropertyInfoV2] = if (currentReadPropertyInfo.isLinkProp) { @@ -3937,19 +3924,6 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon featureFactoryConfig = changeClassLabelsOrCommentsRequest.featureFactoryConfig ) - // Check that the new labels/comments are different from the current ones. - - currentLabelsOrComments: Seq[OntologyLiteralV2] = currentReadClassInfo.entityInfoContent.predicates - .get(changeClassLabelsOrCommentsRequest.predicateToUpdate) match { - case Some(pred) => pred.objects - case None => Seq.empty[OntologyLiteralV2] - } - - _ = if (currentLabelsOrComments == changeClassLabelsOrCommentsRequest.newObjects) { - throw BadRequestException( - s"The submitted objects of ${changeClassLabelsOrCommentsRequest.predicateToUpdate} are the same as the current ones in class ${changeClassLabelsOrCommentsRequest.classIri}") - } - // Do the update. currentTime: Instant = Instant.now diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala index d0711aff7a..be782644d0 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/OntologyResponderV2Spec.scala @@ -1950,6 +1950,41 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } } + "change the labels of a property, submitting the same labels again" in { + val propertyIri = AnythingOntologyIri.makeEntityIri("hasName") + + val newObjects = Seq( + StringLiteralV2("has name", Some("en")), + StringLiteralV2("a nom", Some("fr")), + StringLiteralV2("hat Namen", Some("de")) + ) + + responderManager ! ChangePropertyLabelsOrCommentsRequestV2( + propertyIri = propertyIri, + predicateToUpdate = OntologyConstants.Rdfs.Label.toSmartIri, + newObjects = newObjects, + lastModificationDate = anythingLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { + case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo = externalOntology.properties(propertyIri) + readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( + newObjects) + + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate + } + } + "not allow a user to change the comments of a property if they are not a sysadmin or an admin in the ontology's project" in { val propertyIri = AnythingOntologyIri.makeEntityIri("hasName") @@ -2018,6 +2053,46 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } } + "change the comments of a property, submitting the same comments again" in { + val propertyIri = AnythingOntologyIri.makeEntityIri("hasName") + + val newObjects = Seq( + StringLiteralV2("The name of a Thing", Some("en")), + StringLiteralV2("Le nom d\\'une chose", Some("fr")), // This is SPARQL-escaped as it would be if taken from a JSON-LD request. + StringLiteralV2("Der Name eines Dinges", Some("de")) + ) + + // Make an unescaped copy of the new comments, because this is how we will receive them in the API response. + val newObjectsUnescaped = newObjects.map { + case StringLiteralV2(text, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) + } + + responderManager ! ChangePropertyLabelsOrCommentsRequestV2( + propertyIri = propertyIri, + predicateToUpdate = OntologyConstants.Rdfs.Comment.toSmartIri, + newObjects = newObjects, + lastModificationDate = anythingLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { + case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.properties.size == 1) + val readPropertyInfo = externalOntology.properties(propertyIri) + readPropertyInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( + newObjectsUnescaped) + + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate + } + } + "not allow a user to create a class if they are not a sysadmin or an admin in the ontology's project" in { val classIri = AnythingOntologyIri.makeEntityIri("WildThing") @@ -2449,6 +2524,40 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } } + "change the labels of a class, submitting the same labels again" in { + val classIri = AnythingOntologyIri.makeEntityIri("Nothing") + + val newObjects = Seq( + StringLiteralV2("nothing", Some("en")), + StringLiteralV2("rien", Some("fr")) + ) + + responderManager ! ChangeClassLabelsOrCommentsRequestV2( + classIri = classIri, + predicateToUpdate = OntologyConstants.Rdfs.Label.toSmartIri, + newObjects = newObjects, + lastModificationDate = anythingLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { + case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Label.toSmartIri).objects should ===( + newObjects) + + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate + } + } + "not allow a user to change the comments of a class if they are not a sysadmin or an admin in the ontology's project" in { val classIri = AnythingOntologyIri.makeEntityIri("Nothing") @@ -2513,6 +2622,45 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender { } } + "change the comments of a class, submitting the same comments again" in { + val classIri = AnythingOntologyIri.makeEntityIri("Nothing") + + val newObjects = Seq( + StringLiteralV2("Represents nothing", Some("en")), + StringLiteralV2("ne représente rien", Some("fr")) + ) + + // Make an unescaped copy of the new comments, because this is how we will receive them in the API response. + val newObjectsUnescaped = newObjects.map { + case StringLiteralV2(text, lang) => StringLiteralV2(stringFormatter.fromSparqlEncodedString(text), lang) + } + + responderManager ! ChangeClassLabelsOrCommentsRequestV2( + classIri = classIri, + predicateToUpdate = OntologyConstants.Rdfs.Comment.toSmartIri, + newObjects = newObjects, + lastModificationDate = anythingLastModDate, + apiRequestID = UUID.randomUUID, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingAdminUser + ) + + expectMsgPF(timeout) { + case msg: ReadOntologyV2 => + val externalOntology = msg.toOntologySchema(ApiV2Complex) + assert(externalOntology.classes.size == 1) + val readClassInfo = externalOntology.classes(classIri) + readClassInfo.entityInfoContent.predicates(OntologyConstants.Rdfs.Comment.toSmartIri).objects should ===( + newObjectsUnescaped) + + val metadata = externalOntology.ontologyMetadata + val newAnythingLastModDate = metadata.lastModificationDate.getOrElse( + throw AssertionException(s"${metadata.ontologyIri} has no last modification date")) + assert(newAnythingLastModDate.isAfter(anythingLastModDate)) + anythingLastModDate = newAnythingLastModDate + } + } + "not create a class with the wrong rdf:type" in { val classIri = AnythingOntologyIri.makeEntityIri("WrongClass")