Skip to content

Commit

Permalink
Add comments to ontology metadata (#1703)
Browse files Browse the repository at this point in the history
* feature (ontologyComment) allow comment to be given for ontology

* feature (ontologyComment) responder adds the comment to the ontology metadata

* feature (ontologyComment) return ontology comment when loading the ontology metadata + test

* test (ontologyComment) e2e test

* feature (ontologyComment) test data

* docs (ontologyComment) update documentation
  • Loading branch information
SepidehAlassi committed Sep 9, 2020
1 parent be70f8b commit ab6fe36
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 13 deletions.
7 changes: 7 additions & 0 deletions docs/03-apis/api-v2/ontology-information.md
Expand Up @@ -951,6 +951,13 @@ HTTP POST to http://host/v2/ontologies
The ontology name must follow the rules given in
[Knora IRIs](knora-iris.md).

The ontology metadata can have an optional comment given in the request
body as:

```
"rdfs:comment": "some comment",
```

If the ontology is to be shared by multiple projects, it must be
created in the default shared ontologies project,
`http://www.knora.org/ontology/knora-base#DefaultSharedOntologiesProject`,
Expand Down
Expand Up @@ -61,13 +61,17 @@ case class LoadOntologiesRequestV2(requestingUser: UserADM) extends OntologiesRe
*
* @param ontologyName the name of the ontology to be created.
* @param projectIri the IRI of the project that the ontology will belong to.
* @param isShared the flag that shows if an ontology is a shared one.
* @param label the label of the ontology.
* @param comment the optional comment that described the ontology to be created.
* @param apiRequestID the ID of the API request.
* @param requestingUser the user making the request.
*/
case class CreateOntologyRequestV2(ontologyName: String,
projectIri: SmartIri,
isShared: Boolean = false,
label: String,
comment: Option[String] = None,
apiRequestID: UUID,
requestingUser: UserADM) extends OntologiesResponderRequestV2

Expand Down Expand Up @@ -111,6 +115,7 @@ object CreateOntologyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateOntology

val ontologyName: String = jsonLDDocument.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.OntologyName, stringFormatter.validateProjectSpecificOntologyName)
val label: String = jsonLDDocument.requireStringWithValidation(OntologyConstants.Rdfs.Label, stringFormatter.toSparqlEncodedString)
val comment: Option[String] = jsonLDDocument.maybeStringWithValidation(OntologyConstants.Rdfs.Comment, stringFormatter.toSparqlEncodedString)
val projectIri: SmartIri = jsonLDDocument.requireIriInObject(OntologyConstants.KnoraApiV2Complex.AttachedToProject, stringFormatter.toSmartIriWithErr)
val isShared: Boolean = jsonLDDocument.maybeBoolean(OntologyConstants.KnoraApiV2Complex.IsShared).exists(identity)

