From cbed60134d8a2864b6ce55ba244002893b7a3d8e Mon Sep 17 00:00:00 2001 From: Benjamin Geer Date: Thu, 27 Feb 2020 12:43:06 +0100 Subject: [PATCH] feat(api-v2): Return value UUID on value creation and update (#1602) --- .../paradox/03-apis/api-v2/editing-values.md | 25 +- .../org/knora/webapi/SharedTestDataADM.scala | 4 + .../valuemessages/ValueMessagesV2.scala | 1261 ++++++------- .../responders/v2/ValuesResponderV2.scala | 153 +- .../webapi/routing/v2/ValuesRouteV2.scala | 44 +- .../knora/webapi/util/StringFormatter.scala | 1640 +++++++++-------- .../sparql/v2/changeLinkTarget.scala.txt | 3 +- .../queries/sparql/v2/createLink.scala.txt | 3 + .../queries/sparql/v2/createValue.scala.txt | 3 + .../queries/sparql/v2/deleteValue.scala.txt | 11 +- ...ateInsertStatementsForCreateLink.scala.txt | 4 +- ...teInsertStatementsForCreateValue.scala.txt | 5 +- .../webapi/e2e/v2/ValuesRouteV2E2ESpec.scala | 26 +- .../responders/v2/ValuesResponderV2Spec.scala | 32 +- 14 files changed, 1676 insertions(+), 1538 deletions(-) diff --git a/docs/src/paradox/03-apis/api-v2/editing-values.md b/docs/src/paradox/03-apis/api-v2/editing-values.md index 64b2e9d652..17d9008e06 100644 --- a/docs/src/paradox/03-apis/api-v2/editing-values.md +++ b/docs/src/paradox/03-apis/api-v2/editing-values.md @@ -94,8 +94,12 @@ If permissions are not given, configurable default permissions are used To create a value, the user must have **modify permission** on the containing resource. -The response is a JSON-LD document containing only `@id` and `@type`, returning the IRI -and type of the value that was created. +The response is a JSON-LD document containing: + +- `@id`: the IRI of the value that was created. +- `@type`: the value's type. +- `knora-api:valueHasUUID`, the value's UUID, which remains stable across value versions + (except for link values, as explained below). ### Creating a Link Between Resources @@ -127,6 +131,15 @@ we can create a link like this: As with ordinary values, permissions on links can be specified by adding `knora-api:hasPermissions`. +The response is a JSON-LD document containing: + +- `@id`: the IRI of the value that was created. +- `@type`: the value's type. +- `knora-api:valueHasUUID`, the value's UUID, which remains stable across value versions, + unless the link is changed to point to a different resource, in which case it is + considered a new link and gets a new UUID. Changing a link's metadata, without + changing its target, creates a new version of the link value with the same UUID. + ### Creating a Text Value Without Standoff Markup Use the predicate `knora-api:valueAsString` of `knora-api:TextValue`: @@ -357,6 +370,14 @@ and type of the new value version. If you submit an outdated value ID in a request to update a value, the response will be an HTTP 404 (Not Found) error. +The response to a value update request contains: + +- `@id`: the IRI of the value that was created. +- `@type`: the value's type. +- `knora-api:valueHasUUID`, the value's UUID, which remains stable across value versions, + unless the value is a link value and is changed to point to a different resource, in which + case it is considered a new link and gets a new UUID. + ## Deleting a Value Knora does not normally delete values; instead, it marks them as deleted, which means diff --git a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala index 7badf8c77f..c317e3927f 100644 --- a/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/SharedTestDataADM.scala @@ -21,6 +21,7 @@ package org.knora.webapi import java.net.URLEncoder import java.time.Instant +import java.util.UUID import org.knora.webapi.SharedOntologyTestDataADM._ import org.knora.webapi.messages.admin.responder.groupsmessages.GroupADM @@ -1899,4 +1900,7 @@ object SharedTestDataADM { val treeListNode: IRI = "http://rdfh.ch/lists/0001/treeList01" val otherTreeList: IRI = "http://rdfh.ch/lists/0001/otherTreeList" + + val testResponseValueIri: IRI = "http://rdfh.ch/0001/_GlNQXdYRTyQPhpdh76U1w/values/OGbYaSgNSUCKQtmn9suXlw" + val testResponseValueUUID: UUID = UUID.fromString("84a3af57-ee99-486f-aa9c-e4ca1d19a57d") } diff --git a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala index 81583f836e..de0d8c6157 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/v2/responder/valuemessages/ValueMessagesV2.scala @@ -45,40 +45,40 @@ import org.knora.webapi.util.standoff.{StandoffTagUtilV2, XMLUtil} import scala.concurrent.{ExecutionContext, Future} /** - * A tagging trait for requests handled by [[org.knora.webapi.responders.v2.ValuesResponderV2]]. - */ + * A tagging trait for requests handled by [[org.knora.webapi.responders.v2.ValuesResponderV2]]. + */ sealed trait ValuesResponderRequestV2 extends KnoraRequestV2 /** - * Requests the creation of a value. - * - * @param createValue a [[CreateValueV2]] representing the value to be created. A successful response will be - * a [[CreateValueResponseV2]]. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ + * Requests the creation of a value. + * + * @param createValue a [[CreateValueV2]] representing the value to be created. A successful response will be + * a [[CreateValueResponseV2]]. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ case class CreateValueRequestV2(createValue: CreateValueV2, requestingUser: UserADM, apiRequestID: UUID) extends ValuesResponderRequestV2 /** - * Constructs [[CreateValueRequestV2]] instances based on JSON-LD input. - */ + * Constructs [[CreateValueRequestV2]] instances based on JSON-LD input. + */ object CreateValueRequestV2 extends KnoraJsonLDRequestReaderV2[CreateValueRequestV2] { /** - * Converts JSON-LD input to a [[CreateValueRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a case class instance representing the input. - */ + * Converts JSON-LD input to a [[CreateValueRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a case class instance representing the input. + */ override def fromJsonLD(jsonLDDocument: JsonLDDocument, apiRequestID: UUID, requestingUser: UserADM, @@ -135,16 +135,20 @@ object CreateValueRequestV2 extends KnoraJsonLDRequestReaderV2[CreateValueReques } /** - * Represents a successful response to a [[CreateValueRequestV2]]. - * - * @param valueIri the IRI of the value that was created. - * @param valueType the type of the value that was created. - * @param projectADM the project in which the value was created. - */ + * Represents a successful response to a [[CreateValueRequestV2]]. + * + * @param valueIri the IRI of the value that was created. + * @param valueType the type of the value that was created. + * @param valueUUID the value's UUID. + * @param projectADM the project in which the value was created. + */ case class CreateValueResponseV2(valueIri: IRI, valueType: SmartIri, + valueUUID: UUID, projectADM: ProjectADM) extends KnoraResponseV2 with UpdateResultInProject { override def toJsonLDDocument(targetSchema: ApiV2Schema, settings: SettingsImpl, schemaOptions: Set[SchemaOption]): JsonLDDocument = { + implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance + if (targetSchema != ApiV2Complex) { throw AssertionException(s"CreateValueResponseV2 can only be returned in the complex schema") } @@ -153,7 +157,13 @@ case class CreateValueResponseV2(valueIri: IRI, body = JsonLDObject( Map( JsonLDConstants.ID -> JsonLDString(valueIri), - JsonLDConstants.TYPE -> JsonLDString(valueType.toOntologySchema(ApiV2Complex).toString) + JsonLDConstants.TYPE -> JsonLDString(valueType.toOntologySchema(ApiV2Complex).toString), + OntologyConstants.KnoraApiV2Complex.ValueHasUUID -> JsonLDString(stringFormatter.base64EncodeUuid(valueUUID)) + ) + ), + context = JsonLDUtil.makeContext( + fixedPrefixes = Map( + OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion ) ) ) @@ -161,34 +171,34 @@ case class CreateValueResponseV2(valueIri: IRI, } /** - * Requests an update to a value, i.e. the creation of a new version of an existing value. - * - * @param updateValue an [[UpdateValueV2]] representing the new version of the value. A successful response will be - * an [[UpdateValueResponseV2]]. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ + * Requests an update to a value, i.e. the creation of a new version of an existing value. + * + * @param updateValue an [[UpdateValueV2]] representing the new version of the value. A successful response will be + * an [[UpdateValueResponseV2]]. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ case class UpdateValueRequestV2(updateValue: UpdateValueV2, requestingUser: UserADM, apiRequestID: UUID) extends ValuesResponderRequestV2 /** - * Constructs [[UpdateValueRequestV2]] instances based on JSON-LD input. - */ + * Constructs [[UpdateValueRequestV2]] instances based on JSON-LD input. + */ object UpdateValueRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateValueRequestV2] { /** - * Converts JSON-LD input to a [[CreateValueRequestV2]]. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a case class instance representing the input. - */ + * Converts JSON-LD input to a [[CreateValueRequestV2]]. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a case class instance representing the input. + */ override def fromJsonLD(jsonLDDocument: JsonLDDocument, apiRequestID: UUID, requestingUser: UserADM, @@ -268,16 +278,20 @@ object UpdateValueRequestV2 extends KnoraJsonLDRequestReaderV2[UpdateValueReques } /** - * Represents a successful response to an [[UpdateValueRequestV2]]. - * - * @param valueIri the IRI of the value version that was created. - * @param valueType the type of the value that was updated. - * @param projectADM the project in which the value was updated. - */ + * Represents a successful response to an [[UpdateValueRequestV2]]. + * + * @param valueIri the IRI of the value version that was created. + * @param valueType the type of the value that was updated. + * @param valueUUID the value's UUID. + * @param projectADM the project in which the value was updated. + */ case class UpdateValueResponseV2(valueIri: IRI, valueType: SmartIri, + valueUUID: UUID, projectADM: ProjectADM) extends KnoraResponseV2 with UpdateResultInProject { override def toJsonLDDocument(targetSchema: ApiV2Schema, settings: SettingsImpl, schemaOptions: Set[SchemaOption]): JsonLDDocument = { + implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance + if (targetSchema != ApiV2Complex) { throw AssertionException(s"UpdateValueResponseV2 can only be returned in the complex schema") } @@ -286,7 +300,13 @@ case class UpdateValueResponseV2(valueIri: IRI, body = JsonLDObject( Map( JsonLDConstants.ID -> JsonLDString(valueIri), - JsonLDConstants.TYPE -> JsonLDString(valueType.toOntologySchema(ApiV2Complex).toString) + JsonLDConstants.TYPE -> JsonLDString(valueType.toOntologySchema(ApiV2Complex).toString), + OntologyConstants.KnoraApiV2Complex.ValueHasUUID -> JsonLDString(stringFormatter.base64EncodeUuid(valueUUID)) + ) + ), + context = JsonLDUtil.makeContext( + fixedPrefixes = Map( + OntologyConstants.KnoraApi.KnoraApiOntologyLabel -> OntologyConstants.KnoraApiV2Complex.KnoraApiV2PrefixExpansion ) ) ) @@ -294,17 +314,17 @@ case class UpdateValueResponseV2(valueIri: IRI, } /** - * Requests that a value is marked as deleted. A successful response will be a [[SuccessResponseV2]]. - * - * @param resourceIri the IRI of the containing resource. - * @param resourceClassIri the IRI of the resource class. - * @param propertyIri the IRI of the property pointing to the value to be marked as deleted. - * @param valueIri the IRI of the value to be marked as deleted. - * @param valueTypeIri the IRI of the value class. - * @param deleteComment an optional comment explaining why the value is being marked as deleted. - * @param requestingUser the user making the request. - * @param apiRequestID the API request ID. - */ + * Requests that a value is marked as deleted. A successful response will be a [[SuccessResponseV2]]. + * + * @param resourceIri the IRI of the containing resource. + * @param resourceClassIri the IRI of the resource class. + * @param propertyIri the IRI of the property pointing to the value to be marked as deleted. + * @param valueIri the IRI of the value to be marked as deleted. + * @param valueTypeIri the IRI of the value class. + * @param deleteComment an optional comment explaining why the value is being marked as deleted. + * @param requestingUser the user making the request. + * @param apiRequestID the API request ID. + */ case class DeleteValueRequestV2(resourceIri: IRI, resourceClassIri: SmartIri, propertyIri: SmartIri, @@ -316,19 +336,19 @@ case class DeleteValueRequestV2(resourceIri: IRI, object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueRequestV2] { /** - * Converts JSON-LD input into a case class instance. - * - * @param jsonLDDocument the JSON-LD input. - * @param apiRequestID the UUID of the API request. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param settings the application settings. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a case class instance representing the input. - */ + * Converts JSON-LD input into a case class instance. + * + * @param jsonLDDocument the JSON-LD input. + * @param apiRequestID the UUID of the API request. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param settings the application settings. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a case class instance representing the input. + */ override def fromJsonLD(jsonLDDocument: JsonLDDocument, apiRequestID: UUID, requestingUser: UserADM, @@ -388,28 +408,28 @@ object DeleteValueRequestV2 extends KnoraJsonLDRequestReaderV2[DeleteValueReques } /** - * Requests SPARQL for creating multiple values in a new, empty resource. The resource ''must'' be a new, empty - * resource, i.e. it must have no values. This message is used only internally by Knora, and is not part of the Knora - * v1 API. All pre-update checks must already have been performed before this message is sent. Specifically, the - * sender must ensure that: - * - * - The requesting user has permission to add values to the resource. - * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed - * to point to it. - * - The resource class has a suitable cardinality for each submitted value. - * - All required values are provided. - * - Redundant values are not submitted. - * - Any custom permissions in values have been validated and correctly formatted. - * - The target resources of link values and standoff links exist, if they are expected to exist. - * - The list nodes referred to by list values exist. - * - * A successful response will be a [[GenerateSparqlToCreateMultipleValuesResponseV2]]. - * - * @param resourceIri the IRI of the resource in which values are to be created. - * @param values a map of property IRIs to the values to be added for each property. - * @param creationDate an xsd:dateTimeStamp that will be attached to the values. - * @param requestingUser the user that is creating the values. - */ + * Requests SPARQL for creating multiple values in a new, empty resource. The resource ''must'' be a new, empty + * resource, i.e. it must have no values. This message is used only internally by Knora, and is not part of the Knora + * v1 API. All pre-update checks must already have been performed before this message is sent. Specifically, the + * sender must ensure that: + * + * - The requesting user has permission to add values to the resource. + * - Each submitted value is consistent with the `knora-base:objectClassConstraint` of the property that is supposed + * to point to it. + * - The resource class has a suitable cardinality for each submitted value. + * - All required values are provided. + * - Redundant values are not submitted. + * - Any custom permissions in values have been validated and correctly formatted. + * - The target resources of link values and standoff links exist, if they are expected to exist. + * - The list nodes referred to by list values exist. + * + * A successful response will be a [[GenerateSparqlToCreateMultipleValuesResponseV2]]. + * + * @param resourceIri the IRI of the resource in which values are to be created. + * @param values a map of property IRIs to the values to be added for each property. + * @param creationDate an xsd:dateTimeStamp that will be attached to the values. + * @param requestingUser the user that is creating the values. + */ case class GenerateSparqlToCreateMultipleValuesRequestV2(resourceIri: IRI, values: Map[SmartIri, Seq[GenerateSparqlForValueInNewResourceV2]], creationDate: Instant, @@ -421,31 +441,31 @@ case class GenerateSparqlForValueInNewResourceV2(valueContent: ValueContentV2, permissions: String) extends IOValueV2 /** - * Represents a response to a [[GenerateSparqlToCreateMultipleValuesRequestV2]], providing a string that can be - * included in the `INSERT DATA` clause of a SPARQL update operation to create the requested values. - * - * @param insertSparql a string containing statements that must be inserted into the INSERT clause of the SPARQL - * update that will create the values. - * @param unverifiedValues a map of property IRIs to [[UnverifiedValueV2]] objects describing - * the values that should have been created. - */ + * Represents a response to a [[GenerateSparqlToCreateMultipleValuesRequestV2]], providing a string that can be + * included in the `INSERT DATA` clause of a SPARQL update operation to create the requested values. + * + * @param insertSparql a string containing statements that must be inserted into the INSERT clause of the SPARQL + * update that will create the values. + * @param unverifiedValues a map of property IRIs to [[UnverifiedValueV2]] objects describing + * the values that should have been created. + */ case class GenerateSparqlToCreateMultipleValuesResponseV2(insertSparql: String, unverifiedValues: Map[SmartIri, Seq[UnverifiedValueV2]]) /** - * The value of a Knora property in the context of some particular input or output operation. - * Any implementation of `IOValueV2` is an API operation-specific wrapper of a `ValueContentV2`. - */ + * The value of a Knora property in the context of some particular input or output operation. + * Any implementation of `IOValueV2` is an API operation-specific wrapper of a `ValueContentV2`. + */ trait IOValueV2 { def valueContent: ValueContentV2 } /** - * Provides information about the deletion of a resource or value. - * - * @param deleteDate the date when the resource or value was deleted. - * @param maybeDeleteComment the reason why the resource or value was deleted. - */ + * Provides information about the deletion of a resource or value. + * + * @param deleteDate the date when the resource or value was deleted. + * @param maybeDeleteComment the reason why the resource or value was deleted. + */ case class DeletionInfo(deleteDate: Instant, maybeDeleteComment: Option[String]) { def toJsonLDFields(targetSchema: ApiV2Schema): Map[IRI, JsonLDValue] = { @@ -470,69 +490,69 @@ case class DeletionInfo(deleteDate: Instant, } /** - * Represents a Knora value as read from the triplestore. - */ + * Represents a Knora value as read from the triplestore. + */ sealed trait ReadValueV2 extends IOValueV2 { /** - * The IRI of the value. - */ + * The IRI of the value. + */ def valueIri: IRI /** - * The user that created the value. - */ + * The user that created the value. + */ def attachedToUser: IRI /** - * The value's permissions. - */ + * The value's permissions. + */ def permissions: String /** - * The permission that the requesting user has on the value. - */ + * The permission that the requesting user has on the value. + */ def userPermission: EntityPermission /** - * The date when the value was created. - */ + * The date when the value was created. + */ def valueCreationDate: Instant /** - * The UUID shared by all the versions of this value. - */ + * The UUID shared by all the versions of this value. + */ def valueHasUUID: UUID /** - * The content of the value. - */ + * The content of the value. + */ def valueContent: ValueContentV2 /** - * The IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - */ + * The IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + */ def previousValueIri: Option[IRI] /** - * If the value has been marked as deleted, information about its deletion. - */ + * If the value has been marked as deleted, information about its deletion. + */ def deletionInfo: Option[DeletionInfo] /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the schema that the value should be converted to. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the schema that the value should be converted to. + */ def toOntologySchema(targetSchema: ApiV2Schema): ReadValueV2 /** - * Converts this value to JSON-LD. - * - * @param targetSchema the target schema. - * @param settings the application settings. - * @return a JSON-LD representation of this value. - */ + * Converts this value to JSON-LD. + * + * @param targetSchema the target schema. + * @param settings the application settings. + * @return a JSON-LD representation of this value. + */ def toJsonLD(targetSchema: ApiV2Schema, projectADM: ProjectADM, settings: SettingsImpl, schemaOptions: Set[SchemaOption]): JsonLDValue = { implicit val stringFormatter: StringFormatter = StringFormatter.getGeneralInstance @@ -595,22 +615,22 @@ sealed trait ReadValueV2 extends IOValueV2 { } /** - * A text value, or a page of standoff markup attached to a text value, as read from the triplestore. - * - * @param valueIri the IRI of the value. - * @param attachedToUser the user that created the value. - * @param permissions the permissions that the value grants to user groups. - * @param userPermission the permission that the requesting user has on the value. - * @param valueHasUUID the UUID shared by all the versions of this value. - * @param valueContent the content of the value. - * @param valueHasMaxStandoffStartIndex if this text value has standoff markup, the highest - * `knora-base:standoffTagHasEndIndex` - * used in its standoff tags. - * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - * @param deletionInfo if this value has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ + * A text value, or a page of standoff markup attached to a text value, as read from the triplestore. + * + * @param valueIri the IRI of the value. + * @param attachedToUser the user that created the value. + * @param permissions the permissions that the value grants to user groups. + * @param userPermission the permission that the requesting user has on the value. + * @param valueHasUUID the UUID shared by all the versions of this value. + * @param valueContent the content of the value. + * @param valueHasMaxStandoffStartIndex if this text value has standoff markup, the highest + * `knora-base:standoffTagHasEndIndex` + * used in its standoff tags. + * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + * @param deletionInfo if this value has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ case class ReadTextValueV2(valueIri: IRI, attachedToUser: IRI, permissions: String, @@ -622,10 +642,10 @@ case class ReadTextValueV2(valueIri: IRI, previousValueIri: Option[IRI], deletionInfo: Option[DeletionInfo]) extends ReadValueV2 with KnoraReadV2[ReadTextValueV2] { /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ override def toOntologySchema(targetSchema: ApiV2Schema): ReadTextValueV2 = { copy(valueContent = valueContent.toOntologySchema(targetSchema)) } @@ -671,21 +691,21 @@ case class ReadTextValueV2(valueIri: IRI, } /** - * A link value as read from the triplestore. - * - * @param valueIri the IRI of the value. - * @param attachedToUser the user that created the value. - * @param permissions the permissions that the value grants to user groups. - * @param userPermission the permission that the requesting user has on the value. - * @param valueHasUUID the UUID shared by all the versions of this value. - * @param valueContent the content of the value. - * @param valueHasRefCount if this is a link value, its reference count. Not returned in API responses, but needed - * here for testing. - * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - * @param deletionInfo if this value has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ + * A link value as read from the triplestore. + * + * @param valueIri the IRI of the value. + * @param attachedToUser the user that created the value. + * @param permissions the permissions that the value grants to user groups. + * @param userPermission the permission that the requesting user has on the value. + * @param valueHasUUID the UUID shared by all the versions of this value. + * @param valueContent the content of the value. + * @param valueHasRefCount if this is a link value, its reference count. Not returned in API responses, but needed + * here for testing. + * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + * @param deletionInfo if this value has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ case class ReadLinkValueV2(valueIri: IRI, attachedToUser: IRI, permissions: String, @@ -697,29 +717,29 @@ case class ReadLinkValueV2(valueIri: IRI, previousValueIri: Option[IRI] = None, deletionInfo: Option[DeletionInfo]) extends ReadValueV2 with KnoraReadV2[ReadLinkValueV2] { /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ override def toOntologySchema(targetSchema: ApiV2Schema): ReadLinkValueV2 = { copy(valueContent = valueContent.toOntologySchema(targetSchema)) } } /** - * A non-text, non-link value as read from the triplestore. - * - * @param valueIri the IRI of the value. - * @param attachedToUser the user that created the value. - * @param permissions the permissions that the value grants to user groups. - * @param userPermission the permission that the requesting user has on the value. - * @param valueHasUUID the UUID shared by all the versions of this value. - * @param valueContent the content of the value. - * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed - * here for testing. - * @param deletionInfo if this value has been marked as deleted, provides the date when it was - * deleted and the reason why it was deleted. - */ + * A non-text, non-link value as read from the triplestore. + * + * @param valueIri the IRI of the value. + * @param attachedToUser the user that created the value. + * @param permissions the permissions that the value grants to user groups. + * @param userPermission the permission that the requesting user has on the value. + * @param valueHasUUID the UUID shared by all the versions of this value. + * @param valueContent the content of the value. + * @param previousValueIri the IRI of the previous version of this value. Not returned in API responses, but needed + * here for testing. + * @param deletionInfo if this value has been marked as deleted, provides the date when it was + * deleted and the reason why it was deleted. + */ case class ReadOtherValueV2(valueIri: IRI, attachedToUser: IRI, permissions: String, @@ -730,24 +750,24 @@ case class ReadOtherValueV2(valueIri: IRI, previousValueIri: Option[IRI], deletionInfo: Option[DeletionInfo]) extends ReadValueV2 with KnoraReadV2[ReadOtherValueV2] { /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ override def toOntologySchema(targetSchema: ApiV2Schema): ReadOtherValueV2 = { copy(valueContent = valueContent.toOntologySchema(targetSchema)) } } /** - * Represents a Knora value to be created in an existing resource. - * - * @param resourceIri the resource the new value should be attached to. - * @param resourceClassIri the resource class that the client believes the resource belongs to. - * @param propertyIri the property of the new value. If the client wants to create a link, this must be a link value property. - * @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]]. - * @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults. - */ + * Represents a Knora value to be created in an existing resource. + * + * @param resourceIri the resource the new value should be attached to. + * @param resourceClassIri the resource class that the client believes the resource belongs to. + * @param propertyIri the property of the new value. If the client wants to create a link, this must be a link value property. + * @param valueContent the content of the new value. If the client wants to create a link, this must be a [[LinkValueContentV2]]. + * @param permissions the permissions to be given to the new value. If not provided, these will be taken from defaults. + */ case class CreateValueV2(resourceIri: IRI, resourceClassIri: SmartIri, propertyIri: SmartIri, @@ -756,41 +776,41 @@ case class CreateValueV2(resourceIri: IRI, /** - * A trait for classes representing information to be updated in a value. - */ + * A trait for classes representing information to be updated in a value. + */ trait UpdateValueV2 { /** - * The IRI of the resource containing the value. - */ + * The IRI of the resource containing the value. + */ val resourceIri: IRI /** - * The external IRI of the resource class. - */ + * The external IRI of the resource class. + */ val resourceClassIri: SmartIri /** - * The external IRI of the property pointing to the value. - */ + * The external IRI of the property pointing to the value. + */ val propertyIri: SmartIri /** - * The value IRI. - */ + * The value IRI. + */ val valueIri: IRI } /** - * A new version of a value of a Knora property to be created. - * - * @param resourceIri the resource that the current value version is attached to. - * @param resourceClassIri the resource class that the client believes the resource belongs to. - * @param propertyIri the property that the client believes points to the value. If the value is a link value, - * this must be a link value property. - * @param valueIri the IRI of the value to be updated. - * @param valueContent the content of the new version of the value. - * @param permissions the permissions to be attached to the new value version. - */ + * A new version of a value of a Knora property to be created. + * + * @param resourceIri the resource that the current value version is attached to. + * @param resourceClassIri the resource class that the client believes the resource belongs to. + * @param propertyIri the property that the client believes points to the value. If the value is a link value, + * this must be a link value property. + * @param valueIri the IRI of the value to be updated. + * @param valueContent the content of the new version of the value. + * @param permissions the permissions to be attached to the new value version. + */ case class UpdateValueContentV2(resourceIri: IRI, resourceClassIri: SmartIri, propertyIri: SmartIri, @@ -799,16 +819,16 @@ case class UpdateValueContentV2(resourceIri: IRI, permissions: Option[String] = None) extends IOValueV2 with UpdateValueV2 /** - * New permissions for a value. - * - * @param resourceIri the resource that the current value version is attached to. - * @param resourceClassIri the resource class that the client believes the resource belongs to. - * @param propertyIri the property that the client believes points to the value. If the value is a link value, - * this must be a link value property. - * @param valueIri the IRI of the value to be updated. - * @param valueType the IRI of the value type. - * @param permissions the permissions to be attached to the new value version. - */ + * New permissions for a value. + * + * @param resourceIri the resource that the current value version is attached to. + * @param resourceClassIri the resource class that the client believes the resource belongs to. + * @param propertyIri the property that the client believes points to the value. If the value is a link value, + * this must be a link value property. + * @param valueIri the IRI of the value to be updated. + * @param valueType the IRI of the value type. + * @param permissions the permissions to be attached to the new value version. + */ case class UpdateValuePermissionsV2(resourceIri: IRI, resourceClassIri: SmartIri, propertyIri: SmartIri, @@ -817,97 +837,102 @@ case class UpdateValuePermissionsV2(resourceIri: IRI, permissions: String) extends UpdateValueV2 /** - * The IRI and content of a new value or value version whose existence in the triplestore needs to be verified. - * - * @param newValueIri the IRI that was assigned to the new value. - * @param valueContent the content of the new value (unescaped, as it would be read from the triplestore). - * @param permissions the permissions of the new value. - * @param creationDate the new value's creation date. - */ -case class UnverifiedValueV2(newValueIri: IRI, valueContent: ValueContentV2, permissions: String, creationDate: Instant) + * The IRI and content of a new value or value version whose existence in the triplestore needs to be verified. + * + * @param newValueIri the IRI that was assigned to the new value. + * @param newValueUUID the UUID attached to the new value. + * @param valueContent the content of the new value (unescaped, as it would be read from the triplestore). + * @param permissions the permissions of the new value. + * @param creationDate the new value's creation date. + */ +case class UnverifiedValueV2(newValueIri: IRI, + newValueUUID: UUID, + valueContent: ValueContentV2, + permissions: String, + creationDate: Instant) /** - * The content of the value of a Knora property. - */ + * The content of the value of a Knora property. + */ sealed trait ValueContentV2 extends KnoraContentV2[ValueContentV2] { protected implicit def stringFormatter: StringFormatter = StringFormatter.getGeneralInstance /** - * The IRI of the value type. - */ + * The IRI of the value type. + */ def valueType: SmartIri /** - * The string representation of this `ValueContentV2`. - */ + * The string representation of this `ValueContentV2`. + */ def valueHasString: String /** - * a comment on this [[ValueContentV2]], if any. - */ + * a comment on this [[ValueContentV2]], if any. + */ def comment: Option[String] /** - * Converts this value to the specified ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this value to the specified ontology schema. + * + * @param targetSchema the target schema. + */ def toOntologySchema(targetSchema: OntologySchema): ValueContentV2 /** - * A representation of the `ValueContentV2` as a [[JsonLDValue]]. - * - * @param targetSchema the API schema to be used. - * @param settings the configuration options. - * @return a [[JsonLDValue]] that can be used to generate JSON-LD representing this value. - */ + * A representation of the `ValueContentV2` as a [[JsonLDValue]]. + * + * @param targetSchema the API schema to be used. + * @param settings the configuration options. + * @return a [[JsonLDValue]] that can be used to generate JSON-LD representing this value. + */ def toJsonLDValue(targetSchema: ApiV2Schema, projectADM: ProjectADM, settings: SettingsImpl, schemaOptions: Set[SchemaOption]): JsonLDValue /** - * Undoes the SPARQL-escaping of strings in this [[ValueContentV2]]. - * - * @return the same [[ValueContentV2]] with its strings unescaped. - */ + * Undoes the SPARQL-escaping of strings in this [[ValueContentV2]]. + * + * @return the same [[ValueContentV2]] with its strings unescaped. + */ def unescape: ValueContentV2 /** - * Returns `true` if creating this [[ValueContentV2]] as a new value would duplicate the specified other value. - * This means that if resource `R` has property `P` with value `V1`, and `V1` would duplicate `V2`, the API server - * should not add another instance of property `P` with value `V2`. It does not necessarily mean that `V1 == V2`. - * - * @param that a [[ValueContentV2]] in the same resource, as read from the triplestore. - * @return `true` if `other` would duplicate `this`. - */ + * Returns `true` if creating this [[ValueContentV2]] as a new value would duplicate the specified other value. + * This means that if resource `R` has property `P` with value `V1`, and `V1` would duplicate `V2`, the API server + * should not add another instance of property `P` with value `V2`. It does not necessarily mean that `V1 == V2`. + * + * @param that a [[ValueContentV2]] in the same resource, as read from the triplestore. + * @return `true` if `other` would duplicate `this`. + */ def wouldDuplicateOtherValue(that: ValueContentV2): Boolean /** - * Returns `true` if this [[ValueContentV2]] would be redundant as a new version of an existing value. This means - * that if resource `R` has property `P` with value `V1`, and `V2` would duplicate `V1`, we should not add `V2` - * as a new version of `V1`. It does not necessarily mean that `V1 == V2`. - * - * @param currentVersion the current version of the value, as read from the triplestore. - * @return `true` if this [[ValueContentV2]] would duplicate `currentVersion`. - */ + * Returns `true` if this [[ValueContentV2]] would be redundant as a new version of an existing value. This means + * that if resource `R` has property `P` with value `V1`, and `V2` would duplicate `V1`, we should not add `V2` + * as a new version of `V1`. It does not necessarily mean that `V1 == V2`. + * + * @param currentVersion the current version of the value, as read from the triplestore. + * @return `true` if this [[ValueContentV2]] would duplicate `currentVersion`. + */ def wouldDuplicateCurrentVersion(currentVersion: ValueContentV2): Boolean } /** - * A trait for objects that can convert JSON-LD objects into value content objects (subclasses of [[ValueContentV2]]). - * - * @tparam C a subclass of [[ValueContentV2]]. - */ + * A trait for objects that can convert JSON-LD objects into value content objects (subclasses of [[ValueContentV2]]). + * + * @tparam C a subclass of [[ValueContentV2]]. + */ trait ValueContentReaderV2[C <: ValueContentV2] { /** - * Converts a JSON-LD object to a subclass of [[ValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a subclass of [[ValueContentV2]]. - */ + * Converts a JSON-LD object to a subclass of [[ValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a subclass of [[ValueContentV2]]. + */ def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -921,20 +946,20 @@ trait ValueContentReaderV2[C <: ValueContentV2] { } /** - * Generates instances of value content classes (subclasses of [[ValueContentV2]]) from JSON-LD input. - */ + * Generates instances of value content classes (subclasses of [[ValueContentV2]]) from JSON-LD input. + */ object ValueContentV2 extends ValueContentReaderV2[ValueContentV2] { /** - * Converts a JSON-LD object to a [[ValueContentV2]]. - * - * @param jsonLDObject a JSON-LD object representing a value. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[ValueContentV2]]. - */ + * Converts a JSON-LD object to a [[ValueContentV2]]. + * + * @param jsonLDObject a JSON-LD object representing a value. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[ValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1002,15 +1027,15 @@ object ValueContentV2 extends ValueContentReaderV2[ValueContentV2] { } /** - * Represents a Knora date value. - * - * @param valueHasStartJDN the start of the date as JDN. - * @param valueHasEndJDN the end of the date as JDN. - * @param valueHasStartPrecision the precision of the start date. - * @param valueHasEndPrecision the precision of the end date. - * @param valueHasCalendar the calendar of the date. - * @param comment a comment on this [[DateValueContentV2]], if any. - */ + * Represents a Knora date value. + * + * @param valueHasStartJDN the start of the date as JDN. + * @param valueHasEndJDN the end of the date as JDN. + * @param valueHasStartPrecision the precision of the start date. + * @param valueHasEndPrecision the precision of the end date. + * @param valueHasCalendar the calendar of the date. + * @param comment a comment on this [[DateValueContentV2]], if any. + */ case class DateValueContentV2(ontologySchema: OntologySchema, valueHasStartJDN: Int, valueHasEndJDN: Int, @@ -1110,15 +1135,15 @@ case class DateValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[DateValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[DateValueContentV2]] objects based on JSON-LD input. + */ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { /** - * Parses a string representing a date range in API v2 simple format. - * - * @param dateStr the string to be parsed. - * @return a [[DateValueContentV2]] representing the date range. - */ + * Parses a string representing a date range in API v2 simple format. + * + * @param dateStr the string to be parsed. + * @return a [[DateValueContentV2]] representing the date range. + */ def parse(dateStr: String): DateValueContentV2 = { val dateRange: CalendarDateRangeV2 = CalendarDateRangeV2.parse(dateStr) val (startJDN: Int, endJDN: Int) = dateRange.toJulianDayRange @@ -1134,16 +1159,16 @@ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { } /** - * Converts a JSON-LD object to a [[DateValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[DateValueContentV2]]. - */ + * Converts a JSON-LD object to a [[DateValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[DateValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1233,25 +1258,25 @@ object DateValueContentV2 extends ValueContentReaderV2[DateValueContentV2] { } /** - * Represents a [[StandoffTagV2]] for a standoff tag of a certain type (standoff tag class) that is about to be created in the triplestore. - * - * @param standoffNode the standoff node to be created. - * @param standoffTagInstanceIri the standoff node's IRI. - * @param startParentIri the IRI of the parent of the start tag. - * @param endParentIri the IRI of the parent of the end tag, if any. - */ + * Represents a [[StandoffTagV2]] for a standoff tag of a certain type (standoff tag class) that is about to be created in the triplestore. + * + * @param standoffNode the standoff node to be created. + * @param standoffTagInstanceIri the standoff node's IRI. + * @param startParentIri the IRI of the parent of the start tag. + * @param endParentIri the IRI of the parent of the end tag, if any. + */ case class CreateStandoffTagV2InTriplestore(standoffNode: StandoffTagV2, standoffTagInstanceIri: IRI, startParentIri: Option[IRI] = None, endParentIri: Option[IRI] = None) /** - * Represents a Knora text value, or a page of standoff markup that will be included in a text value. - * - * @param maybeValueHasString the string representation of this text value, if available. - * @param standoff the standoff markup attached to the text value, if any. - * @param mappingIri the IRI of the [[MappingXMLtoStandoff]] used by default with the text value, if any. - * @param mapping the [[MappingXMLtoStandoff]] used by default with the text value, if any. - * @param comment a comment on this [[TextValueContentV2]], if any. - */ + * Represents a Knora text value, or a page of standoff markup that will be included in a text value. + * + * @param maybeValueHasString the string representation of this text value, if available. + * @param standoff the standoff markup attached to the text value, if any. + * @param mappingIri the IRI of the [[MappingXMLtoStandoff]] used by default with the text value, if any. + * @param mapping the [[MappingXMLtoStandoff]] used by default with the text value, if any. + * @param comment a comment on this [[TextValueContentV2]], if any. + */ case class TextValueContentV2(ontologySchema: OntologySchema, maybeValueHasString: Option[String], valueHasLanguage: Option[String] = None, @@ -1266,15 +1291,15 @@ case class TextValueContentV2(ontologySchema: OntologySchema, } /** - * Returns the IRIs of any resources that are target of standoff link tags in this text value. - */ + * Returns the IRIs of any resources that are target of standoff link tags in this text value. + */ lazy val standoffLinkTagTargetResourceIris: Set[IRI] = { standoffLinkTagIriAttributes.map(_.value) } /** - * Returns the IRI attributes representing the target IRIs of any standoff links in this text value. - */ + * Returns the IRI attributes representing the target IRIs of any standoff links in this text value. + */ lazy val standoffLinkTagIriAttributes: Set[StandoffTagIriAttributeV2] = { standoff.foldLeft(Set.empty[StandoffTagIriAttributeV2]) { case (acc, standoffTag: StandoffTagV2) => @@ -1294,15 +1319,15 @@ case class TextValueContentV2(ontologySchema: OntologySchema, override def valueHasString: String = maybeValueHasString.getOrElse(throw AssertionException("Text value has no valueHasString")) /** - * The content of the text value without standoff, suitable for returning in API responses. This removes - * INFORMATION SEPARATOR TWO, which is used only internally. - */ + * The content of the text value without standoff, suitable for returning in API responses. This removes + * INFORMATION SEPARATOR TWO, which is used only internally. + */ lazy val valueHasStringWithoutStandoff: String = valueHasString.replace(StringFormatter.INFORMATION_SEPARATOR_TWO.toString, "") /** - * The maximum start index in the standoff attached to this [[TextValueContentV2]]. This is used - * only when writing a text value to the triplestore. - */ + * The maximum start index in the standoff attached to this [[TextValueContentV2]]. This is used + * only when writing a text value to the triplestore. + */ lazy val computedMaxStandoffStartIndex: Option[Int] = if (standoff.nonEmpty) { Some(standoff.map(_.startIndex).max) } else { @@ -1380,11 +1405,11 @@ case class TextValueContentV2(ontologySchema: OntologySchema, /** - * A convenience method that creates an IRI for each [[StandoffTagV2]] and resolves internal references to standoff node Iris. - * - * @return a list of [[CreateStandoffTagV2InTriplestore]] each representing a [[StandoffTagV2]] object - * along with is standoff tag class and IRI that is going to identify it in the triplestore. - */ + * A convenience method that creates an IRI for each [[StandoffTagV2]] and resolves internal references to standoff node Iris. + * + * @return a list of [[CreateStandoffTagV2InTriplestore]] each representing a [[StandoffTagV2]] object + * along with is standoff tag class and IRI that is going to identify it in the triplestore. + */ def prepareForSparqlInsert(valueIri: IRI): Seq[CreateStandoffTagV2InTriplestore] = { if (standoff.nonEmpty) { @@ -1501,21 +1526,21 @@ case class TextValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[TextValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[TextValueContentV2]] objects based on JSON-LD input. + */ object TextValueContentV2 extends ValueContentReaderV2[TextValueContentV2] { /** - * Converts a JSON-LD object to a [[TextValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[TextValueContentV2]]. - */ + * Converts a JSON-LD object to a [[TextValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[TextValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1579,11 +1604,11 @@ object TextValueContentV2 extends ValueContentReaderV2[TextValueContentV2] { } /** - * Represents a Knora integer value. - * - * @param valueHasInteger the integer value. - * @param comment a comment on this [[IntegerValueContentV2]], if any. - */ + * Represents a Knora integer value. + * + * @param valueHasInteger the integer value. + * @param comment a comment on this [[IntegerValueContentV2]], if any. + */ case class IntegerValueContentV2(ontologySchema: OntologySchema, valueHasInteger: Int, comment: Option[String] = None) extends ValueContentV2 { @@ -1629,21 +1654,21 @@ case class IntegerValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[IntegerValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[IntegerValueContentV2]] objects based on JSON-LD input. + */ object IntegerValueContentV2 extends ValueContentReaderV2[IntegerValueContentV2] { /** - * Converts a JSON-LD object to an [[IntegerValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[IntegerValueContentV2]]. - */ + * Converts a JSON-LD object to an [[IntegerValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[IntegerValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1667,11 +1692,11 @@ object IntegerValueContentV2 extends ValueContentReaderV2[IntegerValueContentV2] } /** - * Represents a Knora decimal value. - * - * @param valueHasDecimal the decimal value. - * @param comment a comment on this [[DecimalValueContentV2]], if any. - */ + * Represents a Knora decimal value. + * + * @param valueHasDecimal the decimal value. + * @param comment a comment on this [[DecimalValueContentV2]], if any. + */ case class DecimalValueContentV2(ontologySchema: OntologySchema, valueHasDecimal: BigDecimal, comment: Option[String] = None) extends ValueContentV2 { @@ -1721,21 +1746,21 @@ case class DecimalValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[DecimalValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[DecimalValueContentV2]] objects based on JSON-LD input. + */ object DecimalValueContentV2 extends ValueContentReaderV2[DecimalValueContentV2] { /** - * Converts a JSON-LD object to a [[DecimalValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[DecimalValueContentV2]]. - */ + * Converts a JSON-LD object to a [[DecimalValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[DecimalValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1763,11 +1788,11 @@ object DecimalValueContentV2 extends ValueContentReaderV2[DecimalValueContentV2] } /** - * Represents a Boolean value. - * - * @param valueHasBoolean the Boolean value. - * @param comment a comment on this [[BooleanValueContentV2]], if any. - */ + * Represents a Boolean value. + * + * @param valueHasBoolean the Boolean value. + * @param comment a comment on this [[BooleanValueContentV2]], if any. + */ case class BooleanValueContentV2(ontologySchema: OntologySchema, valueHasBoolean: Boolean, comment: Option[String] = None) extends ValueContentV2 { @@ -1810,21 +1835,21 @@ case class BooleanValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[BooleanValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[BooleanValueContentV2]] objects based on JSON-LD input. + */ object BooleanValueContentV2 extends ValueContentReaderV2[BooleanValueContentV2] { /** - * Converts a JSON-LD object to a [[BooleanValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[BooleanValueContentV2]]. - */ + * Converts a JSON-LD object to a [[BooleanValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[BooleanValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1848,11 +1873,11 @@ object BooleanValueContentV2 extends ValueContentReaderV2[BooleanValueContentV2] } /** - * Represents a Knora geometry value (a 2D-shape). - * - * @param valueHasGeometry JSON representing a 2D geometrical shape. - * @param comment a comment on this [[GeomValueContentV2]], if any. - */ + * Represents a Knora geometry value (a 2D-shape). + * + * @param valueHasGeometry JSON representing a 2D geometrical shape. + * @param comment a comment on this [[GeomValueContentV2]], if any. + */ case class GeomValueContentV2(ontologySchema: OntologySchema, valueHasGeometry: String, comment: Option[String] = None) extends ValueContentV2 { @@ -1904,21 +1929,21 @@ case class GeomValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[GeomValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[GeomValueContentV2]] objects based on JSON-LD input. + */ object GeomValueContentV2 extends ValueContentReaderV2[GeomValueContentV2] { /** - * Converts a JSON-LD object to a [[GeomValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[GeomValueContentV2]]. - */ + * Converts a JSON-LD object to a [[GeomValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[GeomValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -1943,12 +1968,12 @@ object GeomValueContentV2 extends ValueContentReaderV2[GeomValueContentV2] { /** - * Represents a Knora time interval value. - * - * @param valueHasIntervalStart the start of the time interval. - * @param valueHasIntervalEnd the end of the time interval. - * @param comment a comment on this [[IntervalValueContentV2]], if any. - */ + * Represents a Knora time interval value. + * + * @param valueHasIntervalStart the start of the time interval. + * @param valueHasIntervalEnd the end of the time interval. + * @param comment a comment on this [[IntervalValueContentV2]], if any. + */ case class IntervalValueContentV2(ontologySchema: OntologySchema, valueHasIntervalStart: BigDecimal, valueHasIntervalEnd: BigDecimal, @@ -2013,21 +2038,21 @@ case class IntervalValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[IntervalValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[IntervalValueContentV2]] objects based on JSON-LD input. + */ object IntervalValueContentV2 extends ValueContentReaderV2[IntervalValueContentV2] { /** - * Converts a JSON-LD object to an [[IntervalValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[IntervalValueContentV2]]. - */ + * Converts a JSON-LD object to an [[IntervalValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[IntervalValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -2062,11 +2087,11 @@ object IntervalValueContentV2 extends ValueContentReaderV2[IntervalValueContentV } /** - * Represents a Knora timestamp value. - * - * @param valueHasTimeStamp the timestamp. - * @param comment a comment on this [[TimeValueContentV2]], if any. - */ + * Represents a Knora timestamp value. + * + * @param valueHasTimeStamp the timestamp. + * @param comment a comment on this [[TimeValueContentV2]], if any. + */ case class TimeValueContentV2(ontologySchema: OntologySchema, valueHasTimeStamp: Instant, comment: Option[String] = None) extends ValueContentV2 { @@ -2124,21 +2149,21 @@ case class TimeValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[TimeValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[TimeValueContentV2]] objects based on JSON-LD input. + */ object TimeValueContentV2 extends ValueContentReaderV2[TimeValueContentV2] { /** - * Converts a JSON-LD object to a [[TimeValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return an [[IntervalValueContentV2]]. - */ + * Converts a JSON-LD object to a [[TimeValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return an [[IntervalValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -2166,12 +2191,12 @@ object TimeValueContentV2 extends ValueContentReaderV2[TimeValueContentV2] { } /** - * Represents a value pointing to a Knora hierarchical list node. - * - * @param valueHasListNode the IRI of the hierarchical list node pointed to. - * @param listNodeLabel the label of the hierarchical list node pointed to. - * @param comment a comment on this [[HierarchicalListValueContentV2]], if any. - */ + * Represents a value pointing to a Knora hierarchical list node. + * + * @param valueHasListNode the IRI of the hierarchical list node pointed to. + * @param listNodeLabel the label of the hierarchical list node pointed to. + * @param comment a comment on this [[HierarchicalListValueContentV2]], if any. + */ case class HierarchicalListValueContentV2(ontologySchema: OntologySchema, valueHasListNode: IRI, listNodeLabel: Option[String] = None, @@ -2232,21 +2257,21 @@ case class HierarchicalListValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[HierarchicalListValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[HierarchicalListValueContentV2]] objects based on JSON-LD input. + */ object HierarchicalListValueContentV2 extends ValueContentReaderV2[HierarchicalListValueContentV2] { /** - * Converts a JSON-LD object to a [[HierarchicalListValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[HierarchicalListValueContentV2]]. - */ + * Converts a JSON-LD object to a [[HierarchicalListValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[HierarchicalListValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -2275,11 +2300,11 @@ object HierarchicalListValueContentV2 extends ValueContentReaderV2[HierarchicalL /** - * Represents a Knora color value. - * - * @param valueHasColor a hexadecimal string containing the RGB color value - * @param comment a comment on this [[ColorValueContentV2]], if any. - */ + * Represents a Knora color value. + * + * @param valueHasColor a hexadecimal string containing the RGB color value + * @param comment a comment on this [[ColorValueContentV2]], if any. + */ case class ColorValueContentV2(ontologySchema: OntologySchema, valueHasColor: String, comment: Option[String] = None) extends ValueContentV2 { @@ -2331,21 +2356,21 @@ case class ColorValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[ColorValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[ColorValueContentV2]] objects based on JSON-LD input. + */ object ColorValueContentV2 extends ValueContentReaderV2[ColorValueContentV2] { /** - * Converts a JSON-LD object to a [[ColorValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[ColorValueContentV2]]. - */ + * Converts a JSON-LD object to a [[ColorValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[ColorValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -2369,11 +2394,11 @@ object ColorValueContentV2 extends ValueContentReaderV2[ColorValueContentV2] { } /** - * Represents a Knora URI value. - * - * @param valueHasUri the URI value. - * @param comment a comment on this [[UriValueContentV2]], if any. - */ + * Represents a Knora URI value. + * + * @param valueHasUri the URI value. + * @param comment a comment on this [[UriValueContentV2]], if any. + */ case class UriValueContentV2(ontologySchema: OntologySchema, valueHasUri: String, comment: Option[String] = None) extends ValueContentV2 { @@ -2426,21 +2451,21 @@ case class UriValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[UriValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[UriValueContentV2]] objects based on JSON-LD input. + */ object UriValueContentV2 extends ValueContentReaderV2[UriValueContentV2] { /** - * Converts a JSON-LD object to a [[UriValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[UriValueContentV2]]. - */ + * Converts a JSON-LD object to a [[UriValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[UriValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -2468,12 +2493,12 @@ object UriValueContentV2 extends ValueContentReaderV2[UriValueContentV2] { } /** - * - * Represents a Knora geoname value. - * - * @param valueHasGeonameCode the geoname code. - * @param comment a comment on this [[GeonameValueContentV2]], if any. - */ + * + * Represents a Knora geoname value. + * + * @param valueHasGeonameCode the geoname code. + * @param comment a comment on this [[GeonameValueContentV2]], if any. + */ case class GeonameValueContentV2(ontologySchema: OntologySchema, valueHasGeonameCode: String, comment: Option[String] = None) extends ValueContentV2 { @@ -2525,21 +2550,21 @@ case class GeonameValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[GeonameValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[GeonameValueContentV2]] objects based on JSON-LD input. + */ object GeonameValueContentV2 extends ValueContentReaderV2[GeonameValueContentV2] { /** - * Converts a JSON-LD object to a [[GeonameValueContentV2]]. - * - * @param jsonLDObject the JSON-LD object. - * @param requestingUser the user making the request. - * @param responderManager a reference to the responder manager. - * @param storeManager a reference to the store manager. - * @param log a logging adapter. - * @param timeout a timeout for `ask` messages. - * @param executionContext an execution context for futures. - * @return a [[GeonameValueContentV2]]. - */ + * Converts a JSON-LD object to a [[GeonameValueContentV2]]. + * + * @param jsonLDObject the JSON-LD object. + * @param requestingUser the user making the request. + * @param responderManager a reference to the responder manager. + * @param storeManager a reference to the store manager. + * @param log a logging adapter. + * @param timeout a timeout for `ask` messages. + * @param executionContext an execution context for futures. + * @return a [[GeonameValueContentV2]]. + */ override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, responderManager: ActorRef, @@ -2563,24 +2588,24 @@ object GeonameValueContentV2 extends ValueContentReaderV2[GeonameValueContentV2] } /** - * Represents the basic metadata stored about any file value. - */ + * Represents the basic metadata stored about any file value. + */ case class FileValueV2(internalFilename: String, internalMimeType: String, originalFilename: Option[String], originalMimeType: Option[String]) /** - * Holds a [[FileValueV2]] and the metadata that Sipi returned about the file. - * - * @param fileValue a [[FileValueV2]]. - * @param sipiFileMetadata the metadata that Sipi returned about the file. - */ + * Holds a [[FileValueV2]] and the metadata that Sipi returned about the file. + * + * @param fileValue a [[FileValueV2]]. + * @param sipiFileMetadata the metadata that Sipi returned about the file. + */ case class FileValueWithSipiMetadata(fileValue: FileValueV2, sipiFileMetadata: GetFileMetadataResponseV2) /** - * Constructs [[FileValueWithSipiMetadata]] objects based on JSON-LD input. - */ + * Constructs [[FileValueWithSipiMetadata]] objects based on JSON-LD input. + */ object FileValueWithSipiMetadata { def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, @@ -2608,12 +2633,12 @@ object FileValueWithSipiMetadata { } /** - * A trait for case classes representing different types of file values. - */ + * A trait for case classes representing different types of file values. + */ sealed trait FileValueContentV2 extends ValueContentV2 { /** - * The basic metadata about the file value. - */ + * The basic metadata about the file value. + */ def fileValue: FileValueV2 def toJsonLDValueInSimpleSchema(fileUrl: String): JsonLDObject = { @@ -2636,13 +2661,13 @@ sealed trait FileValueContentV2 extends ValueContentV2 { } /** - * Represents image file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param dimX the with of the the image in pixels. - * @param dimY the height of the the image in pixels. - * @param comment a comment on this `StillImageFileValueContentV2`, if any. - */ + * Represents image file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param dimX the with of the the image in pixels. + * @param dimY the height of the the image in pixels. + * @param comment a comment on this `StillImageFileValueContentV2`, if any. + */ case class StillImageFileValueContentV2(ontologySchema: OntologySchema, fileValue: FileValueV2, dimX: Int, @@ -2701,8 +2726,8 @@ case class StillImageFileValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[StillImageFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[StillImageFileValueContentV2]] objects based on JSON-LD input. + */ object StillImageFileValueContentV2 extends ValueContentReaderV2[StillImageFileValueContentV2] { override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, @@ -2732,14 +2757,14 @@ object StillImageFileValueContentV2 extends ValueContentReaderV2[StillImageFileV } /** - * Represents document file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param pageCount the number of pages in the document. - * @param dimX the with of the the document in pixels. - * @param dimY the height of the the document in pixels. - * @param comment a comment on this `DocumentFileValueContentV2`, if any. - */ + * Represents document file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param pageCount the number of pages in the document. + * @param dimX the with of the the document in pixels. + * @param dimY the height of the the document in pixels. + * @param comment a comment on this `DocumentFileValueContentV2`, if any. + */ case class DocumentFileValueContentV2(ontologySchema: OntologySchema, fileValue: FileValueV2, pageCount: Int, @@ -2802,8 +2827,8 @@ case class DocumentFileValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[DocumentFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[DocumentFileValueContentV2]] objects based on JSON-LD input. + */ object DocumentFileValueContentV2 extends ValueContentReaderV2[DocumentFileValueContentV2] { override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, @@ -2834,11 +2859,11 @@ object DocumentFileValueContentV2 extends ValueContentReaderV2[DocumentFileValue } /** - * Represents text file metadata. - * - * @param fileValue the basic metadata about the file value. - * @param comment a comment on this [[TextFileValueContentV2]], if any. - */ + * Represents text file metadata. + * + * @param fileValue the basic metadata about the file value. + * @param comment a comment on this [[TextFileValueContentV2]], if any. + */ case class TextFileValueContentV2(ontologySchema: OntologySchema, fileValue: FileValueV2, comment: Option[String] = None) extends FileValueContentV2 { @@ -2887,8 +2912,8 @@ case class TextFileValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[TextFileValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[TextFileValueContentV2]] objects based on JSON-LD input. + */ object TextFileValueContentV2 extends ValueContentReaderV2[TextFileValueContentV2] { override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, @@ -2916,16 +2941,16 @@ object TextFileValueContentV2 extends ValueContentReaderV2[TextFileValueContentV } /** - * Represents a Knora link value. - * - * @param referredResourceIri the IRI of resource that this link value refers to (either the source - * of an incoming link, or the target of an outgoing link). - * @param referredResourceExists `true` if the referred resource already exists, `false` if it is being created in the - * same transaction. - * @param isIncomingLink indicates if it is an incoming link. - * @param nestedResource information about the nested resource, if given. - * @param comment a comment on the link. - */ + * Represents a Knora link value. + * + * @param referredResourceIri the IRI of resource that this link value refers to (either the source + * of an incoming link, or the target of an outgoing link). + * @param referredResourceExists `true` if the referred resource already exists, `false` if it is being created in the + * same transaction. + * @param isIncomingLink indicates if it is an incoming link. + * @param nestedResource information about the nested resource, if given. + * @param comment a comment on the link. + */ case class LinkValueContentV2(ontologySchema: OntologySchema, referredResourceIri: IRI, referredResourceExists: Boolean = true, @@ -3017,8 +3042,8 @@ case class LinkValueContentV2(ontologySchema: OntologySchema, } /** - * Constructs [[LinkValueContentV2]] objects based on JSON-LD input. - */ + * Constructs [[LinkValueContentV2]] objects based on JSON-LD input. + */ object LinkValueContentV2 extends ValueContentReaderV2[LinkValueContentV2] { override def fromJsonLDObject(jsonLDObject: JsonLDObject, requestingUser: UserADM, 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 bb28277829..407f732776 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 @@ -20,6 +20,7 @@ package org.knora.webapi.responders.v2 import java.time.Instant +import java.util.UUID import akka.http.scaladsl.util.FastFuture import akka.pattern._ @@ -50,10 +51,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde /** * 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. + * @param newValueIri the IRI that was assigned to the new value. + * @param value the content of the new value. + * @param permissions the permissions attached to the new value. */ - case class VerifiedValueV2(newValueIri: IRI, value: ValueContentV2, permissions: String) + private case class VerifiedValueV2(newValueIri: IRI, value: ValueContentV2, permissions: String) /** * Receives a message of type [[ValuesResponderRequestV2]], and returns an appropriate response message. @@ -262,6 +264,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } yield CreateValueResponseV2( valueIri = verifiedValue.newValueIri, valueType = verifiedValue.value.valueType, + valueUUID = unverifiedValue.newValueUUID, projectADM = resourceInfo.projectADM ) } @@ -367,8 +370,10 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde valuePermissions: String, requestingUser: UserADM): Future[UnverifiedValueV2] = { for { - // Generate an IRI for the new value. + // Generate an IRI and a UUID for the new value. newValueIri <- FastFuture.successful(stringFormatter.makeRandomValueIri(resourceInfo.resourceIri)) + newValueUUID = UUID.randomUUID + currentTime: Instant = Instant.now // If we're creating a text value, update direct links and LinkValues for any resource references in standoff. @@ -397,6 +402,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde resourceIri = resourceInfo.resourceIri, propertyIri = propertyIri, newValueIri = newValueIri, + newValueUUID = newValueUUID, value = value, linkUpdates = standoffLinkUpdates, valueCreator = valueCreator, @@ -415,6 +421,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] } yield UnverifiedValueV2( newValueIri = newValueIri, + newValueUUID = newValueUUID, valueContent = value.unescape, permissions = valuePermissions, creationDate = currentTime @@ -440,6 +447,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde valueCreator: IRI, valuePermissions: String, requestingUser: UserADM): Future[UnverifiedValueV2] = { + val newValueUUID = UUID.randomUUID + for { sparqlTemplateLinkUpdate <- Future(incrementLinkValue( sourceResourceInfo = resourceInfo, @@ -458,6 +467,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde triplestore = settings.triplestoreType, resourceIri = resourceInfo.resourceIri, linkUpdate = sparqlTemplateLinkUpdate, + newValueUUID = newValueUUID, creationDate = currentTime, maybeComment = linkValueContent.comment, stringFormatter = stringFormatter @@ -473,6 +483,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] } yield UnverifiedValueV2( newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, + newValueUUID = newValueUUID, valueContent = linkValueContent.unescape, permissions = valuePermissions, creationDate = currentTime @@ -545,8 +556,9 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde valueHasOrder: Int, creationDate: Instant, requestingUser: UserADM): InsertSparqlWithUnverifiedValue = { - // Make an IRI for the new value. + // Make an IRI and a UUID for the new value. val newValueIri = stringFormatter.makeRandomValueIri(resourceIri) + val newValueUUID = UUID.randomUUID // Generate the SPARQL. val insertSparql: String = valueToCreate.valueContent match { @@ -575,6 +587,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde resourceIri = resourceIri, linkUpdate = sparqlTemplateLinkUpdate, creationDate = creationDate, + newValueUUID = newValueUUID, maybeComment = valueToCreate.valueContent.comment, maybeValueHasOrder = Some(valueHasOrder), stringFormatter = stringFormatter @@ -587,6 +600,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde propertyIri = propertyIri, value = otherValueContentV2, newValueIri = newValueIri, + newValueUUID = newValueUUID, linkUpdates = Seq.empty[SparqlTemplateLinkUpdate], // This is empty because we have to generate SPARQL for standoff links separately. valueCreator = requestingUser.id, valuePermissions = valueToCreate.permissions, @@ -600,6 +614,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde insertSparql = insertSparql, unverifiedValue = UnverifiedValueV2( newValueIri = newValueIri, + newValueUUID = newValueUUID, valueContent = valueToCreate.valueContent.unescape, permissions = valueToCreate.permissions, creationDate = creationDate @@ -841,6 +856,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde unverifiedValue = UnverifiedValueV2( newValueIri = newValueIri, + newValueUUID = currentValue.valueHasUUID, valueContent = currentValue.valueContent, permissions = newValuePermissionLiteral, creationDate = currentTime @@ -855,6 +871,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } yield UpdateValueResponseV2( valueIri = verifiedValue.newValueIri, valueType = unverifiedValue.valueContent.valueType, + valueUUID = currentValue.valueHasUUID, projectADM = resourceInfo.projectADM ) } @@ -966,20 +983,36 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde case _ => FastFuture.successful(()) } + dataNamedGraph: IRI = stringFormatter.projectDataNamedGraphV2(resourceInfo.projectADM) // Create the new value version. - unverifiedValue: UnverifiedValueV2 <- updateValueV2AfterChecks( - dataNamedGraph = dataNamedGraph, - resourceInfo = resourceInfo, - propertyIri = adjustedInternalPropertyInfo.entityInfoContent.propertyIri, - currentValue = currentValue, - newValueVersion = submittedInternalValueContent, - valueCreator = updateValueRequest.requestingUser.id, - valuePermissions = newValueVersionPermissionLiteral, - requestingUser = updateValueRequest.requestingUser - ) + unverifiedValue: UnverifiedValueV2 <- (currentValue, submittedInternalValueContent) match { + case (currentLinkValue: ReadLinkValueV2, newLinkValue: LinkValueContentV2) => + updateLinkValueV2AfterChecks( + dataNamedGraph = dataNamedGraph, + resourceInfo = resourceInfo, + linkPropertyIri = adjustedInternalPropertyInfo.entityInfoContent.propertyIri, + currentLinkValue = currentLinkValue, + newLinkValue = newLinkValue, + valueCreator = updateValueRequest.requestingUser.id, + valuePermissions = newValueVersionPermissionLiteral, + requestingUser = updateValueRequest.requestingUser + ) + + case _ => + updateOrdinaryValueV2AfterChecks( + dataNamedGraph = dataNamedGraph, + resourceInfo = resourceInfo, + propertyIri = adjustedInternalPropertyInfo.entityInfoContent.propertyIri, + currentValue = currentValue, + newValueVersion = submittedInternalValueContent, + valueCreator = updateValueRequest.requestingUser.id, + valuePermissions = newValueVersionPermissionLiteral, + requestingUser = updateValueRequest.requestingUser + ) + } // Check that the value was written correctly to the triplestore. @@ -992,6 +1025,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } yield UpdateValueResponseV2( valueIri = verifiedValue.newValueIri, valueType = unverifiedValue.valueContent.valueType, + valueUUID = unverifiedValue.newValueUUID, projectADM = resourceInfo.projectADM ) } @@ -1028,59 +1062,6 @@ 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]]. - */ - private def updateValueV2AfterChecks(dataNamedGraph: IRI, - resourceInfo: ReadResourceV2, - propertyIri: SmartIri, - currentValue: ReadValueV2, - newValueVersion: ValueContentV2, - valueCreator: IRI, - valuePermissions: String, - requestingUser: UserADM): Future[UnverifiedValueV2] = { - (currentValue.valueContent, newValueVersion) match { - case (currentLinkValue: LinkValueContentV2, newLinkValue: LinkValueContentV2) => - updateLinkValueV2AfterChecks( - dataNamedGraph = dataNamedGraph, - resourceInfo = resourceInfo, - linkPropertyIri = propertyIri, - currentLinkValue = currentLinkValue, - newLinkValue = newLinkValue, - valueCreator = valueCreator, - valuePermissions = valuePermissions, - requestingUser = requestingUser - ) - - case _ => - val newValueIri: IRI = stringFormatter.makeRandomValueIri(resourceInfo.resourceIri) - - updateOrdinaryValueV2AfterChecks( - dataNamedGraph = dataNamedGraph, - resourceInfo = resourceInfo, - propertyIri = propertyIri, - currentValue = currentValue, - newValueIri = newValueIri, - newValueVersion = newValueVersion, - valueCreator = valueCreator, - valuePermissions = valuePermissions, - requestingUser = requestingUser - ) - } - } - /** * Changes an ordinary value (i.e. not a link), assuming that pre-update checks have already been done. * @@ -1088,7 +1069,6 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde * @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. @@ -1099,11 +1079,11 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde resourceInfo: ReadResourceV2, propertyIri: SmartIri, currentValue: ReadValueV2, - newValueIri: IRI, newValueVersion: ValueContentV2, valueCreator: IRI, valuePermissions: String, requestingUser: UserADM): Future[UnverifiedValueV2] = { + val newValueIri: IRI = stringFormatter.makeRandomValueIri(resourceInfo.resourceIri) // If we're updating a text value, update direct links and LinkValues for any resource references in Standoff. val standoffLinkUpdates = (currentValue.valueContent, newValueVersion) match { @@ -1178,6 +1158,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde } yield UnverifiedValueV2( newValueIri = newValueIri, + newValueUUID = currentValue.valueHasUUID, valueContent = newValueVersion.unescape, permissions = valuePermissions, creationDate = currentTime @@ -1187,32 +1168,32 @@ 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. + * @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 [[ReadLinkValueV2]] 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, - currentLinkValue: LinkValueContentV2, + currentLinkValue: ReadLinkValueV2, newLinkValue: LinkValueContentV2, valueCreator: IRI, valuePermissions: String, requestingUser: UserADM): Future[UnverifiedValueV2] = { // Are we changing the link target? - if (currentLinkValue.referredResourceIri != newLinkValue.referredResourceIri) { - // Delete the existing link and decrement its LinkValue's reference count. + if (currentLinkValue.valueContent.referredResourceIri != newLinkValue.referredResourceIri) { + // Yes. Delete the existing link and decrement its LinkValue's reference count. val sparqlTemplateLinkUpdateForCurrentLink: SparqlTemplateLinkUpdate = decrementLinkValue( sourceResourceInfo = resourceInfo, linkPropertyIri = linkPropertyIri, - targetResourceIri = currentLinkValue.referredResourceIri, + targetResourceIri = currentLinkValue.valueContent.referredResourceIri, valueCreator = valueCreator, valuePermissions = valuePermissions, requestingUser = requestingUser @@ -1231,6 +1212,9 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde // Make a timestamp to indicate when the link value was updated. val currentTime: Instant = Instant.now + // Make a new UUID for the new link value. + val newLinkValueUUID = UUID.randomUUID + for { // Generate a SPARQL update string. sparqlUpdate <- Future(queries.sparql.v2.txt.changeLinkTarget( @@ -1239,6 +1223,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde linkSourceIri = resourceInfo.resourceIri, linkUpdateForCurrentLink = sparqlTemplateLinkUpdateForCurrentLink, linkUpdateForNewLink = sparqlTemplateLinkUpdateForNewLink, + newLinkValueUUID = newLinkValueUUID, maybeComment = newLinkValue.comment, currentTime = currentTime, requestingUser = requestingUser.id, @@ -1254,6 +1239,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] } yield UnverifiedValueV2( newValueIri = sparqlTemplateLinkUpdateForNewLink.newLinkValueIri, + newValueUUID = newLinkValueUUID, valueContent = newLinkValue.unescape, permissions = valuePermissions, creationDate = currentTime @@ -1264,7 +1250,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde val sparqlTemplateLinkUpdate: SparqlTemplateLinkUpdate = changeLinkValueMetadata( sourceResourceInfo = resourceInfo, linkPropertyIri = linkPropertyIri, - targetResourceIri = currentLinkValue.referredResourceIri, + targetResourceIri = currentLinkValue.valueContent.referredResourceIri, valueCreator = valueCreator, valuePermissions = valuePermissions, requestingUser = requestingUser @@ -1287,6 +1273,7 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde _ <- (storeManager ? SparqlUpdateRequest(sparqlUpdate)).mapTo[SparqlUpdateResponse] } yield UnverifiedValueV2( newValueIri = sparqlTemplateLinkUpdate.newLinkValueIri, + newValueUUID = currentLinkValue.valueHasUUID, valueContent = newLinkValue.unescape, permissions = valuePermissions, creationDate = currentTime diff --git a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala index b09aa74caa..149cf4391c 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/v2/ValuesRouteV2.scala @@ -358,6 +358,26 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } + private def createValueTestResponse: Future[SourceCodeFileContent] = { + val createValueResponseV2: CreateValueResponseV2 = CreateValueResponseV2( + valueIri = SharedTestDataADM.testResponseValueIri, + valueType = OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri, + valueUUID = SharedTestDataADM.testResponseValueUUID, + projectADM = SharedTestDataADM.anythingProject + ) + + Future { + SourceCodeFileContent( + filePath = SourceCodeFilePath.makeJsonPath("create-value-response"), + text = createValueResponseV2.toJsonLDDocument( + targetSchema = ApiV2Complex, + settings = settings, + schemaOptions = Set.empty + ).toPrettyString + ) + } + } + private def updateValue: Route = path(ValuesBasePath) { put { entity(as[String]) { jsonRequest => @@ -583,6 +603,26 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit ) } + private def updateValueTestResponse: Future[SourceCodeFileContent] = { + val createValueResponseV2: UpdateValueResponseV2 = UpdateValueResponseV2( + valueIri = SharedTestDataADM.testResponseValueIri, + valueType = OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri, + valueUUID = SharedTestDataADM.testResponseValueUUID, + projectADM = SharedTestDataADM.anythingProject + ) + + Future { + SourceCodeFileContent( + filePath = SourceCodeFilePath.makeJsonPath("update-value-response"), + text = createValueResponseV2.toJsonLDDocument( + targetSchema = ApiV2Complex, + settings = settings, + schemaOptions = Set.empty + ).toPrettyString + ) + } + } + private def deleteValue: Route = path(ValuesBasePath / "delete") { post { entity(as[String]) { jsonRequest => @@ -647,6 +687,8 @@ class ValuesRouteV2(routeData: KnoraRouteData) extends KnoraRoute(routeData) wit createRequests: Set[SourceCodeFileContent] <- createValueTestRequests updateRequests: Set[SourceCodeFileContent] <- updateValueTestRequests deleteRequests: Set[SourceCodeFileContent] <- deleteValueTestRequests - } yield getResponses ++ createRequests ++ updateRequests ++ deleteRequests + createValueResponse: SourceCodeFileContent <- createValueTestResponse + updateValueResponse: SourceCodeFileContent <- updateValueTestResponse + } yield getResponses ++ createRequests ++ updateRequests ++ deleteRequests + createValueResponse + updateValueResponse } } diff --git a/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala index 96aff41b5b..7b49dad8c6 100644 --- a/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/util/StringFormatter.scala @@ -53,8 +53,8 @@ import scala.util.matching.Regex import scala.util.{Failure, Success, Try} /** - * Provides instances of [[StringFormatter]], as well as string formatting constants. - */ + * Provides instances of [[StringFormatter]], as well as string formatting constants. + */ object StringFormatter { // A non-printing delimiter character, Unicode INFORMATION SEPARATOR ONE, that should never occur in data. @@ -80,116 +80,116 @@ object StringFormatter { val ANSI_RESET = "\u001B[0m" /** - * Separates the calendar name from the rest of a Knora date. - */ + * Separates the calendar name from the rest of a Knora date. + */ val CalendarSeparator: String = ":" /** - * Separates year, month, and day in a Knora date. - */ + * Separates year, month, and day in a Knora date. + */ val PrecisionSeparator: String = "-" /** - * Separates a date (year, month, day) from the era in a Knora date. - */ + * Separates a date (year, month, day) from the era in a Knora date. + */ val EraSeparator: String = " " /** - * Before Christ (equivalent to BCE) - */ + * Before Christ (equivalent to BCE) + */ val Era_BC: String = "BC" /** - * Before Common Era (equivalent to BC) - */ + * Before Common Era (equivalent to BC) + */ val Era_BCE: String = "BCE" /** - * Anno Domini (equivalent to CE) - */ + * Anno Domini (equivalent to CE) + */ val Era_AD: String = "AD" /** - * Common Era (equivalent to AD) - */ + * Common Era (equivalent to AD) + */ val Era_CE: String = "CE" /** - * String representation of the name of the Gregorian calendar. - */ + * String representation of the name of the Gregorian calendar. + */ val CalendarGregorian: String = "GREGORIAN" /** - * String representation of the name of the Julian calendar. - */ + * String representation of the name of the Julian calendar. + */ val CalendarJulian: String = "JULIAN" /** - * String representation of day precision in a date. - */ + * String representation of day precision in a date. + */ val PrecisionDay: String = "DAY" /** - * String representation of month precision in a date. - */ + * String representation of month precision in a date. + */ val PrecisionMonth: String = "MONTH" /** - * String representation of year precision in a date. - */ + * String representation of year precision in a date. + */ val PrecisionYear: String = "YEAR" /** - * The version number of the current version of Knora's ARK URL format. - */ + * The version number of the current version of Knora's ARK URL format. + */ val ArkVersion: String = "1" /** - * The length of the canonical representation of a UUID. - */ + * The length of the canonical representation of a UUID. + */ val CanonicalUuidLength = 36 /** - * The length of a Base64-encoded UUID. - */ + * The length of a Base64-encoded UUID. + */ val Base64UuidLength = 22 /** - * The maximum number of times that `makeUnusedIri` will try to make a new, unused IRI. - */ + * The maximum number of times that `makeUnusedIri` will try to make a new, unused IRI. + */ val MAX_IRI_ATTEMPTS: Int = 5 /** - * The domain name used to construct Knora IRIs. - */ + * The domain name used to construct Knora IRIs. + */ val IriDomain: String = "rdfh.ch" /** - * A keyword used in IRI entity names to introduce a collection type annotation for client code generation. - */ + * A keyword used in IRI entity names to introduce a collection type annotation for client code generation. + */ val ClientCollectionTypeKeyword: String = "collection:" /** - * A string found in IRIs representing collection type annotations for client code generation. - */ + * A string found in IRIs representing collection type annotations for client code generation. + */ val ClientCollectionEntityNameStart: String = "#" + ClientCollectionTypeKeyword /** - * A container for an XML import namespace and its prefix label. - * - * @param namespace the namespace. - * @param prefixLabel the prefix label. - */ + * A container for an XML import namespace and its prefix label. + * + * @param namespace the namespace. + * @param prefixLabel the prefix label. + */ case class XmlImportNamespaceInfoV1(namespace: IRI, prefixLabel: String) /** - * Represents a parsed object of the property `salsah-gui:guiAttributeDefinition`. - * - * @param attributeName the name of the attribute. - * @param isRequired `true` if the attribute is required. - * @param allowedType the type of the attribute's value. - * @param enumeratedValues the allowed values, if this is an enumerated string attribute. - */ + * Represents a parsed object of the property `salsah-gui:guiAttributeDefinition`. + * + * @param attributeName the name of the attribute. + * @param isRequired `true` if the attribute is required. + * @param allowedType the type of the attribute's value. + * @param enumeratedValues the allowed values, if this is an enumerated string attribute. + */ case class SalsahGuiAttributeDefinition(attributeName: String, isRequired: Boolean, allowedType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value, @@ -197,61 +197,61 @@ object StringFormatter { unparsedString: String) /** - * Represents a parsed object of the property `salsah-gui:guiAttribute`. - * - * @param attributeName the name of the attribute. - * @param attributeValue the value of the attribute. - */ + * Represents a parsed object of the property `salsah-gui:guiAttribute`. + * + * @param attributeName the name of the attribute. + * @param attributeValue the value of the attribute. + */ case class SalsahGuiAttribute(attributeName: String, attributeValue: SalsahGuiAttributeValue) /** - * Represents a parsed value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - */ + * Represents a parsed value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + */ sealed trait SalsahGuiAttributeValue { def attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value } /** - * Represents a parsed integer value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the integer value. - */ + * Represents a parsed integer value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the integer value. + */ case class SalsahGuiIntegerAttributeValue(value: Int) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Integer } /** - * Represents a parsed percent value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the percent value. - */ + * Represents a parsed percent value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the percent value. + */ case class SalsahGuiPercentAttributeValue(value: Int) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Percent } /** - * Represents a parsed decimal value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the decimal value. - */ + * Represents a parsed decimal value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the decimal value. + */ case class SalsahGuiDecimalAttributeValue(value: BigDecimal) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Decimal } /** - * Represents a parsed string value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the string value. - */ + * Represents a parsed string value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the string value. + */ case class SalsahGuiStringAttributeValue(value: String) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Str } /** - * Represents a parsed IRI value of an attribute that is the object of the property `salsah-gui:guiAttribute`. - * - * @param value the IRI value. - */ + * Represents a parsed IRI value of an attribute that is the object of the property `salsah-gui:guiAttribute`. + * + * @param value the IRI value. + */ case class SalsahGuiIriAttributeValue(value: IRI) extends SalsahGuiAttributeValue { override val attributeType: OntologyConstants.SalsahGui.SalsahGuiAttributeType.Value = OntologyConstants.SalsahGui.SalsahGuiAttributeType.Iri } @@ -268,23 +268,23 @@ object StringFormatter { */ /** - * The instance of [[StringFormatter]] that is initialised after the ActorSystem starts, - * and can parse project-specific API v2 ontology IRIs. This instance is used almost - * everywhere in the API server. - */ + * The instance of [[StringFormatter]] that is initialised after the ActorSystem starts, + * and can parse project-specific API v2 ontology IRIs. This instance is used almost + * everywhere in the API server. + */ private var generalInstance: Option[StringFormatter] = None /** - * The instance of [[StringFormatter]] that can be used as soon as the JVM starts, but - * can't parse project-specific API v2 ontology IRIs. This instance is used - * only to initialise the hard-coded API v2 ontologies [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2SimpleTransformationRules]] - * and [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2ComplexTransformationRules]]. - */ + * The instance of [[StringFormatter]] that can be used as soon as the JVM starts, but + * can't parse project-specific API v2 ontology IRIs. This instance is used + * only to initialise the hard-coded API v2 ontologies [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2SimpleTransformationRules]] + * and [[org.knora.webapi.messages.v2.responder.ontologymessages.KnoraBaseToApiV2ComplexTransformationRules]]. + */ private val instanceForConstantOntologies = new StringFormatter(None) /** - * Gets the singleton instance of [[StringFormatter]] that handles IRIs from data. - */ + * Gets the singleton instance of [[StringFormatter]] that handles IRIs from data. + */ def getGeneralInstance: StringFormatter = { generalInstance match { case Some(instance) => instance @@ -293,16 +293,16 @@ object StringFormatter { } /** - * Gets the singleton instance of [[StringFormatter]] that can only handle the IRIs in built-in - * ontologies. - */ + * Gets the singleton instance of [[StringFormatter]] that can only handle the IRIs in built-in + * ontologies. + */ def getInstanceForConstantOntologies: StringFormatter = instanceForConstantOntologies /** - * Initialises the general instance of [[StringFormatter]]. - * - * @param settings the application settings. - */ + * Initialises the general instance of [[StringFormatter]]. + * + * @param settings the application settings. + */ def init(settings: SettingsImpl): Unit = { this.synchronized { generalInstance match { @@ -313,8 +313,8 @@ object StringFormatter { } /** - * Initialises the singleton instance of [[StringFormatter]] for use in a client program that will connect to Knora. - */ + * Initialises the singleton instance of [[StringFormatter]] for use in a client program that will connect to Knora. + */ def initForClient(knoraHostAndPort: String): Unit = { this.synchronized { generalInstance match { @@ -325,8 +325,8 @@ object StringFormatter { } /** - * Initialises the singleton instance of [[StringFormatter]] for a test. - */ + * Initialises the singleton instance of [[StringFormatter]] for a test. + */ def initForTest(): Unit = { this.synchronized { generalInstance match { @@ -337,39 +337,39 @@ object StringFormatter { } /** - * Indicates whether the IRI is a data IRI, a definition IRI, or an IRI of an unknown type. - */ + * Indicates whether the IRI is a data IRI, a definition IRI, or an IRI of an unknown type. + */ private sealed trait IriType /** - * Indicates that the IRI is a data IRI. - */ + * Indicates that the IRI is a data IRI. + */ private case object KnoraDataIri extends IriType /** - * Indicates that the IRI is an ontology or ontology entity IRI. - */ + * Indicates that the IRI is an ontology or ontology entity IRI. + */ private case object KnoraDefinitionIri extends IriType /** - * Indicates that the type of the IRI is unknown. - */ + * Indicates that the type of the IRI is unknown. + */ private case object UnknownIriType extends IriType /** - * Holds information extracted from the IRI. - * - * @param iriType the type of the IRI. - * @param projectCode the IRI's project code, if any. - * @param ontologyName the IRI's ontology name, if any. - * @param entityName the IRI's entity name, if any. - * @param clientCollectionType the [[CollectionType]] represented by this IRI, if any. - * @param resourceID if this is a resource IRI or value IRI, its resource ID. - * @param valueID if this is a value IRI, its value ID. - * @param standoffStartIndex if this is a standoff IRI, its start index. - * @param ontologySchema the IRI's ontology schema, or `None` if it is not a Knora definition IRI. - * @param isBuiltInDef `true` if the IRI refers to a built-in Knora ontology or ontology entity. - */ + * Holds information extracted from the IRI. + * + * @param iriType the type of the IRI. + * @param projectCode the IRI's project code, if any. + * @param ontologyName the IRI's ontology name, if any. + * @param entityName the IRI's entity name, if any. + * @param clientCollectionType the [[CollectionType]] represented by this IRI, if any. + * @param resourceID if this is a resource IRI or value IRI, its resource ID. + * @param valueID if this is a value IRI, its value ID. + * @param standoffStartIndex if this is a standoff IRI, its start index. + * @param ontologySchema the IRI's ontology schema, or `None` if it is not a Knora definition IRI. + * @param isBuiltInDef `true` if the IRI refers to a built-in Knora ontology or ontology entity. + */ private case class SmartIriInfo(iriType: IriType, projectCode: Option[String] = None, ontologyName: Option[String] = None, @@ -383,18 +383,18 @@ object StringFormatter { sharedOntology: Boolean = false) /** - * A cache that maps IRI strings to [[SmartIri]] instances. To keep the cache from getting too large, - * only IRIs from known ontologies are cached. - */ + * A cache that maps IRI strings to [[SmartIri]] instances. To keep the cache from getting too large, + * only IRIs from known ontologies are cached. + */ private lazy val smartIriCache = new ConcurrentHashMap[IRI, SmartIri](2048) /** - * Gets a cached smart IRI, or constructs and caches one. - * - * @param iriStr the IRI in string form. - * @param creationFun a function that creates the smart IRI to be cached. - * @return the smart IRI. - */ + * Gets a cached smart IRI, or constructs and caches one. + * + * @param iriStr the IRI in string form. + * @param creationFun a function that creates the smart IRI to be cached. + * @return the smart IRI. + */ private def getOrCacheSmartIri(iriStr: IRI, creationFun: () => SmartIri): SmartIri = { smartIriCache.computeIfAbsent( iriStr, @@ -404,13 +404,13 @@ object StringFormatter { } /** - * Represents a parsed IRI with Knora-specific functionality. To construct a `SmartIri`, - * `import org.knora.webapi.util.IriConversions.ConvertibleIri`, then call one of the methods that - * it implicitly defines on `String`, e.g.: - * - * - "http://knora.example.org/ontology/0000/example#Something".toSmartIri - * - "http://knora.example.org/ontology/0000/example#Something".toSmartIriWithErr(throw BadRequestException("Invalid IRI")) - */ + * Represents a parsed IRI with Knora-specific functionality. To construct a `SmartIri`, + * `import org.knora.webapi.util.IriConversions.ConvertibleIri`, then call one of the methods that + * it implicitly defines on `String`, e.g.: + * + * - "http://knora.example.org/ontology/0000/example#Something".toSmartIri + * - "http://knora.example.org/ontology/0000/example#Something".toSmartIriWithErr(throw BadRequestException("Invalid IRI")) + */ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { /* @@ -431,199 +431,199 @@ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { */ /** - * Returns this IRI as a string in angle brackets. - */ + * Returns this IRI as a string in angle brackets. + */ def toSparql: String /** - * Returns `true` if this is a Knora data or definition IRI. - */ + * Returns `true` if this is a Knora data or definition IRI. + */ def isKnoraIri: Boolean /** - * Returns `true` if this is a Knora data IRI. - */ + * Returns `true` if this is a Knora data IRI. + */ def isKnoraDataIri: Boolean /** - * Returns `true` if this is a Knora resource IRI. - */ + * Returns `true` if this is a Knora resource IRI. + */ def isKnoraResourceIri: Boolean /** - * Returns `true` if this is a Knora value IRI. - */ + * Returns `true` if this is a Knora value IRI. + */ def isKnoraValueIri: Boolean /** - * Returns `true` if this is a Knora standoff IRI. - */ + * Returns `true` if this is a Knora standoff IRI. + */ def isKnoraStandoffIri: Boolean /** - * Returns `true` if this is a Knora ontology or entity IRI. - */ + * Returns `true` if this is a Knora ontology or entity IRI. + */ def isKnoraDefinitionIri: Boolean /** - * Returns `true` if this is a built-in Knora ontology or entity IRI. - * - * @return - */ + * Returns `true` if this is a built-in Knora ontology or entity IRI. + * + * @return + */ def isKnoraBuiltInDefinitionIri: Boolean /** - * Returns `true` if this IRI belongs to a shared ontology. - */ + * Returns `true` if this IRI belongs to a shared ontology. + */ def isKnoraSharedDefinitionIri: Boolean /** - * Returns `true` if this is an internal Knora ontology or entity IRI. - * - * @return - */ + * Returns `true` if this is an internal Knora ontology or entity IRI. + * + * @return + */ def isKnoraInternalDefinitionIri: Boolean /** - * Returns `true` if this is an internal Knora ontology entity IRI. - */ + * Returns `true` if this is an internal Knora ontology entity IRI. + */ def isKnoraInternalEntityIri: Boolean /** - * Returns `true` if this is a Knora ontology IRI. - */ + * Returns `true` if this is a Knora ontology IRI. + */ def isKnoraOntologyIri: Boolean /** - * Returns `true` if this is a Knora entity IRI. - */ + * Returns `true` if this is a Knora entity IRI. + */ def isKnoraEntityIri: Boolean /** - * Returns `true` if this is a Knora API v2 ontology or entity IRI. - */ + * Returns `true` if this is a Knora API v2 ontology or entity IRI. + */ def isKnoraApiV2DefinitionIri: Boolean /** - * Returns `true` if this is a Knora API v2 ontology entity IRI. - */ + * Returns `true` if this is a Knora API v2 ontology entity IRI. + */ def isKnoraApiV2EntityIri: Boolean /** - * Returns `true` if this IRI represents a collection type for use in client code generation. - */ + * Returns `true` if this IRI represents a collection type for use in client code generation. + */ def isClientCollectionTypeIri: Boolean /** - * Returns the IRI's project code, if any. - */ + * Returns the IRI's project code, if any. + */ def getProjectCode: Option[String] /** - * Returns the IRI's resource ID, if any. - */ + * Returns the IRI's resource ID, if any. + */ def getResourceID: Option[String] /** - * Returns the IRI's value ID, if any. - */ + * Returns the IRI's value ID, if any. + */ def getValueID: Option[String] /** - * Returns the IRI's standoff start index, if any. - */ + * Returns the IRI's standoff start index, if any. + */ def getStandoffStartIndex: Option[Int] /** - * If this is an ontology entity IRI, returns its ontology IRI. - */ + * If this is an ontology entity IRI, returns its ontology IRI. + */ def getOntologyFromEntity: SmartIri /** - * If this is a Knora ontology or entity IRI, returns the name of the ontology. Otherwise, throws [[DataConversionException]]. - */ + * If this is a Knora ontology or entity IRI, returns the name of the ontology. Otherwise, throws [[DataConversionException]]. + */ def getOntologyName: String /** - * If this is a Knora entity IRI, returns the name of the entity. Otherwise, throws [[DataConversionException]]. - */ + * If this is a Knora entity IRI, returns the name of the entity. Otherwise, throws [[DataConversionException]]. + */ def getEntityName: String /** - * If this IRI represents a collection type for use in client code generation, returns the collection type. - */ + * If this IRI represents a collection type for use in client code generation, returns the collection type. + */ def getClientCollectionType: CollectionType /** - * If this is a Knora ontology IRI, constructs a Knora entity IRI based on it. Otherwise, throws [[DataConversionException]]. - * - * @param entityName the name of the entity. - */ + * If this is a Knora ontology IRI, constructs a Knora entity IRI based on it. Otherwise, throws [[DataConversionException]]. + * + * @param entityName the name of the entity. + */ def makeEntityIri(entityName: String): SmartIri /** - * Returns the IRI's [[OntologySchema]], or `None` if this is not a Knora definition IRI. - */ + * Returns the IRI's [[OntologySchema]], or `None` if this is not a Knora definition IRI. + */ def getOntologySchema: Option[OntologySchema] /** - * Checks that the IRI's ontology schema, if present, corresponds to the specified schema. If the IRI - * has no schema, does nothing. If the IRI has a schema that's different to the specified schema, calls - * `errorFun`. - * - * @param allowedSchema the schema to be allowed. - * @param errorFun a function that throws an exception. It will be called if the IRI has a different schema - * to the one specified. - * @return the same IRI - */ + * Checks that the IRI's ontology schema, if present, corresponds to the specified schema. If the IRI + * has no schema, does nothing. If the IRI has a schema that's different to the specified schema, calls + * `errorFun`. + * + * @param allowedSchema the schema to be allowed. + * @param errorFun a function that throws an exception. It will be called if the IRI has a different schema + * to the one specified. + * @return the same IRI + */ def checkApiV2Schema(allowedSchema: ApiV2Schema, errorFun: => Nothing): SmartIri /** - * Converts this IRI to another ontology schema. - * - * @param targetSchema the target schema. - */ + * Converts this IRI to another ontology schema. + * + * @param targetSchema the target schema. + */ override def toOntologySchema(targetSchema: OntologySchema): SmartIri /** - * Constructs a short prefix label for the ontology that the IRI belongs to. - */ + * Constructs a short prefix label for the ontology that the IRI belongs to. + */ def getShortPrefixLabel: String /** - * Constructs a longer prefix label than the one returned by `getShortPrefixLabel`, which may be needed - * if there are ontology name collisions. - */ + * Constructs a longer prefix label than the one returned by `getShortPrefixLabel`, which may be needed + * if there are ontology name collisions. + */ def getLongPrefixLabel: String /** - * If this is the IRI of a link value property, returns the IRI of the corresponding link property. Throws - * [[DataConversionException]] if this IRI is not a Knora entity IRI. - */ + * If this is the IRI of a link value property, returns the IRI of the corresponding link property. Throws + * [[DataConversionException]] if this IRI is not a Knora entity IRI. + */ def fromLinkValuePropToLinkProp: SmartIri /** - * If this is the IRI of a link property, returns the IRI of the corresponding link value property. Throws - * [[DataConversionException]] if this IRI is not a Knora entity IRI. - */ + * If this is the IRI of a link property, returns the IRI of the corresponding link value property. Throws + * [[DataConversionException]] if this IRI is not a Knora entity IRI. + */ def fromLinkPropToLinkValueProp: SmartIri /** - * If this is a Knora data IRI representing a resource, returns an ARK URL for the resource. Throws - * [[DataConversionException]] if this IRI is not a Knora resource IRI. - * - * @param maybeTimestamp an optional timestamp indicating the point in the resource's version history that the ARK URL should - * cite. - */ + * If this is a Knora data IRI representing a resource, returns an ARK URL for the resource. Throws + * [[DataConversionException]] if this IRI is not a Knora resource IRI. + * + * @param maybeTimestamp an optional timestamp indicating the point in the resource's version history that the ARK URL should + * cite. + */ def fromResourceIriToArkUrl(maybeTimestamp: Option[Instant] = None): String /** - * If this is a Knora data IRI representing a value, returns an ARK URL for the value. Throws - * [[DataConversionException]] if this IRI is not a Knora value IRI. - * - * @param maybeTimestamp an optional timestamp indicating the point in the value's version history that the ARK URL should - * cite. - */ + * If this is a Knora data IRI representing a value, returns an ARK URL for the value. Throws + * [[DataConversionException]] if this IRI is not a Knora value IRI. + * + * @param maybeTimestamp an optional timestamp indicating the point in the value's version history that the ARK URL should + * cite. + */ def fromValueIriToArkUrl(valueUUID: UUID, maybeTimestamp: Option[Instant] = None): String override def equals(obj: scala.Any): Boolean = { @@ -640,8 +640,8 @@ sealed trait SmartIri extends Ordered[SmartIri] with KnoraContentV2[SmartIri] { } /** - * Provides `apply` and `unapply` methods to for `SmartIri`. - */ + * Provides `apply` and `unapply` methods to for `SmartIri`. + */ object SmartIri { def apply(iriStr: IRI)(implicit stringFormatter: StringFormatter): SmartIri = stringFormatter.toSmartIri(iriStr) @@ -649,31 +649,31 @@ object SmartIri { } /** - * Provides automatic conversion of IRI strings to [[SmartIri]] objects. See [[https://www.scala-lang.org/api/current/scala/AnyVal.html]] - * for details. - */ + * Provides automatic conversion of IRI strings to [[SmartIri]] objects. See [[https://www.scala-lang.org/api/current/scala/AnyVal.html]] + * for details. + */ object IriConversions { implicit class ConvertibleIri(val self: IRI) extends AnyVal { /** - * Converts an IRI string to a [[SmartIri]]. - */ + * Converts an IRI string to a [[SmartIri]]. + */ def toSmartIri(implicit stringFormatter: StringFormatter): SmartIri = stringFormatter.toSmartIri(self) /** - * Converts an IRI string to a [[SmartIri]]. If the string cannot be converted, a function is called to report - * the error. Use this function to parse IRIs from client input. - * - * @param errorFun A function that throws an exception. It will be called if the string cannot be converted. - */ + * Converts an IRI string to a [[SmartIri]]. If the string cannot be converted, a function is called to report + * the error. Use this function to parse IRIs from client input. + * + * @param errorFun A function that throws an exception. It will be called if the string cannot be converted. + */ def toSmartIriWithErr(errorFun: => Nothing)(implicit stringFormatter: StringFormatter): SmartIri = stringFormatter.toSmartIriWithErr(self, errorFun) } } /** - * Handles string parsing, formatting, conversion, and validation. - */ + * Handles string parsing, formatting, conversion, and validation. + */ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, maybeKnoraHostAndPort: Option[String] = None, initForTest: Boolean = false) { import StringFormatter._ @@ -875,26 +875,26 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma """0+$""".r /** - * A regex that matches a valid username - * - 4 - 50 characters long - * - Only contains alphanumeric characters, underscore and dot. - * - Underscore and dot can't be at the end or start of a username - * - Underscore or dot can't be used multiple times in a row - */ + * A regex that matches a valid username + * - 4 - 50 characters long + * - Only contains alphanumeric characters, underscore and dot. + * - Underscore and dot can't be at the end or start of a username + * - Underscore or dot can't be used multiple times in a row + */ private val UsernameRegex: Regex = """^(?=.{4,50}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(? Nothing) extends SmartIri { def this(iriStr: IRI) = this(iriStr, None, throw DataConversionException(s"Couldn't parse IRI: $iriStr")) @@ -920,11 +920,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma private val iri: IRI = validateAndEscapeIri(iriStr, errorFun) /** - * Determines the API v2 schema of an external IRI. - * - * @param segments the segments of the namespace. - * @return the IRI's API schema. - */ + * Determines the API v2 schema of an external IRI. + * + * @param segments the segments of the namespace. + * @return the IRI's API schema. + */ private def parseApiV2VersionSegments(segments: Vector[String]): ApiV2Schema = { if (segments.length < 2) { errorFun @@ -1587,11 +1587,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. Throws - * [[DataConversionException]] if the IRI is invalid. - * - * @param iri the IRI string to be parsed. - */ + * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. Throws + * [[DataConversionException]] if the IRI is invalid. + * + * @param iri the IRI string to be parsed. + */ def toSmartIri(iri: IRI, requireInternal: Boolean = false): SmartIri = { // Is this a Knora definition IRI? val smartIri: SmartIri = if (CacheableIriStarts.exists(start => iri.startsWith(start))) { @@ -1610,11 +1610,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. - * - * @param iri the IRI string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the IRI is invalid. - */ + * Constructs a [[SmartIri]] by validating and parsing a string representing an IRI. + * + * @param iri the IRI string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the IRI is invalid. + */ def toSmartIriWithErr(iri: IRI, errorFun: => Nothing): SmartIri = { // Is this a Knora definition IRI? if (CacheableIriStarts.exists(start => iri.startsWith(start))) { @@ -1627,13 +1627,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a string represents a valid integer. - * - * @param s the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a - * valid integer. - * @return the integer value of the string. - */ + * Checks that a string represents a valid integer. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a + * valid integer. + * @return the integer value of the string. + */ def validateInt(s: String, errorFun: => Nothing): Int = { try { s.toInt @@ -1643,13 +1643,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a string represents a valid decimal number. - * - * @param s the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a - * valid decimal number. - * @return the decimal value of the string. - */ + * Checks that a string represents a valid decimal number. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a + * valid decimal number. + * @return the decimal value of the string. + */ def validateBigDecimal(s: String, errorFun: => Nothing): BigDecimal = { try { BigDecimal(s) @@ -1659,23 +1659,23 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Returns `true` if a string is an IRI. - * - * @param s the string to be checked. - * @return `true` if the string is an IRI. - */ + * Returns `true` if a string is an IRI. + * + * @param s the string to be checked. + * @return `true` if the string is an IRI. + */ def isIri(s: String): Boolean = { urlValidator.isValid(s) } /** - * Checks that a string represents a valid IRI. Also encodes the IRI, preserving existing %-escapes. - * - * @param s the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * IRI. - * @return the same string. - */ + * Checks that a string represents a valid IRI. Also encodes the IRI, preserving existing %-escapes. + * + * @param s the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * IRI. + * @return the same string. + */ def validateAndEscapeIri(s: String, errorFun: => Nothing): IRI = { val urlEncodedStr = encodeAllowEscapes(s) @@ -1687,13 +1687,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid IRI. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * IRI. - * @return the same optional string. - */ + * Check that an optional string represents a valid IRI. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * IRI. + * @return the same optional string. + */ def toOptionalIri(maybeString: Option[String], errorFun: => Nothing): Option[IRI] = { maybeString match { case Some(s) => Some(validateAndEscapeIri(s, errorFun)) @@ -1702,21 +1702,21 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Returns `true` if an IRI string looks like a Knora project IRI - * - * @param iri the IRI to be checked. - */ + * Returns `true` if an IRI string looks like a Knora project IRI + * + * @param iri the IRI to be checked. + */ def isKnoraProjectIriStr(iri: IRI): Boolean = { isIri(iri) && (iri.startsWith("http://" + IriDomain + "/projects/") || isKnoraBuiltInProjectIriStr(iri)) } /** - * Returns `true` if an IRI string looks like a Knora built-in IRI: - * - http://www.knora.org/ontology/knora-admin#SystemProject - * - http://www.knora.org/ontology/knora-admin#SharedOntologiesProject - * - * @param iri the IRI to be checked. - */ + * Returns `true` if an IRI string looks like a Knora built-in IRI: + * - http://www.knora.org/ontology/knora-admin#SystemProject + * - http://www.knora.org/ontology/knora-admin#SharedOntologiesProject + * + * @param iri the IRI to be checked. + */ def isKnoraBuiltInProjectIriStr(iri: IRI): Boolean = { val builtInProjects = Seq( @@ -1728,33 +1728,33 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Returns `true` if an IRI string looks like a Knora list IRI. - * - * @param iri the IRI to be checked. - */ + * Returns `true` if an IRI string looks like a Knora list IRI. + * + * @param iri the IRI to be checked. + */ def isKnoraListIriStr(iri: IRI): Boolean = { isIri(iri) && iri.startsWith("http://" + IriDomain + "/lists/") } /** - * Returns `true` if an IRI string looks like a Knora user IRI. - * - * @param iri the IRI to be checked. - */ + * Returns `true` if an IRI string looks like a Knora user IRI. + * + * @param iri the IRI to be checked. + */ def isKnoraUserIriStr(iri: IRI): Boolean = { isIri(iri) && iri.startsWith("http://" + IriDomain + "/users/") } /** - * Checks that a string represents a valid resource identifier in a standoff link. - * - * @param s the string to be checked. - * @param acceptClientIDs if `true`, the function accepts either an IRI or an XML NCName prefixed by `ref:`. - * The latter is used to refer to a client's ID for a resource that is described in an XML bulk import. - * If `false`, only an IRI is accepted. - * @param errorFun a function that throws an exception. It will be called if the form of the string is invalid. - * @return the same string. - */ + * Checks that a string represents a valid resource identifier in a standoff link. + * + * @param s the string to be checked. + * @param acceptClientIDs if `true`, the function accepts either an IRI or an XML NCName prefixed by `ref:`. + * The latter is used to refer to a client's ID for a resource that is described in an XML bulk import. + * If `false`, only an IRI is accepted. + * @param errorFun a function that throws an exception. It will be called if the form of the string is invalid. + * @return the same string. + */ def validateStandoffLinkResourceReference(s: String, acceptClientIDs: Boolean, errorFun: => Nothing): IRI = { if (acceptClientIDs) { s match { @@ -1767,11 +1767,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks whether a string is a reference to a client's ID for a resource described in an XML bulk import. - * - * @param s the string to be checked. - * @return `true` if the string is an XML NCName prefixed by `ref:`. - */ + * Checks whether a string is a reference to a client's ID for a resource described in an XML bulk import. + * + * @param s the string to be checked. + * @return `true` if the string is an XML NCName prefixed by `ref:`. + */ def isStandoffLinkReferenceToClientIDForResource(s: String): Boolean = { s match { case StandoffLinkReferenceToClientIDForResourceRegex(_) => true @@ -1780,14 +1780,14 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Accepts a reference from a standoff link to a resource. The reference may be either a real resource IRI - * (referring to a resource that already exists) or a client's ID for a resource that doesn't yet exist and is - * described in an XML bulk import. Returns the real IRI of the target resource. - * - * @param iri an IRI from a standoff link, either in the form of a real resource IRI or in the form of - * a reference to a client's ID for a resource. - * @param clientResourceIDsToResourceIris a map of client resource IDs to real resource IRIs. - */ + * Accepts a reference from a standoff link to a resource. The reference may be either a real resource IRI + * (referring to a resource that already exists) or a client's ID for a resource that doesn't yet exist and is + * described in an XML bulk import. Returns the real IRI of the target resource. + * + * @param iri an IRI from a standoff link, either in the form of a real resource IRI or in the form of + * a reference to a client's ID for a resource. + * @param clientResourceIDsToResourceIris a map of client resource IDs to real resource IRIs. + */ def toRealStandoffLinkTargetResourceIri(iri: IRI, clientResourceIDsToResourceIris: Map[String, IRI]): IRI = { iri match { case StandoffLinkReferenceToClientIDForResourceRegex(clientResourceID) => clientResourceIDsToResourceIris(clientResourceID) @@ -1796,15 +1796,15 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Makes a string safe to be entered in the triplestore by escaping special chars. - * - * If the param `revert` is set to `true`, the string is unescaped. - * - * @param s a string. - * @param errorFun a function that throws an exception. It will be called if the string is empty or contains - * a carriage return (`\r`). - * @return the same string, escaped or unescaped as requested. - */ + * Makes a string safe to be entered in the triplestore by escaping special chars. + * + * If the param `revert` is set to `true`, the string is unescaped. + * + * @param s a string. + * @param errorFun a function that throws an exception. It will be called if the string is empty or contains + * a carriage return (`\r`). + * @return the same string, escaped or unescaped as requested. + */ def toSparqlEncodedString(s: String, errorFun: => Nothing): String = { if (s.isEmpty || s.contains("\r")) errorFun @@ -1818,11 +1818,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Unescapes a string that has been escaped for SPARQL. - * - * @param s the string to be unescaped. - * @return the unescaped string. - */ + * Unescapes a string that has been escaped for SPARQL. + * + * @param s the string to be unescaped. + * @return the unescaped string. + */ def fromSparqlEncodedString(s: String): String = { StringUtils.replaceEach( s, @@ -1832,22 +1832,22 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Encodes a string for use in JSON, and encloses it in quotation marks. - * - * @param s the string to be encoded. - * @return the encoded string. - */ + * Encodes a string for use in JSON, and encloses it in quotation marks. + * + * @param s the string to be encoded. + * @return the encoded string. + */ def toJsonEncodedString(s: String): String = { JsString(s).compactPrint } /** - * Parses an object of `salsah-gui:guiAttributeDefinition`. - * - * @param s the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string is invalid. - * @return a [[SalsahGuiAttributeDefinition]]. - */ + * Parses an object of `salsah-gui:guiAttributeDefinition`. + * + * @param s the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string is invalid. + * @return a [[SalsahGuiAttributeDefinition]]. + */ def toSalsahGuiAttributeDefinition(s: String, errorFun: => Nothing): SalsahGuiAttributeDefinition = { s match { case SalsahGuiAttributeDefinitionRegex(attributeName, Optional(maybeRequired), allowedTypeStr, _, Optional(maybeEnumeratedValuesStr)) => @@ -1877,13 +1877,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Parses an object of `salsah-gui:guiAttribute`. - * - * @param s the string to be parsed. - * @param attributeDefs the values of `salsah-gui:guiAttributeDefinition` for the property. - * @param errorFun a function that throws an exception. It will be called if the string is invalid. - * @return a [[SalsahGuiAttribute]]. - */ + * Parses an object of `salsah-gui:guiAttribute`. + * + * @param s the string to be parsed. + * @param attributeDefs the values of `salsah-gui:guiAttributeDefinition` for the property. + * @param errorFun a function that throws an exception. It will be called if the string is invalid. + * @return a [[SalsahGuiAttribute]]. + */ def toSalsahGuiAttribute(s: String, attributeDefs: Set[SalsahGuiAttributeDefinition], errorFun: => Nothing): SalsahGuiAttribute = { // Try to parse the expression using a regex. s match { @@ -1941,12 +1941,12 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Parses an `xsd:dateTimeStamp`. - * - * @param s the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return an [[Instant]]. - */ + * Parses an `xsd:dateTimeStamp`. + * + * @param s the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return an [[Instant]]. + */ def xsdDateTimeStampToInstant(s: String, errorFun: => Nothing): Instant = { try { val accessor: TemporalAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(s) @@ -1957,12 +1957,12 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Parses a Knora ARK timestamp. - * - * @param timestampStr the string to be parsed. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return an [[Instant]]. - */ + * Parses a Knora ARK timestamp. + * + * @param timestampStr the string to be parsed. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return an [[Instant]]. + */ def arkTimestampToInstant(timestampStr: String, errorFun: => Nothing): Instant = { timestampStr match { case ArkTimestampRegex(year, month, day, hour, minute, second, Optional(maybeFraction)) => @@ -1997,11 +1997,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Formats a Knora ARK timestamp. - * - * @param timestamp the timestamp to be formatted. - * @return a string representation of the timestamp. - */ + * Formats a Knora ARK timestamp. + * + * @param timestamp the timestamp to be formatted. + * @return a string representation of the timestamp. + */ def formatArkTimestamp(timestamp: Instant): String = { val offsetDateTime: OffsetDateTime = timestamp.atOffset(ZoneOffset.UTC) @@ -2025,13 +2025,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a geometry string contains valid JSON. - * - * @param s a geometry string. - * @param errorFun a function that throws an exception. It will be called if the string does not contain valid - * JSON. - * @return the same string. - */ + * Checks that a geometry string contains valid JSON. + * + * @param s a geometry string. + * @param errorFun a function that throws an exception. It will be called if the string does not contain valid + * JSON. + * @return the same string. + */ def validateGeometryString(s: String, errorFun: => Nothing): String = { // TODO: For now, we just make sure that the string is valid JSON. We should stop storing JSON in the triplestore, and represent geometry in RDF instead (issue 169). @@ -2044,13 +2044,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a hexadecimal color code string is valid. - * - * @param s a string containing a hexadecimal color code. - * @param errorFun a function that throws an exception. It will be called if the string does not contain a valid - * hexadecimal color code. - * @return the same string. - */ + * Checks that a hexadecimal color code string is valid. + * + * @param s a string containing a hexadecimal color code. + * @param errorFun a function that throws an exception. It will be called if the string does not contain a valid + * hexadecimal color code. + * @return the same string. + */ def validateColor(s: String, errorFun: => Nothing): String = { ColorRegex.findFirstIn(s) match { case Some(dateStr) => dateStr @@ -2059,12 +2059,12 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that the format of a Knora date string is valid. - * - * @param s a Knora date string. - * @param errorFun a function that throws an exception. It will be called if the date's format is invalid. - * @return the same string. - */ + * Checks that the format of a Knora date string is valid. + * + * @param s a Knora date string. + * @param errorFun a function that throws an exception. It will be called if the date's format is invalid. + * @return the same string. + */ def validateDate(s: String, errorFun: => Nothing): String = { // if the pattern doesn't match (=> None), the date string is formally invalid // Please note that this is a mere formal validation, @@ -2076,13 +2076,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a string contains a valid boolean value. - * - * @param s a string containing a boolean value. - * @param errorFun a function that throws an exception. It will be called if the string does not contain - * a boolean value. - * @return the boolean value of the string. - */ + * Checks that a string contains a valid boolean value. + * + * @param s a string containing a boolean value. + * @param errorFun a function that throws an exception. It will be called if the string does not contain + * a boolean value. + * @return the boolean value of the string. + */ def validateBoolean(s: String, errorFun: => Nothing): Boolean = { try { s.toBoolean @@ -2092,11 +2092,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Map over all standoff tags to collect IRIs that are referred to by linking standoff tags. - * - * @param standoffTags The list of [[StandoffTagV2]]. - * @return a set of Iris referred to in the [[StandoffTagV2]]. - */ + * Map over all standoff tags to collect IRIs that are referred to by linking standoff tags. + * + * @param standoffTags The list of [[StandoffTagV2]]. + * @return a set of Iris referred to in the [[StandoffTagV2]]. + */ def getResourceIrisFromStandoffTags(standoffTags: Seq[StandoffTagV2]): Set[IRI] = { standoffTags.foldLeft(Set.empty[IRI]) { case (acc: Set[IRI], standoffNode: StandoffTagV2) => @@ -2113,14 +2113,14 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Turn a possibly empty string value into a boolean value. - * Returns false if the value is empty or if the given string is cannot be converted to a Boolean `true`. - * - * @param maybe an optional string representation of a boolean value. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed - * as a boolean value. - * @return a Boolean. - */ + * Turn a possibly empty string value into a boolean value. + * Returns false if the value is empty or if the given string is cannot be converted to a Boolean `true`. + * + * @param maybe an optional string representation of a boolean value. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed + * as a boolean value. + * @return a Boolean. + */ def optionStringToBoolean(maybe: Option[String], errorFun: => Nothing): Boolean = { try { maybe.exists(_.toBoolean) @@ -2130,13 +2130,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Converts a string to a boolean. - * - * @param s the string to be converted. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed - * as a boolean value. - * @return a Boolean. - */ + * Converts a string to a boolean. + * + * @param s the string to be converted. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed + * as a boolean value. + * @return a Boolean. + */ def toBoolean(s: String, errorFun: => Nothing): Boolean = { try { s.toBoolean @@ -2146,12 +2146,12 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a string is a valid XML [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. - * - * @param ncName the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string is invalid. - * @return the same string. - */ + * Checks that a string is a valid XML [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. + * + * @param ncName the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string is invalid. + * @return the same string. + */ def validateNCName(ncName: String, errorFun: => Nothing): String = { NCNameRegex.findFirstIn(ncName) match { case Some(value) => value @@ -2160,22 +2160,22 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Returns `true` if an ontology name is reserved for a built-in ontology. - * - * @param ontologyName the ontology name to be checked. - * @return `true` if the ontology name is reserved for a built-in ontology. - */ + * Returns `true` if an ontology name is reserved for a built-in ontology. + * + * @param ontologyName the ontology name to be checked. + * @return `true` if the ontology name is reserved for a built-in ontology. + */ def isBuiltInOntologyName(ontologyName: String): Boolean = { OntologyConstants.BuiltInOntologyLabels.contains(ontologyName) } /** - * Checks that a name is valid as a project-specific ontology name. - * - * @param ontologyName the ontology name to be checked. - * @param errorFun a function that throws an exception. It will be called if the name is invalid. - * @return the same ontology name. - */ + * Checks that a name is valid as a project-specific ontology name. + * + * @param ontologyName the ontology name to be checked. + * @param errorFun a function that throws an exception. It will be called if the name is invalid. + * @return the same ontology name. + */ def validateProjectSpecificOntologyName(ontologyName: String, errorFun: => Nothing): String = { ontologyName match { case NCNameRegex(_*) => () @@ -2203,13 +2203,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Given a valid internal (built-in or project-specific) ontology name and an optional project code, constructs the - * corresponding internal ontology IRI. - * - * @param internalOntologyName the ontology name. - * @param projectCode the project code. - * @return the ontology IRI. - */ + * Given a valid internal (built-in or project-specific) ontology name and an optional project code, constructs the + * corresponding internal ontology IRI. + * + * @param internalOntologyName the ontology name. + * @param projectCode the project code. + * @return the ontology IRI. + */ private def makeInternalOntologyIriStr(internalOntologyName: String, isShared: Boolean, projectCode: Option[String]): IRI = { val internalOntologyIri = new StringBuilder(OntologyConstants.KnoraInternal.InternalOntologyStart) @@ -2230,24 +2230,24 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Given a valid internal ontology name and an optional project code, constructs the corresponding internal - * ontology IRI. - * - * @param internalOntologyName the ontology name. - * @param projectCode the project code. - * @return the ontology IRI. - */ + * Given a valid internal ontology name and an optional project code, constructs the corresponding internal + * ontology IRI. + * + * @param internalOntologyName the ontology name. + * @param projectCode the project code. + * @return the ontology IRI. + */ def makeProjectSpecificInternalOntologyIri(internalOntologyName: String, isShared: Boolean, projectCode: String): SmartIri = { toSmartIri(makeInternalOntologyIriStr(internalOntologyName, isShared, Some(projectCode))) } /** - * Converts an internal ontology name to an external ontology name. This only affects `knora-base`, whose - * external equivalent is `knora-api.` - * - * @param ontologyName an internal ontology name. - * @return the corresponding external ontology name. - */ + * Converts an internal ontology name to an external ontology name. This only affects `knora-base`, whose + * external equivalent is `knora-api.` + * + * @param ontologyName an internal ontology name. + * @return the corresponding external ontology name. + */ private def internalToExternalOntologyName(ontologyName: String): String = { if (ontologyName == OntologyConstants.KnoraBase.KnoraBaseOntologyLabel) { OntologyConstants.KnoraApi.KnoraApiOntologyLabel @@ -2257,12 +2257,12 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Converts an external ontology name to an internal ontology name. This only affects `knora-api`, whose - * internal equivalent is `knora-base.` - * - * @param ontologyName an external ontology name. - * @return the corresponding internal ontology name. - */ + * Converts an external ontology name to an internal ontology name. This only affects `knora-api`, whose + * internal equivalent is `knora-base.` + * + * @param ontologyName an external ontology name. + * @return the corresponding internal ontology name. + */ private def externalToInternalOntologyName(ontologyName: String): String = { if (ontologyName == OntologyConstants.KnoraApi.KnoraApiOntologyLabel) { OntologyConstants.KnoraBase.KnoraBaseOntologyLabel @@ -2272,13 +2272,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Converts the IRI of a project-specific internal ontology (used in the triplestore) to an XML prefix label and - * namespace for use in data import. - * - * @param internalOntologyIri the IRI of the project-specific internal ontology. Any trailing # character will be - * stripped before the conversion. - * @return the corresponding XML prefix label and import namespace. - */ + * Converts the IRI of a project-specific internal ontology (used in the triplestore) to an XML prefix label and + * namespace for use in data import. + * + * @param internalOntologyIri the IRI of the project-specific internal ontology. Any trailing # character will be + * stripped before the conversion. + * @return the corresponding XML prefix label and import namespace. + */ def internalOntologyIriToXmlNamespaceInfoV1(internalOntologyIri: SmartIri): XmlImportNamespaceInfoV1 = { val namespace = new StringBuilder(OntologyConstants.KnoraXmlImportV1.ProjectSpecificXmlImportNamespace.XmlImportNamespaceStart) @@ -2300,14 +2300,14 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Converts an XML namespace (used in XML data import) to the IRI of a project-specific internal ontology (used - * in the triplestore). The resulting IRI will not end in a # character. - * - * @param namespace the XML namespace. - * @param errorFun a function that throws an exception. It will be called if the form of the string is not - * valid for a Knora XML import namespace. - * @return the corresponding project-specific internal ontology IRI. - */ + * Converts an XML namespace (used in XML data import) to the IRI of a project-specific internal ontology (used + * in the triplestore). The resulting IRI will not end in a # character. + * + * @param namespace the XML namespace. + * @param errorFun a function that throws an exception. It will be called if the form of the string is not + * valid for a Knora XML import namespace. + * @return the corresponding project-specific internal ontology IRI. + */ def xmlImportNamespaceToInternalOntologyIriV1(namespace: String, errorFun: => Nothing): SmartIri = { namespace match { case ProjectSpecificXmlImportNamespaceRegex(Optional(maybeShared), _, Optional(maybeProjectCode), ontologyName) if !isBuiltInOntologyName(ontologyName) => @@ -2329,28 +2329,28 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Converts a XML element name in a particular namespace (used in XML data import) to the IRI of a - * project-specific internal ontology entity (used in the triplestore). - * - * @param namespace the XML namespace. - * @param elementLabel the XML element label. - * @param errorFun a function that throws an exception. It will be called if the form of the namespace is not - * valid for a Knora XML import namespace. - * @return the corresponding project-specific internal ontology entity IRI. - */ + * Converts a XML element name in a particular namespace (used in XML data import) to the IRI of a + * project-specific internal ontology entity (used in the triplestore). + * + * @param namespace the XML namespace. + * @param elementLabel the XML element label. + * @param errorFun a function that throws an exception. It will be called if the form of the namespace is not + * valid for a Knora XML import namespace. + * @return the corresponding project-specific internal ontology entity IRI. + */ def xmlImportElementNameToInternalOntologyIriV1(namespace: String, elementLabel: String, errorFun: => Nothing): IRI = { val ontologyIri = xmlImportNamespaceToInternalOntologyIriV1(namespace, errorFun) ontologyIri + "#" + elementLabel } /** - * In XML import data, a property from another ontology is referred to as `prefixLabel__localName`. The prefix label - * may start with a project ID (prefixed with 'p') and a hyphen. This function attempts to parse a property name in - * that format. - * - * @param prefixLabelAndLocalName a string that may refer to a property in the format `prefixLabel__localName`. - * @return if successful, a `Some` containing the entity's internal IRI, otherwise `None`. - */ + * In XML import data, a property from another ontology is referred to as `prefixLabel__localName`. The prefix label + * may start with a project ID (prefixed with 'p') and a hyphen. This function attempts to parse a property name in + * that format. + * + * @param prefixLabelAndLocalName a string that may refer to a property in the format `prefixLabel__localName`. + * @return if successful, a `Some` containing the entity's internal IRI, otherwise `None`. + */ def toPropertyIriFromOtherOntologyInXmlImport(prefixLabelAndLocalName: String): Option[IRI] = { prefixLabelAndLocalName match { case PropertyFromOtherOntologyInXmlImportRegex(_, Optional(maybeProjectID), prefixLabel, localName) => @@ -2377,14 +2377,14 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks that a string represents a valid path for a `knora-base:Map`. A valid path must be a sequence of names - * separated by slashes (`/`). Each name must be a valid XML - * [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. - * - * @param mapPath the path to be checked. - * @param errorFun a function that throws an exception. It will be called if the path is invalid. - * @return the same path. - */ + * Checks that a string represents a valid path for a `knora-base:Map`. A valid path must be a sequence of names + * separated by slashes (`/`). Each name must be a valid XML + * [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. + * + * @param mapPath the path to be checked. + * @param errorFun a function that throws an exception. It will be called if the path is invalid. + * @return the same path. + */ def validateMapPath(mapPath: String, errorFun: => Nothing): String = { val splitPath: Array[String] = mapPath.split('/') @@ -2396,11 +2396,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Determines whether a URL path refers to a built-in API v2 ontology (simple or complex). - * - * @param urlPath the URL path. - * @return true if the path refers to a built-in API v2 ontology. - */ + * Determines whether a URL path refers to a built-in API v2 ontology (simple or complex). + * + * @param urlPath the URL path. + * @return true if the path refers to a built-in API v2 ontology. + */ def isBuiltInApiV2OntologyUrlPath(urlPath: String): Boolean = { urlPath match { case ApiV2OntologyUrlPathRegex(_, _, ontologyName, _) if isBuiltInOntologyName(ontologyName) => true @@ -2409,11 +2409,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Determines whether a URL path refers to a project-specific API v2 ontology (simple or complex). - * - * @param urlPath the URL path. - * @return true if the path refers to a project-specific API v2 ontology. - */ + * Determines whether a URL path refers to a project-specific API v2 ontology (simple or complex). + * + * @param urlPath the URL path. + * @return true if the path refers to a project-specific API v2 ontology. + */ def isProjectSpecificApiV2OntologyUrlPath(urlPath: String): Boolean = { urlPath match { case ApiV2OntologyUrlPathRegex(_, _, ontologyName, _) if !isBuiltInOntologyName(ontologyName) => true @@ -2422,31 +2422,31 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Given the projectInfo calculates the project's data named graph. - * - * @param projectInfo the project's [[ProjectInfoV1]]. - * @return the IRI of the project's data named graph. - */ + * Given the projectInfo calculates the project's data named graph. + * + * @param projectInfo the project's [[ProjectInfoV1]]. + * @return the IRI of the project's data named graph. + */ def projectDataNamedGraph(projectInfo: ProjectInfoV1): IRI = { OntologyConstants.NamedGraphs.DataNamedGraphStart + "/" + projectInfo.shortcode + "/" + projectInfo.shortname } /** - * Given the [[ProjectADM]] calculates the project's data named graph. - * - * @param project the project's [[ProjectADM]]. - * @return the IRI of the project's data named graph. - */ + * Given the [[ProjectADM]] calculates the project's data named graph. + * + * @param project the project's [[ProjectADM]]. + * @return the IRI of the project's data named graph. + */ def projectDataNamedGraphV2(project: ProjectADM): IRI = { OntologyConstants.NamedGraphs.DataNamedGraphStart + "/" + project.shortcode + "/" + project.shortname } /** - * Given the project IRI, checks if it is in a valid format. - * - * @param iri the project's IRI. - * @return the IRI of the project. - */ + * Given the project IRI, checks if it is in a valid format. + * + * @param iri the project's IRI. + * @return the IRI of the project. + */ def validateProjectIri(iri: IRI, errorFun: => Nothing): IRI = { if (isKnoraProjectIriStr(iri)) { iri @@ -2456,13 +2456,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that the supplied IRI represents a valid project IRI. - * - * @param iri the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project IRI. - * @return the same string but escaped. - */ + * Check that the supplied IRI represents a valid project IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project IRI. + * @return the same string but escaped. + */ def validateAndEscapeProjectIri(iri: IRI, errorFun: => Nothing): IRI = { if (isKnoraProjectIriStr(iri)) { toSparqlEncodedString(iri, errorFun) @@ -2472,13 +2472,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid project IRI. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project IRI. - * @return the same optional string but escaped. - */ + * Check that an optional string represents a valid project IRI. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project IRI. + * @return the same optional string but escaped. + */ def validateAndEscapeOptionalProjectIri(maybeString: Option[String], errorFun: => Nothing): Option[String] = { maybeString match { case Some(s) => Some(validateAndEscapeProjectIri(s, errorFun)) @@ -2487,13 +2487,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that the string represents a valid project shortname. - * - * @param value the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortname. - * @return the same string. - */ + * Check that the string represents a valid project shortname. + * + * @param value the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortname. + * @return the same string. + */ def validateAndEscapeProjectShortname(value: String, errorFun: => Nothing): String = { ProjectShortnameRegex.findFirstIn(value) match { case Some(shortname) => toSparqlEncodedString(shortname, errorFun) @@ -2502,13 +2502,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid project shortname. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortname. - * @return the same optional string. - */ + * Check that an optional string represents a valid project shortname. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortname. + * @return the same optional string. + */ def validateAndEscapeOptionalProjectShortname(maybeString: Option[String], errorFun: => Nothing): Option[String] = { maybeString match { case Some(s) => Some(validateAndEscapeProjectShortname(s, errorFun)) @@ -2517,11 +2517,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. - * - * @param shortcode the project's shortcode. - * @return the shortcode in upper case. - */ + * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. + * + * @param shortcode the project's shortcode. + * @return the shortcode in upper case. + */ def validateProjectShortcode(shortcode: String, errorFun: => Nothing): String = { ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { case Some(value) => value @@ -2530,11 +2530,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. - * - * @param shortcode the project's shortcode. - * @return the shortcode in upper case. - */ + * Given the project shortcode, checks if it is in a valid format, and converts it to upper case. + * + * @param shortcode the project's shortcode. + * @return the shortcode in upper case. + */ def validateProjectShortcodeOption(shortcode: String): Option[String] = { ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { case Some(value) => Some(value) @@ -2543,13 +2543,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that a string represents a valid project shortcode. - * - * @param shortcode the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortcode. - * @return the same string. - */ + * Check that a string represents a valid project shortcode. + * + * @param shortcode the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortcode. + * @return the same string. + */ def validateAndEscapeProjectShortcode(shortcode: String, errorFun: => Nothing): String = { ProjectIDRegex.findFirstIn(shortcode.toUpperCase) match { case Some(definedShortcode) => toSparqlEncodedString(definedShortcode, errorFun) @@ -2558,13 +2558,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid project shortcode. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * project shortcode. - * @return the same optional string. - */ + * Check that an optional string represents a valid project shortcode. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * project shortcode. + * @return the same optional string. + */ def validateAndEscapeOptionalProjectShortcode(maybeString: Option[String], errorFun: => Nothing): Option[String] = { maybeString match { case Some(s) => Some(validateAndEscapeProjectShortcode(s, errorFun)) @@ -2573,13 +2573,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that the supplied IRI represents a valid user IRI. - * - * @param iri the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * user IRI. - * @return the same string. - */ + * Check that the supplied IRI represents a valid user IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same string. + */ def validateUserIri(iri: IRI, errorFun: => Nothing): IRI = { if (isKnoraUserIriStr(iri)) { iri @@ -2589,13 +2589,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that the supplied IRI represents a valid user IRI. - * - * @param iri the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * user IRI. - * @return the same string but escaped. - */ + * Check that the supplied IRI represents a valid user IRI. + * + * @param iri the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same string but escaped. + */ def validateAndEscapeUserIri(iri: IRI, errorFun: => Nothing): String = { if (isKnoraUserIriStr(iri)) { toSparqlEncodedString(iri, errorFun) @@ -2605,13 +2605,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid user IRI. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * user IRI. - * @return the same optional string. - */ + * Check that an optional string represents a valid user IRI. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * user IRI. + * @return the same optional string. + */ def validateAndEscapeOptionalUserIri(maybeString: Option[String], errorFun: => Nothing): Option[String] = { maybeString match { case Some(s) => Some(validateAndEscapeUserIri(s, errorFun)) @@ -2620,21 +2620,21 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Given an email address, checks if it is in a valid format. - * - * @param email the email. - * @return the email - */ + * Given an email address, checks if it is in a valid format. + * + * @param email the email. + * @return the email + */ def validateEmail(email: String): Option[String] = { EmailAddressRegex.findFirstIn(email) } /** - * Given an email address, checks if it is in a valid format. - * - * @param email the email. - * @return the email - */ + * Given an email address, checks if it is in a valid format. + * + * @param email the email. + * @return the email + */ def validateEmailAndThrow(email: String, errorFun: => Nothing): String = { EmailAddressRegex.findFirstIn(email) match { case Some(value) => value @@ -2643,13 +2643,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid email address. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * email address. - * @return the same optional string. - */ + * Check that an optional string represents a valid email address. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * email address. + * @return the same optional string. + */ def validateAndEscapeOptionalEmail(maybeString: Option[String], errorFun: => Nothing): Option[String] = { maybeString match { case Some(s) => Some(toSparqlEncodedString(validateEmailAndThrow(s, errorFun), errorFun)) @@ -2673,13 +2673,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that the string represents a valid username and escape any special characters. - * - * @param value the string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * username. - * @return the same string with escaped special characters. - */ + * Check that the string represents a valid username and escape any special characters. + * + * @param value the string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * username. + * @return the same string with escaped special characters. + */ def validateAndEscapeUsername(value: String, errorFun: => Nothing): String = { UsernameRegex.findFirstIn(value) match { case Some(username) => toSparqlEncodedString(username, errorFun) @@ -2688,13 +2688,13 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Check that an optional string represents a valid username. - * - * @param maybeString the optional string to be checked. - * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid - * username. - * @return the same optional string. - */ + * Check that an optional string represents a valid username. + * + * @param maybeString the optional string to be checked. + * @param errorFun a function that throws an exception. It will be called if the string does not represent a valid + * username. + * @return the same optional string. + */ def validateAndEscapeOptionalUsername(maybeString: Option[String], errorFun: => Nothing): Option[String] = { maybeString match { case Some(s) => Some(validateAndEscapeUsername(s, errorFun)) @@ -2703,22 +2703,22 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Generates an ARK URL for a resource or value, as per [[https://tools.ietf.org/html/draft-kunze-ark-18]]. - * - * @param projectID the shortcode of the project that the resource belongs to. - * @param resourceID the resource's ID (the last component of its IRI). - * @param maybeValueUUID if this is an ARK URL for a value, the value's UUID. - * @param maybeTimestamp a timestamp indicating the point in the resource's version history that the ARK URL should - * cite. - * @return an ARK URL that can be resolved to obtain the resource or value. - */ + * Generates an ARK URL for a resource or value, as per [[https://tools.ietf.org/html/draft-kunze-ark-18]]. + * + * @param projectID the shortcode of the project that the resource belongs to. + * @param resourceID the resource's ID (the last component of its IRI). + * @param maybeValueUUID if this is an ARK URL for a value, the value's UUID. + * @param maybeTimestamp a timestamp indicating the point in the resource's version history that the ARK URL should + * cite. + * @return an ARK URL that can be resolved to obtain the resource or value. + */ private def makeArkUrl(projectID: String, resourceID: String, maybeValueUUID: Option[UUID] = None, maybeTimestamp: Option[Instant] = None): String = { /** - * Adds a check digit to a Base64-encoded ID, and escapes '-' as '=', because '-' can be ignored in ARK URLs. - * - * @param id a Base64-encoded ID. - * @return the ID with a check digit added. - */ + * Adds a check digit to a Base64-encoded ID, and escapes '-' as '=', because '-' can be ignored in ARK URLs. + * + * @param id a Base64-encoded ID. + * @return the ID with a check digit added. + */ def addCheckDigitAndEscape(id: String): String = { val checkDigitTry: Try[String] = Try { base64UrlCheckDigit.calculate(id) @@ -2761,12 +2761,12 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Attempts to create a new IRI that isn't already used in the triplestore. Will try up to [[MAX_IRI_ATTEMPTS]] - * times, then throw an exception if an unused IRI could not be created. - * - * @param iriFun a function that generates a random IRI. - * @param storeManager a reference to the Knora store manager actor. - */ + * Attempts to create a new IRI that isn't already used in the triplestore. Will try up to [[MAX_IRI_ATTEMPTS]] + * times, then throw an exception if an unused IRI could not be created. + * + * @param iriFun a function that generates a random IRI. + * @param storeManager a reference to the Knora store manager actor. + */ def makeUnusedIri(iriFun: => IRI, storeManager: ActorRef, log: LoggingAdapter)(implicit timeout: Timeout, executionContext: ExecutionContext): Future[IRI] = { @@ -2792,24 +2792,24 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Generates a type 4 UUID using [[java.util.UUID]], and Base64-encodes it using a URL and filename safe - * Base64 encoder from [[java.util.Base64]], without padding. This results in a 22-character string that - * can be used as a unique identifier in IRIs. - * - * @return a random, Base64-encoded UUID. - */ + * Generates a type 4 UUID using [[java.util.UUID]], and Base64-encodes it using a URL and filename safe + * Base64 encoder from [[java.util.Base64]], without padding. This results in a 22-character string that + * can be used as a unique identifier in IRIs. + * + * @return a random, Base64-encoded UUID. + */ def makeRandomBase64EncodedUuid: String = { val uuid = UUID.randomUUID base64EncodeUuid(uuid) } /** - * Base64-encodes a [[UUID]] using a URL and filename safe Base64 encoder from [[java.util.Base64]], - * without padding. This results in a 22-character string that can be used as a unique identifier in IRIs. - * - * @param uuid the [[UUID]] to be encoded. - * @return a 22-character string representing the UUID. - */ + * Base64-encodes a [[UUID]] using a URL and filename safe Base64 encoder from [[java.util.Base64]], + * without padding. This results in a 22-character string that can be used as a unique identifier in IRIs. + * + * @param uuid the [[UUID]] to be encoded. + * @return a 22-character string representing the UUID. + */ def base64EncodeUuid(uuid: UUID): String = { val bytes = Array.ofDim[Byte](16) val byteBuffer = ByteBuffer.wrap(bytes) @@ -2819,11 +2819,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Decodes a Base64-encoded UUID. - * - * @param base64Uuid the Base64-encoded UUID to be decoded. - * @return the equivalent [[UUID]]. - */ + * Decodes a Base64-encoded UUID. + * + * @param base64Uuid the Base64-encoded UUID to be decoded. + * @return the equivalent [[UUID]]. + */ def base64DecodeUuid(base64Uuid: String): UUID = { val bytes = base64Decoder.decode(base64Uuid) val byteBuffer = ByteBuffer.wrap(bytes) @@ -2831,15 +2831,33 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Encodes a [[UUID]] as a string in one of two formats: - * - * - The canonical 36-character format. - * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. - * - * @param uuid the UUID to be encoded. - * @param useBase64 if `true`, uses Base64 encoding. - * @return the encoded UUID. - */ + * Validates and decodes a Base64-encoded UUID. + * + * @param base64Uuid the UUID to be validated. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return the decoded UUID. + */ + def validateBase64EncodedUuid(base64Uuid: String, errorFun: => Nothing): UUID = { + val decodeTry = Try { + base64DecodeUuid(base64Uuid) + } + + decodeTry match { + case Success(uuid) => uuid + case Failure(_) => errorFun + } + } + + /** + * Encodes a [[UUID]] as a string in one of two formats: + * + * - The canonical 36-character format. + * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. + * + * @param uuid the UUID to be encoded. + * @param useBase64 if `true`, uses Base64 encoding. + * @return the encoded UUID. + */ def encodeUuid(uuid: UUID, useBase64: Boolean): String = { if (useBase64) { base64EncodeUuid(uuid) @@ -2849,25 +2867,25 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Calls `decodeUuidWithErr`, throwing [[InconsistentTriplestoreDataException]] if the string cannot be parsed. - */ + * Calls `decodeUuidWithErr`, throwing [[InconsistentTriplestoreDataException]] if the string cannot be parsed. + */ def decodeUuid(uuidStr: String): UUID = { decodeUuidWithErr(uuidStr, throw InconsistentTriplestoreDataException(s"Invalid UUID: $uuidStr")) } /** - * Decodes a string representing a UUID in one of two formats: - * - * - The canonical 36-character format. - * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. - * - * Shorter strings are padded with leading zeroes to 22 characters and parsed in Base64 format - * (this is non-reversible, and is needed only for working with test data). - * - * @param uuidStr the string to be decoded. - * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. - * @return the decoded [[UUID]]. - */ + * Decodes a string representing a UUID in one of two formats: + * + * - The canonical 36-character format. + * - The 22-character Base64-encoded format returned by [[base64EncodeUuid]]. + * + * Shorter strings are padded with leading zeroes to 22 characters and parsed in Base64 format + * (this is non-reversible, and is needed only for working with test data). + * + * @param uuidStr the string to be decoded. + * @param errorFun a function that throws an exception. It will be called if the string cannot be parsed. + * @return the decoded [[UUID]]. + */ def decodeUuidWithErr(uuidStr: String, errorFun: => Nothing): UUID = { if (uuidStr.length == CanonicalUuidLength) { UUID.fromString(uuidStr) @@ -2881,43 +2899,43 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Checks if a string is the right length to be a canonical or Base64-encoded UUID. - * - * @param idStr the string to check. - * @return `true` if the string is the right length to be a canonical or Base64-encoded UUID. - */ + * Checks if a string is the right length to be a canonical or Base64-encoded UUID. + * + * @param idStr the string to check. + * @return `true` if the string is the right length to be a canonical or Base64-encoded UUID. + */ def couldBeUuid(idStr: String): Boolean = { idStr.length == CanonicalUuidLength || idStr.length == Base64UuidLength } /** - * Creates a new resource IRI based on a UUID. - * - * @param projectShortcode the project's shortcode. - * @return a new resource IRI. - */ + * Creates a new resource IRI based on a UUID. + * + * @param projectShortcode the project's shortcode. + * @return a new resource IRI. + */ def makeRandomResourceIri(projectShortcode: String): IRI = { val knoraResourceID = makeRandomBase64EncodedUuid s"http://$IriDomain/$projectShortcode/$knoraResourceID" } /** - * Creates a new value IRI based on a UUID. - * - * @param resourceIri the IRI of the resource that will contain the value. - * @return a new value IRI. - */ + * Creates a new value IRI based on a UUID. + * + * @param resourceIri the IRI of the resource that will contain the value. + * @return a new value IRI. + */ def makeRandomValueIri(resourceIri: IRI): IRI = { val knoraValueUuid = makeRandomBase64EncodedUuid s"$resourceIri/values/$knoraValueUuid" } /** - * Creates a mapping IRI based on a project IRI and a mapping name. - * - * @param projectIri the IRI of the project the mapping will belong to. - * @return a mapping IRI. - */ + * Creates a mapping IRI based on a project IRI and a mapping name. + * + * @param projectIri the IRI of the project the mapping will belong to. + * @return a mapping IRI. + */ def makeProjectMappingIri(projectIri: IRI, mappingName: String): IRI = { val mappingIri = s"$projectIri/mappings/$mappingName" // check that the mapping IRI is valid (mappingName is user input) @@ -2925,86 +2943,86 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Creates a random IRI for an element of a mapping based on a mapping IRI. - * - * @param mappingIri the IRI of the mapping the element belongs to. - * @return a new mapping element IRI. - */ + * Creates a random IRI for an element of a mapping based on a mapping IRI. + * + * @param mappingIri the IRI of the mapping the element belongs to. + * @return a new mapping element IRI. + */ def makeRandomMappingElementIri(mappingIri: IRI): IRI = { val knoraMappingElementUuid = makeRandomBase64EncodedUuid s"$mappingIri/elements/$knoraMappingElementUuid" } /** - * Creates an IRI used as a lock for the creation of mappings inside a given project. - * This method will always return the same IRI for the given project IRI. - * - * @param projectIri the IRI of the project the mapping will belong to. - * @return an IRI used as a lock for the creation of mappings inside a given project. - */ + * Creates an IRI used as a lock for the creation of mappings inside a given project. + * This method will always return the same IRI for the given project IRI. + * + * @param projectIri the IRI of the project the mapping will belong to. + * @return an IRI used as a lock for the creation of mappings inside a given project. + */ def createMappingLockIriForProject(projectIri: IRI): IRI = { s"$projectIri/mappings" } /** - * Creates a new project IRI based on a UUID or project shortcode. - * - * @param shortcode the required project shortcode. - * @return a new project IRI. - */ + * Creates a new project IRI based on a UUID or project shortcode. + * + * @param shortcode the required project shortcode. + * @return a new project IRI. + */ def makeRandomProjectIri(shortcode: String): IRI = { s"http://$IriDomain/projects/$shortcode" } /** - * Creates a new group IRI based on a UUID. - * - * @param shortcode the required project shortcode. - * @return a new group IRI. - */ + * Creates a new group IRI based on a UUID. + * + * @param shortcode the required project shortcode. + * @return a new group IRI. + */ def makeRandomGroupIri(shortcode: String): String = { val knoraGroupUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/groups/$shortcode/$knoraGroupUuid" } /** - * Creates a new person IRI based on a UUID. - * - * @return a new person IRI. - */ + * Creates a new person IRI based on a UUID. + * + * @return a new person IRI. + */ def makeRandomPersonIri: IRI = { val knoraPersonUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/users/$knoraPersonUuid" } /** - * Creates a new list IRI based on a UUID. - * - * @param shortcode the required project shortcode. - * @return a new list IRI. - */ + * Creates a new list IRI based on a UUID. + * + * @param shortcode the required project shortcode. + * @return a new list IRI. + */ def makeRandomListIri(shortcode: String): String = { val knoraListUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/lists/$shortcode/$knoraListUuid" } /** - * Creates a new standoff tag IRI based on a UUID. - * - * @param valueIri the IRI of the text value containing the standoff tag. - * @param startIndex the standoff tag's start index. - * @return a standoff tag IRI. - */ + * Creates a new standoff tag IRI based on a UUID. + * + * @param valueIri the IRI of the text value containing the standoff tag. + * @param startIndex the standoff tag's start index. + * @return a standoff tag IRI. + */ def makeRandomStandoffTagIri(valueIri: IRI, startIndex: Int): IRI = { s"$valueIri/standoff/$startIndex" } /** - * Converts the IRI of a property that points to a resource into the IRI of the corresponding link value property. - * - * @param linkPropertyIri the IRI of the property that points to a resource. - * @return the IRI of the corresponding link value property. - */ + * Converts the IRI of a property that points to a resource into the IRI of the corresponding link value property. + * + * @param linkPropertyIri the IRI of the property that points to a resource. + * @return the IRI of the corresponding link value property. + */ def linkPropertyIriToLinkValuePropertyIri(linkPropertyIri: IRI): IRI = { implicit val stringFormatter: StringFormatter = this @@ -3012,11 +3030,11 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Converts the IRI of a property that points to a `knora-base:LinkValue` into the IRI of the corresponding link property. - * - * @param linkValuePropertyIri the IRI of the property that points to the `LinkValue`. - * @return the IRI of the corresponding link property. - */ + * Converts the IRI of a property that points to a `knora-base:LinkValue` into the IRI of the corresponding link property. + * + * @param linkValuePropertyIri the IRI of the property that points to the `LinkValue`. + * @return the IRI of the corresponding link property. + */ def linkValuePropertyIri2LinkPropertyIri(linkValuePropertyIri: IRI): IRI = { implicit val stringFormatter: StringFormatter = this @@ -3024,52 +3042,52 @@ class StringFormatter private(val maybeSettings: Option[SettingsImpl] = None, ma } /** - * Creates a new permission IRI based on a UUID. - * - * @param shortcode the required project shortcode. - * @return the IRI of the permission object. - */ + * Creates a new permission IRI based on a UUID. + * + * @param shortcode the required project shortcode. + * @return the IRI of the permission object. + */ def makeRandomPermissionIri(shortcode: String): IRI = { val knoraPermissionUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/permissions/$shortcode/$knoraPermissionUuid" } /** - * Creates an IRI for a `knora-base:Map`. - * - * @param mapPath the map's path, which must be a sequence of names separated by slashes (`/`). Each name must - * be a valid XML [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. - * @return the IRI of the map. - */ + * Creates an IRI for a `knora-base:Map`. + * + * @param mapPath the map's path, which must be a sequence of names separated by slashes (`/`). Each name must + * be a valid XML [[https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName NCName]]. + * @return the IRI of the map. + */ def makeMapIri(mapPath: String): IRI = { s"http://$IriDomain/maps/$mapPath" } /** - * Extracts the path of a persistent map from the IRI of a `knora-base:Map`. - * - * @param mapIri the IRI of the `knora-base:Map`. - * @return the map's path. - */ + * Extracts the path of a persistent map from the IRI of a `knora-base:Map`. + * + * @param mapIri the IRI of the `knora-base:Map`. + * @return the map's path. + */ def mapIriToMapPath(mapIri: IRI): String = { mapIri.stripPrefix(s"http://$IriDomain/maps/") } /** - * Creates a random IRI for a `knora-base:MapEntry`. - */ + * Creates a random IRI for a `knora-base:MapEntry`. + */ def makeRandomMapEntryIri: IRI = { val mapEntryUuid = makeRandomBase64EncodedUuid s"http://$IriDomain/map-entries/$mapEntryUuid" } /** - * Converts a camel-case string like `FooBar` into a string like `foo-bar`. - * - * @param str the string to be converted. - * @param separator the word separator (defaults to `-`). - * @return the converted string. - */ + * Converts a camel-case string like `FooBar` into a string like `foo-bar`. + * + * @param str the string to be converted. + * @param separator the word separator (defaults to `-`). + * @return the converted string. + */ def camelCaseToSeparatedLowerCase(str: String, separator: String = "-"): String = { str.replaceAll( "([A-Z]+)([A-Z][a-z])", diff --git a/webapi/src/main/twirl/queries/sparql/v2/changeLinkTarget.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/changeLinkTarget.scala.txt index 8d00de08e6..d1702507dc 100644 --- a/webapi/src/main/twirl/queries/sparql/v2/changeLinkTarget.scala.txt +++ b/webapi/src/main/twirl/queries/sparql/v2/changeLinkTarget.scala.txt @@ -44,6 +44,7 @@ linkSourceIri: IRI, linkUpdateForCurrentLink: SparqlTemplateLinkUpdate, linkUpdateForNewLink: SparqlTemplateLinkUpdate, + newLinkValueUUID: UUID, maybeComment: Option[String], currentTime: Instant, requestingUser: IRI, @@ -127,7 +128,7 @@ INSERT { knora-base:valueHasRefCount @linkUpdateForNewLink.newReferenceCount ; knora-base:valueHasOrder ?order ; knora-base:isDeleted false ; - knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(UUID.randomUUID)}" ; + knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(newLinkValueUUID)}" ; knora-base:valueCreationDate "@currentTime"^^xsd:dateTime . ?newLinkValueForNewLink knora-base:attachedToUser <@linkUpdateForNewLink.newLinkValueCreator> ; diff --git a/webapi/src/main/twirl/queries/sparql/v2/createLink.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/createLink.scala.txt index 4d174edace..e4a83f6d2c 100644 --- a/webapi/src/main/twirl/queries/sparql/v2/createLink.scala.txt +++ b/webapi/src/main/twirl/queries/sparql/v2/createLink.scala.txt @@ -31,6 +31,7 @@ * @param triplestore the name of the triplestore being used. * @param resourceIri the resource that is the source of the link. * @param linkUpdate a [[LinkUpdate]] object describing the link to insert. + * @param newValueUUID the UUID to be attached to the value. * @param creationDate an xsd:dateTimeStamp that will be attached to the link value. * @param maybeComment an optional comment on the link. * @param stringFormatter a [[StringFormatter]]. @@ -41,6 +42,7 @@ triplestore: String, resourceIri: IRI, linkUpdate: SparqlTemplateLinkUpdate, + newValueUUID: UUID, creationDate: Instant, maybeComment: Option[String], stringFormatter: StringFormatter) @@ -65,6 +67,7 @@ DELETE { queries.sparql.v2.txt.generateInsertStatementsForCreateLink(resourceIri = resourceIri, linkUpdate = linkUpdate, creationDate = creationDate, + newValueUUID = newValueUUID, maybeComment = maybeComment, maybeValueHasOrder = None, stringFormatter = stringFormatter) diff --git a/webapi/src/main/twirl/queries/sparql/v2/createValue.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/createValue.scala.txt index de6cd7f848..c9957456e3 100644 --- a/webapi/src/main/twirl/queries/sparql/v2/createValue.scala.txt +++ b/webapi/src/main/twirl/queries/sparql/v2/createValue.scala.txt @@ -33,6 +33,7 @@ * @param resourceIri the resource to update. * @param propertyIri the resource property to update. * @param newValueIri the new value IRI. + * @param newValueUUID the UUID to be attached to the value. * @param value the value to insert. * @param linkUpdates a list of [[LinkUpdate]] objects describing links and LinkValues that need to be * updated for resource references in Standoff text values. @@ -49,6 +50,7 @@ resourceIri: IRI, propertyIri: SmartIri, newValueIri: IRI, + newValueUUID: UUID, value: ValueContentV2, linkUpdates: Seq[SparqlTemplateLinkUpdate], valueCreator: IRI, @@ -99,6 +101,7 @@ DELETE { propertyIri = propertyIri, value = value, newValueIri = newValueIri, + newValueUUID = newValueUUID, linkUpdates = linkUpdates, valueCreator = valueCreator, valuePermissions = valuePermissions, diff --git a/webapi/src/main/twirl/queries/sparql/v2/deleteValue.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/deleteValue.scala.txt index b45c9c2f37..8aa9de8ed2 100644 --- a/webapi/src/main/twirl/queries/sparql/v2/deleteValue.scala.txt +++ b/webapi/src/main/twirl/queries/sparql/v2/deleteValue.scala.txt @@ -99,12 +99,11 @@ DELETE { @* If we're marking a text value as deleted, update LinkValues for resource references in standoff markup. *@ @for((linkUpdate, linkValueIndex) <- linkUpdates.zipWithIndex) { - @* Insert a direct link for the resource reference if necessary. *@ @if(linkUpdate.insertDirectLink) { @{throw SparqlGenerationException(s"linkUpdate.insertDirectLink must be false in this SPARQL template"); ()} } - @* Insert a LinkValue for the resource reference. *@ + @* Add a new LinkValue version for the resource reference. *@ <@linkUpdate.newLinkValueIri> rdf:type knora-base:LinkValue ; rdf:subject ?resource ; rdf:predicate <@linkUpdate.linkPropertyIri> ; @@ -123,17 +122,11 @@ DELETE { <@linkUpdate.newLinkValueIri> knora-base:attachedToUser <@linkUpdate.newLinkValueCreator> ; knora-base:hasPermissions "@linkUpdate.newLinkValuePermissions" . - @* - - If we're inserting a new version of an existing LinkValue, attach it to the previous version, - and use the previous version's UUID. Otherwise, make a random UUID. - - *@ @if(linkUpdate.linkValueExists) { <@linkUpdate.newLinkValueIri> knora-base:previousValue ?linkValue@linkValueIndex ; knora-base:valueHasUUID ?linkValueUUID@linkValueIndex . } else { - <@linkUpdate.newLinkValueIri> knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(UUID.randomUUID)}" . + @{throw SparqlGenerationException(s"linkUpdate.linkValueExists must be true in this SPARQL template"); ()} } @* Attach the new LinkValue to its containing resource. *@ diff --git a/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateLink.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateLink.scala.txt index 7dccd42b85..b210d2f71a 100644 --- a/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateLink.scala.txt +++ b/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateLink.scala.txt @@ -32,6 +32,7 @@ * @param resourceIri the IRI of the resource. * @param linkUpdate a [[LinkUpdate]] object describing the link to insert. * @param creationDate an xsd:dateTimeStamp that will be attached to the link value. + * @param newValueUUID the UUID to be attached to the value. * @param maybeComment an optional comment on the link. * @param the knora-base:valueHasOrder of the new value. If not provided, the SPARQL variable ?nextOrder will be used. * @param stringFormatter a [[StringFormatter]]. @@ -39,6 +40,7 @@ @(resourceIri: IRI, linkUpdate: SparqlTemplateLinkUpdate, creationDate: Instant, + newValueUUID: UUID, maybeComment: Option[String], maybeValueHasOrder: Option[Int], stringFormatter: StringFormatter) @@ -77,7 +79,7 @@ } } knora-base:isDeleted false ; - knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(UUID.randomUUID)}" ; + knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(newValueUUID)}" ; knora-base:valueCreationDate "@creationDate"^^xsd:dateTime ; knora-base:attachedToUser <@linkUpdate.newLinkValueCreator> ; knora-base:hasPermissions "@linkUpdate.newLinkValuePermissions"^^xsd:string . diff --git a/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateValue.scala.txt b/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateValue.scala.txt index 0b450b7668..4e33ddf912 100644 --- a/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateValue.scala.txt +++ b/webapi/src/main/twirl/queries/sparql/v2/generateInsertStatementsForCreateValue.scala.txt @@ -33,7 +33,7 @@ * @param propertyIri the IRI of the property for which the value is being created. * @param value the value to insert. * @param newValueIri the IRI of the new value. - * @param valueHasUUID the UUID shared by all versions of this value. + * @param newValueUUID the UUID to be attached to the value. * @param linkUpdates a list of [[SparqlTemplateLinkUpdate]] objects describing links and LinkValues that need to be * updated for resource references in Standoff text values. This list will be empty if the links and LinkValues * are being created separately. @@ -47,6 +47,7 @@ propertyIri: SmartIri, value: ValueContentV2, newValueIri: IRI, + newValueUUID: UUID, linkUpdates: Seq[SparqlTemplateLinkUpdate], valueCreator: IRI, valuePermissions: String, @@ -61,7 +62,7 @@ <@newValueIri> rdf:type <@value.valueType> ; knora-base:isDeleted false ; knora-base:valueHasString """@value.valueHasString""" ; - knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(UUID.randomUUID)}" . + knora-base:valueHasUUID "@{stringFormatter.base64EncodeUuid(newValueUUID)}" . @{ queries.sparql.v2.txt.generateInsertStatementsForValueContent(value = value, 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 0671a082d8..3310f883fa 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 @@ -22,6 +22,7 @@ package org.knora.webapi.e2e.v2 import java.io.File import java.net.URLEncoder import java.time.Instant +import java.util.UUID import akka.actor.ActorSystem import akka.http.scaladsl.model._ @@ -63,6 +64,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { private val geonameValueIri = new MutableTestIri private val linkValueIri = new MutableTestIri + private var integerValueUUID = UUID.randomUUID + private var linkValueUUID = UUID.randomUUID override lazy val rdfDataObjects = List( RdfDataObject(path = "_test_data/all_data/anything-data.ttl", name = "http://www.knora.org/data/0001/anything") @@ -305,6 +308,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { intValueIri.set(valueIri) val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri) + integerValueUUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid) val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, @@ -366,7 +370,6 @@ class ValuesRouteV2E2ESpec extends E2ESpec { 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) } @@ -1865,6 +1868,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { linkValueIri.set(valueIri) val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri) + linkValueUUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid) val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, @@ -1900,6 +1904,9 @@ class ValuesRouteV2E2ESpec extends E2ESpec { intValueIri.set(valueIri) val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.IntValue.toSmartIri) + val newIntegerValueUUID: UUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid) + assert(newIntegerValueUUID == integerValueUUID) // The new version should have the same UUID. + integerValueUUID = newIntegerValueUUID val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, @@ -2789,6 +2796,11 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri) + // When you change a link value's target, it gets a new UUID. + val newLinkValueUUID: UUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid) + assert(newLinkValueUUID != linkValueUUID) + linkValueUUID = newLinkValueUUID + val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, maybePreviousLastModDate = maybeResourceLastModDate, @@ -2805,10 +2817,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "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, @@ -2846,6 +2855,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri) + // Since we only changed metadata, the UUID should be the same. + val newLinkValueUUID: UUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid) + assert(newLinkValueUUID == linkValueUUID) + val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, maybePreviousLastModDate = maybeResourceLastModDate, @@ -2884,6 +2897,10 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val valueType: SmartIri = responseJsonDoc.body.requireStringWithValidation(JsonLDConstants.TYPE, stringFormatter.toSmartIriWithErr) valueType should ===(OntologyConstants.KnoraApiV2Complex.LinkValue.toSmartIri) + // Since we only changed metadata, the UUID should be the same. + val newLinkValueUUID: UUID = responseJsonDoc.body.requireStringWithValidation(OntologyConstants.KnoraApiV2Complex.ValueHasUUID, stringFormatter.validateBase64EncodedUuid) + assert(newLinkValueUUID == linkValueUUID) + val savedValue: JsonLDObject = getValue( resourceIri = resourceIri, maybePreviousLastModDate = maybeResourceLastModDate, @@ -2899,7 +2916,6 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "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" 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 8ad5c0e948..84e087c759 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 @@ -93,6 +93,9 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { private val standoffLinkValueIri = new MutableTestIri private val stillImageFileValueIri = new MutableTestIri + private var integerValueUUID = UUID.randomUUID + private var linkValueUUID = UUID.randomUUID + private val sampleStandoff: Vector[StandoffTagV2] = Vector( StandoffTagV2( standoffTagClassIri = OntologyConstants.Standoff.StandoffBoldTag.toSmartIri, @@ -384,6 +387,7 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { case createValueResponse: CreateValueResponseV2 => intValueIri.set(createValueResponse.valueIri) firstIntValueVersionIri.set(createValueResponse.valueIri) + integerValueUUID = createValueResponse.valueUUID } // Read the value back to check that it was added correctly. @@ -464,7 +468,10 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { ) expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => intValueIri.set(updateValueResponse.valueIri) + case updateValueResponse: UpdateValueResponseV2 => + intValueIri.set(updateValueResponse.valueIri) + assert(updateValueResponse.valueUUID == integerValueUUID) + integerValueUUID = updateValueResponse.valueUUID } // Read the value back to check that it was added correctly. @@ -1736,7 +1743,9 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! createValueRequest expectMsgPF(timeout) { - case createValueResponse: CreateValueResponseV2 => linkValueIri.set(createValueResponse.valueIri) + case createValueResponse: CreateValueResponseV2 => + linkValueIri.set(createValueResponse.valueIri) + linkValueUUID = createValueResponse.valueUUID } val valueFromTriplestore = getValue( @@ -3543,7 +3552,12 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => linkValueIri.set(updateValueResponse.valueIri) + case updateValueResponse: UpdateValueResponseV2 => + linkValueIri.set(updateValueResponse.valueIri) + + // When you change a link value's target, it gets a new UUID. + assert(updateValueResponse.valueUUID != linkValueUUID) + linkValueUUID = updateValueResponse.valueUUID } val valueFromTriplestore = getValue( @@ -3616,7 +3630,11 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => linkValueIri.set(updateValueResponse.valueIri) + case updateValueResponse: UpdateValueResponseV2 => + linkValueIri.set(updateValueResponse.valueIri) + + // Since we only changed metadata, the link should have the same UUID. + assert(updateValueResponse.valueUUID == linkValueUUID) } val valueFromTriplestore = getValue( @@ -3692,7 +3710,11 @@ class ValuesResponderV2Spec extends CoreSpec() with ImplicitSender { responderManager ! updateValueRequest expectMsgPF(timeout) { - case updateValueResponse: UpdateValueResponseV2 => linkValueIri.set(updateValueResponse.valueIri) + case updateValueResponse: UpdateValueResponseV2 => + linkValueIri.set(updateValueResponse.valueIri) + + // Since we only changed metadata, the link should have the same UUID. + assert(updateValueResponse.valueUUID == linkValueUUID) } val valueFromTriplestore = getValue(