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

feat(api-v2): Erase a resource #1429

Merged
merged 8 commits into from
Sep 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion docs/src/paradox/03-apis/api-v2/editing-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,32 @@ The optional property `knora-api:deleteComment` specifies a comment to be attach
resource, explaining why it has been marked as deleted.

The response is a JSON-LD document containing the predicate `knora-api:result`
with a confirmation message.
with a confirmation message.

## Erasing a Resource from the Triplestore

Normally, resources are not actually removed from the triplestore; they are
only marked as deleted (see @ref:[Deleting a Resource](#deleting-a-resource)).
However, sometimes it is necessary to erase a resource from the triplestore.
To do so, use this route:

```
HTTP POST to http://host/v2/resources/erase
```

The request body is the same as for @ref:[Deleting a Resource](#deleting-a-resource),
except that `knora-api:deleteComment` is not relevant and will be ignored.

To do this, a user must be a system administrator or an administrator of the
project containing the resource. The user's permissions on the resource are
not otherwise checked.

A resource cannot be erased if any other resource has a link to it. Any such
links must first be changed or marked as deleted
(see @ref:[Updating a Value](editing-values.md#updating-a-value) and
@ref:[Deleting a Value](editing-values.md#deleting-a-value)). Then,
when the resource is erased, the deleted link values that referred to
it will also be erased.

This operation cannot be undone (except by restoring the repository from a
backup), so use it with care.
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ Responders are not expected to know which triplestore is being used or how it
is accessed. To perform a SPARQL SELECT query, a responder sends a `SparqlSelectRequest`
message to the `storeManager` actor, like this:

@@snip [OntologyResponderV2.scala]($src$/org/knora/webapi/responders/v2/OntologyResponderV2.scala) { #sparql-select }
@@snip [OntologyResponderV2.scala]($src$/org/knora/webapi/responders/Responder.scala) { #sparql-select }

The reply message, `SparqlSelectResponse`, is a data structure containing the rows
that were returned as the query result.
Expand Down

Large diffs are not rendered by default.

122 changes: 85 additions & 37 deletions webapi/src/main/scala/org/knora/webapi/responders/Responder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,91 +21,139 @@ package org.knora.webapi.responders

import akka.actor.{ActorRef, ActorSystem}
import akka.http.scaladsl.util.FastFuture
import akka.pattern._
import akka.util.Timeout
import com.typesafe.scalalogging.{LazyLogging, Logger}
import org.knora.webapi.util.StringFormatter
import org.knora.webapi.messages.store.triplestoremessages.{SparqlSelectRequest, SparqlSelectResponse}
import org.knora.webapi.util.{SmartIri, StringFormatter}
import org.knora.webapi.{KnoraDispatchers, Settings, SettingsImpl, UnexpectedMessageException}

import scala.concurrent.{ExecutionContext, Future}
import scala.language.postfixOps

/**
* Responder helper methods.
*/
* Responder helper methods.
*/
object Responder {

/**
* An responder use this method to handle unexpected request messages in a consistent way.
*
* @param message the message that was received.
* @param log a [[Logger]].
* @param who the responder receiving the message.
*/
* An responder use this method to handle unexpected request messages in a consistent way.
*
* @param message the message that was received.
* @param log a [[Logger]].
* @param who the responder receiving the message.
*/
def handleUnexpectedMessage(message: Any, log: Logger, who: String): Future[Nothing] = {
val unexpectedMessageException = UnexpectedMessageException(s"$who received an unexpected message $message of type ${message.getClass.getCanonicalName}")
FastFuture.failed(unexpectedMessageException)
}
}

/**
* Data needed to be passed to each responder.
*
* @param system the actor system.
* @param appActor the main application actor ActorRef.
*/
* Data needed to be passed to each responder.
*
* @param system the actor system.
* @param appActor the main application actor ActorRef.
*/
case class ResponderData(system: ActorSystem, appActor: ActorRef)

/**
* An abstract class providing values that are commonly used in Knora responders.
*/
* An abstract class providing values that are commonly used in Knora responders.
*/
abstract class Responder(responderData: ResponderData) extends LazyLogging {

/**
* The actor system.
*/
* The actor system.
*/
protected implicit val system: ActorSystem = responderData.system

/**
* The execution context for futures created in Knora actors.
*/
* The execution context for futures created in Knora actors.
*/
protected implicit val executionContext: ExecutionContext = system.dispatchers.lookup(KnoraDispatchers.KnoraActorDispatcher)

/**
* The application settings.
*/
* The application settings.
*/
protected val settings: SettingsImpl = Settings(system)

/**
* The reference to the main application actor which will forward messages
* for the responder manager to the responder manager.
*/
* The reference to the main application actor which will forward messages
* for the responder manager to the responder manager.
*/
protected val responderManager: ActorRef = responderData.appActor

/**
* The reference to the main application actor which will forward messages
* for the store manager to the store manager.
*/
* The reference to the main application actor which will forward messages
* for the store manager to the store manager.
*/
protected val storeManager: ActorRef = responderData.appActor

/**
* The reference to the main application actor
*/
* The reference to the main application actor
*/
protected val appActor: ActorRef = responderData.appActor

/**
* A string formatter.
*/
* A string formatter.
*/
protected implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance

/**
* The application's default timeout for `ask` messages.
*/
* The application's default timeout for `ask` messages.
*/
protected implicit val timeout: Timeout = settings.defaultTimeout

/**
* Provides logging
*/
* Provides logging
*/
val log: Logger = logger
}

/**
* Checks whether an entity is used in the triplestore.
*
* @param entityIri the IRI of the entity.
* @param errorFun a function that throws an exception. It will be called if the entity is used.
* @param ignoreKnoraConstraints if `true`, ignores the use of the entity in Knora subject or object constraints.
* @param ignoreRdfSubjectAndObject if `true`, ignores the use of the entity in `rdf:subject` and `rdf:object`.
*/
protected def isEntityUsed(entityIri: SmartIri,
errorFun: => Nothing,
ignoreKnoraConstraints: Boolean = false,
ignoreRdfSubjectAndObject: Boolean = false): Future[Unit] = {
// #sparql-select
for {
isEntityUsedSparql <- Future(queries.sparql.v2.txt.isEntityUsed(
triplestore = settings.triplestoreType,
entityIri = entityIri,
ignoreKnoraConstraints = ignoreKnoraConstraints,
ignoreRdfSubjectAndObject = ignoreRdfSubjectAndObject
).toString())

isEntityUsedResponse: SparqlSelectResponse <- (storeManager ? SparqlSelectRequest(isEntityUsedSparql)).mapTo[SparqlSelectResponse]
// #sparql-select

_ = if (isEntityUsedResponse.results.bindings.nonEmpty) {
errorFun
}
} yield ()
}

/**
* Checks whether an entity exists in the triplestore.
*
* @param entityIri the IRI of the entity.
* @return `true` if the entity exists.
*/
protected def checkEntityExists(entityIri: SmartIri): Future[Boolean] = {
for {
checkEntityExistsSparql <- Future(queries.sparql.v2.txt.checkEntityExists(
triplestore = settings.triplestoreType,
entityIri = entityIri
).toString())

entityExistsResponse: SparqlSelectResponse <- (storeManager ? SparqlSelectRequest(checkEntityExistsSparql)).mapTo[SparqlSelectResponse]
result: Boolean = entityExistsResponse.results.bindings.nonEmpty
} yield result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3384,31 +3384,6 @@ class OntologyResponderV2(responderData: ResponderData) extends Responder(respon
} yield taskResult
}

/**
* Checks whether an entity is used in the triplestore.
*
* @param entityIri the IRI of the entity.
* @param errorFun a function that throws an exception. It will be called if the entity is used.
* @param ignoreKnoraConstraints if true, ignores the use of the entity in Knora subject or object constraints.
*/
private def isEntityUsed(entityIri: SmartIri, errorFun: => Nothing, ignoreKnoraConstraints: Boolean = false): Future[Unit] = {
// #sparql-select
for {
isEntityUsedSparql <- Future(queries.sparql.v2.txt.isEntityUsed(
triplestore = settings.triplestoreType,
entityIri = entityIri,
ignoreKnoraConstraints = ignoreKnoraConstraints
).toString())

isEntityUsedResponse: SparqlSelectResponse <- (storeManager ? SparqlSelectRequest(isEntityUsedSparql)).mapTo[SparqlSelectResponse]
// #sparql-select

_ = if (isEntityUsedResponse.results.bindings.nonEmpty) {
errorFun
}
} yield ()
}

/**
* Before an update of an ontology entity, checks that the entity's external IRI, and that of its ontology,
* are valid, and checks that the user has permission to update the ontology.
Expand Down
Loading