Expand All @@ -119,6 +124,7 @@ object CreateOntologyRequestV2 extends KnoraJsonLDRequestReaderV2[CreateOntology
projectIri = projectIri,
isShared = isShared,
label = label,
comment = comment,
apiRequestID = apiRequestID,
requestingUser = requestingUser
)
Expand Down Expand Up @@ -1358,6 +1364,8 @@ object InputOntologyV2 {

val ontologyLabel: Option[String] = ontologyObj.maybeStringWithValidation(OntologyConstants.Rdfs.Label, stringFormatter.toSparqlEncodedString)

val ontologyComment: Option[String] = ontologyObj.maybeStringWithValidation(OntologyConstants.Rdfs.Comment, stringFormatter.toSparqlEncodedString)

val lastModificationDate: Option[Instant] = ontologyObj.maybeDatatypeValueInObject(
key = OntologyConstants.KnoraApiV2Complex.LastModificationDate,
expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri,
Expand All @@ -1368,6 +1376,7 @@ object InputOntologyV2 {
ontologyIri = externalOntologyIri,
projectIri = projectIri,
label = ontologyLabel,
comment = ontologyComment,
lastModificationDate = lastModificationDate
)

Expand Down Expand Up @@ -3008,12 +3017,14 @@ case class SubClassInfoV2(id: SmartIri, label: String)
* @param ontologyIri the IRI of the ontology.
* @param projectIri the IRI of the project that the ontology belongs to.
* @param label the label of the ontology, if any.
* @param comment the comment of the ontology, if any.
* @param lastModificationDate the ontology's last modification date, if any.
* @param ontologyVersion the version string attached to the ontology, if any.
*/
case class OntologyMetadataV2(ontologyIri: SmartIri,
projectIri: Option[SmartIri] = None,
label: Option[String] = None,
comment: Option[String] = None,
lastModificationDate: Option[Instant] = None,
ontologyVersion: Option[String] = None) extends KnoraContentV2[OntologyMetadataV2] {
implicit private val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance
Expand Down Expand Up @@ -3071,6 +3082,10 @@ case class OntologyMetadataV2(ontologyIri: SmartIri,
labelStr => OntologyConstants.Rdfs.Label -> JsonLDString(labelStr)
}

val commentStatement: Option[(IRI, JsonLDString)] = comment.map {
commentStr => OntologyConstants.Rdfs.Comment -> JsonLDString(commentStr)
}

val lastModDateStatement: Option[(IRI, JsonLDObject)] = if (targetSchema == ApiV2Complex) {
lastModificationDate.map {
lastModDate =>
Expand All @@ -3085,6 +3100,6 @@ case class OntologyMetadataV2(ontologyIri: SmartIri,

Map(JsonLDConstants.ID -> JsonLDString(ontologyIri.toString),
JsonLDConstants.TYPE -> JsonLDString(OntologyConstants.Owl.Ontology)
) ++ projectIriStatement ++ labelStatement ++ lastModDateStatement ++ isSharedStatement ++ isBuiltInStatement
) ++ projectIriStatement ++ labelStatement ++ commentStatement ++ lastModDateStatement ++ isSharedStatement ++ isBuiltInStatement
}
}
Expand Up @@ -1638,6 +1638,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon

val projectIris: Seq[String] = statementMap.getOrElse(OntologyConstants.KnoraBase.AttachedToProject, throw InconsistentTriplestoreDataException(s"Ontology $internalOntologyIri has no knora-base:attachedToProject"))
val labels: Seq[String] = statementMap.getOrElse(OntologyConstants.Rdfs.Label, Seq.empty[String])
val comments: Seq[String] = statementMap.getOrElse(OntologyConstants.Rdfs.Comment, Seq.empty[String])
val lastModDates: Seq[String] = statementMap.getOrElse(OntologyConstants.KnoraBase.LastModificationDate, Seq.empty[String])

val projectIri = if (projectIris.size > 1) {
Expand All @@ -1664,6 +1665,10 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
labels.head
}

val comment: Option[String] = if (comments.size > 1) {
throw InconsistentTriplestoreDataException(s"Ontology $internalOntologyIri has more than one rdfs:comment")
} else comments.headOption

val lastModificationDate: Option[Instant] = if (lastModDates.size > 1) {
throw InconsistentTriplestoreDataException(s"Ontology $internalOntologyIri has more than one ${OntologyConstants.KnoraBase.LastModificationDate}")
} else if (lastModDates.isEmpty) {
Expand All @@ -1677,6 +1682,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
ontologyIri = internalOntologyIri,
projectIri = Some(projectIri),
label = Some(label),
comment = comment,
lastModificationDate = lastModificationDate
))

Expand Down Expand Up @@ -1725,6 +1731,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
projectIri = createOntologyRequest.projectIri,
isShared = createOntologyRequest.isShared,
ontologyLabel = createOntologyRequest.label,
ontologyComment = createOntologyRequest.comment,
currentTime = currentTime
).toString

Expand All @@ -1736,6 +1743,7 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
ontologyIri = internalOntologyIri,
projectIri = Some(createOntologyRequest.projectIri),
label = Some(createOntologyRequest.label),
comment = createOntologyRequest.comment,
lastModificationDate = Some(currentTime)
).unescape

