Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add comments to ontology metadata #1703

Merged
merged 7 commits into from
Sep 9, 2020
7 changes: 7 additions & 0 deletions docs/03-apis/api-v2/ontology-information.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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