diff --git a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
index 3a8c1431a6..7badf8c77f 100644
--- a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
+++ b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
@@ -1005,22 +1005,44 @@ object SharedTestDataADM {
}
def createLinkValueRequest(resourceIri: IRI,
- targetResourceIri: IRI): String = {
- s"""{
- | "@id" : "$resourceIri",
- | "@type" : "anything:Thing",
- | "anything:hasOtherThingValue" : {
- | "@type" : "knora-api:LinkValue",
- | "knora-api:linkValueHasTargetIri" : {
- | "@id" : "$targetResourceIri"
- | }
- | },
- | "@context" : {
- | "xsd" : "http://www.w3.org/2001/XMLSchema#",
- | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
- | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
- | }
- |}""".stripMargin
+ targetResourceIri: IRI,
+ valueHasComment: Option[String] = None): String = {
+ valueHasComment match {
+ case Some(comment) =>
+ s"""{
+ | "@id" : "$resourceIri",
+ | "@type" : "anything:Thing",
+ | "anything:hasOtherThingValue" : {
+ | "@type" : "knora-api:LinkValue",
+ | "knora-api:linkValueHasTargetIri" : {
+ | "@id" : "$targetResourceIri"
+ | },
+ | "knora-api:valueHasComment" : "$comment"
+ | },
+ | "@context" : {
+ | "xsd" : "http://www.w3.org/2001/XMLSchema#",
+ | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
+ | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
+ | }
+ |}""".stripMargin
+
+ case None =>
+ s"""{
+ | "@id" : "$resourceIri",
+ | "@type" : "anything:Thing",
+ | "anything:hasOtherThingValue" : {
+ | "@type" : "knora-api:LinkValue",
+ | "knora-api:linkValueHasTargetIri" : {
+ | "@id" : "$targetResourceIri"
+ | }
+ | },
+ | "@context" : {
+ | "xsd" : "http://www.w3.org/2001/XMLSchema#",
+ | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
+ | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
+ | }
+ |}""".stripMargin
+ }
}
def updateIntValueRequest(resourceIri: IRI,
@@ -1430,23 +1452,46 @@ object SharedTestDataADM {
def updateLinkValueRequest(resourceIri: IRI,
valueIri: IRI,
- targetResourceIri: IRI): String = {
- s"""{
- | "@id" : "$resourceIri",
- | "@type" : "anything:Thing",
- | "anything:hasOtherThingValue" : {
- | "@id" : "$valueIri",
- | "@type" : "knora-api:LinkValue",
- | "knora-api:linkValueHasTargetIri" : {
- | "@id" : "$targetResourceIri"
- | }
- | },
- | "@context" : {
- | "xsd" : "http://www.w3.org/2001/XMLSchema#",
- | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
- | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
- | }
- |}""".stripMargin
+ targetResourceIri: IRI,
+ comment: Option[String] = None): String = {
+ comment match {
+ case Some(definedComment) =>
+ s"""{
+ | "@id" : "$resourceIri",
+ | "@type" : "anything:Thing",
+ | "anything:hasOtherThingValue" : {
+ | "@id" : "$valueIri",
+ | "@type" : "knora-api:LinkValue",
+ | "knora-api:linkValueHasTargetIri" : {
+ | "@id" : "$targetResourceIri"
+ | },
+ | "knora-api:valueHasComment" : "$definedComment"
+ | },
+ | "@context" : {
+ | "xsd" : "http://www.w3.org/2001/XMLSchema#",
+ | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
+ | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
+ | }
+ |}""".stripMargin
+
+ case None =>
+ s"""{
+ | "@id" : "$resourceIri",
+ | "@type" : "anything:Thing",
+ | "anything:hasOtherThingValue" : {
+ | "@id" : "$valueIri",
+ | "@type" : "knora-api:LinkValue",
+ | "knora-api:linkValueHasTargetIri" : {
+ | "@id" : "$targetResourceIri"
+ | }
+ | },
+ | "@context" : {
+ | "xsd" : "http://www.w3.org/2001/XMLSchema#",
+ | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
+ | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
+ | }
+ |}""".stripMargin
+ }
}
def updateStillImageFileValueRequest(resourceIri: IRI,
diff --git a/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala b/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala
index 5b32e41d7c..bb28277829 100644
--- a/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala
+++ b/webapi/src/main/scala/org/knora/webapi/responders/v2/ValuesResponderV2.scala
@@ -43,21 +43,21 @@ import org.knora.webapi.util.{PermissionUtilADM, SmartIri}
import scala.concurrent.Future
/**
- * Handles requests to read and write Knora values.
- */
+ * Handles requests to read and write Knora values.
+ */
class ValuesResponderV2(responderData: ResponderData) extends Responder(responderData) {
/**
- * The IRI and content of a new value or value version whose existence in the triplestore has been verified.
- *
- * @param newValueIri the IRI that was assigned to the new value.
- * @param value the content of the new value.
- */
+ * The IRI and content of a new value or value version whose existence in the triplestore has been verified.
+ *
+ * @param newValueIri the IRI that was assigned to the new value.
+ * @param value the content of the new value.
+ */
case class VerifiedValueV2(newValueIri: IRI, value: ValueContentV2, permissions: String)
/**
- * Receives a message of type [[ValuesResponderRequestV2]], and returns an appropriate response message.
- */
+ * Receives a message of type [[ValuesResponderRequestV2]], and returns an appropriate response message.
+ */
def receive(msg: ValuesResponderRequestV2) = msg match {
case createValueRequest: CreateValueRequestV2 => createValueV2(createValueRequest)
case updateValueRequest: UpdateValueRequestV2 => updateValueV2(updateValueRequest)
@@ -67,11 +67,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Creates a new value in an existing resource.
- *
- * @param createValueRequest the request to create the value.
- * @return a [[CreateValueResponseV2]].
- */
+ * Creates a new value in an existing resource.
+ *
+ * @param createValueRequest the request to create the value.
+ * @return a [[CreateValueResponseV2]].
+ */
private def createValueV2(createValueRequest: CreateValueRequestV2): Future[CreateValueResponseV2] = {
def makeTaskFuture: Future[CreateValueResponseV2] = {
for {
@@ -301,20 +301,20 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Creates a new value (either an ordinary value or a link), using an existing transaction, assuming that
- * pre-update checks have already been done.
- *
- * @param dataNamedGraph the named graph in which the value is to be created.
- * @param projectIri the IRI of the project in which to create the value.
- * @param resourceInfo information about the the resource in which to create the value.
- * @param propertyIri the IRI of the property that will point from the resource to the value, or, if the
- * value is a link value, the IRI of the link property.
- * @param value the value to create.
- * @param valueCreator the IRI of the new value's owner.
- * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return an [[UnverifiedValueV2]].
- */
+ * Creates a new value (either an ordinary value or a link), using an existing transaction, assuming that
+ * pre-update checks have already been done.
+ *
+ * @param dataNamedGraph the named graph in which the value is to be created.
+ * @param projectIri the IRI of the project in which to create the value.
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param propertyIri the IRI of the property that will point from the resource to the value, or, if the
+ * value is a link value, the IRI of the link property.
+ * @param value the value to create.
+ * @param valueCreator the IRI of the new value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return an [[UnverifiedValueV2]].
+ */
private def createValueV2AfterChecks(dataNamedGraph: IRI,
projectIri: IRI,
resourceInfo: ReadResourceV2,
@@ -349,16 +349,16 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Creates an ordinary value (i.e. not a link), using an existing transaction, assuming that pre-update checks have already been done.
- *
- * @param resourceInfo information about the the resource in which to create the value.
- * @param propertyIri the property that should point to the value.
- * @param value an [[ValueContentV2]] describing the value.
- * @param valueCreator the IRI of the new value's owner.
- * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return an [[UnverifiedValueV2]].
- */
+ * Creates an ordinary value (i.e. not a link), using an existing transaction, assuming that pre-update checks have already been done.
+ *
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param propertyIri the property that should point to the value.
+ * @param value an [[ValueContentV2]] describing the value.
+ * @param valueCreator the IRI of the new value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return an [[UnverifiedValueV2]].
+ */
private def createOrdinaryValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
propertyIri: SmartIri,
@@ -422,17 +422,17 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Creates a link, using an existing transaction, assuming that pre-update checks have already been done.
- *
- * @param dataNamedGraph the named graph in which the link is to be created.
- * @param resourceInfo information about the the resource in which to create the value.
- * @param linkPropertyIri the link property.
- * @param linkValueContent a [[LinkValueContentV2]] specifying the target resource.
- * @param valueCreator the IRI of the new link value's owner.
- * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return an [[UnverifiedValueV2]].
- */
+ * Creates a link, using an existing transaction, assuming that pre-update checks have already been done.
+ *
+ * @param dataNamedGraph the named graph in which the link is to be created.
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param linkPropertyIri the link property.
+ * @param linkValueContent a [[LinkValueContentV2]] specifying the target resource.
+ * @param valueCreator the IRI of the new link value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return an [[UnverifiedValueV2]].
+ */
private def createLinkValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
linkPropertyIri: SmartIri,
@@ -480,20 +480,20 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Represents SPARQL generated to create one of multiple values in a new resource.
- *
- * @param insertSparql the generated SPARQL.
- * @param unverifiedValue an [[UnverifiedValueV2]] representing the value that is to be created.
- */
+ * Represents SPARQL generated to create one of multiple values in a new resource.
+ *
+ * @param insertSparql the generated SPARQL.
+ * @param unverifiedValue an [[UnverifiedValueV2]] representing the value that is to be created.
+ */
private case class InsertSparqlWithUnverifiedValue(insertSparql: String, unverifiedValue: UnverifiedValueV2)
/**
- * Generates SPARQL for creating multiple values.
- *
- * @param createMultipleValuesRequest the request to create multiple values.
- * @return a [[GenerateSparqlToCreateMultipleValuesResponseV2]] containing the generated SPARQL and information
- * about the values to be created.
- */
+ * Generates SPARQL for creating multiple values.
+ *
+ * @param createMultipleValuesRequest the request to create multiple values.
+ * @return a [[GenerateSparqlToCreateMultipleValuesResponseV2]] containing the generated SPARQL and information
+ * about the values to be created.
+ */
private def generateSparqToCreateMultipleValuesV2(createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV2): Future[GenerateSparqlToCreateMultipleValuesResponseV2] = {
for {
// Generate SPARQL to create links and LinkValues for standoff links in text values.
@@ -529,16 +529,16 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Generates SPARQL to create one of multiple values in a new resource.
- *
- * @param resourceIri the IRI of the resource.
- * @param propertyIri the IRI of the property that will point to the value.
- * @param valueToCreate the value to be created.
- * @param valueHasOrder the value's `knora-base:valueHasOrder`.
- * @param creationDate the timestamp to be used as the value creation time.
- * @param requestingUser the user making the request.
- * @return a [[InsertSparqlWithUnverifiedValue]] containing the generated SPARQL and an [[UnverifiedValueV2]].
- */
+ * Generates SPARQL to create one of multiple values in a new resource.
+ *
+ * @param resourceIri the IRI of the resource.
+ * @param propertyIri the IRI of the property that will point to the value.
+ * @param valueToCreate the value to be created.
+ * @param valueHasOrder the value's `knora-base:valueHasOrder`.
+ * @param creationDate the timestamp to be used as the value creation time.
+ * @param requestingUser the user making the request.
+ * @return a [[InsertSparqlWithUnverifiedValue]] containing the generated SPARQL and an [[UnverifiedValueV2]].
+ */
private def generateInsertSparqlWithUnverifiedValue(resourceIri: IRI,
propertyIri: SmartIri,
valueToCreate: GenerateSparqlForValueInNewResourceV2,
@@ -608,11 +608,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * When processing a request to create multiple values, generates SPARQL for standoff links in text values.
- *
- * @param createMultipleValuesRequest the request to create multiple values.
- * @return SPARQL INSERT statements.
- */
+ * When processing a request to create multiple values, generates SPARQL for standoff links in text values.
+ *
+ * @param createMultipleValuesRequest the request to create multiple values.
+ * @return SPARQL INSERT statements.
+ */
private def generateInsertSparqlForStandoffLinksInMultipleValues(createMultipleValuesRequest: GenerateSparqlToCreateMultipleValuesRequestV2): String = {
// To create LinkValues for the standoff links in the values to be created, we need to compute
// the initial reference count of each LinkValue. This is equal to the number of TextValues in the resource
@@ -672,39 +672,39 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Creates a new version of an existing value.
- *
- * @param updateValueRequest the request to update the value.
- * @return a [[UpdateValueResponseV2]].
- */
+ * Creates a new version of an existing value.
+ *
+ * @param updateValueRequest the request to update the value.
+ * @return a [[UpdateValueResponseV2]].
+ */
private def updateValueV2(updateValueRequest: UpdateValueRequestV2): Future[UpdateValueResponseV2] = {
/**
- * Information about a resource, a submitted property, and a value of the property.
- *
- * @param resource the contents of the resource.
- * @param submittedInternalPropertyIri the internal IRI of the submitted property.
- * @param adjustedInternalPropertyInfo the internal definition of the submitted property, adjusted
- * as follows: an adjusted version of the submitted property:
- * if it's a link value property, substitute the
- * corresponding link property.
- * @param value the requested value.
- */
+ * Information about a resource, a submitted property, and a value of the property.
+ *
+ * @param resource the contents of the resource.
+ * @param submittedInternalPropertyIri the internal IRI of the submitted property.
+ * @param adjustedInternalPropertyInfo the internal definition of the submitted property, adjusted
+ * as follows: an adjusted version of the submitted property:
+ * if it's a link value property, substitute the
+ * corresponding link property.
+ * @param value the requested value.
+ */
case class ResourcePropertyValue(resource: ReadResourceV2,
submittedInternalPropertyIri: SmartIri,
adjustedInternalPropertyInfo: ReadPropertyInfoV2,
value: ReadValueV2)
/**
- * Gets information about a resource, a submitted property, and a value of the property, and does
- * some checks to see if the submitted information is correct.
- *
- * @param resourceIri the IRI of the resource.
- * @param submittedExternalResourceClassIri the submitted external IRI of the resource class.
- * @param submittedExternalPropertyIri the submitted external IRI of the property.
- * @param valueIri the IRI of the value.
- * @param submittedExternalValueType the submitted external IRI of the value type.
- * @return a [[ResourcePropertyValue]].
- */
+ * Gets information about a resource, a submitted property, and a value of the property, and does
+ * some checks to see if the submitted information is correct.
+ *
+ * @param resourceIri the IRI of the resource.
+ * @param submittedExternalResourceClassIri the submitted external IRI of the resource class.
+ * @param submittedExternalPropertyIri the submitted external IRI of the property.
+ * @param valueIri the IRI of the value.
+ * @param submittedExternalValueType the submitted external IRI of the value type.
+ * @return a [[ResourcePropertyValue]].
+ */
def getResourcePropertyValue(resourceIri: IRI,
submittedExternalResourceClassIri: SmartIri,
submittedExternalPropertyIri: SmartIri,
@@ -776,11 +776,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Updates the permissions attached to a value.
- *
- * @param updateValuePermissionsV2 the update request.
- * @return an [[UpdateValueResponseV2]].
- */
+ * Updates the permissions attached to a value.
+ *
+ * @param updateValuePermissionsV2 the update request.
+ * @return an [[UpdateValueResponseV2]].
+ */
def makeTaskFutureToUpdateValuePermissions(updateValuePermissionsV2: UpdateValuePermissionsV2): Future[UpdateValueResponseV2] = {
for {
// Do the initial checks, and get information about the resource, the property, and the value.
@@ -860,11 +860,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Updates the contents of a value.
- *
- * @param updateValueContentV2 the update request.
- * @return an [[UpdateValueResponseV2]].
- */
+ * Updates the contents of a value.
+ *
+ * @param updateValueContentV2 the update request.
+ * @return an [[UpdateValueResponseV2]].
+ */
def makeTaskFutureToUpdateValueContent(updateValueContentV2: UpdateValueContentV2): Future[UpdateValueResponseV2] = {
for {
// Do the initial checks, and get information about the resource, the property, and the value.
@@ -1029,20 +1029,20 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Updates an existing value (either an ordinary value or a link), using an existing transaction, assuming that
- * pre-update checks have already been done.
- *
- * @param dataNamedGraph the named graph in which the value is to be created.
- * @param resourceInfo information about the the resource in which to create the value.
- * @param propertyIri the IRI of the property that will point from the resource to the value, or, if the
- * value is a link value, the IRI of the link property.
- * @param currentValue the value to be updated.
- * @param newValueVersion the new version of the value.
- * @param valueCreator the IRI of the new value's owner.
- * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return an [[UnverifiedValueV2]].
- */
+ * Updates an existing value (either an ordinary value or a link), using an existing transaction, assuming that
+ * pre-update checks have already been done.
+ *
+ * @param dataNamedGraph the named graph in which the value is to be created.
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param propertyIri the IRI of the property that will point from the resource to the value, or, if the
+ * value is a link value, the IRI of the link property.
+ * @param currentValue the value to be updated.
+ * @param newValueVersion the new version of the value.
+ * @param valueCreator the IRI of the new value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return an [[UnverifiedValueV2]].
+ */
private def updateValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
propertyIri: SmartIri,
@@ -1082,19 +1082,19 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done.
- *
- * @param dataNamedGraph the IRI of the named graph to be updated.
- * @param resourceInfo information about the resource containing the value.
- * @param propertyIri the IRI of the property that points to the value.
- * @param currentValue a [[ReadValueV2]] representing the existing value version.
- * @param newValueIri the IRI of the new value.
- * @param newValueVersion a [[ValueContentV2]] representing the new value version, in the internal schema.
- * @param valueCreator the IRI of the new value's owner.
- * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return an [[UnverifiedValueV2]].
- */
+ * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done.
+ *
+ * @param dataNamedGraph the IRI of the named graph to be updated.
+ * @param resourceInfo information about the resource containing the value.
+ * @param propertyIri the IRI of the property that points to the value.
+ * @param currentValue a [[ReadValueV2]] representing the existing value version.
+ * @param newValueIri the IRI of the new value.
+ * @param newValueVersion a [[ValueContentV2]] representing the new value version, in the internal schema.
+ * @param valueCreator the IRI of the new value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return an [[UnverifiedValueV2]].
+ */
private def updateOrdinaryValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
propertyIri: SmartIri,
@@ -1185,18 +1185,18 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Changes a link, assuming that pre-update checks have already been done.
- *
- * @param dataNamedGraph the IRI of the named graph to be updated.
- * @param resourceInfo information about the resource containing the link.
- * @param linkPropertyIri the IRI of the link property.
- * @param currentLinkValue a [[LinkValueContentV2]] representing the `knora-base:LinkValue` for the existing link.
- * @param newLinkValue a [[LinkValueContentV2]] indicating the new target resource.
- * @param valueCreator the IRI of the new link value's owner.
- * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return an [[UnverifiedValueV2]].
- */
+ * Changes a link, assuming that pre-update checks have already been done.
+ *
+ * @param dataNamedGraph the IRI of the named graph to be updated.
+ * @param resourceInfo information about the resource containing the link.
+ * @param linkPropertyIri the IRI of the link property.
+ * @param currentLinkValue a [[LinkValueContentV2]] representing the `knora-base:LinkValue` for the existing link.
+ * @param newLinkValue a [[LinkValueContentV2]] indicating the new target resource.
+ * @param valueCreator the IRI of the new link value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return an [[UnverifiedValueV2]].
+ */
private def updateLinkValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
linkPropertyIri: SmartIri,
@@ -1205,63 +1205,100 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
valueCreator: IRI,
valuePermissions: String,
requestingUser: UserADM): Future[UnverifiedValueV2] = {
- // Delete the existing link and decrement its LinkValue's reference count.
- val sparqlTemplateLinkUpdateForCurrentLink: SparqlTemplateLinkUpdate = decrementLinkValue(
- sourceResourceInfo = resourceInfo,
- linkPropertyIri = linkPropertyIri,
- targetResourceIri = currentLinkValue.referredResourceIri,
- valueCreator = valueCreator,
- valuePermissions = valuePermissions,
- requestingUser = requestingUser
- )
- // Create a new link, and create a new LinkValue for it.
- val sparqlTemplateLinkUpdateForNewLink: SparqlTemplateLinkUpdate = incrementLinkValue(
- sourceResourceInfo = resourceInfo,
- linkPropertyIri = linkPropertyIri,
- targetResourceIri = newLinkValue.referredResourceIri,
- valueCreator = valueCreator,
- valuePermissions = valuePermissions,
- requestingUser = requestingUser
- )
+ // Are we changing the link target?
+ if (currentLinkValue.referredResourceIri != newLinkValue.referredResourceIri) {
+ // Delete the existing link and decrement its LinkValue's reference count.
+ val sparqlTemplateLinkUpdateForCurrentLink: SparqlTemplateLinkUpdate = decrementLinkValue(
+ sourceResourceInfo = resourceInfo,
+ linkPropertyIri = linkPropertyIri,
+ targetResourceIri = currentLinkValue.referredResourceIri,
+ valueCreator = valueCreator,
+ valuePermissions = valuePermissions,
+ requestingUser = requestingUser
+ )
- // Make a timestamp to indicate when the link value was updated.
- val currentTime: Instant = Instant.now
+ // Create a new link, and create a new LinkValue for it.
+ val sparqlTemplateLinkUpdateForNewLink: SparqlTemplateLinkUpdate = incrementLinkValue(
+ sourceResourceInfo = resourceInfo,
+ linkPropertyIri = linkPropertyIri,
+ targetResourceIri = newLinkValue.referredResourceIri,
+ valueCreator = valueCreator,
+ valuePermissions = valuePermissions,
+ requestingUser = requestingUser
+ )
- for {
- // Generate a SPARQL update string.
- sparqlUpdate <- Future(queries.sparql.v2.txt.changeLink(
- dataNamedGraph = dataNamedGraph,
- triplestore = settings.triplestoreType,
- linkSourceIri = resourceInfo.resourceIri,
- linkUpdateForCurrentLink = sparqlTemplateLinkUpdateForCurrentLink,
- linkUpdateForNewLink = sparqlTemplateLinkUpdateForNewLink,
- maybeComment = newLinkValue.comment,
- currentTime = currentTime,
- requestingUser = requestingUser.id,
- stringFormatter = stringFormatter
- ).toString())
+ // Make a timestamp to indicate when the link value was updated.
+ val currentTime: Instant = Instant.now
- /*
- _ = println("================ Update link ================")
- _ = println(sparqlUpdate)
- _ = println("==============================================")
- */
+ for {
+ // Generate a SPARQL update string.
+ sparqlUpdate <- Future(queries.sparql.v2.txt.changeLinkTarget(
+ dataNamedGraph = dataNamedGraph,
+ triplestore = settings.triplestoreType,
+ linkSourceIri = resourceInfo.resourceIri,
+ linkUpdateForCurrentLink = sparqlTemplateLinkUpdateForCurrentLink,
+ linkUpdateForNewLink = sparqlTemplateLinkUpdateForNewLink,
+ maybeComment = newLinkValue.comment,
+ currentTime = currentTime,
+ requestingUser = requestingUser.id,
+ stringFormatter = stringFormatter
+ ).toString())
- _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse]
- } yield UnverifiedValueV2(
- newValueIri = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri,
- valueContent = newLinkValue.unescape,
- permissions = valuePermissions,
- creationDate = currentTime
- )
+ /*
+ _ = println("================ Update link ================")
+ _ = println(sparqlUpdate)
+ _ = println("==============================================")
+ */
+
+ _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse]
+ } yield UnverifiedValueV2(
+ newValueIri = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri,
+ valueContent = newLinkValue.unescape,
+ permissions = valuePermissions,
+ creationDate = currentTime
+ )
+ } else {
+ // We're not changing the link target, just the metadata on the LinkValue.
+
+ val sparqlTemplateLinkUpdate: SparqlTemplateLinkUpdate = changeLinkValueMetadata(
+ sourceResourceInfo = resourceInfo,
+ linkPropertyIri = linkPropertyIri,
+ targetResourceIri = currentLinkValue.referredResourceIri,
+ valueCreator = valueCreator,
+ valuePermissions = valuePermissions,
+ requestingUser = requestingUser
+ )
+
+ // Make a timestamp to indicate when the link value was updated.
+ val currentTime: Instant = Instant.now
+
+ for {
+ sparqlUpdate <- Future(queries.sparql.v2.txt.changeLinkMetadata(
+ dataNamedGraph = dataNamedGraph,
+ triplestore = settings.triplestoreType,
+ linkSourceIri = resourceInfo.resourceIri,
+ linkUpdate = sparqlTemplateLinkUpdate,
+ maybeComment = newLinkValue.comment,
+ currentTime = currentTime,
+ requestingUser = requestingUser.id
+ ).toString())
+
+ _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse]
+ } yield UnverifiedValueV2(
+ newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri,
+ valueContent = newLinkValue.unescape,
+ permissions = valuePermissions,
+ creationDate = currentTime
+ )
+ }
}
/**
- * Marks a value as deleted.
- *
- * @param deleteValueRequest the request to mark the value as deleted.
- */
+ * Marks a value as deleted.
+ *
+ * @param deleteValueRequest the request to mark the value as deleted.
+ */
private def deleteValueV2(deleteValueRequest: DeleteValueRequestV2): Future[SuccessResponseV2] = {
def makeTaskFuture: Future[SuccessResponseV2] = {
for {
@@ -1413,17 +1450,17 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
/**
- * Deletes a value (either an ordinary value or a link), using an existing transaction, assuming that
- * pre-update checks have already been done.
- *
- * @param dataNamedGraph the named graph in which the value is to be deleted.
- * @param resourceInfo information about the the resource in which to create the value.
- * @param propertyIri the IRI of the property that points from the resource to the value.
- * @param currentValue the value to be deleted.
- * @param deleteComment an optional comment explaining why the value is being deleted.
- * @param requestingUser the user making the request.
- * @return the IRI of the value that was marked as deleted.
- */
+ * Deletes a value (either an ordinary value or a link), using an existing transaction, assuming that
+ * pre-update checks have already been done.
+ *
+ * @param dataNamedGraph the named graph in which the value is to be deleted.
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param propertyIri the IRI of the property that points from the resource to the value.
+ * @param currentValue the value to be deleted.
+ * @param deleteComment an optional comment explaining why the value is being deleted.
+ * @param requestingUser the user making the request.
+ * @return the IRI of the value that was marked as deleted.
+ */
private def deleteValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
propertyIri: SmartIri,
@@ -1454,16 +1491,16 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Deletes a link after checks.
- *
- * @param dataNamedGraph the named graph in which the value is to be deleted.
- * @param resourceInfo information about the the resource in which to create the value.
- * @param propertyIri the IRI of the property that points from the resource to the value.
- * @param currentValue the value to be deleted.
- * @param deleteComment an optional comment explaining why the value is being deleted.
- * @param requestingUser the user making the request.
- * @return the IRI of the value that was marked as deleted.
- */
+ * Deletes a link after checks.
+ *
+ * @param dataNamedGraph the named graph in which the value is to be deleted.
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param propertyIri the IRI of the property that points from the resource to the value.
+ * @param currentValue the value to be deleted.
+ * @param deleteComment an optional comment explaining why the value is being deleted.
+ * @param requestingUser the user making the request.
+ * @return the IRI of the value that was marked as deleted.
+ */
private def deleteLinkValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
propertyIri: SmartIri,
@@ -1507,16 +1544,16 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Deletes an ordinary value after checks.
- *
- * @param dataNamedGraph the named graph in which the value is to be deleted.
- * @param resourceInfo information about the the resource in which to create the value.
- * @param propertyIri the IRI of the property that points from the resource to the value.
- * @param currentValue the value to be deleted.
- * @param deleteComment an optional comment explaining why the value is being deleted.
- * @param requestingUser the user making the request.
- * @return the IRI of the value that was marked as deleted.
- */
+ * Deletes an ordinary value after checks.
+ *
+ * @param dataNamedGraph the named graph in which the value is to be deleted.
+ * @param resourceInfo information about the the resource in which to create the value.
+ * @param propertyIri the IRI of the property that points from the resource to the value.
+ * @param currentValue the value to be deleted.
+ * @param deleteComment an optional comment explaining why the value is being deleted.
+ * @param requestingUser the user making the request.
+ * @return the IRI of the value that was marked as deleted.
+ */
private def deleteOrdinaryValueV2AfterChecks(dataNamedGraph: IRI,
resourceInfo: ReadResourceV2,
propertyIri: SmartIri,
@@ -1566,15 +1603,15 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * When a property IRI is submitted for an update, makes an adjusted version of the submitted property:
- * if it's a link value property, substitutes the corresponding link property, whose objects we will need to query.
- *
- * @param submittedPropertyIri the submitted property IRI, in the API v2 complex schema.
- * @param maybeSubmittedValueType the submitted value type, if provided, in the API v2 complex schema.
- * @param propertyInfoForSubmittedProperty ontology information about the submitted property, in the internal schema.
- * @param requestingUser the requesting user.
- * @return ontology information about the adjusted property.
- */
+ * When a property IRI is submitted for an update, makes an adjusted version of the submitted property:
+ * if it's a link value property, substitutes the corresponding link property, whose objects we will need to query.
+ *
+ * @param submittedPropertyIri the submitted property IRI, in the API v2 complex schema.
+ * @param maybeSubmittedValueType the submitted value type, if provided, in the API v2 complex schema.
+ * @param propertyInfoForSubmittedProperty ontology information about the submitted property, in the internal schema.
+ * @param requestingUser the requesting user.
+ * @return ontology information about the adjusted property.
+ */
private def getAdjustedInternalPropertyInfo(submittedPropertyIri: SmartIri,
maybeSubmittedValueType: Option[SmartIri],
propertyInfoForSubmittedProperty: ReadPropertyInfoV2,
@@ -1610,12 +1647,12 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Given a set of resource IRIs, checks that they point to Knora resources.
- * If not, throws an exception.
- *
- * @param targetResourceIris the IRIs to be checked.
- * @param requestingUser the user making the request.
- */
+ * Given a set of resource IRIs, checks that they point to Knora resources.
+ * If not, throws an exception.
+ *
+ * @param targetResourceIris the IRIs to be checked.
+ * @param requestingUser the user making the request.
+ */
private def checkResourceIris(targetResourceIris: Set[IRI], requestingUser: UserADM): Future[Unit] = {
if (targetResourceIris.isEmpty) {
FastFuture.successful(())
@@ -1637,17 +1674,17 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Returns a resource's metadata and its values, if any, for the specified property. If the property is a link property, the result
- * will contain any objects of the corresponding link value property (link values), as well as metadata for any resources that the link property points to.
- * If the property's object type is `knora-base:TextValue`, the result will contain any objects of the property (text values), as well metadata
- * for any resources that are objects of `knora-base:hasStandoffLinkTo`.
- *
- * @param resourceIri the resource IRI.
- * @param propertyInfo the property definition (in the internal schema). If the caller wants to query a link, this must be the link property,
- * not the link value property.
- * @param requestingUser the user making the request.
- * @return a [[ReadResourceV2]] containing only the resource's metadata and its values for the specified property.
- */
+ * Returns a resource's metadata and its values, if any, for the specified property. If the property is a link property, the result
+ * will contain any objects of the corresponding link value property (link values), as well as metadata for any resources that the link property points to.
+ * If the property's object type is `knora-base:TextValue`, the result will contain any objects of the property (text values), as well metadata
+ * for any resources that are objects of `knora-base:hasStandoffLinkTo`.
+ *
+ * @param resourceIri the resource IRI.
+ * @param propertyInfo the property definition (in the internal schema). If the caller wants to query a link, this must be the link property,
+ * not the link value property.
+ * @param requestingUser the user making the request.
+ * @return a [[ReadResourceV2]] containing only the resource's metadata and its values for the specified property.
+ */
private def getResourceWithPropertyValues(resourceIri: IRI, propertyInfo: ReadPropertyInfoV2, requestingUser: UserADM): Future[ReadResourceV2] = {
for {
// Get the property's object class constraint.
@@ -1681,14 +1718,14 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Verifies that a value was written correctly to the triplestore.
- *
- * @param resourceIri the IRI of the resource that the value belongs to.
- * @param propertyIri the internal IRI of the property that points to the value. If the value is a link value,
- * this is the link value property.
- * @param unverifiedValue the value that should have been written to the triplestore.
- * @param requestingUser the user making the request.
- */
+ * Verifies that a value was written correctly to the triplestore.
+ *
+ * @param resourceIri the IRI of the resource that the value belongs to.
+ * @param propertyIri the internal IRI of the property that points to the value. If the value is a link value,
+ * this is the link value property.
+ * @param unverifiedValue the value that should have been written to the triplestore.
+ * @param requestingUser the user making the request.
+ */
private def verifyValue(resourceIri: IRI,
propertyIri: SmartIri,
unverifiedValue: UnverifiedValueV2,
@@ -1739,13 +1776,13 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Checks that a link value points to a resource with the correct type for the link property's object class constraint.
- *
- * @param linkPropertyIri the IRI of the link property.
- * @param objectClassConstraint the object class constraint of the link property.
- * @param linkValueContent the link value.
- * @param requestingUser the user making the request.
- */
+ * Checks that a link value points to a resource with the correct type for the link property's object class constraint.
+ *
+ * @param linkPropertyIri the IRI of the link property.
+ * @param objectClassConstraint the object class constraint of the link property.
+ * @param linkValueContent the link value.
+ * @param requestingUser the user making the request.
+ */
private def checkLinkPropertyObjectClassConstraint(linkPropertyIri: SmartIri, objectClassConstraint: SmartIri, linkValueContent: LinkValueContentV2, requestingUser: UserADM): Future[Unit] = {
for {
// Get a preview of the target resource, because we only need to find out its class and whether the user has permission to view it.
@@ -1781,13 +1818,13 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Checks that a non-link value has the correct type for a property's object class constraint.
- *
- * @param propertyIri the IRI of the property that should point to the value.
- * @param objectClassConstraint the property's object class constraint.
- * @param valueContent the value.
- * @param requestingUser the user making the request.
- */
+ * Checks that a non-link value has the correct type for a property's object class constraint.
+ *
+ * @param propertyIri the IRI of the property that should point to the value.
+ * @param objectClassConstraint the property's object class constraint.
+ * @param valueContent the value.
+ * @param requestingUser the user making the request.
+ */
private def checkNonLinkPropertyObjectClassConstraint(propertyIri: SmartIri, objectClassConstraint: SmartIri, valueContent: ValueContentV2, requestingUser: UserADM): Future[Unit] = {
// Is the value type the same as the property's object class constraint?
if (objectClassConstraint == valueContent.valueType) {
@@ -1814,13 +1851,13 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Checks that a value to be updated has the correct type for the `knora-base:objectClassConstraint` of
- * the property that is supposed to point to it.
- *
- * @param propertyInfo the property whose object class constraint is to be checked. If the value is a link value, this is the link property.
- * @param valueContent the value to be updated.
- * @param requestingUser the user making the request.
- */
+ * Checks that a value to be updated has the correct type for the `knora-base:objectClassConstraint` of
+ * the property that is supposed to point to it.
+ *
+ * @param propertyInfo the property whose object class constraint is to be checked. If the value is a link value, this is the link property.
+ * @param valueContent the value to be updated.
+ * @param requestingUser the user making the request.
+ */
private def checkPropertyObjectClassConstraint(propertyInfo: ReadPropertyInfoV2, valueContent: ValueContentV2, requestingUser: UserADM): Future[Unit] = {
for {
objectClassConstraint: SmartIri <- Future(propertyInfo.entityInfoContent.requireIriObject(OntologyConstants.KnoraBase.ObjectClassConstraint.toSmartIri, throw InconsistentTriplestoreDataException(s"Property ${propertyInfo.entityInfoContent.propertyIri} has no knora-base:objectClassConstraint")))
@@ -1856,14 +1893,14 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Given a [[ReadResourceV2]], finds a link that uses the specified property and points to the specified target
- * resource.
- *
- * @param sourceResourceInfo a [[ReadResourceV2]] describing the source of the link.
- * @param linkPropertyIri the IRI of the link property.
- * @param targetResourceIri the IRI of the target resource.
- * @return a [[ReadLinkValueV2]] describing the link value, if found.
- */
+ * Given a [[ReadResourceV2]], finds a link that uses the specified property and points to the specified target
+ * resource.
+ *
+ * @param sourceResourceInfo a [[ReadResourceV2]] describing the source of the link.
+ * @param linkPropertyIri the IRI of the link property.
+ * @param targetResourceIri the IRI of the target resource.
+ * @return a [[ReadLinkValueV2]] describing the link value, if found.
+ */
private def findLinkValue(sourceResourceInfo: ReadResourceV2,
linkPropertyIri: SmartIri,
targetResourceIri: IRI): Option[ReadLinkValueV2] = {
@@ -1878,27 +1915,27 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to create a `LinkValue` or to
- * increment the reference count of an existing `LinkValue`. This happens in two cases:
- *
- * - When the user creates a link. In this case, neither the link nor the `LinkValue` exist yet. The
- * [[SparqlTemplateLinkUpdate]] will specify that the link should be created, and that the `LinkValue` should be
- * created with a reference count of 1.
- * - When a text value is updated so that its standoff markup refers to a resource that it did not previously
- * refer to. Here there are two possibilities:
- * - If there is currently a `knora-base:hasStandoffLinkTo` link between the source and target resources, with a
- * corresponding `LinkValue`, a new version of the `LinkValue` will be made, with an incremented reference count.
- * - If that link and `LinkValue` don't yet exist, they will be created, and the `LinkValue` will be given
- * a reference count of 1.
- *
- * @param sourceResourceInfo information about the source resource.
- * @param linkPropertyIri the IRI of the property that links the source resource to the target resource.
- * @param targetResourceIri the IRI of the target resource.
- * @param valueCreator the IRI of the new link value's owner.
- * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template.
- */
+ * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to create a `LinkValue` or to
+ * increment the reference count of an existing `LinkValue`. This happens in two cases:
+ *
+ * - When the user creates a link. In this case, neither the link nor the `LinkValue` exist yet. The
+ * [[SparqlTemplateLinkUpdate]] will specify that the link should be created, and that the `LinkValue` should be
+ * created with a reference count of 1.
+ * - When a text value is updated so that its standoff markup refers to a resource that it did not previously
+ * refer to. Here there are two possibilities:
+ * - If there is currently a `knora-base:hasStandoffLinkTo` link between the source and target resources, with a
+ * corresponding `LinkValue`, a new version of the `LinkValue` will be made, with an incremented reference count.
+ * - If that link and `LinkValue` don't yet exist, they will be created, and the `LinkValue` will be given
+ * a reference count of 1.
+ *
+ * @param sourceResourceInfo information about the source resource.
+ * @param linkPropertyIri the IRI of the property that links the source resource to the target resource.
+ * @param targetResourceIri the IRI of the target resource.
+ * @param valueCreator the IRI of the new link value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template.
+ */
private def incrementLinkValue(sourceResourceInfo: ReadResourceV2,
linkPropertyIri: SmartIri,
targetResourceIri: IRI,
@@ -1955,25 +1992,25 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to decrement the reference count
- * of a `LinkValue`. This happens in two cases:
- *
- * - When the user deletes (or changes) a user-created link. In this case, the current reference count will be 1.
- * The existing link will be removed. A new version of the `LinkValue` be made with a reference count of 0, and
- * will be marked as deleted.
- * - When a resource reference is removed from standoff markup on a text value, so that the text value no longer
- * contains any references to that target resource. In this case, a new version of the `LinkValue` will be
- * made, with a decremented reference count. If the new reference count is 0, the link will be removed and the
- * `LinkValue` will be marked as deleted.
- *
- * @param sourceResourceInfo information about the source resource.
- * @param linkPropertyIri the IRI of the property that links the source resource to the target resource.
- * @param targetResourceIri the IRI of the target resource.
- * @param valueCreator the IRI of the new link value's owner.
- * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
- * @param requestingUser the user making the request.
- * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template.
- */
+ * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to decrement the reference count
+ * of a `LinkValue`. This happens in two cases:
+ *
+ * - When the user deletes (or changes) a user-created link. In this case, the current reference count will be 1.
+ * The existing link will be removed. A new version of the `LinkValue` be made with a reference count of 0, and
+ * will be marked as deleted.
+ * - When a resource reference is removed from standoff markup on a text value, so that the text value no longer
+ * contains any references to that target resource. In this case, a new version of the `LinkValue` will be
+ * made, with a decremented reference count. If the new reference count is 0, the link will be removed and the
+ * `LinkValue` will be marked as deleted.
+ *
+ * @param sourceResourceInfo information about the source resource.
+ * @param linkPropertyIri the IRI of the property that links the source resource to the target resource.
+ * @param targetResourceIri the IRI of the target resource.
+ * @param valueCreator the IRI of the new link value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template.
+ */
private def decrementLinkValue(sourceResourceInfo: ReadResourceV2,
linkPropertyIri: SmartIri,
targetResourceIri: IRI,
@@ -2025,8 +2062,63 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}
/**
- * The permissions that are granted by every `knora-base:LinkValue` describing a standoff link.
- */
+ * Generates a [[SparqlTemplateLinkUpdate]] to tell a SPARQL update template how to change the metadata
+ * on a `LinkValue`.
+ *
+ * @param sourceResourceInfo information about the source resource.
+ * @param linkPropertyIri the IRI of the property that links the source resource to the target resource.
+ * @param targetResourceIri the IRI of the target resource.
+ * @param valueCreator the IRI of the new link value's owner.
+ * @param valuePermissions the literal that should be used as the object of the new link value's `knora-base:hasPermissions` predicate.
+ * @param requestingUser the user making the request.
+ * @return a [[SparqlTemplateLinkUpdate]] that can be passed to a SPARQL update template.
+ */
+ private def changeLinkValueMetadata(sourceResourceInfo: ReadResourceV2,
+ linkPropertyIri: SmartIri,
+ targetResourceIri: IRI,
+ valueCreator: IRI,
+ valuePermissions: String,
+ requestingUser: UserADM): SparqlTemplateLinkUpdate = {
+
+ // Check whether a LinkValue already exists for this link.
+ val maybeLinkValueInfo: Option[ReadLinkValueV2] = findLinkValue(
+ sourceResourceInfo = sourceResourceInfo,
+ linkPropertyIri = linkPropertyIri,
+ targetResourceIri = targetResourceIri
+ )
+
+ // Did we find it?
+ maybeLinkValueInfo match {
+ case Some(linkValueInfo) =>
+ // Yes. Make a SparqlTemplateLinkUpdate.
+
+ // Generate an IRI for the new LinkValue.
+ val newLinkValueIri = stringFormatter.makeRandomValueIri(sourceResourceInfo.resourceIri)
+
+ SparqlTemplateLinkUpdate(
+ linkPropertyIri = linkPropertyIri,
+ directLinkExists = true,
+ insertDirectLink = false,
+ deleteDirectLink = false,
+ linkValueExists = true,
+ linkTargetExists = true,
+ newLinkValueIri = newLinkValueIri,
+ linkTargetIri = targetResourceIri,
+ currentReferenceCount = linkValueInfo.valueHasRefCount,
+ newReferenceCount = linkValueInfo.valueHasRefCount,
+ newLinkValueCreator = valueCreator,
+ newLinkValuePermissions = valuePermissions
+ )
+
+ case None =>
+ // We didn't find the LinkValue. This shouldn't happen.
+ throw InconsistentTriplestoreDataException(s"There should be a knora-base:LinkValue describing a direct link from resource <${sourceResourceInfo.resourceIri}> to resource <$targetResourceIri> using property <$linkPropertyIri>, but it seems to be missing")
+ }
+ }
+
+ /**
+ * The permissions that are granted by every `knora-base:LinkValue` describing a standoff link.
+ */
lazy val standoffLinkValuePermissions: String = {
val permissions: Set[PermissionADM] = Set(
PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.SystemUser),
diff --git a/webapi/src/main/twirl/queries/sparql/v2/changeLinkMetadata.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/changeLinkMetadata.scala.txt
new file mode 100644
index 0000000000..27b4cd07f9
--- /dev/null
+++ b/webapi/src/main/twirl/queries/sparql/v2/changeLinkMetadata.scala.txt
@@ -0,0 +1,147 @@
+@*
+ * Copyright © 2015-2019 the contributors (see Contributors.md).
+ *
+ * This file is part of Knora.
+ *
+ * Knora is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Knora is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with Knora. If not, see .
+ *@
+
+@import java.time.Instant
+@import org.knora.webapi._
+@import org.knora.webapi.twirl.SparqlTemplateLinkUpdate
+@import org.knora.webapi.messages.v1.responder.valuemessages._
+
+@**
+ * Changes the metadata on a LinkValue.
+ *
+ * @param dataNamedGraph the named graph in which the project stores its data.
+ * @param triplestore the name of the triplestore being used.
+ * @param linkSourceIri the resource that is the source of the link.
+ * @param linkUpdate a [[SparqlTemplateLinkUpdate]] specifying how to update the link value.
+ * @param currentTime an xsd:dateTimeStamp that will be attached to the resources.
+ * @param requestingUser the IRI of the user making the request.
+ *
+ * To find out whether the update succeeded, the application must query the deleted link.
+ *@
+@(dataNamedGraph: IRI,
+ triplestore: String,
+ linkSourceIri: IRI,
+ linkUpdate: SparqlTemplateLinkUpdate,
+ maybeComment: Option[String],
+ currentTime: Instant,
+ requestingUser: IRI)
+
+PREFIX rdf:
+PREFIX rdfs:
+PREFIX owl:
+PREFIX xsd:
+PREFIX knora-base:
+
+DELETE {
+ GRAPH ?dataNamedGraph {
+ @* Delete the link source's last modification date so we can update it. *@
+ ?linkSource knora-base:lastModificationDate ?linkSourceLastModificationDate .
+
+ @* Detach the LinkValue from the link source. *@
+ @if(linkUpdate.linkValueExists) {
+ ?linkSource ?linkValueProperty ?currentLinkValue .
+
+ @* Delete the UUID from the current version of the link value, because the new version will store it. *@
+ ?currentLinkValue knora-base:valueHasUUID ?currentLinkUUID .
+ } else {
+ @{throw SparqlGenerationException(s"linkUpdate.linkValueExists must be true in this SPARQL template"); ()}
+ }
+ }
+}
+INSERT {
+ GRAPH ?dataNamedGraph {
+ @* Insert a new version of the LinkValue. *@
+ ?newLinkValue rdf:type knora-base:LinkValue ;
+ rdf:subject ?linkSource ;
+ rdf:predicate ?linkProperty ;
+ rdf:object ?linkTarget ;
+ knora-base:valueHasString "@linkUpdate.linkTargetIri"^^xsd:string ;
+ knora-base:valueHasRefCount @linkUpdate.newReferenceCount ;
+ knora-base:valueCreationDate "@currentTime"^^xsd:dateTime ;
+ knora-base:previousValue ?currentLinkValue ;
+ knora-base:valueHasUUID ?currentLinkUUID ;
+ knora-base:isDeleted false ;
+ @maybeComment match {
+ case Some(comment) => {
+ knora-base:valueHasComment """@comment""" ;
+ }
+
+ case None => {}
+ }
+ knora-base:attachedToUser <@linkUpdate.newLinkValueCreator> ;
+ knora-base:hasPermissions "@linkUpdate.newLinkValuePermissions"^^xsd:string .
+
+ @* Attach the new LinkValue to its containing resource. *@
+ ?linkSource ?linkValueProperty ?newLinkValue .
+
+ @* Update the link source's last modification date. *@
+ ?linkSource knora-base:lastModificationDate "@currentTime"^^xsd:dateTime .
+ }
+}
+@* Ensure that inference is not used in the WHERE clause of this update. *@
+@if(triplestore.startsWith("graphdb")) {
+ USING
+}
+WHERE {
+ BIND(IRI("@dataNamedGraph") AS ?dataNamedGraph)
+ BIND(IRI("@linkSourceIri") AS ?linkSource)
+ BIND(IRI("@linkUpdate.linkPropertyIri") AS ?linkProperty)
+ BIND(IRI("@{linkUpdate.linkPropertyIri}Value") AS ?linkValueProperty)
+ BIND(IRI("@linkUpdate.linkTargetIri") AS ?linkTarget)
+ BIND(IRI("@linkUpdate.newLinkValueIri") AS ?newLinkValue)
+
+ @* Do nothing if the link source doesn't exist, is marked as deleted, or isn't a knora-base:Resource. *@
+
+ ?linkSource rdf:type ?linkSourceClass ;
+ knora-base:isDeleted false .
+ ?linkSourceClass rdfs:subClassOf* knora-base:Resource .
+
+ @* Make sure a direct link exists between the two resources. *@
+
+ @if(linkUpdate.directLinkExists) {
+ ?linkSource ?linkProperty ?linkTarget .
+ } else {
+ @{throw SparqlGenerationException(s"linkUpdate.directLinkExists must be true in this SPARQL template"); ()}
+ }
+
+ @*
+
+ Make sure a knora-base:LinkValue exists describing the direct link, and has the correct reference count.
+
+ *@
+
+ @if(linkUpdate.linkValueExists) {
+ ?linkSource ?linkValueProperty ?currentLinkValue .
+ ?currentLinkValue rdf:type knora-base:LinkValue ;
+ rdf:subject ?linkSource ;
+ rdf:predicate ?linkProperty ;
+ rdf:object ?linkTarget ;
+ knora-base:valueHasRefCount @linkUpdate.currentReferenceCount ;
+ knora-base:isDeleted false ;
+ knora-base:valueHasUUID ?currentLinkUUID .
+ } else {
+ @{throw SparqlGenerationException(s"linkUpdate.linkValueExists must be true in this SPARQL template"); ()}
+ }
+
+ @* Get the link source's last modification date, if it has one, so we can update it. *@
+
+ OPTIONAL {
+ ?linkSource knora-base:lastModificationDate ?linkSourceLastModificationDate .
+ }
+}
diff --git a/webapi/src/main/twirl/queries/sparql/v2/changeLink.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/changeLinkTarget.scala.txt
similarity index 100%
rename from webapi/src/main/twirl/queries/sparql/v2/changeLink.scala.txt
rename to webapi/src/main/twirl/queries/sparql/v2/changeLinkTarget.scala.txt
diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala
index fc3c682e2a..0671a082d8 100644
--- a/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala
+++ b/webapi/src/test/scala/org/knora/webapi/e2e/v2/ValuesRouteV2E2ESpec.scala
@@ -321,9 +321,9 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
hasPermissions should ===(customPermissions)
}
- "create a text value without standoff" in {
+ "create a text value without standoff and without a comment" in {
val resourceIri: IRI = SharedTestDataADM.AThing.iri
- val valueAsString: String = "Comment 1a"
+ val valueAsString: String = "text without standoff"
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
@@ -353,6 +353,194 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
savedValueAsString should ===(valueAsString)
}
+
+ "not update a text value without a comment without changing it" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = "text without standoff"
+
+ val jsonLDEntity = SharedTestDataADM.updateTextValueWithoutStandoffRequest(
+ resourceIri = resourceIri,
+ valueIri = textValueWithoutStandoffIri.get,
+ valueAsString = valueAsString
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+ assert(response.status == StatusCodes.BadRequest, response.toString)
+ }
+
+ "not update a text value so it's empty" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = ""
+
+ val jsonLDEntity = SharedTestDataADM.updateTextValueWithoutStandoffRequest(
+ resourceIri = resourceIri,
+ valueIri = textValueWithoutStandoffIri.get,
+ valueAsString = valueAsString
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.BadRequest, response.toString)
+ }
+
+ "update a text value without standoff" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = "text without standoff updated"
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLDEntity = SharedTestDataADM.updateTextValueWithoutStandoffRequest(
+ resourceIri = resourceIri,
+ valueIri = textValueWithoutStandoffIri.get,
+ valueAsString = valueAsString
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ textValueWithoutStandoffIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.TextValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = textValueWithoutStandoffIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
+ savedValueAsString should ===(valueAsString)
+ }
+
+ "update a text value without standoff, adding a comment" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = "text without standoff updated"
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLDEntity = SharedTestDataADM.updateTextValueWithCommentRequest(
+ resourceIri = resourceIri,
+ valueIri = textValueWithoutStandoffIri.get,
+ valueAsString = valueAsString,
+ valueHasComment = "Adding a comment"
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ textValueWithoutStandoffIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.TextValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = textValueWithoutStandoffIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
+ savedValueAsString should ===(valueAsString)
+ }
+
+ "not update a text value without standoff and with a comment without changing it" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = "text without standoff updated"
+
+ val jsonLDEntity = SharedTestDataADM.updateTextValueWithCommentRequest(
+ resourceIri = resourceIri,
+ valueIri = textValueWithoutStandoffIri.get,
+ valueAsString = valueAsString,
+ valueHasComment = "Adding a comment"
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.BadRequest, response.toString)
+ }
+
+ "update a text value without standoff, changing only the a comment" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = "text without standoff updated"
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLDEntity = SharedTestDataADM.updateTextValueWithCommentRequest(
+ resourceIri = resourceIri,
+ valueIri = textValueWithoutStandoffIri.get,
+ valueAsString = valueAsString,
+ valueHasComment = "Updated comment"
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ textValueWithoutStandoffIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.TextValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = textValueWithoutStandoffIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
+ savedValueAsString should ===(valueAsString)
+ }
+
+ "create a text value without standoff and with a comment" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val valueAsString: String = "this is a text value that has a comment"
+ val valueHasComment: String = "this is a comment"
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLDEntity = SharedTestDataADM.createTextValueWithCommentRequest(
+ resourceIri = resourceIri,
+ valueAsString = valueAsString,
+ valueHasComment = valueHasComment
+ )
+
+ val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ textValueWithoutStandoffIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.TextValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = textValueWithoutStandoffIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
+ savedValueAsString should ===(valueAsString)
+ val savedValueHasComment: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasComment)
+ savedValueHasComment should ===(valueHasComment)
+ }
"create a text value with standoff" in {
val resourceIri: IRI = SharedTestDataADM.AThing.iri
@@ -1007,43 +1195,6 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
assert(savedTextValueAsXml.contains(textValueAsXml))
}
- "create a text value with a comment" in {
- val resourceIri: IRI = SharedTestDataADM.AThing.iri
- val valueAsString: String = "this is a text value that has a comment"
- val valueHasComment: String = "this is a comment"
- val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
- val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
-
- val jsonLDEntity = SharedTestDataADM.createTextValueWithCommentRequest(
- resourceIri = resourceIri,
- valueAsString = valueAsString,
- valueHasComment = valueHasComment
- )
-
- val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
- val response: HttpResponse = singleAwaitingRequest(request)
- assert(response.status == StatusCodes.OK, response.toString)
- val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
- val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
- textValueWithoutStandoffIri.set(valueIri)
- val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
- valueType should ===(OntologyConstants.KnoraApiV2Complex.TextValue.toSmartIri)
-
- val savedValue: JsonLDObject = getValue(
- resourceIri = resourceIri,
- maybePreviousLastModDate = maybeResourceLastModDate,
- propertyIriForGravsearch = propertyIri,
- propertyIriInResult = propertyIri,
- expectedValueIri = textValueWithoutStandoffIri.get,
- userEmail = anythingUserEmail
- )
-
- val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
- savedValueAsString should ===(valueAsString)
- val savedValueHasComment: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasComment)
- savedValueHasComment should ===(valueHasComment)
- }
-
"not create an empty text value" in {
val resourceIri: IRI = SharedTestDataADM.AThing.iri
val valueAsString: String = ""
@@ -1694,7 +1845,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
savedGeonameCode should ===(geonameCode)
}
- "create a link between two resources" in {
+ "create a link between two resources, without a comment" in {
val resourceIri: IRI = SharedTestDataADM.AThing.iri
val linkPropertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing".toSmartIri
val linkValuePropertyIri: SmartIri = linkPropertyIri.fromLinkPropToLinkValueProp
@@ -1899,40 +2050,6 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
savedDecimalValue should ===(decimalValue)
}
- "update a text value without standoff" in {
- val resourceIri: IRI = SharedTestDataADM.AThing.iri
- val valueAsString: String = "Comment 1a updated"
- val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasText".toSmartIri
- val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
-
- val jsonLDEntity = SharedTestDataADM.updateTextValueWithoutStandoffRequest(
- resourceIri = resourceIri,
- valueIri = textValueWithoutStandoffIri.get,
- valueAsString = valueAsString
- )
-
- val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
- val response: HttpResponse = singleAwaitingRequest(request)
- assert(response.status == StatusCodes.OK, response.toString)
- val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
- val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
- textValueWithoutStandoffIri.set(valueIri)
- val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
- valueType should ===(OntologyConstants.KnoraApiV2Complex.TextValue.toSmartIri)
-
- val savedValue: JsonLDObject = getValue(
- resourceIri = resourceIri,
- maybePreviousLastModDate = maybeResourceLastModDate,
- propertyIriForGravsearch = propertyIri,
- propertyIriInResult = propertyIri,
- expectedValueIri = textValueWithoutStandoffIri.get,
- userEmail = anythingUserEmail
- )
-
- val savedValueAsString: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueAsString)
- savedValueAsString should ===(valueAsString)
- }
-
"update a text value with standoff" in {
val resourceIri: IRI = SharedTestDataADM.AThing.iri
@@ -2038,21 +2155,6 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
savedValueHasComment should ===(valueHasComment)
}
- "not update a text value so it's empty" in {
- val resourceIri: IRI = SharedTestDataADM.AThing.iri
- val valueAsString: String = ""
-
- val jsonLDEntity = SharedTestDataADM.updateTextValueWithoutStandoffRequest(
- resourceIri = resourceIri,
- valueIri = textValueWithoutStandoffIri.get,
- valueAsString = valueAsString
- )
-
- val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
- val response: HttpResponse = singleAwaitingRequest(request)
- assert(response.status == StatusCodes.BadRequest, response.toString)
- }
-
"update a date value representing a range with day precision" in {
val resourceIri: IRI = SharedTestDataADM.AThing.iri
val propertyIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasDate".toSmartIri
@@ -2701,6 +2803,158 @@ class ValuesRouteV2E2ESpec extends E2ESpec {
savedTargetIri should ===(linkTargetIri)
}
+ "not update a link without a comment without changing it" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val linkPropertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing".toSmartIri
+ val linkValuePropertyIri: SmartIri = linkPropertyIri.fromLinkPropToLinkValueProp
+ val linkTargetIri: IRI = "http://rdfh.ch/0001/5IEswyQFQp2bxXDrOyEfEA"
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLdEntity = SharedTestDataADM.updateLinkValueRequest(
+ resourceIri = resourceIri,
+ valueIri = linkValueIri.get,
+ targetResourceIri = linkTargetIri
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.BadRequest, response.toString)
+ }
+
+ "update a link between two resources, adding a comment" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val linkPropertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing".toSmartIri
+ val linkValuePropertyIri: SmartIri = linkPropertyIri.fromLinkPropToLinkValueProp
+ val linkTargetIri: IRI = "http://rdfh.ch/0001/5IEswyQFQp2bxXDrOyEfEA"
+ val comment = "adding a comment"
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLdEntity = SharedTestDataADM.updateLinkValueRequest(
+ resourceIri = resourceIri,
+ valueIri = linkValueIri.get,
+ targetResourceIri = linkTargetIri,
+ comment = Some(comment)
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ linkValueIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = linkPropertyIri,
+ propertyIriInResult = linkValuePropertyIri,
+ expectedValueIri = linkValueIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedComment: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasComment)
+ savedComment should ===(comment)
+ }
+
+ "update a link between two resources, changing only the comment" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val linkPropertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing".toSmartIri
+ val linkValuePropertyIri: SmartIri = linkPropertyIri.fromLinkPropToLinkValueProp
+ val linkTargetIri: IRI = "http://rdfh.ch/0001/5IEswyQFQp2bxXDrOyEfEA"
+ val comment = "changing only the comment"
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+
+ val jsonLdEntity = SharedTestDataADM.updateLinkValueRequest(
+ resourceIri = resourceIri,
+ valueIri = linkValueIri.get,
+ targetResourceIri = linkTargetIri,
+ comment = Some(comment)
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ linkValueIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = linkPropertyIri,
+ propertyIriInResult = linkValuePropertyIri,
+ expectedValueIri = linkValueIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedComment: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasComment)
+ savedComment should ===(comment)
+ }
+
+ "not update a link with a comment without changing it" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val linkPropertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing".toSmartIri
+ val linkTargetIri: IRI = "http://rdfh.ch/0001/5IEswyQFQp2bxXDrOyEfEA"
+ val comment = "changing only the comment"
+
+ val jsonLdEntity = SharedTestDataADM.updateLinkValueRequest(
+ resourceIri = resourceIri,
+ valueIri = linkValueIri.get,
+ targetResourceIri = linkTargetIri,
+ comment = Some(comment)
+ )
+
+ val request = Put(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.BadRequest, response.toString)
+ }
+
+ "create a link between two resources, with a comment" in {
+ val resourceIri: IRI = SharedTestDataADM.AThing.iri
+ val linkPropertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasOtherThing".toSmartIri
+ val linkValuePropertyIri: SmartIri = linkPropertyIri.fromLinkPropToLinkValueProp
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail)
+ val comment = "Initial comment"
+
+ val jsonLdEntity = SharedTestDataADM.createLinkValueRequest(
+ resourceIri = resourceIri,
+ targetResourceIri = SharedTestDataADM.TestDing.iri,
+ valueHasComment = Some(comment)
+ )
+
+ val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLdEntity)) ~> addCredentials(BasicHttpCredentials(anythingUserEmail, password))
+ val response: HttpResponse = singleAwaitingRequest(request)
+ assert(response.status == StatusCodes.OK, response.toString)
+ val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response)
+
+ val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.ID, stringFormatter.validateAndEscapeIri)
+ linkValueIri.set(valueIri)
+ val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr)
+ valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri)
+
+ val savedValue: JsonLDObject = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = linkPropertyIri,
+ propertyIriInResult = linkValuePropertyIri,
+ expectedValueIri = linkValueIri.get,
+ userEmail = anythingUserEmail
+ )
+
+ val savedTarget: JsonLDObject = savedValue.requireObject(OntologyConstants.KnoraApiV2Complex.LinkValueHasTarget)
+ val savedTargetIri: IRI = savedTarget.requireString(JsonLDConstants.ID)
+ savedTargetIri should ===(SharedTestDataADM.TestDing.iri)
+
+ val savedComment: String = savedValue.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasComment)
+ savedComment should ===(comment)
+ }
+
"delete an integer value" in {
val jsonLdEntity = SharedTestDataADM.deleteIntValueRequest(
resourceIri = SharedTestDataADM.AThing.iri,
diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala
index a4a57c4647..8ad5c0e948 100644
--- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala
+++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ValuesResponderV2Spec.scala
@@ -403,14 +403,289 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
}
}
- "create an integer value with custom permissions" in {
+ "not create a duplicate integer value" in {
+ val resourceIri: IRI = aThingIri
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
+ val intValue = 4
+
+ responderManager ! CreateValueRequestV2(
+ CreateValueV2(
+ resourceIri = resourceIri,
+ resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
+ propertyIri = propertyIri,
+ valueContent = IntegerValueContentV2(
+ ontologySchema = ApiV2Complex,
+ valueHasInteger = intValue
+ )
+ ),
+ requestingUser = anythingUser1,
+ apiRequestID = UUID.randomUUID
+ )
+
+ expectMsgPF(timeout) {
+ case msg: akka.actor.Status.Failure =>
+ msg.cause.isInstanceOf[DuplicateValueException] should ===(true)
+ }
+ }
+
+ "update an integer value" in {
+ val resourceIri: IRI = aThingIri
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUser1)
+
+ // Get the value before update.
+ val previousValueFromTriplestore: ReadValueV2 = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIri.get,
+ requestingUser = anythingUser1,
+ checkLastModDateChanged = false
+ )
+
+ // Update the value.
+
+ val intValue = 5
+
+ responderManager ! UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
+ propertyIri = propertyIri,
+ valueIri = intValueIri.get,
+ valueContent = IntegerValueContentV2(
+ ontologySchema = ApiV2Complex,
+ valueHasInteger = intValue
+ )
+ ),
+ requestingUser = anythingUser1,
+ apiRequestID = UUID.randomUUID
+ )
+
+ expectMsgPF(timeout) {
+ case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri)
+ }
+
+ // Read the value back to check that it was added correctly.
+
+ val updatedValueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIri.get,
+ requestingUser = anythingUser1
+ )
+
+ updatedValueFromTriplestore.valueContent match {
+ case savedValue: IntegerValueContentV2 =>
+ savedValue.valueHasInteger should ===(intValue)
+ updatedValueFromTriplestore.permissions should ===(previousValueFromTriplestore.permissions)
+ updatedValueFromTriplestore.valueHasUUID should ===(previousValueFromTriplestore.valueHasUUID)
+
+ case _ => throw AssertionException(s"Expected integer value, got $updatedValueFromTriplestore")
+ }
+
+ // Check that the permissions and UUID were deleted from the previous version of the value.
+ assert(getValueUUID(previousValueFromTriplestore.valueIri).isEmpty)
+ assert(getValuePermissions(previousValueFromTriplestore.valueIri).isEmpty)
+ }
+
+ "not update an integer value without a comment without changing it" in {
+ val resourceIri: IRI = aThingIri
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
+ val intValue = 5
+
+ responderManager ! UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
+ propertyIri = propertyIri,
+ valueIri = intValueIri.get,
+ valueContent = IntegerValueContentV2(
+ ontologySchema = ApiV2Complex,
+ valueHasInteger = intValue
+ )
+ ),
+ requestingUser = anythingUser1,
+ apiRequestID = UUID.randomUUID
+ )
+
+ expectMsgPF(timeout) {
+ case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true)
+ }
+ }
+
+ "update an integer value, adding a comment" in {
+ val resourceIri: IRI = aThingIri
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUser1)
+ val comment = "Added a comment"
+
+ // Get the value before update.
+ val previousValueFromTriplestore: ReadValueV2 = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIri.get,
+ requestingUser = anythingUser1,
+ checkLastModDateChanged = false
+ )
+
+ // Update the value.
+
+ val intValue = 5
+
+ responderManager ! UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
+ propertyIri = propertyIri,
+ valueIri = intValueIri.get,
+ valueContent = IntegerValueContentV2(
+ ontologySchema = ApiV2Complex,
+ valueHasInteger = intValue,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = anythingUser1,
+ apiRequestID = UUID.randomUUID
+ )
+
+ expectMsgPF(timeout) {
+ case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri)
+ }
+
+ // Read the value back to check that it was added correctly.
+
+ val updatedValueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIri.get,
+ requestingUser = anythingUser1
+ )
+
+ updatedValueFromTriplestore.valueContent match {
+ case savedValue: IntegerValueContentV2 =>
+ savedValue.valueHasInteger should ===(intValue)
+ updatedValueFromTriplestore.permissions should ===(previousValueFromTriplestore.permissions)
+ updatedValueFromTriplestore.valueHasUUID should ===(previousValueFromTriplestore.valueHasUUID)
+ assert(updatedValueFromTriplestore.valueContent.comment.contains(comment))
+
+ case _ => throw AssertionException(s"Expected integer value, got $updatedValueFromTriplestore")
+ }
+
+ // Check that the permissions and UUID were deleted from the previous version of the value.
+ assert(getValueUUID(previousValueFromTriplestore.valueIri).isEmpty)
+ assert(getValuePermissions(previousValueFromTriplestore.valueIri).isEmpty)
+ }
+
+ "not update an integer value with a comment without changing it" in {
+ val resourceIri: IRI = aThingIri
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
+ val intValue = 5
+ val comment = "Added a comment"
+
+ responderManager ! UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
+ propertyIri = propertyIri,
+ valueIri = intValueIri.get,
+ valueContent = IntegerValueContentV2(
+ ontologySchema = ApiV2Complex,
+ valueHasInteger = intValue,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = anythingUser1,
+ apiRequestID = UUID.randomUUID
+ )
+
+ expectMsgPF(timeout) {
+ case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true)
+ }
+ }
+
+ "update an integer value with a comment, changing only the comment" in {
+ val resourceIri: IRI = aThingIri
+ val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUser1)
+ val comment = "An updated comment"
+
+ // Get the value before update.
+ val previousValueFromTriplestore: ReadValueV2 = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIri.get,
+ requestingUser = anythingUser1,
+ checkLastModDateChanged = false
+ )
+
+ // Update the value.
+
+ val intValue = 5
+
+ responderManager ! UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
+ propertyIri = propertyIri,
+ valueIri = intValueIri.get,
+ valueContent = IntegerValueContentV2(
+ ontologySchema = ApiV2Complex,
+ valueHasInteger = intValue,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = anythingUser1,
+ apiRequestID = UUID.randomUUID
+ )
+
+ expectMsgPF(timeout) {
+ case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri)
+ }
+
+ // Read the value back to check that it was added correctly.
+
+ val updatedValueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIri.get,
+ requestingUser = anythingUser1
+ )
+
+ updatedValueFromTriplestore.valueContent match {
+ case savedValue: IntegerValueContentV2 =>
+ savedValue.valueHasInteger should ===(intValue)
+ updatedValueFromTriplestore.permissions should ===(previousValueFromTriplestore.permissions)
+ updatedValueFromTriplestore.valueHasUUID should ===(previousValueFromTriplestore.valueHasUUID)
+ assert(updatedValueFromTriplestore.valueContent.comment.contains(comment))
+
+ case _ => throw AssertionException(s"Expected integer value, got $updatedValueFromTriplestore")
+ }
+
+ // Check that the permissions and UUID were deleted from the previous version of the value.
+ assert(getValueUUID(previousValueFromTriplestore.valueIri).isEmpty)
+ assert(getValuePermissions(previousValueFromTriplestore.valueIri).isEmpty)
+ }
+
+ "create an integer value with a comment" in {
// Add the value.
val resourceIri: IRI = aThingIri
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
- val intValue = 1
- val permissions = "CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher"
+ val intValue = 8
val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUser1)
+ val comment = "Initial comment"
responderManager ! CreateValueRequestV2(
CreateValueV2(
@@ -419,16 +694,17 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
propertyIri = propertyIri,
valueContent = IntegerValueContentV2(
ontologySchema = ApiV2Complex,
- valueHasInteger = intValue
- ),
- permissions = Some(permissions)
+ valueHasInteger = intValue,
+ comment = Some(comment)
+ )
),
requestingUser = anythingUser1,
apiRequestID = UUID.randomUUID
)
expectMsgPF(timeout) {
- case createValueResponse: CreateValueResponseV2 => intValueIriWithCustomPermissions.set(createValueResponse.valueIri)
+ case createValueResponse: CreateValueResponseV2 =>
+ intValueIri.set(createValueResponse.valueIri)
}
// Read the value back to check that it was added correctly.
@@ -438,24 +714,27 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
maybePreviousLastModDate = maybeResourceLastModDate,
propertyIriForGravsearch = propertyIri,
propertyIriInResult = propertyIri,
- expectedValueIri = intValueIriWithCustomPermissions.get,
+ expectedValueIri = intValueIri.get,
requestingUser = anythingUser1
)
valueFromTriplestore.valueContent match {
case savedValue: IntegerValueContentV2 =>
savedValue.valueHasInteger should ===(intValue)
- PermissionUtilADM.parsePermissions(valueFromTriplestore.permissions) should ===(PermissionUtilADM.parsePermissions(permissions))
+ assert(savedValue.comment.contains(comment))
case _ => throw AssertionException(s"Expected integer value, got $valueFromTriplestore")
}
}
- "not create an integer value with syntactically invalid custom permissions" in {
+ "create an integer value with custom permissions" in {
+ // Add the value.
+
val resourceIri: IRI = aThingIri
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
- val intValue = 1024
- val permissions = "M knora-admin:Creator,V knora-admin:KnownUser"
+ val intValue = 1
+ val permissions = "CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher"
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUser1)
responderManager ! CreateValueRequestV2(
CreateValueV2(
@@ -473,16 +752,34 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
)
expectMsgPF(timeout) {
- case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true)
+ case createValueResponse: CreateValueResponseV2 => intValueIriWithCustomPermissions.set(createValueResponse.valueIri)
}
+ // Read the value back to check that it was added correctly.
+
+ val valueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = propertyIri,
+ propertyIriInResult = propertyIri,
+ expectedValueIri = intValueIriWithCustomPermissions.get,
+ requestingUser = anythingUser1
+ )
+
+ valueFromTriplestore.valueContent match {
+ case savedValue: IntegerValueContentV2 =>
+ savedValue.valueHasInteger should ===(intValue)
+ PermissionUtilADM.parsePermissions(valueFromTriplestore.permissions) should ===(PermissionUtilADM.parsePermissions(permissions))
+
+ case _ => throw AssertionException(s"Expected integer value, got $valueFromTriplestore")
+ }
}
- "not create an integer value with custom permissions referring to a nonexistent group" in {
+ "not create an integer value with syntactically invalid custom permissions" in {
val resourceIri: IRI = aThingIri
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
val intValue = 1024
- val permissions = "M knora-admin:Creator|V http://rdfh.ch/groups/0001/nonexistent-group"
+ val permissions = "M knora-admin:Creator,V knora-admin:KnownUser"
responderManager ! CreateValueRequestV2(
CreateValueV2(
@@ -500,14 +797,16 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
)
expectMsgPF(timeout) {
- case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true)
+ case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[BadRequestException] should ===(true)
}
+
}
- "not create a value if the user does not have modify permission on the resource" in {
+ "not create an integer value with custom permissions referring to a nonexistent group" in {
val resourceIri: IRI = aThingIri
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
- val intValue = 5
+ val intValue = 1024
+ val permissions = "M knora-admin:Creator|V http://rdfh.ch/groups/0001/nonexistent-group"
responderManager ! CreateValueRequestV2(
CreateValueV2(
@@ -517,21 +816,22 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
valueContent = IntegerValueContentV2(
ontologySchema = ApiV2Complex,
valueHasInteger = intValue
- )
+ ),
+ permissions = Some(permissions)
),
- requestingUser = incunabulaUser,
+ requestingUser = anythingUser1,
apiRequestID = UUID.randomUUID
)
expectMsgPF(timeout) {
- case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true)
+ case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[NotFoundException] should ===(true)
}
}
- "not create a duplicate integer value" in {
+ "not create a value if the user does not have modify permission on the resource" in {
val resourceIri: IRI = aThingIri
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
- val intValue = 4
+ val intValue = 5
responderManager ! CreateValueRequestV2(
CreateValueV2(
@@ -543,13 +843,12 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
valueHasInteger = intValue
)
),
- requestingUser = anythingUser1,
+ requestingUser = incunabulaUser,
apiRequestID = UUID.randomUUID
)
expectMsgPF(timeout) {
- case msg: akka.actor.Status.Failure =>
- msg.cause.isInstanceOf[DuplicateValueException] should ===(true)
+ case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[ForbiddenException] should ===(true)
}
}
@@ -1854,70 +2153,6 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
standoffLinkValueIri.set(linkValueFromTriplestore.valueIri)
}
- "update an integer value" in {
- val resourceIri: IRI = aThingIri
- val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
- val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUser1)
-
- // Get the value before update.
- val previousValueFromTriplestore: ReadValueV2 = getValue(
- resourceIri = resourceIri,
- maybePreviousLastModDate = maybeResourceLastModDate,
- propertyIriForGravsearch = propertyIri,
- propertyIriInResult = propertyIri,
- expectedValueIri = intValueIri.get,
- requestingUser = anythingUser1,
- checkLastModDateChanged = false
- )
-
- // Update the value.
-
- val intValue = 5
-
- responderManager ! UpdateValueRequestV2(
- UpdateValueContentV2(
- resourceIri = resourceIri,
- resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
- propertyIri = propertyIri,
- valueIri = intValueIri.get,
- valueContent = IntegerValueContentV2(
- ontologySchema = ApiV2Complex,
- valueHasInteger = intValue
- )
- ),
- requestingUser = anythingUser1,
- apiRequestID = UUID.randomUUID
- )
-
- expectMsgPF(timeout) {
- case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri)
- }
-
- // Read the value back to check that it was added correctly.
-
- val updatedValueFromTriplestore = getValue(
- resourceIri = resourceIri,
- maybePreviousLastModDate = maybeResourceLastModDate,
- propertyIriForGravsearch = propertyIri,
- propertyIriInResult = propertyIri,
- expectedValueIri = intValueIri.get,
- requestingUser = anythingUser1
- )
-
- updatedValueFromTriplestore.valueContent match {
- case savedValue: IntegerValueContentV2 =>
- savedValue.valueHasInteger should ===(intValue)
- updatedValueFromTriplestore.permissions should ===(previousValueFromTriplestore.permissions)
- updatedValueFromTriplestore.valueHasUUID should ===(previousValueFromTriplestore.valueHasUUID)
-
- case _ => throw AssertionException(s"Expected integer value, got $updatedValueFromTriplestore")
- }
-
- // Check that the permissions and UUID were deleted from the previous version of the value.
- assert(getValueUUID(previousValueFromTriplestore.valueIri).isEmpty)
- assert(getValuePermissions(previousValueFromTriplestore.valueIri).isEmpty)
- }
-
"not update a value if an outdated value IRI is given" in {
val resourceIri: IRI = aThingIri
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
@@ -2238,31 +2473,6 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
}
}
- "not update an integer value without changing it" in {
- val resourceIri: IRI = aThingIri
- val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri
- val intValue = 6
-
- responderManager ! UpdateValueRequestV2(
- UpdateValueContentV2(
- resourceIri = resourceIri,
- resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri,
- propertyIri = propertyIri,
- valueIri = intValueIri.get,
- valueContent = IntegerValueContentV2(
- ontologySchema = ApiV2Complex,
- valueHasInteger = intValue
- )
- ),
- requestingUser = anythingUser1,
- apiRequestID = UUID.randomUUID
- )
-
- expectMsgPF(timeout) {
- case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true)
- }
- }
-
"update a text value (without submitting standoff)" in {
val valueHasString = "This updated comment has no standoff"
val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0803/incunabula/v2#book_comment".toSmartIri
@@ -3354,7 +3564,7 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
}
}
- "not update a link without changing it" in {
+ "not update a link without a comment without changing it" in {
val resourceIri: IRI = "http://rdfh.ch/0803/cb1a74e3e2f6"
val linkValuePropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkToValue.toSmartIri
@@ -3380,6 +3590,177 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
}
}
+ "update a link, adding a comment" in {
+ val resourceIri: IRI = "http://rdfh.ch/0803/cb1a74e3e2f6"
+ val linkPropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkTo.toSmartIri
+ val linkValuePropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkToValue.toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, incunabulaUser)
+ val comment: String = "Adding a comment"
+
+ val updateValueRequest = UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = OntologyConstants.KnoraApiV2Complex.LinkObj.toSmartIri,
+ propertyIri = linkValuePropertyIri,
+ valueIri = linkValueIri.get,
+ valueContent = LinkValueContentV2(
+ ontologySchema = ApiV2Complex,
+ referredResourceIri = generationeIri,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = incunabulaUser,
+ apiRequestID = UUID.randomUUID
+ )
+
+ responderManager ! updateValueRequest
+
+ expectMsgPF(timeout) {
+ case updateValueResponse: UpdateValueResponseV2 => linkValueIri.set(updateValueResponse.valueIri)
+ }
+
+ val valueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = linkPropertyIri,
+ propertyIriInResult = linkValuePropertyIri,
+ expectedValueIri = linkValueIri.get,
+ requestingUser = incunabulaUser
+ )
+
+ valueFromTriplestore match {
+ case readLinkValueV2: ReadLinkValueV2 =>
+ readLinkValueV2.valueContent.referredResourceIri should ===(generationeIri)
+ readLinkValueV2.valueHasRefCount should ===(1)
+ assert(readLinkValueV2.valueContent.comment.contains(comment))
+
+ case _ => throw AssertionException(s"Expected link value, got $valueFromTriplestore")
+ }
+ }
+
+ "not update a link with a comment without changing it" in {
+ val resourceIri: IRI = "http://rdfh.ch/0803/cb1a74e3e2f6"
+ val linkValuePropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkToValue.toSmartIri
+ val comment: String = "Adding a comment"
+
+ val updateValueRequest = UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = OntologyConstants.KnoraApiV2Complex.LinkObj.toSmartIri,
+ propertyIri = linkValuePropertyIri,
+ valueIri = linkValueIri.get,
+ valueContent = LinkValueContentV2(
+ ontologySchema = ApiV2Complex,
+ referredResourceIri = generationeIri,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = incunabulaUser,
+ apiRequestID = UUID.randomUUID
+ )
+
+ responderManager ! updateValueRequest
+
+ expectMsgPF(timeout) {
+ case msg: akka.actor.Status.Failure => msg.cause.isInstanceOf[DuplicateValueException] should ===(true)
+ }
+ }
+
+ "update a link with a comment, changing only the comment" in {
+ val resourceIri: IRI = "http://rdfh.ch/0803/cb1a74e3e2f6"
+ val linkPropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkTo.toSmartIri
+ val linkValuePropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkToValue.toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, incunabulaUser)
+ val comment = "An updated comment"
+
+ val updateValueRequest = UpdateValueRequestV2(
+ UpdateValueContentV2(
+ resourceIri = resourceIri,
+ resourceClassIri = OntologyConstants.KnoraApiV2Complex.LinkObj.toSmartIri,
+ propertyIri = linkValuePropertyIri,
+ valueIri = linkValueIri.get,
+ valueContent = LinkValueContentV2(
+ ontologySchema = ApiV2Complex,
+ referredResourceIri = generationeIri,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = incunabulaUser,
+ apiRequestID = UUID.randomUUID
+ )
+
+ responderManager ! updateValueRequest
+
+ expectMsgPF(timeout) {
+ case updateValueResponse: UpdateValueResponseV2 => linkValueIri.set(updateValueResponse.valueIri)
+ }
+
+ val valueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = linkPropertyIri,
+ propertyIriInResult = linkValuePropertyIri,
+ expectedValueIri = linkValueIri.get,
+ requestingUser = incunabulaUser
+ )
+
+ valueFromTriplestore match {
+ case readLinkValueV2: ReadLinkValueV2 =>
+ readLinkValueV2.valueContent.referredResourceIri should ===(generationeIri)
+ readLinkValueV2.valueHasRefCount should ===(1)
+ assert(readLinkValueV2.valueContent.comment.contains(comment))
+
+ case _ => throw AssertionException(s"Expected link value, got $valueFromTriplestore")
+ }
+ }
+
+ "create a link with a comment" in {
+ val resourceIri: IRI = "http://rdfh.ch/0803/cb1a74e3e2f6"
+ val linkPropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkTo.toSmartIri
+ val linkValuePropertyIri: SmartIri = OntologyConstants.KnoraApiV2Complex.HasLinkToValue.toSmartIri
+ val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, incunabulaUser)
+ val comment = "Initial comment"
+
+ val createValueRequest = CreateValueRequestV2(
+ CreateValueV2(
+ resourceIri = resourceIri,
+ propertyIri = linkValuePropertyIri,
+ resourceClassIri = OntologyConstants.KnoraApiV2Complex.LinkObj.toSmartIri,
+ valueContent = LinkValueContentV2(
+ ontologySchema = ApiV2Complex,
+ referredResourceIri = zeitglöckleinIri,
+ comment = Some(comment)
+ )
+ ),
+ requestingUser = incunabulaUser,
+ apiRequestID = UUID.randomUUID
+ )
+
+ responderManager ! createValueRequest
+
+ expectMsgPF(timeout) {
+ case createValueResponse: CreateValueResponseV2 => linkValueIri.set(createValueResponse.valueIri)
+ }
+
+ val valueFromTriplestore = getValue(
+ resourceIri = resourceIri,
+ maybePreviousLastModDate = maybeResourceLastModDate,
+ propertyIriForGravsearch = linkPropertyIri,
+ propertyIriInResult = linkValuePropertyIri,
+ expectedValueIri = linkValueIri.get,
+ requestingUser = incunabulaUser
+ )
+
+ valueFromTriplestore match {
+ case readLinkValueV2: ReadLinkValueV2 =>
+ readLinkValueV2.valueContent.referredResourceIri should ===(zeitglöckleinIri)
+ readLinkValueV2.valueHasRefCount should ===(1)
+ assert(readLinkValueV2.valueContent.comment.contains(comment))
+
+ case _ => throw AssertionException(s"Expected link value, got $valueFromTriplestore")
+ }
+ }
+
"not update a standoff link directly" in {
responderManager ! UpdateValueRequestV2(
UpdateValueContentV2(
@@ -3694,7 +4075,7 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender {
maybePreviousLastModDate = maybeResourceLastModDate,
propertyIriForGravsearch = linkPropertyIri,
propertyIriInResult = linkValuePropertyIri,
- valueIri = intValueIri.get,
+ valueIri = linkValueIri.get,
requestingUser = anythingUser1)
}