Skip to content

Commit 84f7c14

Browse files
SepidehAlassiBenjamin Geer
andauthored
feat(api-v2): Return events describing version history of resources and 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>
1 parent d8dbb4f commit 84f7c14

32 files changed

Lines changed: 1434 additions & 90 deletions

‎docs/03-apis/api-v2/editing-resources.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ Here is an example:
277277
"knora-api:lastModificationDate" : {
278278
"@type" : "xsd:dateTimeStamp",
279279
"@value" : "2017-11-20T15:55:17Z"
280-
}
280+
},
281281
"knora-api:newModificationDate" : {
282282
"@type" : "xsd:dateTimeStamp",
283283
"@value" : "2018-12-21T16:56:18Z"

‎docs/03-apis/api-v2/reading-and-searching-resources.md‎

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,3 +558,133 @@ The `orderByProperty` parameter is optional; if it is not supplied, resources wi
558558
be sorted alphabetically by resource IRI (an arbitrary but consistent order).
559559
The value of `page` is a 0-based integer page number. Paging works as it does
560560
in [Gravsearch](query-language.md)).
561+
562+
### Get the Version History of Resources and Values of a Project
563+
564+
To get a list of the changes that have been made to resources and values of a project since their creation ordered by date
565+
use this route:
566+
567+
```
568+
HTTP GET to http://host/v2/resources/projectHistory/projectIRI
569+
```
570+
571+
The project IRI must be URL-encoded. The response is a list of events describing changes made to the resource and its values,
572+
in chronological order. Each entry has the properties:
573+
`knora-api:eventType` (the type of the operation performed on a specific date. The operation can be either
574+
`createResource`, `deleteResource`, `createValue`, `updateValueContent`, `updateValuePermissions`, or `deleteValue`.),
575+
`knora-api:versionDate` (the date when the change was made),
576+
`knora-api:author` (the IRI of the user who made the change),
577+
`knora-api:eventBody` (the information necessary to make the same request). For example:
578+
579+
```jsonld
580+
{
581+
"@graph" : [
582+
{
583+
"knora-api:eventType": "createResource",
584+
"knora-api:author": {
585+
"@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q"
586+
},
587+
"knora-api:eventBody": {
588+
"rdfs:label": "A thing with version history",
589+
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
590+
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing",
591+
"knora-api:hasPermissions": "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:UnknownUser",
592+
"knora-api:creationDate": {
593+
"@value": "2019-02-08T15:05:10Z",
594+
"@type": "xsd:dateTimeStamp"
595+
},
596+
"knora-api:attachedToProject": {
597+
"@id": "http://rdfh.ch/projects/0001"
598+
}
599+
},
600+
"knora-api:versionDate": {
601+
"@value": "2019-02-08T15:05:10Z",
602+
"@type": "xsd:dateTimeStamp"
603+
}
604+
},
605+
{
606+
"knora-api:eventType": "createValue",
607+
"knora-api:author": {
608+
"@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q"
609+
},
610+
"knora-api:eventBody": {
611+
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
612+
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing",
613+
"knora-api:valueCreationDate": {
614+
"@value": "2019-02-10T10:30:10Z",
615+
"@type": "xsd:dateTimeStamp"
616+
},
617+
"knora-api:valueHasUUID": "IZGOjVqxTfSNO4ieKyp0SA",
618+
"knora-api:hasPermissions": "V knora-admin:UnknownUser|M knora-admin:ProjectMember",
619+
"@type": "knora-base:LinkValue",
620+
"http://www.knora.org/ontology/0001/anything#hasOtherThingValue": {
621+
"knora-api:linkValueHasTargetIri": {
622+
"@id": "http://rdfh.ch/0001/2qMtTWvVRXWMBcRNlduvCQ"
623+
}
624+
},
625+
"rdf:Property": "http://www.knora.org/ontology/0001/anything#hasOtherThingValue",
626+
"@id": "http://rdfh.ch/0001/thing-with-history/values/3a"
627+
},
628+
"knora-api:versionDate": {
629+
"@value": "2019-02-10T10:30:10Z",
630+
"@type": "xsd:dateTimeStamp"
631+
}
632+
},
633+
{
634+
"knora-api:eventType": "updateValueContent",
635+
"knora-api:author": {
636+
"@id": "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ"
637+
},
638+
"knora-api:eventBody": {
639+
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
640+
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing"
641+
"http://www.knora.org/ontology/0001/anything#hasText": {
642+
"knora-api:valueAsString": "two"
643+
},
644+
"knora-api:valueCreationDate": {
645+
"@value": "2019-02-11T10:05:10Z",
646+
"@type": "xsd:dateTimeStamp"
647+
},
648+
"knora-base:previousValue": "http://rdfh.ch/0001/thing-with-history/values/2a",
649+
"knora-api:valueHasUUID": "W5fm67e0QDWxRZumcXcs6g",
650+
"@type": "knora-base:TextValue",
651+
"rdf:Property": "http://www.knora.org/ontology/0001/anything#hasText",
652+
"@id": "http://rdfh.ch/0001/thing-with-history/values/2b"
653+
},
654+
"knora-api:versionDate": {
655+
"@value": "2019-02-11T10:05:10Z",
656+
"@type": "xsd:dateTimeStamp"
657+
}
658+
},
659+
{
660+
"knora-api:eventType": "deleteValue",
661+
"knora-api:author": {
662+
"@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q"
663+
},
664+
"knora-api:eventBody": {
665+
"knora-api:resourceIri": "http://rdfh.ch/0001/thing-with-history",
666+
"knora-api:resourceClassIri": "http://www.knora.org/ontology/0001/anything#Thing",
667+
"knora-base:previousValue": "http://rdfh.ch/0001/thing-with-history/values/3a",
668+
"knora-api:deleteDate": {
669+
"@type": "xsd:dateTimeStamp",
670+
"@value": "2019-02-13T09:00:10Z"
671+
},
672+
"knora-api:isDeleted": true,
673+
"@type": "knora-base:LinkValue",
674+
"rdf:Property": "http://www.knora.org/ontology/0001/anything#hasOtherThingValue",
675+
"@id": "http://rdfh.ch/0001/thing-with-history/values/3b"
676+
},
677+
"knora-api:versionDate": {
678+
"@value": "2019-02-13T09:00:10Z",
679+
"@type": "xsd:dateTimeStamp"
680+
}
681+
}
682+
],
683+
"@context" : {
684+
"rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
685+
"rdfs" : "http://www.w3.org/2000/01/rdf-schema#",
686+
"xsd" : "http://www.w3.org/2001/XMLSchema#",
687+
"knora-api" : "http://api.knora.org/ontology/knora-api/v2#"
688+
}
689+
}
690+
```

