From f86de53ddcee6e4de6dfbe46fb33c6c9775ef872 Mon Sep 17 00:00:00 2001 From: Sepideh Alassi Date: Thu, 24 Jun 2021 15:09:42 +0430 Subject: [PATCH] feat(resourceHistoryEvents): route for resource history events (DSP-1749) (#1882) * feat (resourceHistoryEvents): responder for getting history events of a single resource * feat (resourceHistoryEvents): add route * refactor (historyEvents): clean up * docs (resourceHistoryEvents): add documentation * refactor(resourceHistoryEvents): clean up --- .../api-v2/reading-and-searching-resources.md | 49 ++++-- .../resourcemessages/ResourceMessagesV2.scala | 34 ++-- .../resourceAndValueEventsUtil.scala | 14 +- .../responders/v2/ResourcesResponderV2.scala | 166 +++++++++++------- .../webapi/routing/v2/ResourcesRouteV2.scala | 33 +++- .../e2e/v2/ResourcesRouteV2E2ESpec.scala | 11 +- .../ResourcesMessagesV2Spec.scala | 28 ++- .../v2/ResourcesResponderV2Spec.scala | 138 +++++---------- 8 files changed, 276 insertions(+), 197 deletions(-) diff --git a/docs/03-apis/api-v2/reading-and-searching-resources.md b/docs/03-apis/api-v2/reading-and-searching-resources.md index 4197e4f961..082a31d4bd 100644 --- a/docs/03-apis/api-v2/reading-and-searching-resources.md +++ b/docs/03-apis/api-v2/reading-and-searching-resources.md @@ -571,28 +571,32 @@ 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 +### Get the Full History of a Resource and its Values as Events -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: +To get a list of the changes that have been made to a resource and its values since its creation as events ordered by +date: ``` -HTTP GET to http://host/v2/resources/projectHistory/projectIRI +HTTP GET to http://host/v2/resources/resourceHistoryEvents/ ``` -The project IRI must be URL-encoded. The response is a list of events describing changes made to the resource and its values, +The resource 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`.), + `createdResource`, `updatedResourceMetadata`, `deletedResource`, `createdValue`, `updatedValueContent`, + `updatedValuePermissions`, or `deletedValue`.), `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: +`knora-api:eventBody` (the information necessary to make the same request). + +For example, the following response contains the list of events describing the version history of the resource +`http://rdfh.ch/0001/thing-with-history` ordered by date: ```jsonld { "@graph" : [ { - "knora-api:eventType": "createResource", + "knora-api:eventType": "createdResource", "knora-api:author": { "@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" }, @@ -615,7 +619,7 @@ The project IRI must be URL-encoded. The response is a list of events describing } }, { - "knora-api:eventType": "createValue", + "knora-api:eventType": "createdValue", "knora-api:author": { "@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" }, @@ -643,7 +647,7 @@ The project IRI must be URL-encoded. The response is a list of events describing } }, { - "knora-api:eventType": "updateValueContent", + "knora-api:eventType": "updatedValueContent", "knora-api:author": { "@id": "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ" }, @@ -669,7 +673,7 @@ The project IRI must be URL-encoded. The response is a list of events describing } }, { - "knora-api:eventType": "deleteValue", + "knora-api:eventType": "deletedValue", "knora-api:author": { "@id": "http://rdfh.ch/users/9XBCrDV3SRa7kS1WwynB4Q" }, @@ -699,4 +703,25 @@ The project IRI must be URL-encoded. The response is a list of events describing "knora-api" : "http://api.knora.org/ontology/knora-api/v2#" } } -``` \ No newline at end of file +``` + +Since the history of changes made to the metadata of a resource is not part of resouce's version history, there are no +events describing the changes on metadata elements like its `rdfs:label` or `rdfs:comment`. +The only record depicting a change in a resource's metadata is the `knora-api:lastModificationDate` of the resource. Thus +the event `updatedResourceMetadata` indicates a change in a resource's metadata, its `knora-api:eventBody` contains the +payload needed to update the value of the resource's `lastModificationDate`, see +[modifying metadata of a resource](editing-resources.md#modifying-a-resources-metadata). + + + +### Get the Full History of all Resources of a Project as Events + +To get a list of the changes that have been made to the resources and their values of a project as events ordered by +date: + +``` +HTTP GET to http://host/v2/resources/projectHistoryEvents/ +``` + +The project IRI must be URL-encoded. The response contains the resource history events of all resources that belong to +the specified project. \ No newline at end of file diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala index 9df7bb71e6..21171de41d 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/ResourceMessagesV2.scala @@ -148,15 +148,19 @@ case class ResourceVersionHistoryGetRequestV2(resourceIri: IRI, * Requests the full version history of a resource and its values as events. * * @param resourceIri the IRI of the resource. - * @param resourceVersionHistory the version history of the resource and its values. * @param featureFactoryConfig the feature factory configuration. * @param requestingUser the user making the request. */ -case class ResourceFullHistoryGetRequestV2(resourceIri: IRI, - resourceVersionHistory: Seq[ResourceHistoryEntry], - featureFactoryConfig: FeatureFactoryConfig, - requestingUser: UserADM) - extends ResourcesResponderRequestV2 +case class ResourceHistoryEventsGetRequestV2(resourceIri: IRI, + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM) + extends ResourcesResponderRequestV2 { + private val stringFormatter = StringFormatter.getInstanceForConstantOntologies + stringFormatter.validateAndEscapeIri(resourceIri, throw BadRequestException(s"Invalid resource IRI: $resourceIri")) + if (!stringFormatter.toSmartIri(resourceIri).isKnoraResourceIri) { + throw BadRequestException(s"Given IRI is not a resource IRI: $resourceIri") + } +} /** * Requests the version history of all resources of a project. @@ -1330,7 +1334,7 @@ case class GraphDataGetResponseV2(nodes: Seq[GraphNodeV2], edges: Seq[GraphEdgeV } /** - * Represents the version history of a resource or a values as events. + * Represents the version history of a resource or a value as events. * * @param eventType the type of the operation that is one of [[ResourceAndValueEventsUtil]] * @param versionDate the version date of the event. @@ -1338,10 +1342,10 @@ case class GraphDataGetResponseV2(nodes: Seq[GraphNodeV2], edges: Seq[GraphEdgeV * @param eventBody the request body in the form of [[ResourceOrValueEventBody]] needed for the operation indicated * by eventType. */ -case class ResourceAndValueHistoryV2(eventType: String, - versionDate: Instant, - author: IRI, - eventBody: ResourceOrValueEventBody) +case class ResourceAndValueHistoryEvent(eventType: String, + versionDate: Instant, + author: IRI, + eventBody: ResourceOrValueEventBody) abstract class ResourceOrValueEventBody @@ -1549,9 +1553,9 @@ case class ValueEventBody(resourceIri: IRI, } /** - * Represents the history of the project resources and values. + * Represents the resource and value history events. */ -case class ResourceAndValueVersionHistoryResponseV2(projectHistory: Seq[ResourceAndValueHistoryV2]) +case class ResourceAndValueVersionHistoryResponseV2(historyEvents: Seq[ResourceAndValueHistoryEvent]) extends KnoraJsonLDResponseV2 { /** @@ -1571,7 +1575,7 @@ case class ResourceAndValueVersionHistoryResponseV2(projectHistory: Seq[Resource // Convert the history entries to an array of JSON-LD objects. - val projectHistoryAsJsonLD: Seq[JsonLDObject] = projectHistory.map { historyEntry: ResourceAndValueHistoryV2 => + val historyEventsAsJsonLD: Seq[JsonLDObject] = historyEvents.map { historyEntry: ResourceAndValueHistoryEvent => // convert event body to JsonLD object val eventBodyAsJsonLD: JsonLDObject = historyEntry.eventBody match { case valueEventBody: ValueEventBody => valueEventBody.toJsonLD(targetSchema, settings, schemaOptions) @@ -1609,7 +1613,7 @@ case class ResourceAndValueVersionHistoryResponseV2(projectHistory: Seq[Resource // Make the JSON-LD document. - val body = JsonLDObject(Map(JsonLDKeywords.GRAPH -> JsonLDArray(projectHistoryAsJsonLD))) + val body = JsonLDObject(Map(JsonLDKeywords.GRAPH -> JsonLDArray(historyEventsAsJsonLD))) JsonLDDocument(body = body, context = context) } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala index 4ecd327a6c..7473e31dfb 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/resourcemessages/resourceAndValueEventsUtil.scala @@ -5,11 +5,11 @@ package org.knora.webapi.messages.v2.responder.resourcemessages */ object ResourceAndValueEventsUtil { - val CREATE_RESOURCE_EVENT = "createResource" - val DELETE_RESOURCE_EVENT = "deleteResource" - val UPDATE_RESOURCE_METADATA_EVENT = "updateResourceMetadata" - val CREATE_VALUE_EVENT = "createValue" - val UPDATE_VALUE_CONTENT_EVENT = "updateValueContent" - val UPDATE_VALUE_PERMISSION_EVENT = "updateValuePermission" - val DELETE_VALUE_EVENT = "deleteValue" + val CREATE_RESOURCE_EVENT = "createdResource" + val DELETE_RESOURCE_EVENT = "deletedResource" + val UPDATE_RESOURCE_METADATA_EVENT = "updatedResourceMetadata" + val CREATE_VALUE_EVENT = "createdValue" + val UPDATE_VALUE_CONTENT_EVENT = "updatedValueContent" + val UPDATE_VALUE_PERMISSION_EVENT = "updatedValuePermission" + val DELETE_VALUE_EVENT = "deletedValue" } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala index 9003f22e60..2debeb85d6 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ResourcesResponderV2.scala @@ -154,9 +154,11 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt case resourceIIIFManifestRequest: ResourceIIIFManifestGetRequestV2 => getIIIFManifestV2(resourceIIIFManifestRequest) - case projectResourcesWithHistoryRequestV2: ProjectResourcesWithHistoryGetRequestV2 => - getProjectResourcesWithHistoryV2(projectResourcesWithHistoryRequestV2) - case resourceFullHistRequest: ResourceFullHistoryGetRequestV2 => getResourceHistoryEvents(resourceFullHistRequest) + case resourceHistoryEventsRequest: ResourceHistoryEventsGetRequestV2 => + getResourceHistoryEvents(resourceHistoryEventsRequest) + + case projectHistoryEventsRequestV2: ProjectResourcesWithHistoryGetRequestV2 => + getProjectResourceHistoryEvents(projectHistoryEventsRequestV2) case other => handleUnexpectedMessage(other, log, this.getClass.getName) @@ -2439,19 +2441,44 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Returns the resources of a project with version history ordered by date. + * Returns all events describing the history of a resource ordered by version date. * - * @param projectResourcesGetRequest the resources with version history request. - * @return the all resources of project with ordered version history. + * @param resourceHistoryEventsGetRequest the request for events describing history of a resource. + * @return the events extracted from full representation of a resource at each time point in its history ordered by version date. */ - def getProjectResourcesWithHistoryV2(projectResourcesGetRequest: ProjectResourcesWithHistoryGetRequestV2) + def getResourceHistoryEvents(resourceHistoryEventsGetRequest: ResourceHistoryEventsGetRequestV2) + : Future[ResourceAndValueVersionHistoryResponseV2] = + for { + resourceHistory: ResourceVersionHistoryResponseV2 <- getResourceHistoryV2( + ResourceVersionHistoryGetRequestV2( + resourceIri = resourceHistoryEventsGetRequest.resourceIri, + withDeletedResource = true, + featureFactoryConfig = resourceHistoryEventsGetRequest.featureFactoryConfig, + requestingUser = resourceHistoryEventsGetRequest.requestingUser + )) + resourceFullHist: Seq[ResourceAndValueHistoryEvent] <- extractEventsFromHistory( + resourceIri = resourceHistoryEventsGetRequest.resourceIri, + resourceHistory = resourceHistory.history, + featureFactoryConfig = resourceHistoryEventsGetRequest.featureFactoryConfig, + requestingUser = resourceHistoryEventsGetRequest.requestingUser + ) + sortedResourceHistory = resourceFullHist.sortBy(_.versionDate) + } yield ResourceAndValueVersionHistoryResponseV2(historyEvents = sortedResourceHistory) + + /** + * Returns events representing the history of all resources and values belonging to a project ordered by date. + * + * @param projectResourceHistoryEventsGetRequest the request for history events of a project. + * @return the all history events of resources of a project ordered by version date. + */ + def getProjectResourceHistoryEvents(projectResourceHistoryEventsGetRequest: ProjectResourcesWithHistoryGetRequestV2) : Future[ResourceAndValueVersionHistoryResponseV2] = for { // Get the project; checks if a project with given IRI exists. projectInfoResponse: ProjectGetResponseADM <- (responderManager ? ProjectGetRequestADM( - identifier = ProjectIdentifierADM(maybeIri = Some(projectResourcesGetRequest.projectIri)), - featureFactoryConfig = projectResourcesGetRequest.featureFactoryConfig, - requestingUser = projectResourcesGetRequest.requestingUser + identifier = ProjectIdentifierADM(maybeIri = Some(projectResourceHistoryEventsGetRequest.projectIri)), + featureFactoryConfig = projectResourceHistoryEventsGetRequest.featureFactoryConfig, + requestingUser = projectResourceHistoryEventsGetRequest.requestingUser )).mapTo[ProjectGetResponseADM] // Do a SELECT prequery to get the IRIs of the resources that belong to the project. @@ -2465,59 +2492,61 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt sparqlSelectResponse <- (storeManager ? SparqlSelectRequest(prequery)).mapTo[SparqlSelectResult] mainResourceIris: Seq[IRI] = sparqlSelectResponse.results.bindings.map(_.rowMap("resource")) // For each resource IRI return history events - historyOfResourcesAsSeqOfFutures: Seq[Future[Seq[ResourceAndValueHistoryV2]]] = mainResourceIris.map { + historyOfResourcesAsSeqOfFutures: Seq[Future[Seq[ResourceAndValueHistoryEvent]]] = mainResourceIris.map { resourceIri => for { resourceHistory: ResourceVersionHistoryResponseV2 <- getResourceHistoryV2( ResourceVersionHistoryGetRequestV2( resourceIri = resourceIri, withDeletedResource = true, - featureFactoryConfig = projectResourcesGetRequest.featureFactoryConfig, - requestingUser = projectResourcesGetRequest.requestingUser - )) - resourceFullHist: Seq[ResourceAndValueHistoryV2] <- getResourceHistoryEvents( - ResourceFullHistoryGetRequestV2( - resourceIri = resourceIri, - resourceVersionHistory = resourceHistory.history, - featureFactoryConfig = projectResourcesGetRequest.featureFactoryConfig, - requestingUser = projectResourcesGetRequest.requestingUser + featureFactoryConfig = projectResourceHistoryEventsGetRequest.featureFactoryConfig, + requestingUser = projectResourceHistoryEventsGetRequest.requestingUser )) + resourceFullHist: Seq[ResourceAndValueHistoryEvent] <- extractEventsFromHistory( + resourceIri = resourceIri, + resourceHistory = resourceHistory.history, + featureFactoryConfig = projectResourceHistoryEventsGetRequest.featureFactoryConfig, + requestingUser = projectResourceHistoryEventsGetRequest.requestingUser + ) } yield resourceFullHist } - projectHistory: Seq[Seq[ResourceAndValueHistoryV2]] <- Future.sequence(historyOfResourcesAsSeqOfFutures) - sortedProjectHistory: Seq[ResourceAndValueHistoryV2] = projectHistory.flatten.sortBy(_.versionDate) + projectHistory: Seq[Seq[ResourceAndValueHistoryEvent]] <- Future.sequence(historyOfResourcesAsSeqOfFutures) + sortedProjectHistory: Seq[ResourceAndValueHistoryEvent] = projectHistory.flatten.sortBy(_.versionDate) - } yield ResourceAndValueVersionHistoryResponseV2(projectHistory = sortedProjectHistory) + } yield ResourceAndValueVersionHistoryResponseV2(historyEvents = sortedProjectHistory) /** - * Returns the full history of a resource as events. + * Extract events from full representations of resource in each point of its history. * - * @param resourceFullHistRequest the version history of a resource. - * @return the full history of resource as sequence of [[ResourceAndValueHistoryV2]]. + * @param resourceIri the IRI of the resource. + * @param resourceHistory the full representations of the resource in each point in its history. + * @param featureFactoryConfig the feature factory configuration. + * @param requestingUser the user making the request. + * @return the full history of resource as sequence of [[ResourceAndValueHistoryEvent]]. */ - def getResourceHistoryEvents( - resourceFullHistRequest: ResourceFullHistoryGetRequestV2): Future[Seq[ResourceAndValueHistoryV2]] = { - - val resourceHist: Seq[ResourceHistoryEntry] = resourceFullHistRequest.resourceVersionHistory.reverse - // Collect the full representations of the resource for each version date - val histories: Seq[Future[(ResourceHistoryEntry, ReadResourceV2)]] = resourceHist.map { hist => - for { - fullRepresentations <- getResourceAtGivenTime( - resourceIri = resourceFullHistRequest.resourceIri, + def extractEventsFromHistory(resourceIri: IRI, + resourceHistory: Seq[ResourceHistoryEntry], + featureFactoryConfig: FeatureFactoryConfig, + requestingUser: UserADM): Future[Seq[ResourceAndValueHistoryEvent]] = + for { + resourceHist: Seq[ResourceHistoryEntry] <- Future.successful(resourceHistory.reverse) + // Collect the full representations of the resource for each version date + histories: Seq[Future[(ResourceHistoryEntry, ReadResourceV2)]] = resourceHist.map { hist => + getResourceAtGivenTime( + resourceIri = resourceIri, versionHist = hist, - featureFactoryConfig = resourceFullHistRequest.featureFactoryConfig, - requestingUser = resourceFullHistRequest.requestingUser + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser ) - } yield fullRepresentations - } - for { + } + fullReps: Seq[(ResourceHistoryEntry, ReadResourceV2)] <- Future.sequence(histories) // Create an event for the resource at creation time (creationTimeHist, resourceAtCreation) = fullReps.head - resourceCreationEvent: Seq[ResourceAndValueHistoryV2] = getResourceCreationEvent(resourceAtCreation, - creationTimeHist) + resourceCreationEvent: Seq[ResourceAndValueHistoryEvent] = getResourceCreationEvent(resourceAtCreation, + creationTimeHist) // If there is a version history for deletion of the event, create a delete resource event for it. (deletionRep, resourceAtValueChanges) = fullReps.tail.partition { @@ -2530,17 +2559,17 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt resourceDeleteEvent = getResourceDeletionEvents(deletionRep) // For each value version, form an event - valuesEvents: Seq[ResourceAndValueHistoryV2] = resourceAtValueChanges.flatMap { + valuesEvents: Seq[ResourceAndValueHistoryEvent] = resourceAtValueChanges.flatMap { case (versionHist, readResource) => getValueEvents(readResource, versionHist, fullReps) } // Get the update resource metadata event, if there is any. - resourceMetadataUpdateEvent: Seq[ResourceAndValueHistoryV2] = getResourceMetadataUpdateEvent(fullReps.last, - valuesEvents, - resourceDeleteEvent) + resourceMetadataUpdateEvent: Seq[ResourceAndValueHistoryEvent] = getResourceMetadataUpdateEvent( + fullReps.last, + valuesEvents, + resourceDeleteEvent) } yield resourceCreationEvent ++ resourceDeleteEvent ++ valuesEvents ++ resourceMetadataUpdateEvent - } /** * Returns the full representation of a resource at a given date. @@ -2569,14 +2598,15 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } yield versionHist -> resourceAtCreationTime /** - * Returns a createResource event as [[ResourceAndValueHistoryV2]] with request body of the form [[ResourceEventBody]]. + * Returns a createResource event as [[ResourceAndValueHistoryEvent]] with request body of the form [[ResourceEventBody]]. * * @param resourceAtTimeOfCreation the full representation of the resource at creation date. * @param versionInfoAtCreation the history info of the version; i.e. versionDate and author. * @return a createResource event. */ - private def getResourceCreationEvent(resourceAtTimeOfCreation: ReadResourceV2, - versionInfoAtCreation: ResourceHistoryEntry): Seq[ResourceAndValueHistoryV2] = { + private def getResourceCreationEvent( + resourceAtTimeOfCreation: ReadResourceV2, + versionInfoAtCreation: ResourceHistoryEntry): Seq[ResourceAndValueHistoryEvent] = { val requestBody: ResourceEventBody = ResourceEventBody( resourceIri = resourceAtTimeOfCreation.resourceIri, @@ -2591,7 +2621,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt ) Seq( - ResourceAndValueHistoryV2( + ResourceAndValueHistoryEvent( eventType = ResourceAndValueEventsUtil.CREATE_RESOURCE_EVENT, versionDate = versionInfoAtCreation.versionDate, author = versionInfoAtCreation.author, @@ -2600,14 +2630,14 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Returns resourceDeletion events as Seq[[ResourceAndValueHistoryV2]] with request body of the form [[ResourceEventBody]]. + * Returns resourceDeletion events as Seq[[ResourceAndValueHistoryEvent]] with request body of the form [[ResourceEventBody]]. * * @param resourceDeletionInfo A sequence of resource deletion info containing version history of deletion and * the full representation of resource at time of deletion. * @return a seq of deleteResource events. */ private def getResourceDeletionEvents( - resourceDeletionInfo: Seq[(ResourceHistoryEntry, ReadResourceV2)]): Seq[ResourceAndValueHistoryV2] = { + resourceDeletionInfo: Seq[(ResourceHistoryEntry, ReadResourceV2)]): Seq[ResourceAndValueHistoryEvent] = { resourceDeletionInfo.map { case (delHist, fullRepresentation) => val requestBody: ResourceEventBody = ResourceEventBody( @@ -2617,7 +2647,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt lastModificationDate = fullRepresentation.lastModificationDate, deletionInfo = fullRepresentation.deletionInfo ) - ResourceAndValueHistoryV2( + ResourceAndValueHistoryEvent( eventType = ResourceAndValueEventsUtil.DELETE_RESOURCE_EVENT, versionDate = delHist.versionDate, author = delHist.author, @@ -2627,7 +2657,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Returns a value event as [[ResourceAndValueHistoryV2]] with body of the form [[ValueEventBody]]. + * Returns a value event as [[ResourceAndValueHistoryEvent]] with body of the form [[ValueEventBody]]. * * @param resourceAtGivenTime the full representation of the resource at the given time. * @param versionHist the history info of the version; i.e. versionDate and author. @@ -2637,7 +2667,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt private def getValueEvents( resourceAtGivenTime: ReadResourceV2, versionHist: ResourceHistoryEntry, - allResourceVersions: Seq[(ResourceHistoryEntry, ReadResourceV2)]): Seq[ResourceAndValueHistoryV2] = { + allResourceVersions: Seq[(ResourceHistoryEntry, ReadResourceV2)]): Seq[ResourceAndValueHistoryEvent] = { val resourceIri = resourceAtGivenTime.resourceIri /** returns the values of the resource which have the given version date. */ @@ -2659,7 +2689,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt val valuesWithAskedVersionDate: Map[SmartIri, ReadValueV2] = findValuesWithGivenVersionDate( resourceAtGivenTime.values) - val valueEvents: Seq[ResourceAndValueHistoryV2] = valuesWithAskedVersionDate.map { + val valueEvents: Seq[ResourceAndValueHistoryEvent] = valuesWithAskedVersionDate.map { case (propIri, readValue) => val event = //Is the given date a deletion date? @@ -2675,7 +2705,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt deletionInfo = readValue.deletionInfo, previousValueIri = readValue.previousValueIri ) - ResourceAndValueHistoryV2( + ResourceAndValueHistoryEvent( eventType = ResourceAndValueEventsUtil.DELETE_VALUE_EVENT, versionDate = versionHist.versionDate, author = versionHist.author, @@ -2698,7 +2728,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt permissions = Some(readValue.permissions), valueComment = readValue.valueContent.comment ) - ResourceAndValueHistoryV2( + ResourceAndValueHistoryEvent( eventType = ResourceAndValueEventsUtil.CREATE_VALUE_EVENT, versionDate = versionHist.versionDate, author = versionHist.author, @@ -2708,7 +2738,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt // No. return updateValue event val (updateEventType: String, updateEventRequestBody: ValueEventBody) = getValueUpdateEventType(propIri, readValue, allResourceVersions, resourceAtGivenTime) - ResourceAndValueHistoryV2( + ResourceAndValueHistoryEvent( eventType = updateEventType, versionDate = versionHist.versionDate, author = versionHist.author, @@ -2793,7 +2823,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt } /** - * Returns an updateResourceMetadata event as [[ResourceAndValueHistoryV2]] with request body of the form + * Returns an updateResourceMetadata event as [[ResourceAndValueHistoryEvent]] with request body of the form * [[ResourceMetadataEventBody]] with information necessary to make update metadata of resource request with a * given modification date. * @@ -2804,21 +2834,21 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt */ private def getResourceMetadataUpdateEvent( latestVersionOfResource: (ResourceHistoryEntry, ReadResourceV2), - valueEvents: Seq[ResourceAndValueHistoryV2], - resourceDeleteEvents: Seq[ResourceAndValueHistoryV2]): Seq[ResourceAndValueHistoryV2] = { + valueEvents: Seq[ResourceAndValueHistoryEvent], + resourceDeleteEvents: Seq[ResourceAndValueHistoryEvent]): Seq[ResourceAndValueHistoryEvent] = { val readResource: ReadResourceV2 = latestVersionOfResource._2 val author: IRI = latestVersionOfResource._1.author // Is lastModificationDate of resource None readResource.lastModificationDate match { // Yes. Do nothing. - case None => Seq.empty[ResourceAndValueHistoryV2] + case None => Seq.empty[ResourceAndValueHistoryEvent] // No. Either a value or the resource metadata must have been modified. case Some(modDate) => val deletionEventWithSameDate = resourceDeleteEvents.find(event => event.versionDate == modDate) // Is the lastModificationDate of the resource the same as its deletion date? val updateMetadataEvent = if (deletionEventWithSameDate.isDefined) { // Yes. Do noting. - Seq.empty[ResourceAndValueHistoryV2] + Seq.empty[ResourceAndValueHistoryEvent] // No. Is there any value event? } else if (valueEvents.isEmpty) { // No. After creation of the resource its metadata must have been updated, use creation date as the lastModification date of the event. @@ -2828,7 +2858,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt lastModificationDate = readResource.creationDate, newModificationDate = modDate ) - val event = ResourceAndValueHistoryV2( + val event = ResourceAndValueHistoryEvent( eventType = ResourceAndValueEventsUtil.UPDATE_RESOURCE_METADATA_EVENT, versionDate = modDate, author = author, @@ -2842,7 +2872,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt val modDateExists = valueEvents.find(event => event.versionDate == modDate) modDateExists match { // Yes. The last modification date of the resource reflects the modification of a value. Return nothing. - case Some(_) => Seq.empty[ResourceAndValueHistoryV2] + case Some(_) => Seq.empty[ResourceAndValueHistoryEvent] // No. The last modification date of the resource reflects update of a resource's metadata. Return an updateMetadataEvent case None => // Find the event with version date before resource's last modification date. @@ -2863,7 +2893,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt lastModificationDate = oldModDate, newModificationDate = modDate ) - val event = ResourceAndValueHistoryV2( + val event = ResourceAndValueHistoryEvent( eventType = ResourceAndValueEventsUtil.UPDATE_RESOURCE_METADATA_EVENT, versionDate = modDate, author = author, diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala index fa5513b0e5..3a46e707a0 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ResourcesRouteV2.scala @@ -67,6 +67,7 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) updateResourceMetadata(featureFactoryConfig) ~ getResourcesInProject(featureFactoryConfig) ~ getResourceHistory(featureFactoryConfig) ~ + getResourceHistoryEvents(featureFactoryConfig) ~ getProjectResourceAndValueHistory(featureFactoryConfig) ~ getResources(featureFactoryConfig) ~ getResourcesPreview(featureFactoryConfig) ~ @@ -302,8 +303,38 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) } } + private def getResourceHistoryEvents(featureFactoryConfig: FeatureFactoryConfig): Route = + path(ResourcesBasePath / "resourceHistoryEvents" / Segment) { resourceIri: IRI => + get { requestContext => + { + val requestMessageFuture: Future[ResourceHistoryEventsGetRequestV2] = for { + requestingUser <- getUserADM( + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig + ) + } yield + ResourceHistoryEventsGetRequestV2( + resourceIri = resourceIri, + featureFactoryConfig = featureFactoryConfig, + requestingUser = requestingUser + ) + + RouteUtilV2.runRdfRouteWithFuture( + requestMessageF = requestMessageFuture, + requestContext = requestContext, + featureFactoryConfig = featureFactoryConfig, + settings = settings, + responderManager = responderManager, + log = log, + targetSchema = ApiV2Complex, + schemaOptions = RouteUtilV2.getSchemaOptions(requestContext) + ) + } + } + } + private def getProjectResourceAndValueHistory(featureFactoryConfig: FeatureFactoryConfig): Route = - path(ResourcesBasePath / "projectHistory" / Segment) { projectIri: IRI => + path(ResourcesBasePath / "projectHistoryEvents" / Segment) { projectIri: IRI => get { requestContext => { val requestMessageFuture: Future[ProjectResourcesWithHistoryGetRequestV2] = for { diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala index 3a8c48a6a7..2e16e90bc3 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ResourcesRouteV2E2ESpec.scala @@ -611,9 +611,18 @@ class ResourcesRouteV2E2ESpec extends E2ESpec(ResourcesRouteV2E2ESpec.config) { } } + "return all history events for a given resource" in { + val resourceIri = URLEncoder.encode("http://rdfh.ch/0001/a-thing-picture", "UTF-8") + val resourceHistoryRequest = Get(s"$baseApiUrl/v2/resources/resourceHistoryEvents/$resourceIri") + .addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, password)) + val resourceHistoryResponse: HttpResponse = singleAwaitingRequest(resourceHistoryRequest) + val historyResponseAsString = responseToString(resourceHistoryResponse) + assert(resourceHistoryResponse.status == StatusCodes.OK, historyResponseAsString) + } + "return entire resource and value history events for a given project" in { val projectIri = URLEncoder.encode("http://rdfh.ch/projects/0001", "UTF-8") - val projectHistoryRequest = Get(s"$baseApiUrl/v2/resources/projectHistory/$projectIri") + val projectHistoryRequest = Get(s"$baseApiUrl/v2/resources/projectHistoryEvents/$projectIri") .addCredentials(BasicHttpCredentials(SharedTestDataADM.anythingAdminUser.email, password)) val projectHistoryResponse: HttpResponse = singleAwaitingRequest(projectHistoryRequest) val historyResponseAsString = responseToString(projectHistoryResponse) diff --git a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala index ef84a01efa..d8d0965bfb 100644 --- a/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/messages/v2/responder/resourcesmessages/ResourcesMessagesV2Spec.scala @@ -9,7 +9,7 @@ import org.knora.webapi.sharedtestdata._ * Tests [[ResourceMessagesV2]]. */ class ResourcesMessagesV2Spec extends CoreSpec() { - "All Resources of a Project With History Get Requests" should { + "Get history events of all resources of a project" should { "fail if given project IRI is not valid" in { val projectIri = "invalid-project-IRI" val caught = intercept[BadRequestException]( @@ -33,4 +33,30 @@ class ResourcesMessagesV2Spec extends CoreSpec() { assert(caught.getMessage === "Given IRI is not a project IRI.") } } + + "Get history events of a single resource" should { + "fail if given resource IRI is not valid" in { + val resourceIri = "invalid-resource-IRI" + val caught = intercept[BadRequestException]( + ResourceHistoryEventsGetRequestV2( + resourceIri = resourceIri, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = SharedTestDataADM.imagesUser01 + ) + ) + assert(caught.getMessage === s"Invalid resource IRI: $resourceIri") + } + + "fail if given IRI is not a resource IRI" in { + val resourceIri = "http://rdfh.ch/projects/0001" + val caught = intercept[BadRequestException]( + ResourceHistoryEventsGetRequestV2( + resourceIri = resourceIri, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = SharedTestDataADM.imagesUser01 + ) + ) + assert(caught.getMessage === s"Given IRI is not a resource IRI: $resourceIri") + } + } } diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index bcd422f7ad..dd888228a0 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -2385,35 +2385,17 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { } } - "not return resources of a project which does not exist" in { - - responderManager ! ProjectResourcesWithHistoryGetRequestV2( - projectIri = "http://rdfh.ch/projects/1111", - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = SharedTestDataADM.anythingAdminUser - ) - expectMsgPF(timeout) { - case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) - } - } - "return full history of a-thing-picture resource" in { val resourceIri = "http://rdfh.ch/0001/a-thing-picture" - responderManager ! ResourceVersionHistoryGetRequestV2( - resourceIri = resourceIri, - withDeletedResource = true, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - responderManager ! ResourceFullHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - resourceVersionHistory = response.history, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size shouldEqual (3) val createResourceEvents = events.filter(historyEvent => historyEvent.eventType == ResourceAndValueEventsUtil.CREATE_RESOURCE_EVENT) @@ -2429,21 +2411,14 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { "return full history of a resource as events" in { val resourceIri = "http://rdfh.ch/0001/thing-with-history" - responderManager ! ResourceVersionHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - withDeletedResource = true, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - - responderManager ! ResourceFullHistoryGetRequestV2( - resourceIri = resourceIri, - resourceVersionHistory = response.history, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(9) } @@ -2467,23 +2442,16 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { val updateValuePermissionResponse = expectMsgType[UpdateValueResponseV2](timeout) - responderManager ! ResourceVersionHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - withDeletedResource = true, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - - responderManager ! ResourceFullHistoryGetRequestV2( - resourceIri = resourceIri, - resourceVersionHistory = response.history, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(10) - val updatePermissionEvent: Option[ResourceAndValueHistoryV2] = + val updatePermissionEvent: Option[ResourceAndValueHistoryEvent] = events.find(event => event.eventType == ResourceAndValueEventsUtil.UPDATE_VALUE_PERMISSION_EVENT) assert(updatePermissionEvent.isDefined) val updatePermissionPayload = updatePermissionEvent.get.eventBody @@ -2516,23 +2484,16 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgType[CreateValueResponseV2](timeout) - responderManager ! ResourceVersionHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - withDeletedResource = true, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - - responderManager ! ResourceFullHistoryGetRequestV2( - resourceIri = resourceIri, - resourceVersionHistory = response.history, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(11) - val createValueEvent: Option[ResourceAndValueHistoryV2] = + val createValueEvent: Option[ResourceAndValueHistoryEvent] = events.find( event => event.eventType == ResourceAndValueEventsUtil.CREATE_VALUE_EVENT && event.eventBody @@ -2566,23 +2527,17 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { apiRequestID = UUID.randomUUID ) expectMsgType[SuccessResponseV2](timeout) - responderManager ! ResourceVersionHistoryGetRequestV2( - resourceIri = resourceIri, - withDeletedResource = true, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - responderManager ! ResourceFullHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - resourceVersionHistory = response.history, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(12) - val deleteValueEvent: Option[ResourceAndValueHistoryV2] = + val deleteValueEvent: Option[ResourceAndValueHistoryEvent] = events.find( event => event.eventType == ResourceAndValueEventsUtil.DELETE_VALUE_EVENT && event.eventBody @@ -2593,24 +2548,18 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { "return full history of a deleted resource" in { val resourceIri = "http://rdfh.ch/0001/PHbbrEsVR32q5D_ioKt6pA" - responderManager ! ResourceVersionHistoryGetRequestV2( - resourceIri = resourceIri, - withDeletedResource = true, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - responderManager ! ResourceFullHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - resourceVersionHistory = response.history, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(2) - val deleteResourceEvent: Option[ResourceAndValueHistoryV2] = + val deleteResourceEvent: Option[ResourceAndValueHistoryEvent] = events.find(event => event.eventType == ResourceAndValueEventsUtil.DELETE_RESOURCE_EVENT) assert(deleteResourceEvent.isDefined) val deletionInfo = deleteResourceEvent.get.eventBody.asInstanceOf[ResourceEventBody].deletionInfo.get @@ -2630,27 +2579,32 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { expectMsgType[SuccessResponseV2](timeout) - responderManager ! ResourceVersionHistoryGetRequestV2( - resourceIri = resourceIri, - withDeletedResource = true, - featureFactoryConfig = defaultFeatureFactoryConfig, - requestingUser = anythingUserProfile - ) - val response: ResourceVersionHistoryResponseV2 = expectMsgType[ResourceVersionHistoryResponseV2](timeout) - - responderManager ! ResourceFullHistoryGetRequestV2( + responderManager ! ResourceHistoryEventsGetRequestV2( resourceIri = resourceIri, - resourceVersionHistory = response.history, featureFactoryConfig = defaultFeatureFactoryConfig, requestingUser = anythingUserProfile ) - val events: Seq[ResourceAndValueHistoryV2] = expectMsgType[Seq[ResourceAndValueHistoryV2]](timeout) + val response: ResourceAndValueVersionHistoryResponseV2 = + expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) + val events: Seq[ResourceAndValueHistoryEvent] = response.historyEvents events.size should be(2) - val updateMetadataEvent: Option[ResourceAndValueHistoryV2] = + val updateMetadataEvent: Option[ResourceAndValueHistoryEvent] = events.find(event => event.eventType == ResourceAndValueEventsUtil.UPDATE_RESOURCE_METADATA_EVENT) assert(updateMetadataEvent.isDefined) } + "not return resources of a project which does not exist" in { + + responderManager ! ProjectResourcesWithHistoryGetRequestV2( + projectIri = "http://rdfh.ch/projects/1111", + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = SharedTestDataADM.anythingAdminUser + ) + expectMsgPF(timeout) { + case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true) + } + } + "return seq of full history events for each resource of a project" in { responderManager ! ProjectResourcesWithHistoryGetRequestV2( projectIri = "http://rdfh.ch/projects/0001", @@ -2659,7 +2613,7 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { ) val response: ResourceAndValueVersionHistoryResponseV2 = expectMsgType[ResourceAndValueVersionHistoryResponseV2](timeout) - response.projectHistory.size should be > 1 + response.historyEvents.size should be > 1 } }