Skip to content
Permalink
Browse files
feat(api-v2): Return events describing version history of resources a…
…nd values of a project ordered by data (DSP-1528) (#1844)

* feature(api-v2): route to get history of resources within project + message tests

* feat(api-v2): add templates for getting all resources of a project

* refactor(api-v2): rename twirl templates for getting resources of a project by class

* test(api-v2): tests for returning resource IRIs of a project

* fix(application.conf): increase TCP connecting-timeout

* feat (api-v2): return version history of all resources in a project

* feat(api-v2): get full representation of each resource in each time stamp in its history

* feat(api-v2): model event for response

* feat (histEvent): return a createResource event for each resource within a project as it was initially created

* fix (historyEvents): fix test

* feat(historyEvents): extract full value histories

* feat (historyEvents): return create/update/delete events for values of a resource

* feat (historyEvents): update Permission event

* fix (historyEvent) use isAfter method of instant

* fix(testData): creation date of the resource a-thing-picture cannot be after creationdate of its values

* feat(historyEvents): return all resource and value histories of a project as events

* feat (historyEvents): sort events by date

* feat(historyEvent) serialize project history response as JSONLD

* feat(historyEvent): return deleted values as part of full representation if asked, make delete events for them

* test(historyEvent): e2e test

* test (historyEvent): test for serialization of event metadata

* test (historyEvents): e2e test

* feat(historyEvent): serialize value contents

* fix (historyEvent): fix the failing test

* refactor (historyEvent): some refactoring and clean up

* docs(historyEvent): add documentation

* fix (historyEvents): fix the failing test

* fix (historyEvent): fix the remaining wrong dates

* refactor(historyEvents): some restructuring

* feat(historyEvent): create an event for a deleted resource

* feat (historyEvent): return delete comment when getting the full representation of the resource

* fix (historyEvent): fix the build problem

* refactor (docs): clean up documentation

Co-authored-by: Benjamin Geer <benjaminlewis.geer@unibas.ch>
  • Loading branch information
SepidehAlassi and Benjamin Geer committed May 3, 2021
1 parent d8dbb4f commit 84f7c148216fc63a1df7b1994668e335aab12c51
Showing with 1,434 additions and 90 deletions.
  1. +1 −1 docs/03-apis/api-v2/editing-resources.md
  2. +130 −0 docs/03-apis/api-v2/reading-and-searching-resources.md
  3. +3 −2 test_data/all_data/anything-data.ttl
  4. +2 −2 test_data/resourcesR2RV2/ThingWithPicture.jsonld
  5. +2 −2 test_data/valuesE2EV2/get-still-image-file-value-response.jsonld
  6. +4 −0 webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala
  7. +18 −14 webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala
  8. +285 −2 ...i/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala
  9. +14 −0 ...in/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala
  10. +374 −10 webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala
  11. +2 −1 webapi/src/main/scala/org/knora/webapi/responders/v2/SearchResponderV2.scala
  12. +1 −0 webapi/src/main/scala/org/knora/webapi/responders/v2/StandoffResponderV2.scala
  13. +31 −0 webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala
  14. +40 −0 ...wirl/org/knora/webapi/messages/twirl/queries/sparql/v2/getAllResourcesInProjectPrequery.scala.txt
  15. +44 −0 ...g/knora/webapi/messages/twirl/queries/sparql/v2/getAllResourcesInProjectPrequeryGraphDB.scala.txt
  16. +42 −0 .../knora/webapi/messages/twirl/queries/sparql/v2/getAllResourcesInProjectPrequeryStandard.scala.txt
  17. +4 −0 .../twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/getResourcePropertiesAndValues.scala.txt
  18. +29 −6 ...org/knora/webapi/messages/twirl/queries/sparql/v2/getResourcePropertiesAndValuesGraphDB.scala.txt
  19. +31 −7 ...rg/knora/webapi/messages/twirl/queries/sparql/v2/getResourcePropertiesAndValuesStandard.scala.txt
  20. +8 −4 .../twirl/org/knora/webapi/messages/twirl/queries/sparql/v2/getResourceValueVersionHistory.scala.txt
  21. +15 −6 ...org/knora/webapi/messages/twirl/queries/sparql/v2/getResourceValueVersionHistoryGraphDB.scala.txt
  22. +15 −6 ...rg/knora/webapi/messages/twirl/queries/sparql/v2/getResourceValueVersionHistoryStandard.scala.txt
  23. +9 −9 ...rql/v2/{getResourcesInProjectPrequery.scala.txt → getResourcesByClassInProjectPrequery.scala.txt}
  24. +6 −6 ...ourcesInProjectPrequeryGraphDB.scala.txt → getResourcesByClassInProjectPrequeryGraphDB.scala.txt}
  25. +7 −7 ...rcesInProjectPrequeryStandard.scala.txt → getResourcesByClassInProjectPrequeryStandard.scala.txt}
  26. +1 −0 webapi/src/test/scala/org/knora/webapi/e2e/v2/BUILD.bazel
  27. +9 −0 webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala
  28. +1 −1 webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala
  29. +22 −0 webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/BUILD.bazel
  30. +36 −0 ...test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala
  31. +1 −1 webapi/src/test/scala/org/knora/webapi/responders/v2/BUILD.bazel
  32. +247 −3 webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala
@@ -277,7 +277,7 @@ Here is an example:
"knora-api:lastModificationDate" : {
"@type" : "xsd:dateTimeStamp",
"@value" : "2017-11-20T15:55:17Z"
}
},
"knora-api:newModificationDate" : {
"@type" : "xsd:dateTimeStamp",
"@value" : "2018-12-21T16:56:18Z"
@@ -558,3 +558,133 @@ The `orderByProperty` parameter is optional; if it is not supplied, resources wi
be sorted alphabetically by resource IRI (an arbitrary but consistent order).
The value of `page` is a 0-based integer page number. Paging works as it does
in [Gravsearch](query-language.md)).

### Get the Version History of Resources and Values of a Project

To get a list of the changes that have been made to resources and values of a project since their creation ordered by date
use this route:

```
HTTP GET to http://host/v2/resources/projectHistory/projectIRI
```

The project IRI must be URL-encoded. The response is a list of events describing changes made to the resource and its values,
in chronological order. Each entry has the properties:
`knora-api:eventType` (the type of the operation performed on a specific date. The operation can be either
`createResource`, `deleteResource`, `createValue`, `updateValueContent`, `updateValuePermissions`, or `deleteValue`.),
`knora-api:versionDate` (the date when the change was made),
`knora-api:author` (the IRI of the user who made the change),
`knora-api:eventBody` (the information necessary to make the same request). For example:

```jsonld
{
"@graph" : [
{
"knora-api:eventType": "createResource",
"knora-api:author": {
"@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q"
},
"knora-api:eventBody": {
"rdfs:label": "A thing with version history",
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing",
"knora-api:hasPermissions": "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser",
"knora-api:creationDate": {
"@value": "2019-02-08T15:05:10Z",
"@type": "xsd:dateTimeStamp"
},
"knora-api:attachedToProject": {
"@id": "http://rdfh.ch/projects/0001"
}
},
"knora-api:versionDate": {
"@value": "2019-02-08T15:05:10Z",
"@type": "xsd:dateTimeStamp"
}
},
{
"knora-api:eventType": "createValue",
"knora-api:author": {
"@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q"
},
"knora-api:eventBody": {
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing",
"knora-api:valueCreationDate": {
"@value": "2019-02-10T10:30:10Z",
"@type": "xsd:dateTimeStamp"
},
"knora-api:valueHasUUID": "IZGOjVqxTfSNO4ieKyp0SA",
"knora-api:hasPermissions": "V knora-admin:UnknownUser|M knora-admin:ProjectMember",
"@type": "knora-base:LinkValue",
"http://www.knora.org/ontology/0001/anything#hasOtherThingValue": {
"knora-api:linkValueHasTargetIri": {
"@id": "http://rdfh.ch/0001/2qMtTWvVRXWMBcRNlduvCQ"
}
},
"rdf:Property": "http://www.knora.org/ontology/0001/anything#hasOtherThingValue",
"@id": "http://rdfh.ch/0001/thing-with-history/values/3a"
},
"knora-api:versionDate": {
"@value": "2019-02-10T10:30:10Z",
"@type": "xsd:dateTimeStamp"
}
},
{
"knora-api:eventType": "updateValueContent",
"knora-api:author": {
"@id": "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ"
},
"knora-api:eventBody": {
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing"
"http://www.knora.org/ontology/0001/anything#hasText": {
"knora-api:valueAsString": "two"
},
"knora-api:valueCreationDate": {
"@value": "2019-02-11T10:05:10Z",
"@type": "xsd:dateTimeStamp"
},
"knora-base:previousValue": "http://rdfh.ch/0001/thing-with-history/values/2a",
"knora-api:valueHasUUID": "W5fm67e0QDWxRZumcXcs6g",
"@type": "knora-base:TextValue",
"rdf:Property": "http://www.knora.org/ontology/0001/anything#hasText",
"@id": "http://rdfh.ch/0001/thing-with-history/values/2b"
},
"knora-api:versionDate": {
"@value": "2019-02-11T10:05:10Z",
"@type": "xsd:dateTimeStamp"
}
},
{
"knora-api:eventType": "deleteValue",
"knora-api:author": {
"@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q"
},
"knora-api:eventBody": {
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing",
"knora-base:previousValue": "http://rdfh.ch/0001/thing-with-history/values/3a",
"knora-api:deleteDate": {
"@type": "xsd:dateTimeStamp",
"@value": "2019-02-13T09:00:10Z"
},
"knora-api:isDeleted": true,
"@type": "knora-base:LinkValue",
"rdf:Property": "http://www.knora.org/ontology/0001/anything#hasOtherThingValue",
"@id": "http://rdfh.ch/0001/thing-with-history/values/3b"
},
"knora-api:versionDate": {
"@value": "2019-02-13T09:00:10Z",
"@type": "xsd:dateTimeStamp"
}
}
],
"@context" : {
"rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs" : "http://www.w3.org/2000/01/rdf-schema#",
"xsd" : "http://www.w3.org/2001/XMLSchema#",
"knora-api" : "http://api.knora.org/ontology/knora-api/v2#"
}
}
```
@@ -711,7 +711,7 @@
knora-base:attachedToProject <http://rdfh.ch/projects/0001>;
rdfs:label "A thing with a picture";
knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|RV knora-admin:UnknownUser";
knora-base:creationDate "2016-03-02T15:05:10Z"^^xsd:dateTime;
knora-base:creationDate "2011-03-02T15:05:10Z"^^xsd:dateTime;
knora-base:hasStillImageFileValue <http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q> .

