Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api-v2): Accept custom timestamps in update/delete requests #1686

Merged
merged 11 commits into from Aug 14, 2020
5 changes: 5 additions & 0 deletions docs/03-apis/api-v2/editing-resources.md
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](https://www.w3.org/TR/xmlschema11-2/#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/editing-values.md
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](knora-iris.md#iris-for-data) form) specified by the `@id` attribute, a custom creation date specified by adding
`knora-api:creationDate` (an [xsd:dateTimeStamp](https://www.w3.org/TR/xmlschema11-2/#dateTimeStamp)), or a custom UUID
`knora-api:valueCreationDate` (an [xsd:dateTimeStamp](https://www.w3.org/TR/xmlschema11-2/#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" : "http://www.w3.org/2001/XMLSchema#"
}
```

The format of the object of `knora-api:hasPermissions` is described in
[Permissions](../../02-knora-ontologies/knora-base.md#permissions).

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](https://www.w3.org/TR/xmlschema11-2/#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](https://www.w3.org/TR/xmlschema11-2/#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)

maybeUUID
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
OntologyConstants.KnoraApiV2Complex.CreationDate
)

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

val valuesArray: JsonLDArray = jsonLDDocument.requireArray(propertyIriStr)

val propertyValues = valuesArray.value.map {
val valueFuturesSeq: Seq[Future[CreateValueInNewResourceV2]] = valuesArray.value.map {
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.
SepidehAlassi marked this conversation as resolved.
Show resolved Hide resolved
maybeCustomValueCreationDate: Option[Instant] = valueJsonLDObject.maybeDatatypeValueInObject(
key = OntologyConstants.KnoraApiV2Complex.ValueCreationDate,
expectedDatatype = OntologyConstants.Xsd.DateTimeStamp.toSmartIri,
validationFun = stringFormatter.xsdDateTimeStampToInstant
).orElse(valueJsonLDObject.maybeDatatypeValueInObject(
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
}.toMap

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
)).mapTo[ProjectGetResponseADM]

} 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
)

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