‎test_data/all_data/anything-data.ttl‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@
711711
knora-base:attachedToProject <http://rdfh.ch/projects/0001>;
712712
rdfs:label "A thing with a picture";
713713
knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|RV knora-admin:UnknownUser";
714-
knora-base:creationDate "2016-03-02T15:05:10Z"^^xsd:dateTime;
714+
knora-base:creationDate "2011-03-02T15:05:10Z"^^xsd:dateTime;
715715
knora-base:hasStillImageFileValue <http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q> .
716716

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

17211722
<http://rdfh.ch/0001/PHbbrEsVR32q5D_ioKt6pA/values/7BIm9QAiQqKixcgXDWf12Q> a knora-base:IntValue;
17221723
knora-base:attachedToUser <http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q>;

‎test_data/resourcesR2RV2/ThingWithPicture.jsonld‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@
2525
},
2626
"rdfs:label": "A thing with a picture",
2727
"knora-api:versionArkUrl": {
28-
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20160302T150510Z",
28+
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20110302T150510Z",
2929
"@type": "xsd:anyURI"
3030
},
3131
"knora-api:attachedToProject": {
3232
"@id": "http://rdfh.ch/projects/0001"
3333
},
3434
"knora-api:userHasPermission": "RV",
3535
"knora-api:creationDate": {
36-
"@value": "2016-03-02T15:05:10Z",
36+
"@value": "2011-03-02T15:05:10Z",
3737
"@type": "xsd:dateTimeStamp"
3838
},
3939
"knora-api:attachedToUser": {

‎test_data/valuesE2EV2/get-still-image-file-value-response.jsonld‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@
3939
"@id": "http://rdfh.ch/0001/a-thing-picture/values/goZ7JFRNSeqF-dNxsqAS7Q"
4040
},
4141
"knora-api:versionArkUrl": {
42-
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20160302T150510Z",
42+
"@value": "http://0.0.0.0:3336/ark:/72163/1/0001/a=thing=picture0.20110302T150510Z",
4343
"@type": "xsd:anyURI"
4444
},
4545
"knora-api:attachedToProject": {
4646
"@id": "http://rdfh.ch/projects/0001"
4747
},
4848
"knora-api:userHasPermission": "CR",
4949
"knora-api:creationDate": {
50-
"@value": "2016-03-02T15:05:10Z",
50+
"@value": "2011-03-02T15:05:10Z",
5151
"@type": "xsd:dateTimeStamp"
5252
},
5353
"knora-api:attachedToUser": {

‎webapi/src/main/scala/org/knora/webapi/messages/OntologyConstants.scala‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,10 @@ object OntologyConstants {
735735
val Result: IRI = KnoraApiV2PrefixExpansion + "result"
736736
val Error: IRI = KnoraApiV2PrefixExpansion + "error"
737737
val MayHaveMoreResults: IRI = KnoraApiV2PrefixExpansion + "mayHaveMoreResults"
738+
val EventType: IRI = KnoraApiV2PrefixExpansion + "eventType"
739+
val EventBody: IRI = KnoraApiV2PrefixExpansion + "eventBody"
740+
val ResourceClassIri: IRI = KnoraApiV2PrefixExpansion + "resourceClassIri"
741+
val ResourceIri: IRI = KnoraApiV2PrefixExpansion + "resourceIri"
738742

739743
val IsShared: IRI = KnoraApiV2PrefixExpansion + "isShared"
740744
val IsBuiltIn: IRI = KnoraApiV2PrefixExpansion + "isBuiltIn"

‎webapi/src/main/scala/org/knora/webapi/messages/util/ConstructResponseUtilV2.scala‎

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,20 +1419,24 @@ object ConstructResponseUtilV2 {
14191419
timeout: Timeout,
14201420
executionContext: ExecutionContext): Future[ReadResourceV2] = {
14211421
def getDeletionInfo(rdfData: RdfData): Option[DeletionInfo] = {
1422-
val isDeleted: Boolean = rdfData.requireBooleanObject(OntologyConstants.KnoraBase.IsDeleted.toSmartIri)
1423-
1424-
if (isDeleted) {
1425-
val deleteDate = rdfData.requireDateTimeObject(OntologyConstants.KnoraBase.DeleteDate.toSmartIri)
1426-
val maybeDeleteComment = rdfData.maybeStringObject(OntologyConstants.KnoraBase.DeleteComment.toSmartIri)
1427-
1428-
Some(
1429-
DeletionInfo(
1430-
deleteDate = deleteDate,
1431-
maybeDeleteComment = maybeDeleteComment
1432-
)
1433-
)
1434-
} else {
1435-
None
1422+
val mayHaveDeletedStatements: Option[Boolean] =
1423+
rdfData.maybeBooleanObject(OntologyConstants.KnoraBase.IsDeleted.toSmartIri)
1424+
mayHaveDeletedStatements match {
1425+
case Some(isDeleted: Boolean) =>
1426+
if (isDeleted) {
1427+
val deleteDate = rdfData.requireDateTimeObject(OntologyConstants.KnoraBase.DeleteDate.toSmartIri)
1428+
val maybeDeleteComment = rdfData.maybeStringObject(OntologyConstants.KnoraBase.DeleteComment.toSmartIri)
1429+
1430+
Some(
1431+
DeletionInfo(
1432+
deleteDate = deleteDate,
1433+
maybeDeleteComment = maybeDeleteComment
1434+
)
1435+
)
1436+
} else {
1437+
None
1438+
}
1439+
case _ => None
14361440
}
14371441
}
14381442

0 commit comments

Comments
 (0)