<http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q> a knora-base:StillImageFileValue;
@@ -1716,7 +1716,8 @@
anything:hasInteger <http://rdfh.ch/0001/XTxSMt0ySraVmwXD-bD2wQ/values/PVPAa37xR--K_wxQwlvSsg>;
rdfs:label "deleted thing";
knora-base:isDeleted true;
knora-base:deleteDate "2020-04-07T14:59:28.960124Z"^^xsd:dateTime .
knora-base:deleteDate "2020-04-07T14:59:28.960124Z"^^xsd:dateTime ;
knora-base:deleteComment "a comment for the deleted thing."^^xsd:string .

<http://rdfh.ch/0001/PHbbrEsVR32q5D_ioKt6pA/values/7BIm9QAiQqKixcgXDWf12Q> a knora-base:IntValue;
knora-base:attachedToUser <http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q>;
@@ -25,15 +25,15 @@
},
"rdfs:label": "A thing with a picture",
"knora-api:versionArkUrl": {
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20160302T150510Z",
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20110302T150510Z",
"@type": "xsd:anyURI"
},
"knora-api:attachedToProject": {
"@id": "http://rdfh.ch/projects/0001"
},
"knora-api:userHasPermission": "RV",
"knora-api:creationDate": {
"@value": "2016-03-02T15:05:10Z",
"@value": "2011-03-02T15:05:10Z",
"@type": "xsd:dateTimeStamp"
},
"knora-api:attachedToUser": {
@@ -39,15 +39,15 @@
"@id": "http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q"
},
"knora-api:versionArkUrl": {
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20160302T150510Z",
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20110302T150510Z",
"@type": "xsd:anyURI"
},
"knora-api:attachedToProject": {
"@id": "http://rdfh.ch/projects/0001"
},
"knora-api:userHasPermission": "CR",
"knora-api:creationDate": {
"@value": "2016-03-02T15:05:10Z",
"@value": "2011-03-02T15:05:10Z",
"@type": "xsd:dateTimeStamp"
},
"knora-api:attachedToUser": {
@@ -735,6 +735,10 @@ object OntologyConstants {
val Result: IRI = KnoraApiV2PrefixExpansion + "result"
val Error: IRI = KnoraApiV2PrefixExpansion + "error"
val MayHaveMoreResults: IRI = KnoraApiV2PrefixExpansion + "mayHaveMoreResults"
val EventType: IRI = KnoraApiV2PrefixExpansion + "eventType"
val EventBody: IRI = KnoraApiV2PrefixExpansion + "eventBody"
val ResourceClassIri: IRI = KnoraApiV2PrefixExpansion + "resourceClassIri"
val ResourceIri: IRI = KnoraApiV2PrefixExpansion + "resourceIri"

val IsShared: IRI = KnoraApiV2PrefixExpansion + "isShared"
val IsBuiltIn: IRI = KnoraApiV2PrefixExpansion + "isBuiltIn"
@@ -1419,20 +1419,24 @@ object ConstructResponseUtilV2 {
timeout: Timeout,
executionContext: ExecutionContext): Future[ReadResourceV2] = {
def getDeletionInfo(rdfData: RdfData): Option[DeletionInfo] = {
val isDeleted: Boolean = rdfData.requireBooleanObject(OntologyConstants.KnoraBase.IsDeleted.toSmartIri)

if (isDeleted) {
val deleteDate = rdfData.requireDateTimeObject(OntologyConstants.KnoraBase.DeleteDate.toSmartIri)
val maybeDeleteComment = rdfData.maybeStringObject(OntologyConstants.KnoraBase.DeleteComment.toSmartIri)

Some(
DeletionInfo(
deleteDate = deleteDate,
maybeDeleteComment = maybeDeleteComment
)
)
} else {
None
val mayHaveDeletedStatements: Option[Boolean] =
rdfData.maybeBooleanObject(OntologyConstants.KnoraBase.IsDeleted.toSmartIri)
mayHaveDeletedStatements match {
case Some(isDeleted: Boolean) =>
if (isDeleted) {
val deleteDate = rdfData.requireDateTimeObject(OntologyConstants.KnoraBase.DeleteDate.toSmartIri)
val maybeDeleteComment = rdfData.maybeStringObject(OntologyConstants.KnoraBase.DeleteComment.toSmartIri)

Some(
DeletionInfo(
deleteDate = deleteDate,
maybeDeleteComment = maybeDeleteComment
)
)
} else {
None
}
case _ => None
}
}

0 comments on commit 84f7c14

Please sign in to comment.