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): Specify custom IRIs when creating resources/values #1646

Merged
merged 22 commits into from Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e58738a
feat(api-v2): Specify custom IRIs when creating resources/values (ong…
May 13, 2020
e11967e
Merge branch 'develop' into wip/DSP-159-custom-iris
May 13, 2020
37c3e02
Merge branch 'develop' into wip/DSP-159-custom-iris
SepidehAlassi May 27, 2020
667eeaa
feature (customIRI) test for creation of resource with supplied IRI
SepidehAlassi May 28, 2020
49b954b
feature (custormIri) allow custom iri for values + test for it
SepidehAlassi May 28, 2020
5bc6d18
Merge branch 'develop' into wip/DSP-159-custom-iris
SepidehAlassi May 29, 2020
cbd718a
feature (customIRI) create resources with values that have custom IRI…
SepidehAlassi May 29, 2020
f30be5a
feature (customIRI) createValueRequestV2 should accept custom value I…
SepidehAlassi May 29, 2020
7f23403
feature (customIri) create link values with supplied value IRI
SepidehAlassi Jun 3, 2020
00f2b76
feature (customIRI) create values with custom uuid
SepidehAlassi Jun 3, 2020
8b5ad48
feature (customIri) test for creation of values with custom UUIDs
SepidehAlassi Jun 3, 2020
5447fd7
Merge branch 'develop' into wip/DSP-159-custom-iris
SepidehAlassi Jun 4, 2020
f560e58
feature (customIri) create values with custom creation date + test
SepidehAlassi Jun 4, 2020
0bab1b9
feature (customIri) more tests + test data
SepidehAlassi Jun 4, 2020
07d0fec
doc (customIRI) documentation about creation of a resource with custo…
SepidehAlassi Jun 4, 2020
37ac911
doc (custom IRI) creation of values with custom IRI, UUID, and creati…
SepidehAlassi Jun 4, 2020
af0b410
Merge branch 'develop' into wip/DSP-159-custom-iris
SepidehAlassi Jun 5, 2020
13b47eb
fix (customIri) get rid of the added resource
SepidehAlassi Jun 5, 2020
162ed46
Merge branch 'develop' into wip/DSP-159-custom-iris
SepidehAlassi Jun 8, 2020
75c9495
refactor (customIri) blank lines added, and description of UUID encod…
SepidehAlassi Jun 9, 2020
b869b48
feature (customIri) create resource with values that have custom crea…
SepidehAlassi Jun 9, 2020
06c6ada
Merge branch 'develop' into wip/DSP-159-custom-iris
SepidehAlassi Jun 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/paradox/03-apis/api-v2/editing-resources.md
Expand Up @@ -189,7 +189,7 @@ In addition to the creation date, in the body of the request, it is possible to
the `@id` attribute which will then be assigned to the resource; otherwise the resource will get a unique random IRI.
Similarly, it is possible to assign a custom IRI to the values using their `@id` attributes; if not given, random IRIs
will be assigned to the values. An optional custom UUID of a value can also be given by adding `knora-api:valueHasUUID`.
For example:
Each custom UUID must be [base64url-encoded](rfc:4648#section-5), without padding. For example:
```jsonld
{
"@id" : "http://rdfh.ch/0001/a-custom-thing",
Expand Down
3 changes: 2 additions & 1 deletion docs/src/paradox/03-apis/api-v2/editing-values.md
Expand Up @@ -87,7 +87,8 @@ Permissions for the new value can be given by adding `knora-api:hasPermissions`.
```
Each value can have an optional custom IRI 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
given by `knora-api:valueHasUUID`. For example:
given by `knora-api:valueHasUUID`. Each custom UUID must be [base64url-encoded](rfc:4648#section-5), without padding.
For example:


```jsonld
Expand Down
27 changes: 27 additions & 0 deletions webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala
Expand Up @@ -408,6 +408,7 @@ object SharedTestDataADM {
val customValueUUID = "IN4R19yYR0ygi3K2VEHpUQ"
val customValueIRI: IRI = "http://rdfh.ch/0001/a-thing-with-value-IRI/values/a-value-with-IRI"
val customResourceCreationDate: Instant = Instant.parse("2019-01-09T15:45:54.502951Z")
val customValueCreationDate: Instant = Instant.parse("2020-06-09T17:04:54.502951Z")

def anythingAdminUser: UserADM = UserADM(
id = "http://rdfh.ch/users/AnythingAdminUser",
Expand Down Expand Up @@ -1934,6 +1935,32 @@ object SharedTestDataADM {
|}""".stripMargin
}

def createResourceWithCustomValueCreationDate(creationDate: Instant): String = {
s"""{
| "@type" : "anything:Thing",
| "knora-api:attachedToProject" : {
| "@id" : "http://rdfh.ch/projects/0001"
| },
| "anything:hasBoolean" : {
| "@type" : "knora-api:BooleanValue",
| "knora-api:booleanValueAsBoolean" : false,
| "knora-api:creationDate" : {
| "@type" : "xsd:dateTimeStamp",
| "@value" : "$creationDate"
| }
| },
| "rdfs:label" : "test thing with value has creation date",
| "@context" : {
| "rdf" : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
| "knora-api" : "http://api.knora.org/ontology/knora-api/v2#",
| "rdfs" : "http://www.w3.org/2000/01/rdf-schema#",
| "xsd" : "http://www.w3.org/2001/XMLSchema#",
| "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#"
| }
|}""".stripMargin
}


def createResourceWithCustomResourceIriAndCreationDateAndValueWithCustomIRIAndUUID(customResourceIRI: IRI,
customCreationDate: Instant,
customValueIRI: IRI,
Expand Down
Expand Up @@ -441,14 +441,16 @@ case class ReadResourceV2(resourceIri: IRI,
/**
* The value of a Knora property sent to Knora to be created in a new resource.
*
* @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]].
* @param customValueIri the optional custom value IRI.
* @param customValueUUID the optional custom value UUID.
* @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults.
* @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]].
* @param customValueIri the optional custom value IRI.
* @param customValueUUID the optional custom value UUID.
* @param customValueCreationDate the optional custom value creation date.
* @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults.
*/
case class CreateValueInNewResourceV2(valueContent: ValueContentV2,
customValueIri: Option[SmartIri] = None,
customValueUUID: Option[UUID] = None,
customValueCreationDate: Option[Instant] = None,
permissions: Option[String] = None) extends IOValueV2

/**
Expand Down Expand Up @@ -605,11 +607,19 @@ object CreateResourceRequestV2 extends KnoraJsonLDRequestReaderV2[CreateResource
)
maybeCustomValueIri: Option[SmartIri] = valueJsonLDObject.maybeIDAsKnoraDataIri
maybeCustomValueUUID: Option[UUID] = valueJsonLDObject.maybeUUID

// Get the values's creation date.
maybeCustomValueCreationDate: Option[Instant] = 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,
customValueIri = maybeCustomValueIri,
customValueUUID = maybeCustomValueUUID,
customValueCreationDate = maybeCustomValueCreationDate,
permissions = maybePermissions
)
}
Expand Down
Expand Up @@ -116,8 +116,10 @@ object CreateValueRequestV2 extends KnoraJsonLDRequestReaderV2[CreateValueReques

// Get the custom value IRI if provided.
maybeCustomValueIri: Option[SmartIri] = jsonLDObject.maybeIDAsKnoraDataIri

// Get the custom value UUID if provided.
SepidehAlassi marked this conversation as resolved.
Show resolved Hide resolved
maybeCustomUUID: Option[UUID] = jsonLDObject.maybeUUID

// Get the value's creation date.
maybeCreationDate: Option[Instant] = jsonLDObject.maybeDatatypeValueInObject(
key = OntologyConstants.KnoraApiV2Complex.CreationDate,
Expand Down Expand Up @@ -456,6 +458,7 @@ case class GenerateSparqlToCreateMultipleValuesRequestV2(resourceIri: IRI,
case class GenerateSparqlForValueInNewResourceV2(valueContent: ValueContentV2,
customValueIri: Option[SmartIri],
customValueUUID: Option[UUID],
customValueCreationDate: Option[Instant],
permissions: String) extends IOValueV2

/**
Expand Down
Expand Up @@ -927,6 +927,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt
valueContent = valueToCreate.valueContent,
customValueIri = valueToCreate.customValueIri,
customValueUUID = valueToCreate.customValueUUID,
customValueCreationDate = valueToCreate.customValueCreationDate,
permissions = validatedCustomPermissions
)

Expand All @@ -937,6 +938,7 @@ class ResourcesResponderV2(responderData: ResponderData) extends ResponderWithSt
valueContent = valueToCreate.valueContent,
customValueIri = valueToCreate.customValueIri,
customValueUUID = valueToCreate.customValueUUID,
customValueCreationDate = valueToCreate.customValueCreationDate,
permissions = defaultPropertyPermissions(propertyIri)
)
}
Expand Down
Expand Up @@ -274,6 +274,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
}

val triplestoreUpdateFuture: Future[CreateValueResponseV2] = for {

// Don't allow anonymous users to create values.
_ <- Future {
if (createValueRequest.requestingUser.isAnonymousUser) {
Expand Down Expand Up @@ -394,15 +395,20 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
valuePermissions: String,
requestingUser: UserADM): Future[UnverifiedValueV2] = {
for {
// Make an IRI and a UUID for the new value.

// Make an IRI for the new value.
newValueIri: IRI <- maybeValueIri match {
case Some(customValueIri) => FastFuture.successful(customValueIri.toString)
case None => FastFuture.successful(stringFormatter.makeRandomValueIri(resourceInfo.resourceIri))
}

// Make a UUID for the new value
newValueUUID: UUID = maybeValueUUID match {
SepidehAlassi marked this conversation as resolved.
Show resolved Hide resolved
case Some(customValueUUID) => customValueUUID
case None => UUID.randomUUID
}

// Make a creation date for the new value
creationDate: Instant = maybeValueCreationDate match {
case Some(customCreationDate) => customCreationDate
case None => Instant.now
Expand Down Expand Up @@ -566,7 +572,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
propertyIri = propertyIri,
valueToCreate = valueToCreate,
valueHasOrder = valueHasOrder,
creationDate = createMultipleValuesRequest.creationDate,
resourceCreationDate = createMultipleValuesRequest.creationDate,
requestingUser = createMultipleValuesRequest.requestingUser
)
}
Expand All @@ -588,30 +594,40 @@ 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.
* @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 resourceCreationDate the timestamp to be used as the value creation time.
SepidehAlassi marked this conversation as resolved.
Show resolved Hide resolved
* @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,
valueHasOrder: Int,
creationDate: Instant,
resourceCreationDate: Instant,
requestingUser: UserADM): InsertSparqlWithUnverifiedValue = {
// Make an IRI and a UUID for the new value.

// Make an IRI for the new value.
val newValueIri: IRI = valueToCreate.customValueIri match {
case Some(customValueIri) => customValueIri.toString
case None => stringFormatter.makeRandomValueIri(resourceIri)
}

// Make a UUID for the new value.
val newValueUUID: UUID = valueToCreate.customValueUUID match {
case Some(customValueUUID) => customValueUUID
case None => UUID.randomUUID
}

// Make a creation date for the value. If a custom creation date is given for a value, consider that otherwise
// use resource creation date for the value.
val valueCreationDate: Instant = valueToCreate.customValueCreationDate match {
case Some(customValueCreationDate) => customValueCreationDate
case None => resourceCreationDate
}

// Generate the SPARQL.
val insertSparql: String = valueToCreate.valueContent match {
case linkValueContentV2: LinkValueContentV2 =>
Expand All @@ -638,7 +654,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
queries.sparql.v2.txt.generateInsertStatementsForCreateLink(
resourceIri = resourceIri,
linkUpdate = sparqlTemplateLinkUpdate,
creationDate = creationDate,
creationDate = valueCreationDate,
newValueUUID = newValueUUID,
maybeComment = valueToCreate.valueContent.comment,
maybeValueHasOrder = Some(valueHasOrder),
Expand All @@ -656,7 +672,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
linkUpdates = Seq.empty[SparqlTemplateLinkUpdate], // This is empty because we have to generate SPARQL for standoff links separately.
valueCreator = requestingUser.id,
valuePermissions = valueToCreate.permissions,
creationDate = creationDate,
creationDate = valueCreationDate,
maybeValueHasOrder = Some(valueHasOrder),
stringFormatter = stringFormatter
).toString()
Expand All @@ -669,7 +685,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde
newValueUUID = newValueUUID,
valueContent = valueToCreate.valueContent.unescape,
permissions = valueToCreate.permissions,
creationDate = creationDate
creationDate = valueCreationDate
)
)
}
Expand Down
Expand Up @@ -128,6 +128,10 @@ class ResourcesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData)
filePath = TestDataFilePath.makeJsonPath("create-resource-with-custom-value-UUID-request"),
text = SharedTestDataADM.createResourceWithCustomValueUUID(SharedTestDataADM.customValueIRI_withResourceIriAndValueIRIAndValueUUID)
),
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-resource-with-custom-value-creationDate-request"),
text = SharedTestDataADM.createResourceWithCustomValueCreationDate(SharedTestDataADM.customValueCreationDate)
),
TestDataFileContent(
filePath = TestDataFilePath.makeJsonPath("create-resource-with-custom-resourceIRI-creationDate-ValueIri-ValueUUID-request"),
text = SharedTestDataADM.createResourceWithCustomResourceIriAndCreationDateAndValueWithCustomIRIAndUUID(
Expand Down