Expand Down
Expand Up @@ -906,21 +906,35 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
}
}

private def createOntologyTestRequest: Future[TestDataFileContent] = {
private def createOntologyTestRequest: Future[Set[TestDataFileContent]] = {
FastFuture.successful(
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-empty-foo-ontology-request"),
text = SharedTestDataADM.createOntology(SharedTestDataADM.IMAGES_PROJECT_IRI, "The foo ontology")
Set(
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-empty-foo-ontology-request"),
text = SharedTestDataADM.createOntology(SharedTestDataADM.IMAGES_PROJECT_IRI, "The foo ontology")
),
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-ontology-with-comment-request"),
text = SharedTestDataADM.createOntologyWithComment(SharedTestDataADM.IMAGES_PROJECT_IRI,
"The bar ontology", "some comment")
)
)
)
}

private def createOntologyTestResponse: Future[TestDataFileContent] = {
private def createOntologyTestResponse: Future[Set[TestDataFileContent]] = {
FastFuture.successful(
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-empty-foo-ontology-response"),
text = SharedTestDataADM.createOntologyResponse
Set(
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-empty-foo-ontology-response"),
text = SharedTestDataADM.createFooOntologyResponse
),
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-ontology-with-comment-response"),
text = SharedTestDataADM.createOntologyWithCommentResponse
)
)

)
}

Expand Down Expand Up @@ -979,8 +993,8 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
projectOntologiesResponses: Set[TestDataFileContent] <- getOntologyMetadataForProjectsTestResponses
ontologyClassResponses: Set[TestDataFileContent] <- getClassesTestResponses
ontologyPropertyResponses: Set[TestDataFileContent] <- getPropertiesTestResponses
createOntologyRequest: TestDataFileContent <- createOntologyTestRequest
createOntologyResponse: TestDataFileContent <- createOntologyTestResponse
createOntologyRequest: Set[TestDataFileContent] <- createOntologyTestRequest
createOntologyResponse: Set[TestDataFileContent] <- createOntologyTestResponse
updateOntologyMetadataRequest: TestDataFileContent <- updateOntologyMetadataTestRequest
createClassRequest: Set[TestDataFileContent] <- createClassTestRequest
createClassResponse: TestDataFileContent <- createClassTestResponse
Expand All @@ -991,7 +1005,7 @@ class OntologiesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
updatePropertyRequest: Set[TestDataFileContent] <- updatePropertyTestRequest
deleteOntologyResponse: TestDataFileContent <- deleteOntologyTestResponse
} yield ontologyResponses + ontologyMetadataResponses ++ projectOntologiesResponses ++ ontologyClassResponses ++
ontologyPropertyResponses + createOntologyRequest + createOntologyResponse + updateOntologyMetadataRequest ++
ontologyPropertyResponses ++ createOntologyRequest ++ createOntologyResponse + updateOntologyMetadataRequest ++
createClassRequest + createClassResponse + addCardinalitiesRequest + createPropertyRequest ++
updateClassRequest ++ replaceCardinalitiesRequest ++ updatePropertyRequest + deleteOntologyResponse
}
Expand Down
Expand Up @@ -2314,7 +2314,23 @@ object SharedTestDataADM {
|}""".stripMargin
}

val createOntologyResponse: String =
def createOntologyWithComment(projectIri: IRI, label: String, comment: String): String = {
s"""
|{
| "knora-api:ontologyName": "bar",
| "knora-api:attachedToProject": {
| "@id": "$projectIri"
| },
| "rdfs:label": "$label",
| "rdfs:comment": "$comment",
| "@context": {
| "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
| "knora-api": "http://api.knora.org/ontology/knora-api/v2#"
| }
|}""".stripMargin
}

val createFooOntologyResponse: String =
"""{
| "@id" : "http://0.0.0.0:3333/ontology/00FF/foo/v2",
| "@type" : "owl:Ontology",
Expand All @@ -2334,6 +2350,27 @@ object SharedTestDataADM {
| }
|}""".stripMargin

val createOntologyWithCommentResponse: String =
"""{
| "@id": "http://0.0.0.0:3333/ontology/00FF/bar/v2",
| "@type": "owl:Ontology",
| "knora-api:attachedToProject": {
| "@id": "http://rdfh.ch/projects/00FF"
| },
| "knora-api:lastModificationDate": {
| "@type": "xsd:dateTimeStamp",
| "@value": "2020-09-09T09:37:19.137090Z"
| },
| "rdfs:comment": "some comment",
| "rdfs:label": "The bar ontology",
| "@context": {
| "knora-api": "http://api.knora.org/ontology/knora-api/v2#",
| "xsd": "http://www.w3.org/2001/XMLSchema#",
| "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
| "owl": "http://www.w3.org/2002/07/owl#"
| }
|}""".stripMargin

def changeOntologyMetadata(ontologyIri: IRI, newLabel: String, modificationDate: Instant): String = {
s"""
|{
Expand Down
Expand Up @@ -35,6 +35,7 @@
projectIri: SmartIri,
isShared: Boolean,
ontologyLabel: String,
ontologyComment: Option[String],
currentTime: Instant)

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
Expand All @@ -49,6 +50,9 @@ INSERT {
knora-base:attachedToProject ?project ;
knora-base:isShared @isShared ;
rdfs:label """@ontologyLabel"""^^xsd:string ;
@if(ontologyComment.nonEmpty) {
rdfs:comment """@ontologyComment.get"""^^xsd:string ;
}
knora-base:lastModificationDate "@currentTime"^^xsd:dateTime .
}
}
Expand Down
Expand Up @@ -255,6 +255,23 @@ class OntologyV2R2RSpec extends R2RSpec {
}
}

