Skip to content

Commit

Permalink
update ontology metadata with a comment (#1710)
Browse files Browse the repository at this point in the history
* feature (changeOntologyComment) can change the existing comment of ontology metadata or add a new one + unit tests

* feature (changeOntologyComment) add e2e test and test data

* docs (changeOntologyComment) update documentation about updating ontology metadata

* fix (changeOntologyComment) use cached metadata instead of loading
  • Loading branch information
SepidehAlassi committed Sep 18, 2020
1 parent 3571dab commit 7f95dcc
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 25 deletions.
24 changes: 22 additions & 2 deletions docs/03-apis/api-v2/ontology-information.md
Expand Up @@ -979,8 +979,8 @@ be a valid XML [NCName](https://www.w3.org/TR/xml-names/#NT-NCName).

### Changing an Ontology's Metadata

Currently, the only modifiable ontology metadata is the ontology's
`rdfs:label`.
One can modify an ontology's metadata by updating its `rdfs:label` or `rdfs:comment`
or both. The example below shows the request for changing the label of an ontology.

```
HTTP PUT to http://host/v2/ontologies/metadata
Expand All @@ -1002,6 +1002,26 @@ HTTP PUT to http://host/v2/ontologies/metadata
}
```

Similarly, a user can change an ontology's existing comment or add one by specifying
the new comment in the request body:

```jsonld
{
"@id" : "ONTOLOGY_IRI",
"rdfs:comment" : "NEW_ONTOLOGY_COMMENT",
"knora-api:lastModificationDate" : {
"@type" : "xsd:dateTimeStamp",
"@value" : "ONTOLOGY_LAST_MODIFICATION_DATE"
},
"@context" : {
"xsd" : "http://www.w3.org/2001/XMLSchema#",
"rdfs" : "http://www.w3.org/2000/01/rdf-schema#",
"knora-api" : "http://api.knora.org/ontology/knora-api/v2#"
}
}
```

The request body can also contain a new label and a new comment for the ontology's metadata.
A successful response will be a JSON-LD document providing only the
ontology's metadata.

Expand Down
Expand Up @@ -19,6 +19,7 @@

package org.knora.webapi.messages.v2.responder.ontologymessages

import java.io
import java.time.Instant
import java.util.UUID

Expand Down Expand Up @@ -827,12 +828,14 @@ object ChangeClassLabelsOrCommentsRequestV2 extends KnoraJsonLDRequestReaderV2[C
*
* @param ontologyIri the external ontology IRI.
* @param label the ontology's new label.
* @param comment the ontology's new comment.
* @param lastModificationDate the ontology's last modification date, returned in a previous operation.
* @param apiRequestID the ID of the API request.
* @param requestingUser the user making the request.
*/
case class ChangeOntologyMetadataRequestV2(ontologyIri: SmartIri,
label: String,
label: Option[String] = None,
comment: Option[String] = None,
lastModificationDate: Instant,
apiRequestID: UUID,
requestingUser: UserADM) extends OntologiesResponderRequestV2
Expand Down Expand Up @@ -876,12 +879,14 @@ object ChangeOntologyMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Change
val inputOntologyV2 = InputOntologyV2.fromJsonLD(jsonLDDocument)
val inputMetadata = inputOntologyV2.ontologyMetadata
val ontologyIri = inputMetadata.ontologyIri
val label = inputMetadata.label.getOrElse(throw BadRequestException(s"No rdfs:label submitted"))
val label: Option[String] = inputMetadata.label
val comment: Option[String] = inputMetadata.comment
val lastModificationDate = inputMetadata.lastModificationDate.getOrElse(throw BadRequestException("No knora-api:lastModificationDate submitted"))

ChangeOntologyMetadataRequestV2(
ontologyIri = ontologyIri,
label = label,
comment = comment,
lastModificationDate = lastModificationDate,
apiRequestID = apiRequestID,
requestingUser = requestingUser
Expand Down
Expand Up @@ -1816,6 +1816,11 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
// Check that the ontology exists and has not been updated by another user since the client last read its metadata.
_ <- checkOntologyLastModificationDateBeforeUpdate(internalOntologyIri = internalOntologyIri, expectedLastModificationDate = changeOntologyMetadataRequest.lastModificationDate)

// get the metadata of the ontology.
oldMetadata: OntologyMetadataV2 = cacheData.ontologies(internalOntologyIri).ontologyMetadata
// Was there a comment in the ontology metadata?
ontologyHasComment: Boolean = oldMetadata.comment.nonEmpty

// Update the metadata.

currentTime: Instant = Instant.now
Expand All @@ -1825,6 +1830,8 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
ontologyNamedGraphIri = internalOntologyIri,
ontologyIri = internalOntologyIri,
newLabel = changeOntologyMetadataRequest.label,
hasOldComment = ontologyHasComment,
newComment = changeOntologyMetadataRequest.comment,
lastModificationDate = changeOntologyMetadataRequest.lastModificationDate,
currentTime = currentTime
).toString()
Expand All @@ -1833,10 +1840,20 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon

// Check that the update was successful. To do this, we have to undo the SPARQL-escaping of the input.

// Is there any new label given?
label = if (changeOntologyMetadataRequest.label.isEmpty) {
// No. Consider the old label for checking the update.
oldMetadata.label
} else {
// Yes. Consider the new label for checking the update.
changeOntologyMetadataRequest.label
}

unescapedNewMetadata = OntologyMetadataV2(
ontologyIri = internalOntologyIri,
projectIri = Some(projectIri),
label = Some(changeOntologyMetadataRequest.label),
label = label,
comment = changeOntologyMetadataRequest.comment,
lastModificationDate = Some(currentTime)
).unescape

Expand Down
Expand Up @@ -188,12 +188,13 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
val ontologyIri = SharedOntologyTestDataADM.FOO_ONTOLOGY_IRI_LocalHost

val newLabel = "The modified foo ontology"
val newComment = "new comment"
val newModificationDate = Instant.now
FastFuture.successful(
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("update-ontology-metadata-request"),
text = SharedTestDataADM.changeOntologyMetadata(
ontologyIri, newLabel, newModificationDate
ontologyIri, newLabel, newComment, newModificationDate
)
)
)
Expand Down
Expand Up @@ -2464,11 +2464,12 @@ object SharedTestDataADM {
| }
|}""".stripMargin

def changeOntologyMetadata(ontologyIri: IRI, newLabel: String, modificationDate: Instant): String = {
def changeOntologyMetadata(ontologyIri: IRI, newLabel: String, newComment: String, modificationDate: Instant): String = {
s"""
|{
| "@id": "$ontologyIri",
| "rdfs:label": "$newLabel",
| "rdfs:comment": "$newComment",
| "knora-api:lastModificationDate": {
| "@type" : "xsd:dateTimeStamp",
| "@value" : "$modificationDate"
Expand Down
Expand Up @@ -27,13 +27,16 @@
* @param ontologyNamedGraphIri the IRI of the named graph where the ontology should be stored.
* @param ontologyIri the IRI of the ontology to be created.
* @param newLabel the ontology's new label.
* @param newComment the ontology's new comment.
* @param lastModificationDate the xsd:dateTimeStamp that was attached to the ontology when it was last modified.
* @param currentTime an xsd:dateTimeStamp that will be attached to the ontology.
*@
@(triplestore: String,
ontologyNamedGraphIri: SmartIri,
ontologyIri: SmartIri,
newLabel: String,
newLabel: Option[String],
hasOldComment: Boolean,
newComment: Option[String],
lastModificationDate: Instant,
currentTime: Instant)

Expand All @@ -45,13 +48,24 @@ PREFIX knora-base: <http://www.knora.org/ontology/knora-base#>

DELETE {
GRAPH ?ontologyNamedGraph {
?ontology rdfs:label ?oldLabel ;
knora-base:lastModificationDate "@lastModificationDate"^^xsd:dateTime .
@if(newLabel.nonEmpty) {
?ontology rdfs:label ?oldLabel .
}
@if(hasOldComment && newComment.nonEmpty) {
?ontology rdfs:comment ?oldComment .
}
?ontology knora-base:lastModificationDate "@lastModificationDate"^^xsd:dateTime .

}
} INSERT {
GRAPH ?ontologyNamedGraph {
?ontology rdfs:label """@newLabel"""^^xsd:string ;
knora-base:lastModificationDate "@currentTime"^^xsd:dateTime .
@if(newLabel.nonEmpty) {
?ontology rdfs:label """@newLabel.get"""^^xsd:string .
}
@if(newComment.nonEmpty) {
?ontology rdfs:comment """@newComment.get"""^^xsd:string .
}
?ontology knora-base:lastModificationDate "@currentTime"^^xsd:dateTime .
}
}
@* Ensure that inference is not used in the WHERE clause of this update. *@
Expand All @@ -64,7 +78,12 @@ WHERE {

GRAPH ?ontologyNamedGraph {
?ontology rdf:type owl:Ontology ;
@if(newLabel.nonEmpty) {
rdfs:label ?oldLabel ;
}
@if(hasOldComment && newComment.nonEmpty) {
rdfs:comment ?oldComment ;
}
knora-base:lastModificationDate "@lastModificationDate"^^xsd:dateTime .
}
}
Expand Up @@ -274,8 +274,8 @@ class OntologyV2R2RSpec extends R2RSpec {

"change the metadata of 'foo'" in {
val newLabel = "The modified foo ontology"

val params = SharedTestDataADM.changeOntologyMetadata(fooIri.get, newLabel, fooLastModDate)
val newComment = "new comment"
val params = SharedTestDataADM.changeOntologyMetadata(fooIri.get, newLabel, newComment, fooLastModDate)


Put("/v2/ontologies/metadata", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials(BasicHttpCredentials(imagesUsername, password)) ~> ontologiesPath ~> check {
Expand All @@ -285,6 +285,7 @@ class OntologyV2R2RSpec extends R2RSpec {
val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value
assert(ontologyIri == fooIri.get)
assert(metadata.value(OntologyConstants.Rdfs.Label) == JsonLDString(newLabel))
assert(metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString(newComment))

val lastModDate = metadata.requireDatatypeValueInObject(
key = OntologyConstants.KnoraApiV2Complex.LastModificationDate,
Expand Down
Expand Up @@ -59,6 +59,8 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {

private val fooIri = new MutableTestIri
private var fooLastModDate: Instant = Instant.now
private val barIri = new MutableTestIri
private var barLastModDate: Instant = Instant.now

private val chairIri = new MutableTestIri
private var chairLastModDate: Instant = Instant.now
Expand Down Expand Up @@ -117,6 +119,48 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {
fooLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
}

"change the label in the metadata of 'foo'" in {
val newLabel = "The modified foo ontology"

responderManager ! ChangeOntologyMetadataRequestV2(
ontologyIri = fooIri.get.toSmartIri.toOntologySchema(ApiV2Complex),
label = Some(newLabel),
lastModificationDate = fooLastModDate,
apiRequestID = UUID.randomUUID,
requestingUser = imagesUser
)

val response = expectMsgType[ReadOntologyMetadataV2](timeout)
assert(response.ontologies.size == 1)
val metadata = response.ontologies.head
assert(metadata.ontologyIri == fooIri.get.toSmartIri)
assert(metadata.label.contains(newLabel))
val newFooLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
assert(newFooLastModDate.isAfter(fooLastModDate))
fooLastModDate = newFooLastModDate
}

"add a comment to the metadata of 'foo' ontology" in {
val aComment = "a comment"

responderManager ! ChangeOntologyMetadataRequestV2(
ontologyIri = fooIri.get.toSmartIri.toOntologySchema(ApiV2Complex),
comment = Some(aComment),
lastModificationDate = fooLastModDate,
apiRequestID = UUID.randomUUID,
requestingUser = imagesUser
)

val response = expectMsgType[ReadOntologyMetadataV2](timeout)
assert(response.ontologies.size == 1)
val metadata = response.ontologies.head
assert(metadata.ontologyIri == fooIri.get.toSmartIri)
assert(metadata.comment.contains(aComment))
val newFooLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
assert(newFooLastModDate.isAfter(fooLastModDate))
fooLastModDate = newFooLastModDate
}

"create an empty ontology called 'bar' with a comment" in {
responderManager ! CreateOntologyRequestV2(
ontologyName = "bar",
Expand All @@ -133,27 +177,29 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {
assert(metadata.ontologyIri.toString == "http://www.knora.org/ontology/00FF/bar")
val returnedComment: String = metadata.comment.getOrElse(throw AssertionException("The bar ontology has no comment!"))
assert(returnedComment == "some comment")
barIri.set(metadata.ontologyIri.toString)
barLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
}

"change the metadata of 'foo'" in {
val newLabel = "The modified foo ontology"
"change the existing comment in the metadata of 'bar' ontology" in {
val newComment = "a new comment"

responderManager ! ChangeOntologyMetadataRequestV2(
ontologyIri = fooIri.get.toSmartIri.toOntologySchema(ApiV2Complex),
label = newLabel,
lastModificationDate = fooLastModDate,
ontologyIri = barIri.get.toSmartIri.toOntologySchema(ApiV2Complex),
comment = Some(newComment),
lastModificationDate = barLastModDate,
apiRequestID = UUID.randomUUID,
requestingUser = imagesUser
)

val response = expectMsgType[ReadOntologyMetadataV2](timeout)
assert(response.ontologies.size == 1)
val metadata = response.ontologies.head
assert(metadata.ontologyIri == fooIri.get.toSmartIri)
assert(metadata.label.contains(newLabel))
val newFooLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
assert(newFooLastModDate.isAfter(fooLastModDate))
fooLastModDate = newFooLastModDate
assert(metadata.ontologyIri == barIri.get.toSmartIri)
assert(metadata.comment.contains(newComment))
val newBarLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
assert(newBarLastModDate.isAfter(barLastModDate))
barLastModDate = newBarLastModDate
}

"not create 'foo' again" in {
Expand Down Expand Up @@ -2043,7 +2089,7 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {

responderManager ! ChangeOntologyMetadataRequestV2(
ontologyIri = AnythingOntologyIri,
label = newLabel,
label = Some(newLabel),
lastModificationDate = anythingLastModDate,
apiRequestID = UUID.randomUUID,
requestingUser = anythingAdminUser
Expand Down

0 comments on commit 7f95dcc

Please sign in to comment.