Skip to content


Browse files Browse the repository at this point in the history
feat(api-v2): Accept custom timestamps in update/delete requests (#1686)
  • Loading branch information
Benjamin Geer committed Aug 14, 2020
1 parent fa48e61 commit 0fbe5a8
Show file tree
Hide file tree
Showing 16 changed files with 785 additions and 195 deletions.
5 changes: 5 additions & 0 deletions docs/03-apis/api-v2/
Expand Up @@ -330,6 +330,11 @@ The request body is a JSON-LD object containing the following information about
The optional property `knora-api:deleteComment` specifies a comment to be attached to the
resource, explaining why it has been marked as deleted.

The optional property `knora-api:deleteDate`
(an [xsd:dateTimeStamp](
indicates when the resource was marked as deleted; if not given, the current
time is used.

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

Expand Down
14 changes: 11 additions & 3 deletions docs/03-apis/api-v2/
Expand Up @@ -83,8 +83,9 @@ Permissions for the new value can be given by adding `knora-api:hasPermissions`.

Each value can have an optional custom IRI (of [Knora IRI]( form) specified by the `@id` attribute, a custom creation date specified by adding
`knora-api:creationDate` (an [xsd:dateTimeStamp](, or a custom UUID
`knora-api:valueCreationDate` (an [xsd:dateTimeStamp](, or a custom UUID
given by `knora-api:valueHasUUID`. Each custom UUID must be [base64url-encoded](rfc:4648#section-5), without padding.
For example:

Expand All @@ -97,7 +98,7 @@ For example:
"@type" : "knora-api:IntValue",
"knora-api:intValueAsInt" : 21,
"knora-api:valueHasUUID" : "IN4R19yYR0ygi3K2VEHpUQ",
"knora-api:creationDate" : {
"knora-api:valueCreationDate" : {
"@type" : "xsd:dateTimeStamp",
"@value" : "2020-06-04T12:58:54.502951Z"
Expand All @@ -108,6 +109,7 @@ For example:
"xsd" : ""

The format of the object of `knora-api:hasPermissions` is described in

Expand Down Expand Up @@ -386,6 +388,9 @@ To update only the permissions on a value, submit it with the new permissions an
To update a link, the user must have **modify permission** on the containing resource as
well as on the value.

To update a value and give it a custom timestamp, add
`knora-api:valueCreationDate` (an [xsd:dateTimeStamp](

The response is a JSON-LD document containing only `@id` and `@type`, returning the IRI
and type of the new value version.

Expand Down Expand Up @@ -431,7 +436,10 @@ the resource to the value, and the value's ID and type. For example:

The optional property `knora-api:deleteComment` specifies a comment to be attached to the
value, explaining why it has been marked as deleted.
value, explaining why it has been marked as deleted

The optional property `knora-api:deleteDate` (an [xsd:dateTimeStamp](
specifies a custom timestamp indicating when the value was deleted. If not specified, the current time is used.

The response is a JSON-LD document containing the predicate `knora-api:result`
with a confirmation message.
Expand Up @@ -461,16 +461,13 @@ case class JsonLDObject(value: Map[String, JsonLDValue]) extends JsonLDValue {

* Validates the optional `uuid` of a JSON-LD object as a value uuid.
* Validates an optional Base64-encoded UUID in a JSON-LD object.
* @return an optional validated decoded UUID.
def maybeUUID: Option[UUID] = {
def maybeUUID(key: String): Option[UUID] = {
implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance

val maybeUUID: Option[UUID] = maybeStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid)

maybeStringWithValidation(key, stringFormatter.validateBase64EncodedUuid)

Expand Down Expand Up @@ -678,6 +675,11 @@ case class JsonLDDocument(body: JsonLDObject, context: JsonLDObject = JsonLDObje
def requireResourcePropertyValue: (SmartIri, JsonLDObject) = body.requireResourcePropertyApiV2ComplexValue

* A convenience function that calls `body.maybeUUID`.
def maybeUUID(key: String): Option[UUID] = body.maybeUUID(key: String)

* Converts this JSON-LD object to its compacted Java representation.
Expand Down
Expand Up @@ -584,13 +584,12 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource

valueFutures: Map[SmartIri, Seq[Future[CreateValueInNewResourceV2]]] = {
propertyValueFuturesMap: Map[SmartIri, Seq[Future[CreateValueInNewResourceV2]]] = {
propertyIriStr =>
val propertyIri: SmartIri = propertyIriStr.toSmartIriWithErr(throw BadRequestException(s"Invalid property IRI: <$propertyIriStr>"))

val valuesArray: JsonLDArray = jsonLDDocument.requireArray(propertyIriStr)

val propertyValues = {
val valueFuturesSeq: Seq[Future[CreateValueInNewResourceV2]] = {
valueJsonLD =>
val valueJsonLDObject = valueJsonLD match {
case jsonLDObject: JsonLDObject => jsonLDObject
Expand All @@ -606,15 +605,22 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource
settings = settings,
log = log

maybeCustomValueIri: Option[SmartIri] = valueJsonLDObject.maybeIDAsKnoraDataIri
maybeCustomValueUUID: Option[UUID] = valueJsonLDObject.maybeUUID
maybeCustomValueUUID: Option[UUID] = valueJsonLDObject.maybeUUID(OntologyConstants.KnoraApiV2Complex.ValueHasUUID)

// Get the values's creation date.
// Get the value's creation date.
// TODO: creationDate for values is a bug, and will not be supported in future. Use valueCreationDate instead.
maybeCustomValueCreationDate: Option[Instant] = valueJsonLDObject.maybeDatatypeValueInObject(
key = OntologyConstants.KnoraApiV2Complex.ValueCreationDate,
expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri,
validationFun = stringFormatter.xsdDateTimeStampToInstant
key = OntologyConstants.KnoraApiV2Complex.CreationDate,
expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri,
validationFun = stringFormatter.xsdDateTimeStampToInstant

maybePermissions: Option[String] = valueJsonLDObject.maybeStringWithValidation(OntologyConstants.KnoraApiV2Complex.HasPermissions, stringFormatter.toSparqlEncodedString)
} yield CreateValueInNewResourceV2(
valueContent = valueContent,
Expand All @@ -625,23 +631,22 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource

propertyIri -> propertyValues
propertyIri -> valueFuturesSeq

values: Map[SmartIri, Seq[CreateValueInNewResourceV2]] <- ActorUtil.sequenceSeqFuturesInMap(valueFutures)
propertyValuesMap: Map[SmartIri, Seq[CreateValueInNewResourceV2]] <- ActorUtil.sequenceSeqFuturesInMap(propertyValueFuturesMap)

// Get information about the project that the resource should be created in.
projectInfoResponse: ProjectGetResponseADM <- (responderManager ? ProjectGetRequestADM(
ProjectIdentifierADM(maybeIri = Some(projectIri.toString)),
requestingUser = requestingUser

} yield CreateResourceRequestV2(
createResource = CreateResourceV2(
resourceIri = maybeCustomResourceIri,
resourceClassIri = resourceClassIri,
label = label,
values = values,
values = propertyValuesMap,
projectADM = projectInfoResponse.project,
permissions = permissions,
creationDate = creationDate
Expand Down Expand Up @@ -751,12 +756,15 @@ object UpdateResourceMetadataRequestV2 extends KnoraJsonLDRequestReaderV2[Update
* @param resourceIri the IRI of the resource.
* @param resourceClassIri the IRI of the resource class.
* @param maybeDeleteComment a comment explaining why the resource is being marked as deleted.
* @param maybeDeleteDate a timestamp indicating when the resource was marked as deleted. If not supplied,
* the current time will be used.
* @param maybeLastModificationDate the resource's last modification date, if any.
* @param erase if `true`, the resource will be erased from the triplestore, otherwise it will be marked as deleted.
case class DeleteOrEraseResourceRequestV2(resourceIri: IRI,
resourceClassIri: SmartIri,
maybeDeleteComment: Option[String] = None,
maybeDeleteDate: Option[Instant] = None,
maybeLastModificationDate: Option[Instant] = None,
erase: Boolean = false,
requestingUser: UserADM,
Expand Down Expand Up @@ -812,10 +820,17 @@ object DeleteOrEraseResourceRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteO

val maybeDeleteComment: Option[String] = jsonLDDocument.maybeStringWithValidation(OntologyConstants.KnoraApiV2Complex.DeleteComment, stringFormatter.toSparqlEncodedString)

val maybeDeleteDate: Option[Instant] = jsonLDDocument.maybeDatatypeValueInObject(
key = OntologyConstants.KnoraApiV2Complex.DeleteDate,
expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri,
validationFun = stringFormatter.xsdDateTimeStampToInstant

resourceIri = resourceIri.toString,
resourceClassIri = resourceClassIri,
maybeDeleteComment = maybeDeleteComment,
maybeDeleteDate = maybeDeleteDate,
maybeLastModificationDate = maybeLastModificationDate,
requestingUser = requestingUser,
apiRequestID = apiRequestID
Expand Down

0 comments on commit 0fbe5a8

Please sign in to comment.