"create an empty ontology called 'bar' with a comment" in {
val label = "The bar ontology"
var comment = "some comment"

val params = SharedTestDataADM.createOntologyWithComment(imagesProjectIri, label, comment)


Post("/v2/ontologies", HttpEntity(RdfMediaTypes.`application/ld+json`, params)) ~> addCredentials(BasicHttpCredentials(imagesUsername, password)) ~> ontologiesPath ~> check {
assert(status == StatusCodes.OK, response.toString)
val responseJsonDoc = responseToJsonLDDocument(response)
val metadata = responseJsonDoc.body
val ontologyIri = metadata.value("@id").asInstanceOf[JsonLDString].value
assert(ontologyIri == "http://0.0.0.0:3333/ontology/00FF/bar/v2")
assert(metadata.value(OntologyConstants.Rdfs.Comment) == JsonLDString(comment))
}
}

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

Expand Down
Expand Up @@ -117,6 +117,24 @@ class OntologyResponderV2Spec extends CoreSpec() with ImplicitSender {
fooLastModDate = metadata.lastModificationDate.getOrElse(throw AssertionException(s"${metadata.ontologyIri} has no last modification date"))
}

"create an empty ontology called 'bar' with a comment" in {
responderManager ! CreateOntologyRequestV2(
ontologyName = "bar",
projectIri = imagesProjectIri,
label = "The bar ontology",
comment = Some("some comment"),
apiRequestID = UUID.randomUUID,
requestingUser = imagesUser
)

val response = expectMsgType[ReadOntologyMetadataV2](timeout)
assert(response.ontologies.size == 1)
val metadata = response.ontologies.head
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")
}

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

Expand Down

0 comments on commit ab6fe36

Please sign in to comment.