From 593d9cb30a7fb332f8062898bcfa07abf1e7951d Mon Sep 17 00:00:00 2001 From: Sepideh Alassi Date: Tue, 29 Jun 2021 20:23:02 +0430 Subject: [PATCH] feat(customIRIs): custom IRIs must contain a UUID (DSP-1763) (#1884) * feat (customIRIs): custom IRIs must contain a UUID * fix(customIRIs): fix the custom IRIs given to new permissions of a new project * fix (customIRIs): fix failing tests * fix (customIRI): custom project IRI can contain UUID instead of project shortcode, * docs (custom IRIs): update documentation * fix (customIRI): custom value UUID should be part of custom value IRI * fix (customIri): fix the failing tests * docs (customIRI): add documentation about breaking changes of V14 * refactor(customIRI): clean up Co-authored-by: Ivan Subotic <400790+subotic@users.noreply.github.com> --- docs/03-apis/api-admin/groups.md | 2 +- docs/03-apis/api-admin/lists.md | 26 ++-- .../lists_new-list-admin-routes_v1.md | 16 +-- docs/03-apis/api-admin/permissions.md | 6 +- docs/03-apis/api-admin/projects.md | 2 +- docs/03-apis/api-admin/users.md | 2 +- docs/03-apis/api-v2/editing-resources.md | 4 +- docs/03-apis/api-v2/editing-values.md | 11 +- docs/03-apis/api-v2/knora-iris.md | 2 +- .../development/migrationNotes.md | 8 ++ .../webapi/messages/StringFormatter.scala | 10 +- .../knora/webapi/responders/Responder.scala | 20 ++- .../admin/ProjectsResponderADM.scala | 16 +-- .../responders/v2/ValuesResponderV2.scala | 66 +++++++--- .../webapi/e2e/admin/GroupsADME2ESpec.scala | 11 +- .../e2e/admin/PermissionsADME2ESpec.scala | 14 +-- .../webapi/e2e/admin/ProjectsADME2ESpec.scala | 8 +- .../webapi/e2e/admin/UsersADME2ESpec.scala | 12 +- .../NewListsRoutesADMFeatureE2ESpec.scala | 7 +- .../OldListsRouteADMFeatureE2ESpec.scala | 7 +- .../webapi/e2e/v2/ValuesRouteV2E2ESpec.scala | 78 +++++++++--- .../admin/PermissionsResponderADMSpec.scala | 10 +- .../admin/ProjectsResponderADMSpec.scala | 117 +++++++++--------- .../v2/ResourcesResponderV2Spec.scala | 107 +++++++++++++++- .../sharedtestdata/SharedTestDataADM.scala | 12 +- 25 files changed, 380 insertions(+), 194 deletions(-) create mode 100644 docs/05-internals/development/migrationNotes.md diff --git a/docs/03-apis/api-admin/groups.md b/docs/03-apis/api-admin/groups.md index 04f6e67bfd..323b4dae4d 100644 --- a/docs/03-apis/api-admin/groups.md +++ b/docs/03-apis/api-admin/groups.md @@ -63,7 +63,7 @@ specified by the `id` in the request body as below: ```json { - "id": "http://rdfh.ch/groups/00FF/group-with-custom-Iri", + "id": "http://rdfh.ch/groups/00FF/a95UWs71KUklnFOe1rcw1w", "name": "GroupWithCustomIRI", "description": "A new group with a custom IRI", "project": "http://rdfh.ch/projects/00FF", diff --git a/docs/03-apis/api-admin/lists.md b/docs/03-apis/api-admin/lists.md index 4d53071507..ee3f896b27 100644 --- a/docs/03-apis/api-admin/lists.md +++ b/docs/03-apis/api-admin/lists.md @@ -75,7 +75,7 @@ Additionally, each list can have an optional custom IRI (of [Knora IRI](../api-v ```json { - "id": "http://rdfh.ch/lists/0001/a-list", + "id": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "a new list", "labels": [{ "value": "Neue Liste mit IRI", "language": "de"}], @@ -90,7 +90,7 @@ The response will contain the basic information of the list, `listinfo` and an e "children": [], "listinfo": { "comments": [], - "id": "http://rdfh.ch/lists/0001/a-list", + "id": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "isRootNode": true, "labels": [ { @@ -121,7 +121,7 @@ list and the IRI of the project it belongs to. - BODY: ```json - { "listIri": "http://rdfh.ch/lists/0001/a-list", + { "listIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "new name for the list", "labels": [{ "value": "a new label for the list", "language": "en"}], @@ -139,7 +139,7 @@ The response will contain the basic information of the list, `listinfo`, without "language": "en" } ], - "id": "http://rdfh.ch/lists/0001/a-list", + "id": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "isRootNode": true, "labels": [ { @@ -232,7 +232,7 @@ There is no need to specify the project IRI because it is automatically extracte ```json { - "parentNodeIri": "http://rdfh.ch/lists/0001/a-list", + "parentNodeIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "a child", "labels": [{ "value": "New List Node", "language": "en"}], @@ -243,8 +243,8 @@ There is no need to specify the project IRI because it is automatically extracte Additionally, each child node can have an optional custom IRI (of [Knora IRI](../api-v2/knora-iris.md#iris-for-data) form) specified by the `id` in the request body as below: ```json -{ "id": "http://rdfh.ch/lists/0001/a-childNode", - "parentNodeIri": "http://rdfh.ch/lists/0001/a-list", +{ "id": "http://rdfh.ch/lists/0001/8u37MxBVMbX3XQ8-d31x6w", + "parentNodeIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "a child", "labels": [{ "value": "New List Node", "language": "en"}], @@ -257,8 +257,8 @@ The response will contain the basic information of the node, `nodeinfo`, as belo { "nodeinfo": { "comments": [], - "hasRootNode": "http://rdfh.ch/lists/0001/a-list", - "id": "http://rdfh.ch/lists/0001/a-childNode", + "hasRootNode": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", + "id": "http://rdfh.ch/lists/0001/8u37MxBVMbX3XQ8-d31x6w", "labels": [ { "value": "New List Node", @@ -275,7 +275,7 @@ according to the given position, the sibling nodes will be shifted. Note that `p number of existing children. ```json -{ "parentNodeIri": "http://rdfh.ch/lists/0001/a-list", +{ "parentNodeIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "Inserted new child", "position": 0, @@ -298,7 +298,7 @@ node and the IRI of the project it belongs to. - BODY: ```json - { "listIri": "http://rdfh.ch/lists/0001/a-childNode", + { "listIri": "http://rdfh.ch/lists/0001/8u37MxBVMbX3XQ8-d31x6w", "projectIri": "http://rdfh.ch/projects/0001", "name": "new node name", "labels": [{ "value": "new node label", "language": "en"}], @@ -317,8 +317,8 @@ The response will contain the basic information of the node as `nodeInfo` withou "language": "en" } ], - "hasRootNode": "http://rdfh.ch/lists/0001/a-list", - "id": "http://rdfh.ch/lists/0001/a-childNode", + "hasRootNode": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", + "id": "http://rdfh.ch/lists/0001/8u37MxBVMbX3XQ8-d31x6w", "labels": [ { "value": "new node label", diff --git a/docs/03-apis/api-admin/lists_new-list-admin-routes_v1.md b/docs/03-apis/api-admin/lists_new-list-admin-routes_v1.md index b311bb735e..306029de51 100644 --- a/docs/03-apis/api-admin/lists_new-list-admin-routes_v1.md +++ b/docs/03-apis/api-admin/lists_new-list-admin-routes_v1.md @@ -94,7 +94,7 @@ Additionally, each list can have an optional custom IRI (of [Knora IRI](../api-v ```json { - "id": "http://rdfh.ch/lists/0001/a-list", + "id": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "a new list", "labels": [{ "value": "Neue Liste mit IRI", "language": "de"}], @@ -110,7 +110,7 @@ The response will contain the basic information of the list, `listinfo` and an e "children": [], "listinfo": { "comments": [], - "id": "http://rdfh.ch/lists/0001/a-list", + "id": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "isRootNode": true, "labels": [ { @@ -131,7 +131,7 @@ Furthermore, the request body should also contain the project IRI of the list an ```json { - "parentNodeIri": "http://rdfh.ch/lists/0001/a-list", + "parentNodeIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "a child", "labels": [{ "value": "New List Node", "language": "en"}], @@ -142,8 +142,8 @@ Furthermore, the request body should also contain the project IRI of the list an Additionally, each child node can have an optional custom IRI (of [Knora IRI](../api-v2/knora-iris.md#iris-for-data) form) specified by the `id` in the request body as below: ```json -{ "id": "http://rdfh.ch/lists/0001/a-childNode", - "parentNodeIri": "http://rdfh.ch/lists/0001/a-list", +{ "id": "http://rdfh.ch/lists/0001/8u37MxBVMbX3XQ8-d31x6w", + "parentNodeIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "a child", "labels": [{ "value": "New List Node", "language": "en"}], @@ -157,8 +157,8 @@ The response will contain the basic information of the node, `nodeinfo`, as belo { "nodeinfo": { "comments": [], - "hasRootNode": "http://rdfh.ch/lists/0001/a-list", - "id": "http://rdfh.ch/lists/0001/a-childNode", + "hasRootNode": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", + "id": "http://rdfh.ch/lists/0001/8u37MxBVMbX3XQ8-d31x6w", "labels": [ { "value": "New List Node", @@ -176,7 +176,7 @@ according to the given position, the sibling nodes will be shifted. Note that `p number of existing children. ```json -{ "parentNodeIri": "http://rdfh.ch/lists/0001/a-list", +{ "parentNodeIri": "http://rdfh.ch/lists/0001/yWQEGXl53Z4C4DYJ-S2c5A", "projectIri": "http://rdfh.ch/projects/0001", "name": "Inserted new child", "position": 0, diff --git a/docs/03-apis/api-admin/permissions.md b/docs/03-apis/api-admin/permissions.md index 9b23eb147f..eea9d13c64 100644 --- a/docs/03-apis/api-admin/permissions.md +++ b/docs/03-apis/api-admin/permissions.md @@ -58,7 +58,7 @@ the `@id` attribute which will then be assigned to the permission; otherwise the A custom permission IRI must be `http://rdfh.ch/permissions/PROJECT_SHORTCODE/` (where `PROJECT_SHORTCODE` is the shortcode of the project that the permission belongs to), plus a custom ID string. For example: ``` -"id": "http://rdfh.ch/permissions/0001/AP-with-customIri", +"id": "http://rdfh.ch/permissions/0001/jKIYuaEUETBcyxpenUwRzQ", ``` As a response, the created administrative permission and its IRI are returned as below: @@ -108,7 +108,7 @@ a resource class of a specific project: ```json { - "id": "http://rdfh.ch/permissions/00FF/DOAP-with-customIri", + "id": "http://rdfh.ch/permissions/00FF/fSw7w1sI5IwDjEfFi1jOeQ", "forGroup":null, "forProject":"http://rdfh.ch/projects/00FF", "forProperty":null, @@ -133,7 +133,7 @@ The response contains the newly created permission and its IRI, as: "permissionCode": 7 } ], - "iri": "http://rdfh.ch/permissions/00FF/DOAP-with-customIri" + "iri": "http://rdfh.ch/permissions/00FF/fSw7w1sI5IwDjEfFi1jOeQ" } } ``` diff --git a/docs/03-apis/api-admin/projects.md b/docs/03-apis/api-admin/projects.md index 7f925f5f82..fb1e533c4c 100644 --- a/docs/03-apis/api-admin/projects.md +++ b/docs/03-apis/api-admin/projects.md @@ -89,7 +89,7 @@ Additionally, each project can have an optional custom IRI (of [Knora IRI](../ap ```json { - "id": "http://rdfh.ch/projects/3333", + "id": "http://rdfh.ch/projects/9TaSVMUuiRhQsuWHDPr8rw", "shortname": "newprojectWithIri", "shortcode": "3333", "longname": "new project with a custom IRI", diff --git a/docs/03-apis/api-admin/users.md b/docs/03-apis/api-admin/users.md index de93b09bf1..110532de22 100644 --- a/docs/03-apis/api-admin/users.md +++ b/docs/03-apis/api-admin/users.md @@ -96,7 +96,7 @@ specified by the `id` in the request body as below: ```json { - "id" : "http://rdfh.ch/users/donaldDuck", + "id" : "http://rdfh.ch/users/FnjFfIQFVDvI7ex8zSyUyw", "email": "donald.duck@example.org", "givenName": "Donald", "familyName": "Duck", diff --git a/docs/03-apis/api-v2/editing-resources.md b/docs/03-apis/api-v2/editing-resources.md index 6e454ed16a..9a6bfe5260 100644 --- a/docs/03-apis/api-v2/editing-resources.md +++ b/docs/03-apis/api-v2/editing-resources.md @@ -202,13 +202,13 @@ For example: ```jsonld { - "@id" : "http://rdfh.ch/0001/a-custom-thing", + "@id" : "http://rdfh.ch/0001/oveR1dQltEUwNrls9Lu5Rw", "@type" : "anything:Thing", "knora-api:attachedToProject" : { "@id" : "http://rdfh.ch/projects/0001" }, "anything:hasInteger" : { - "@id" : "http://rdfh.ch/0001/a-custom-thing/values/int-value-IRI", + "@id" : "http://rdfh.ch/0001/oveR1dQltEUwNrls9Lu5Rw/values/IN4R19yYR0ygi3K2VEHpUQ", "@type" : "knora-api:IntValue", "knora-api:intValueAsInt" : 10, "knora-api:valueHasUUID" : "IN4R19yYR0ygi3K2VEHpUQ", diff --git a/docs/03-apis/api-v2/editing-values.md b/docs/03-apis/api-v2/editing-values.md index 53ee369e70..80b7a18416 100644 --- a/docs/03-apis/api-v2/editing-values.md +++ b/docs/03-apis/api-v2/editing-values.md @@ -84,9 +84,12 @@ Permissions for the new value can be given by adding `knora-api:hasPermissions`. } ``` -Each value can have an optional custom IRI (of [Knora IRI](knora-iris.md#iris-for-data) form) specified by the `@id` attribute, a custom creation date specified by adding -`knora-api:valueCreationDate` (an [xsd:dateTimeStamp](https://www.w3.org/TR/xmlschema11-2/#dateTimeStamp)), or a custom UUID -given by `knora-api:valueHasUUID`. Each custom UUID must be [base64url-encoded](rfc:4648#section-5), without padding. +Each value can have an optional custom IRI (of [Knora IRI](knora-iris.md#iris-for-data) form) specified by the `@id` attribute, +a custom creation date specified by adding `knora-api:valueCreationDate` (an [xsd:dateTimeStamp](https://www.w3.org/TR/xmlschema11-2/#dateTimeStamp)), +or a custom UUID given by `knora-api:valueHasUUID`. Each custom UUID must be [base64url-encoded](rfc:4648#section-5), without padding. +If a custom UUID is provided, it will be used in value IRI. If a custom IRI is given for the value, its UUID should match +the given custom UUID. If a custom IRI is provided, but there +is no custom UUID provided, then the UUID given in the IRI will be assigned to the `knora-api:valueHasUUID`. A custom value IRI must be the IRI of the containing resource, followed by a `/values/` and a custom ID string. For example: @@ -95,7 +98,7 @@ by a `/values/` and a custom ID string. For example: "@id" : "http://rdfh.ch/0001/a-thing", "@type" : "anything:Thing", "anything:hasInteger" : { - "@id" : "http://rdfh.ch/0001/a-thing/values/int-value-IRI", + "@id" : "http://rdfh.ch/0001/a-thing/values/IN4R19yYR0ygi3K2VEHpUQ", "@type" : "knora-api:IntValue", "knora-api:intValueAsInt" : 21, "knora-api:valueHasUUID" : "IN4R19yYR0ygi3K2VEHpUQ", diff --git a/docs/03-apis/api-v2/knora-iris.md b/docs/03-apis/api-v2/knora-iris.md index 11d95e723d..bdeccc5c56 100644 --- a/docs/03-apis/api-v2/knora-iris.md +++ b/docs/03-apis/api-v2/knora-iris.md @@ -225,7 +225,7 @@ follows: `http://rdfh.ch/PROJECT_SHORTCODE/mappings/MAPPING_NAME` - XML-to-standoff mapping element: `http://rdfh.ch/PROJECT_SHORTCODE/mappings/MAPPING_NAME/elements/MAPPING_ELEMENT_UUID` - - Project: `http://rdfh.ch/projects/PROJECT_SHORTCODE` + - Project: `http://rdfh.ch/projects/PROJECT_SHORTCODE` (or `http://rdfh.ch/projects/PROJECT_UUID`) - Group: `http://rdfh.ch/groups/PROJECT_SHORTCODE/GROUP_UUID` - Permission: `http://rdfh.ch/permissions/PROJECT_SHORTCODE/PERMISSION_UUID` diff --git a/docs/05-internals/development/migrationNotes.md b/docs/05-internals/development/migrationNotes.md new file mode 100644 index 0000000000..3f890008ab --- /dev/null +++ b/docs/05-internals/development/migrationNotes.md @@ -0,0 +1,8 @@ +#Breaking Changes and Migration Notes + +##dsp-api V14 +We are slowly moving towards unifying the form of all entity IRIs (project, user, resource, value, etc.). All these +entities should end with a unique base64Encoded-UUID without padding as 22-characters string. Following breaking changes +are implemented: +- Enforce all custom IRIs given to entities during creation to end with a valid base64Encoded UUID +([PR #1884](https://github.com/dasch-swiss/dsp-api/pull/1884)). \ No newline at end of file diff --git a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala index 2b51c8ddac..be2b5e38b2 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/StringFormatter.scala @@ -3068,11 +3068,15 @@ class StringFormatter private (val maybeSettings: Option[KnoraSettingsImpl] = No * Creates a new value IRI based on a UUID. * * @param resourceIri the IRI of the resource that will contain the value. + * @param givenUUID the optional given UUID of the value. If not provided, create a random one. * @return a new value IRI. */ - def makeRandomValueIri(resourceIri: IRI): IRI = { - val knoraValueUuid = makeRandomBase64EncodedUuid - s"$resourceIri/values/$knoraValueUuid" + def makeRandomValueIri(resourceIri: IRI, givenUUID: Option[UUID] = None): IRI = { + val valueUUID = givenUUID match { + case Some(uuid: UUID) => base64EncodeUuid(uuid) + case _ => makeRandomBase64EncodedUuid + } + s"$resourceIri/values/$valueUUID" } /** diff --git a/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala b/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala index b993a235c9..8e1b4638cb 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/Responder.scala @@ -20,13 +20,12 @@ package org.knora.webapi package responders -import exceptions.{DuplicateValueException, UnexpectedMessageException} +import exceptions.{BadRequestException, DuplicateValueException, UnexpectedMessageException} import messages.store.triplestoremessages.SparqlSelectRequest import messages.util.ResponderData import messages.util.rdf.SparqlSelectResult import messages.{SmartIri, StringFormatter} import settings.{KnoraDispatchers, KnoraSettings, KnoraSettingsImpl} - import akka.actor.{ActorRef, ActorSystem} import akka.event.LoggingAdapter import akka.http.scaladsl.util.FastFuture @@ -174,14 +173,23 @@ abstract class Responder(responderData: ResponderData) extends LazyLogging { * @return IRI of the entity. */ protected def checkOrCreateEntityIri(entityIri: Option[SmartIri], iriFormatter: => IRI): Future[IRI] = { + entityIri match { - case Some(customResourceIri) => + case Some(customEntityIri: SmartIri) => + val entityIriAsString = customEntityIri.toString for { - result <- stringFormatter.checkIriExists(customResourceIri.toString, storeManager) + + result <- stringFormatter.checkIriExists(entityIriAsString, storeManager) _ = if (result) { - throw DuplicateValueException(s"IRI: '${customResourceIri.toString}' already exists, try another one.") + throw DuplicateValueException(s"IRI: '$entityIriAsString' already exists, try another one.") } - } yield customResourceIri.toString + // Check that given entityIRI ends with a UUID + ending: String = entityIriAsString.split('/').last + _ = stringFormatter.validateBase64EncodedUuid( + ending, + throw BadRequestException(s"IRI: '$entityIriAsString' must end with a valid base 64 UUID.")) + + } yield entityIriAsString case None => stringFormatter.makeUnusedIri(iriFormatter, storeManager, loggingAdapter) } diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala index 6817c509b4..920eb56c46 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala @@ -31,7 +31,6 @@ import org.knora.webapi.exceptions._ import org.knora.webapi.feature.FeatureFactoryConfig import org.knora.webapi.instrumentation.InstrumentationSupport import org.knora.webapi.messages.IriConversions._ -import org.knora.webapi.messages.StringFormatter.IriDomain import org.knora.webapi.messages.admin.responder.projectsmessages._ import org.knora.webapi.messages.admin.responder.usersmessages.{ UserADM, @@ -982,11 +981,9 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo */ def createPermissionsForAdminsAndMembersOfNewProject(projectIri: IRI, projectShortCode: String): Future[Unit] = for { - baseIri: String <- Future.successful(s"http://$IriDomain/permissions/$projectShortCode/") // Give the admins of the new project rights for any operation in project level, and rights to create resources. - apPermissionForProjectAdmin: AdministrativePermissionCreateResponseADM <- (responderManager ? AdministrativePermissionCreateRequestADM( + _ <- (responderManager ? AdministrativePermissionCreateRequestADM( createRequest = CreateAdministrativePermissionAPIRequestADM( - id = Some(baseIri + "defaultApForAdmin"), forProject = projectIri, forGroup = OntologyConstants.KnoraAdmin.ProjectAdmin, hasPermissions = @@ -998,9 +995,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo )).mapTo[AdministrativePermissionCreateResponseADM] // Give the members of the new project rights to create resources. - apPermissionForProjectMember: AdministrativePermissionCreateResponseADM <- (responderManager ? AdministrativePermissionCreateRequestADM( + _ <- (responderManager ? AdministrativePermissionCreateRequestADM( createRequest = CreateAdministrativePermissionAPIRequestADM( - id = Some(baseIri + "defaultApForMember"), forProject = projectIri, forGroup = OntologyConstants.KnoraAdmin.ProjectMember, hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission) @@ -1012,9 +1008,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // Give the admins of the new project rights to change rights, modify, delete, view, // and restricted view of all resources and values that belong to the project. - doapForProjectAdmin <- (responderManager ? DefaultObjectAccessPermissionCreateRequestADM( + _ <- (responderManager ? DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( - id = Some(baseIri + "defaultDoapForAdmin"), forProject = projectIri, forGroup = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), hasPermissions = Set( @@ -1032,9 +1027,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo // Give the members of the new project rights to modify, view, and restricted view of all resources and values // that belong to the project. - doapForProjectMember <- (responderManager ? DefaultObjectAccessPermissionCreateRequestADM( + _ <- (responderManager ? DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( - id = Some(baseIri + "defaultDoapForMember"), forProject = projectIri, forGroup = Some(OntologyConstants.KnoraAdmin.ProjectMember), hasPermissions = Set( @@ -1109,7 +1103,7 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo ) .toString - createProjectResponse <- (storeManager ? SparqlUpdateRequest(createNewProjectSparqlString)) + _ <- (storeManager ? SparqlUpdateRequest(createNewProjectSparqlString)) .mapTo[SparqlUpdateResponse] // try to retrieve newly created project (will also add to cache) 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 e1cab211c1..67548d18f6 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 @@ -435,15 +435,13 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde requestingUser: UserADM): Future[UnverifiedValueV2] = { for { - // Make an IRI for the new value. - newValueIri: IRI <- checkOrCreateEntityIri(maybeValueIri, - stringFormatter.makeRandomValueIri(resourceInfo.resourceIri)) + // Make a new value UUID. + newValueUUID: UUID <- Future.successful(makeNewValueUUID(maybeValueIri, maybeValueUUID)) - // Make a UUID for the new value - newValueUUID: UUID = maybeValueUUID match { - case Some(customValueUUID) => customValueUUID - case None => UUID.randomUUID - } + // Make an IRI for the new value. + newValueIri: IRI <- checkOrCreateEntityIri( + maybeValueIri, + stringFormatter.makeRandomValueIri(resourceInfo.resourceIri, Some(newValueUUID))) // Make a creation date for the new value creationDate: Instant = maybeValueCreationDate match { @@ -532,11 +530,8 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde valueCreator: IRI, valuePermissions: String, requestingUser: UserADM): Future[UnverifiedValueV2] = { - - val newValueUUID: UUID = maybeValueUUID match { - case Some(customValueUUID) => customValueUUID - case None => UUID.randomUUID - } + // Make a new value UUID. + val newValueUUID: UUID = makeNewValueUUID(maybeValueIri, maybeValueUUID) for { sparqlTemplateLinkUpdate <- incrementLinkValue( @@ -666,14 +661,12 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde resourceCreationDate: Instant, requestingUser: UserADM): Future[InsertSparqlWithUnverifiedValue] = { for { - newValueIri: IRI <- checkOrCreateEntityIri(valueToCreate.customValueIri, - stringFormatter.makeRandomValueIri(resourceIri)) + // Make new value UUID. + newValueUUID: UUID <- Future.successful( + makeNewValueUUID(valueToCreate.customValueIri, valueToCreate.customValueUUID)) - // Make a UUID for the new value. - newValueUUID: UUID = valueToCreate.customValueUUID match { - case Some(customValueUUID) => customValueUUID - case None => UUID.randomUUID - } + newValueIri: IRI <- checkOrCreateEntityIri(valueToCreate.customValueIri, + stringFormatter.makeRandomValueIri(resourceIri, Some(newValueUUID))) // Make a creation date for the value. If a custom creation date is given for a value, consider that otherwise // use resource creation date for the value. @@ -2465,4 +2458,37 @@ class ValuesResponderV2(responderData: ResponderData) extends Responder(responde private def makeUnusedValueIri(resourceIri: IRI): Future[IRI] = { stringFormatter.makeUnusedIri(stringFormatter.makeRandomValueIri(resourceIri), storeManager, loggingAdapter) } + + /** + * Make a new value UUID considering optional custom value UUID and custom value IRI. + * If a custom UUID is given, this method checks that it matches the ending of a given IRI, if there was any. + * If no custom UUID is given for a value, it checks if a custom value IRI is given or not. If yes, it extracts the + * UUID from the given IRI. If no custom value IRI was given, it generates a random UUID. + * + * @param maybeCustomIri the optional value IRI. + * @param maybeCustomUUID the optional value UUID. + * @return the new value UUID. + */ + private def makeNewValueUUID(maybeCustomIri: Option[SmartIri], maybeCustomUUID: Option[UUID]): UUID = { + // Is there any custom value UUID given? + maybeCustomUUID match { + case Some(customValueUUID) => + // Yes. Check that if a custom IRI is given, it ends with the same UUID + if (maybeCustomIri.nonEmpty && stringFormatter.base64DecodeUuid(maybeCustomIri.get.toString.split("/").last) != customValueUUID) { + throw BadRequestException( + s" Given custom IRI ${maybeCustomIri.get} should contain the given custom UUID ${stringFormatter + .base64EncodeUuid(customValueUUID)}.") + } + customValueUUID + case None => + // No. Is there a custom IRI given? + maybeCustomIri match { + case Some(customIri: SmartIri) => + // Yes. Get the UUID from the given value IRI + val endingUUID: UUID = stringFormatter.base64DecodeUuid(customIri.toString.split("/").last) + endingUUID + case None => UUID.randomUUID + } + } + } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala index ef68ea59b0..159ef0a1e8 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala @@ -101,10 +101,10 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs } "given a custom Iri" should { + val customGroupIri = "http://rdfh.ch/groups/00FF/3eFYejZEduOCowwXQq5Iqg" "create a group with the provided custom IRI " in { - val createGroupWithCustomIriRequest: String = - s"""{ "id": "http://rdfh.ch/groups/00FF/group-with-customIri", + s"""{ "id": "$customGroupIri", | "name": "NewGroupWithCustomIri", | "description": "A new group with a custom Iri", | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", @@ -134,7 +134,7 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs val result: GroupADM = AkkaHttpUtils.httpResponseToJson(response).fields("group").convertTo[GroupADM] //check that the custom IRI is correctly assigned - result.id should be("http://rdfh.ch/groups/00FF/group-with-customIri") + result.id should be(customGroupIri) clientTestDataCollector.addFile( TestDataFileContent( filePath = TestDataFilePath( @@ -149,7 +149,7 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs "return 'BadRequest' if the supplied IRI for the group is not unique" in { val params = - s"""{ "id": "http://rdfh.ch/groups/00FF/group-with-customIri", + s"""{ "id": "$customGroupIri", | "name": "NewGroupWithDuplicateCustomIri", | "description": "A new group with a duplicate custom Iri", | "project": "${SharedTestDataADM.IMAGES_PROJECT_IRI}", @@ -163,8 +163,7 @@ class GroupsADME2ESpec extends E2ESpec(GroupsADME2ESpec.config) with GroupsADMJs response.status should be(StatusCodes.BadRequest) val errorMessage: String = Await.result(Unmarshal(response.entity).to[String], 1.second) - val invalidIri: Boolean = errorMessage.contains( - s"IRI: 'http://rdfh.ch/groups/00FF/group-with-customIri' already exists, try another one.") + val invalidIri: Boolean = errorMessage.contains(s"IRI: '$customGroupIri' already exists, try another one.") invalidIri should be(true) } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala index 4462299df7..0ea343a2c9 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/PermissionsADME2ESpec.scala @@ -51,7 +51,7 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T // Collects client test data private val clientTestDataCollector = new ClientTestDataCollector(settings) - + private val customDOAPIri = "http://rdfh.ch/permissions/00FF/eIAywlYBJA3a_5yI77UsMQ" "The Permissions Route ('admin/permissions')" when { "getting permissions" should { @@ -215,10 +215,10 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T text = responseToString(response) ) ) - + val customAPIri = "http://rdfh.ch/permissions/0001/u0PRnDl3kgcbrehZnRlEfA" val createAdministrativePermissionWithCustomIriRequest: String = s"""{ - | "id": "http://rdfh.ch/permissions/0001/AP-with-customIri", + | "id": "$customAPIri, | "forGroup":"${SharedTestDataADM.thingSearcherGroup.id}", | "forProject":"${SharedTestDataADM.ANYTHING_PROJECT_IRI}", | "hasPermissions":[{"additionalInformation":null,"name":"ProjectAdminGroupAllPermission","permissionCode":null}] @@ -247,7 +247,7 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T | "permissionCode": null | } | ], - | "iri": "http://rdfh.ch/permissions/0001/AP-with-customIri" + | "iri": "$customAPIri" | } |}""".stripMargin @@ -363,7 +363,7 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val createDefaultObjectAccessPermissionWithCustomIriRequest: String = s"""{ - | "id": "http://rdfh.ch/permissions/00FF/DOAP-with-customIri", + | "id": "$customDOAPIri", | "forGroup":null, | "forProject":"${SharedTestDataADM.IMAGES_PROJECT_IRI}", | "forProperty":null, @@ -394,7 +394,7 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T val permissionIri = result .getOrElse("iri", throw DeserializationException("The expected field 'iri' is missing.")) .convertTo[String] - assert(permissionIri == "http://rdfh.ch/permissions/00FF/DOAP-with-customIri") + assert(permissionIri == customDOAPIri) val forResourceClassIRI = result .getOrElse("forResourceClass", throw DeserializationException("The expected field 'forResourceClass' is missing.")) @@ -685,7 +685,7 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T "delete request" should { "erase a defaultObjectAccess permission" in { - val permissionIri = "http://rdfh.ch/permissions/00FF/DOAP-with-customIri" + val permissionIri = customDOAPIri val encodedPermissionIri = java.net.URLEncoder.encode(permissionIri, "utf-8") val request = Delete(baseApiUrl + s"/admin/permissions/" + encodedPermissionIri) ~> addCredentials( BasicHttpCredentials(SharedTestDataADM.rootUser.email, SharedTestDataADM.testPass)) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala index bf7ec18de8..fecc3180f8 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/ProjectsADME2ESpec.scala @@ -190,11 +190,9 @@ class ProjectsADME2ESpec } "given a custom Iri" should { - + val customProjectIri: IRI = "http://rdfh.ch/projects/XGze0w7g6P3p3z6tj_SXPQ" "CREATE a new project with the provided custom Iri" in { - val customProjectIri: IRI = "http://rdfh.ch/projects/3333" - val createProjectWithCustomIRIRequest: String = s"""{ | "id": "$customProjectIri", @@ -255,7 +253,7 @@ class ProjectsADME2ESpec "return 'BadRequest' if the supplied project IRI is not unique" in { val params = s"""{ - | "id": "http://rdfh.ch/projects/3333", + | "id": "$customProjectIri", | "shortname": "newprojectWithDuplicateIri", | "shortcode": "2222", | "longname": "new project with a duplicate custom invalid IRI", @@ -273,7 +271,7 @@ class ProjectsADME2ESpec val errorMessage: String = Await.result(Unmarshal(response.entity).to[String], 1.second) val invalidIri: Boolean = - errorMessage.contains(s"IRI: 'http://rdfh.ch/projects/3333' already exists, try another one.") + errorMessage.contains(s"IRI: '$customProjectIri' already exists, try another one.") invalidIri should be(true) } } diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala index 53cd7ac261..2f225113e9 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala @@ -105,6 +105,8 @@ class UsersADME2ESpec // Collects client test data private val clientTestDataCollector = new ClientTestDataCollector(settings) + private val customUserIri = "http://rdfh.ch/users/prWbAoyJA7fECqhKwhSUtQ" + /** * Convenience method returning the users project memberships. * @@ -336,7 +338,7 @@ class UsersADME2ESpec "create a user with the provided custom IRI " in { val createUserWithCustomIriRequest: String = s"""{ - | "id": "http://rdfh.ch/users/userWithCustomIri", + | "id": "$customUserIri", | "username": "userWithCustomIri", | "email": "userWithCustomIri@example.org", | "givenName": "a user", @@ -366,7 +368,7 @@ class UsersADME2ESpec val result: UserADM = AkkaHttpUtils.httpResponseToJson(response).fields("user").convertTo[UserADM] //check that the custom IRI is correctly assigned - result.id should be("http://rdfh.ch/users/userWithCustomIri") + result.id should be(customUserIri) clientTestDataCollector.addFile( TestDataFileContent( @@ -383,7 +385,7 @@ class UsersADME2ESpec "return 'BadRequest' if the supplied IRI for the user is not unique" in { val params = s"""{ - | "id": "http://rdfh.ch/users/userWithCustomIri", + | "id": "$customUserIri", | "username": "userWithDuplicateCustomIri", | "email": "userWithDuplicateCustomIri@example.org", | "givenName": "a user", @@ -400,7 +402,7 @@ class UsersADME2ESpec val errorMessage: String = Await.result(Unmarshal(response.entity).to[String], 1.second) val invalidIri: Boolean = - errorMessage.contains(s"IRI: 'http://rdfh.ch/users/userWithCustomIri' already exists, try another one.") + errorMessage.contains(s"IRI: '$customUserIri' already exists, try another one.") invalidIri should be(true) } } @@ -726,7 +728,7 @@ class UsersADME2ESpec } "delete a user" in { - val userIriEncoded = java.net.URLEncoder.encode("http://rdfh.ch/users/userWithCustomIri", "utf-8") + val userIriEncoded = java.net.URLEncoder.encode(customUserIri, "utf-8") val request = Delete(baseApiUrl + s"/admin/users/iri/$userIriEncoded") ~> addCredentials( BasicHttpCredentials(rootCreds.email, rootCreds.password)) val response: HttpResponse = singleAwaitingRequest(request) diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala index 1c5946f771..8b54138b8b 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/NewListsRoutesADMFeatureE2ESpec.scala @@ -89,6 +89,7 @@ class NewListsRouteADMFeatureE2ESpec private val treeListInfo: ListRootNodeInfoADM = SharedListsTestDataADM.treeListInfo private val treeListNodes: Seq[ListChildNodeADM] = SharedListsTestDataADM.treeListChildNodes + private val customChildNodeIRI = "http://rdfh.ch/lists/0001/JbKZ-L_i5rTwHlv4dSNp4A" def addChildListNodeRequest(parentNodeIri: IRI, name: String, label: String, comment: String): String = { s"""{ @@ -296,7 +297,6 @@ class NewListsRouteADMFeatureE2ESpec } "given a custom Iri" should { - "create a list with the provided custom Iri" in { val createListWithCustomIriRequest: String = s"""{ @@ -371,8 +371,6 @@ class NewListsRouteADMFeatureE2ESpec "add a child with a custom IRI" in { - val customChildNodeIRI = "http://rdfh.ch/lists/0001/a-child-node-with-IRI" - val createChildNodeWithCustomIriRequest = s""" |{ "id": "$customChildNodeIRI", @@ -1016,10 +1014,9 @@ class NewListsRouteADMFeatureE2ESpec } "update node information of node that has custom IRI with a new name" in { val newName = "modified third child" - val customChildNodeIRI = "http://rdfh.ch/lists/0001/a-child-node-with-IRI" val updateNodeName = s"""{ - | "listIri": "${customChildNodeIRI}", + | "listIri": "$customChildNodeIRI", | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", | "name": "${newName}" |}""".stripMargin diff --git a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala index 8ee67eee79..157ed38521 100644 --- a/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/e2e/admin/lists/OldListsRouteADMFeatureE2ESpec.scala @@ -88,7 +88,7 @@ class OldListsRouteADMFeatureE2ESpec private val treeListInfo: ListRootNodeInfoADM = SharedListsTestDataADM.treeListInfo private val treeListNodes: Seq[ListChildNodeADM] = SharedListsTestDataADM.treeListChildNodes - + private val customChildNodeIRI = "http://rdfh.ch/lists/0001/JbKZ-L_i5rTwHlv4dSNp4A" def addChildListNodeRequest(parentNodeIri: IRI, name: String, label: String, comment: String): String = { s"""{ | "parentNodeIri": "$parentNodeIri", @@ -361,8 +361,6 @@ class OldListsRouteADMFeatureE2ESpec "add a child with a custom IRI" in { - val customChildNodeIRI = "http://rdfh.ch/lists/0001/a-child-node-with-IRI" - val createChildNodeWithCustomIriRequest = s""" |{ "id": "$customChildNodeIRI", @@ -1077,10 +1075,9 @@ class OldListsRouteADMFeatureE2ESpec "update node information of a node that has custom IRI with a new name" in { val newName = "modified third child" - val customChildNodeIRI = "http://rdfh.ch/lists/0001/a-child-node-with-IRI" val updateNodeName = s"""{ - | "listIri": "${customChildNodeIRI}", + | "listIri": "$customChildNodeIRI", | "projectIri": "${SharedTestDataADM.ANYTHING_PROJECT_IRI}", | "name": "${newName}" |}""".stripMargin 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 1786aa2674..8c8a885030 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 @@ -682,6 +682,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { ) ) } + private val customValueUUID = "CpO1TIDf1IS55dQbyIuDsA" + private val customValueIri: IRI = s"http://rdfh.ch/0001/a-thing/values/$customValueUUID" "The values v2 endpoint" should { "get the latest versions of values, given their UUIDs" in { @@ -814,7 +816,6 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "create an integer value with a custom value IRI" in { val resourceIri: IRI = AThing.iri val intValue: Int = 30 - val customValueIri: IRI = "http://rdfh.ch/0001/a-thing/values/int-with-valueIRI" val jsonLDEntity = s"""{ @@ -851,6 +852,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val valueIri: IRI = responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) assert(valueIri == customValueIri) + val valueUUID = responseJsonDoc.body.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasUUID) + assert(valueUUID == customValueUUID) } "return a DuplicateValueException during value creation when the supplied value IRI is not unique" in { @@ -861,7 +864,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { | "@id" : "${AThing.iri}", | "@type" : "anything:Thing", | "anything:hasInteger" : { - | "@id" : "http://rdfh.ch/0001/a-thing/values/int-with-valueIRI", + | "@id" : "$customValueIri", | "@type" : "knora-api:IntValue", | "knora-api:intValueAsInt" : 43 | }, @@ -878,15 +881,14 @@ class ValuesRouteV2E2ESpec extends E2ESpec { assert(response.status == StatusCodes.BadRequest, response.toString) val errorMessage: String = Await.result(Unmarshal(response.entity).to[String], 1.second) - val invalidIri: Boolean = errorMessage.contains( - s"IRI: 'http://rdfh.ch/0001/a-thing/values/int-with-valueIRI' already exists, try another one.") + val invalidIri: Boolean = errorMessage.contains(s"IRI: '$customValueIri' already exists, try another one.") invalidIri should be(true) } "create an integer value with a custom UUID" in { val resourceIri: IRI = AThing.iri val intValue: Int = 45 - val customValueUUID = "IN4R19yYR0ygi3K2VEHpUQ" + val intValueCustomUUID = "IN4R19yYR0ygi3K2VEHpUQ" val jsonLDEntity = s"""{ @@ -895,7 +897,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { | "anything:hasInteger" : { | "@type" : "knora-api:IntValue", | "knora-api:intValueAsInt" : $intValue, - | "knora-api:valueHasUUID" : "$customValueUUID" + | "knora-api:valueHasUUID" : "$intValueCustomUUID" | }, | "@context" : { | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", @@ -921,8 +923,52 @@ class ValuesRouteV2E2ESpec extends E2ESpec { assert(response.status == StatusCodes.OK, response.toString) val responseJsonDoc: JsonLDDocument = responseToJsonLDDocument(response) - val valueUUID = responseJsonDoc.body.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasUUID) - assert(valueUUID == customValueUUID) + val valueUUID: String = responseJsonDoc.body.requireString(OntologyConstants.KnoraApiV2Complex.ValueHasUUID) + assert(valueUUID == intValueCustomUUID) + val valueIri: IRI = + responseJsonDoc.body.requireStringWithValidation(JsonLDKeywords.ID, stringFormatter.validateAndEscapeIri) + assert(valueIri.endsWith(valueUUID)) + + } + + "do not create an integer value if the custom UUID is not part of the custom IRI" in { + val resourceIri: IRI = AThing.iri + val intValue: Int = 45 + val aUUID = "IN4R19yYR0ygi3K2VEHpUQ" + val valueIri = s"http://rdfh.ch/0001/a-thing/values/IN4R19yYR0ygi3K2VEHpNN" + val jsonLDEntity = + s"""{ + | "@id" : "$resourceIri", + | "@type" : "anything:Thing", + | "anything:hasInteger" : { + | "@id" : "$valueIri", + | "@type" : "knora-api:IntValue", + | "knora-api:intValueAsInt" : $intValue, + | "knora-api:valueHasUUID" : "$aUUID" + | }, + | "@context" : { + | "knora-api" : "http://api.knora.org/ontology/knora-api/v2#", + | "anything" : "http://0.0.0.0:3333/ontology/0001/anything/v2#", + | "xsd" : "http://www.w3.org/2001/XMLSchema#" + | } + |}""".stripMargin + + clientTestDataCollector.addFile( + TestDataFileContent( + filePath = TestDataFilePath( + directoryPath = clientTestDataPath, + filename = "create-int-value-with-custom-UUID-request", + fileExtension = "json" + ), + text = jsonLDEntity + ) + ) + + val request = Post(baseApiUrl + "/v2/values", HttpEntity(RdfMediaTypes.`application/ld+json`, jsonLDEntity)) ~> addCredentials( + BasicHttpCredentials(anythingUserEmail, password)) + val response: HttpResponse = singleAwaitingRequest(request) + + assert(response.status == StatusCodes.BadRequest, response.toString) } "create an integer value with a custom creation date" in { @@ -982,8 +1028,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "create an integer value with custom IRI, UUID, and creation date" in { val resourceIri: IRI = AThing.iri val intValue: Int = 10 - val customValueIri: IRI = "http://rdfh.ch/0001/a-thing/values/int-with-IRI" - val customValueUUID = "IN4R19yYR0ygi3K2VEHpUQ" + val customValueIri: IRI = "http://rdfh.ch/0001/a-thing/values/7VDvMOnuitf_r1Ju7BglsQ" + val customValueUUID = "7VDvMOnuitf_r1Ju7BglsQ" val customCreationDate: Instant = Instant.parse("2020-06-04T12:58:54.502951Z") val jsonLDEntity = @@ -3218,8 +3264,8 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "create a link between two resources with a custom link value IRI, UUID, creationDate" in { val resourceIri: IRI = AThing.iri val targetResourceIri: IRI = "http://rdfh.ch/0001/CNhWoNGGT7iWOrIwxsEqvA" - val customValueIri: IRI = "http://rdfh.ch/0001/a-thing/values/link-Value-With-IRI" - val customValueUUID = "IN4R19yYR0ygi3K2VEHpUQ" + val customValueIri: IRI = "http://rdfh.ch/0001/a-thing/values/mr9i2aUUJolv64V_9hYdTw" + val customValueUUID = "mr9i2aUUJolv64V_9hYdTw" val customCreationDate: Instant = Instant.parse("2020-06-04T11:36:54.502951Z") val jsonLDEntity = @@ -3229,7 +3275,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { | "anything:hasOtherThingValue" : { | "@id" : "$customValueIri", | "@type" : "knora-api:LinkValue", - | "knora-api:valueHasUUID": "IN4R19yYR0ygi3K2VEHpUQ", + | "knora-api:valueHasUUID": "$customValueUUID", | "knora-api:linkValueHasTargetIri" : { | "@id" : "$targetResourceIri" | }, @@ -3425,7 +3471,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { val propertyIri: SmartIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri val intValue: Int = 7 val maybeResourceLastModDate: Option[Instant] = getResourceLastModificationDate(resourceIri, anythingUserEmail) - val newValueVersionIri: IRI = s"http://rdfh.ch/0001/a-thing/values/updated-int-value" + val newValueVersionIri: IRI = s"http://rdfh.ch/0001/a-thing/values/DrXts3Up3DijGriI403nhg" val jsonLDEntity = updateIntValueWithCustomNewValueVersionIriRequest( resourceIri = resourceIri, @@ -3461,7 +3507,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "not update an integer value with a custom new value version IRI that is the same as the current IRI" in { val resourceIri: IRI = AThing.iri val intValue: Int = 8 - val newValueVersionIri: IRI = s"http://rdfh.ch/0001/a-thing/values/updated-int-value" + val newValueVersionIri: IRI = s"http://rdfh.ch/0001/a-thing/values/DrXts3Up3DijGriI403nhg" val jsonLDEntity = updateIntValueWithCustomNewValueVersionIriRequest( resourceIri = resourceIri, @@ -3518,7 +3564,7 @@ class ValuesRouteV2E2ESpec extends E2ESpec { "not update an integer value with a custom new value version IRI that refers to the wrong resource" in { val resourceIri: IRI = AThing.iri val intValue: Int = 8 - val newValueVersionIri: IRI = "http://rdfh.ch/0001/nResNuvARcWYUdWyo0GWGw/values/foo" + val newValueVersionIri: IRI = "http://rdfh.ch/0001/nResNuvARcWYUdWyo0GWGw/values/iEYi6E7Ntjvj2syzJZiXlg" val jsonLDEntity = updateIntValueWithCustomNewValueVersionIriRequest( resourceIri = resourceIri, diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala index af6b2a0cb1..e8cff4f441 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala @@ -248,9 +248,10 @@ class PermissionsResponderADMSpec } "create and return an administrative permission with a custom IRI" in { + val customIri = "http://rdfh.ch/permissions/0001/YCHszszUkzmZp5YURnmBDA" responderManager ! AdministrativePermissionCreateRequestADM( createRequest = CreateAdministrativePermissionAPIRequestADM( - id = Some("http://rdfh.ch/permissions/0001/AP-with-customIri"), + id = Some(customIri), forProject = SharedTestDataADM.ANYTHING_PROJECT_IRI, forGroup = SharedTestDataADM.thingSearcherGroup.id, hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission) @@ -261,7 +262,7 @@ class PermissionsResponderADMSpec ) val received: AdministrativePermissionCreateResponseADM = expectMsgType[AdministrativePermissionCreateResponseADM] - assert(received.administrativePermission.iri == "http://rdfh.ch/permissions/0001/AP-with-customIri") + assert(received.administrativePermission.iri == customIri) assert(received.administrativePermission.forProject == SharedTestDataADM.ANYTHING_PROJECT_IRI) assert(received.administrativePermission.forGroup == SharedTestDataADM.thingSearcherGroup.id) } @@ -430,9 +431,10 @@ class PermissionsResponderADMSpec } "create a DefaultObjectAccessPermission for project and group with custom IRI" in { + val customIri = "http://rdfh.ch/permissions/0001/mnL_QA0D06zkE7p9LMqBCA" responderManager ! DefaultObjectAccessPermissionCreateRequestADM( createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM( - id = Some("http://rdfh.ch/permissions/0001/DOAP-with-customIri"), + id = Some(customIri), forProject = SharedTestDataADM.ANYTHING_PROJECT_IRI, forGroup = Some(OntologyConstants.KnoraAdmin.UnknownUser), hasPermissions = Set(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser)) @@ -443,7 +445,7 @@ class PermissionsResponderADMSpec ) val received: DefaultObjectAccessPermissionCreateResponseADM = expectMsgType[DefaultObjectAccessPermissionCreateResponseADM] - assert(received.defaultObjectAccessPermission.iri == "http://rdfh.ch/permissions/0001/DOAP-with-customIri") + assert(received.defaultObjectAccessPermission.iri == customIri) assert(received.defaultObjectAccessPermission.forProject == SharedTestDataADM.ANYTHING_PROJECT_IRI) assert(received.defaultObjectAccessPermission.forGroup.contains(OntologyConstants.KnoraAdmin.UnknownUser)) assert( diff --git a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index e1085e9edd..283b9794ef 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -32,8 +32,14 @@ import org.knora.webapi._ import org.knora.webapi.exceptions.{BadRequestException, DuplicateValueException, NotFoundException} import org.knora.webapi.messages.{OntologyConstants, StringFormatter} import org.knora.webapi.messages.admin.responder.permissionsmessages.{ + AdministrativePermissionADM, AdministrativePermissionGetResponseADM, + AdministrativePermissionsForProjectGetRequestADM, + AdministrativePermissionsForProjectGetResponseADM, + DefaultObjectAccessPermissionADM, DefaultObjectAccessPermissionGetResponseADM, + DefaultObjectAccessPermissionsForProjectGetRequestADM, + DefaultObjectAccessPermissionsForProjectGetResponseADM, PermissionADM, PermissionByIriGetRequestADM } @@ -237,72 +243,65 @@ class ProjectsResponderADMSpec extends CoreSpec(ProjectsResponderADMSpec.config) newProjectIri.set(received.project.id) - /* Check that ProjectAdmin group has got administrative and default object access permissions */ - // Check Administrative Permission of ProjectAdmin - responderManager ! PermissionByIriGetRequestADM( - permissionIri = s"http://rdfh.ch/permissions/${shortCode.toUpperCase}/defaultApForAdmin", - requestingUser = rootUser + // Check Administrative Permissions + responderManager ! AdministrativePermissionsForProjectGetRequestADM( + projectIri = received.project.id, + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() ) + // Check Administrative Permission of ProjectAdmin + val receivedApAdmin: AdministrativePermissionsForProjectGetResponseADM = + expectMsgType[AdministrativePermissionsForProjectGetResponseADM] + val hasAPForProjectAdmin = receivedApAdmin.administrativePermissions.filter { ap: AdministrativePermissionADM => + ap.forProject == received.project.id && ap.forGroup == OntologyConstants.KnoraAdmin.ProjectAdmin && + ap.hasPermissions + .equals(Set(PermissionADM.ProjectAdminAllPermission, PermissionADM.ProjectResourceCreateAllPermission)) + } - val receivedApAdmin: AdministrativePermissionGetResponseADM = - expectMsgType[AdministrativePermissionGetResponseADM] - receivedApAdmin.administrativePermission.forProject should be(received.project.id) - receivedApAdmin.administrativePermission.forGroup should be(OntologyConstants.KnoraAdmin.ProjectAdmin) - val expectedAdminApPermissions: Set[PermissionADM] = - Set(PermissionADM.ProjectAdminAllPermission, PermissionADM.ProjectResourceCreateAllPermission) - assert(receivedApAdmin.administrativePermission.hasPermissions === expectedAdminApPermissions) + hasAPForProjectAdmin.size shouldBe 1 - // Check Default Object Access permission of ProjectAdmin - responderManager ! PermissionByIriGetRequestADM( - permissionIri = s"http://rdfh.ch/permissions/${shortCode.toUpperCase}/defaultDoapForAdmin", - requestingUser = rootUser - ) - val receivedDoapAdmin: DefaultObjectAccessPermissionGetResponseADM = - expectMsgType[DefaultObjectAccessPermissionGetResponseADM] - receivedDoapAdmin.defaultObjectAccessPermission.forProject should be(received.project.id) - receivedDoapAdmin.defaultObjectAccessPermission.forGroup should be( - Some(OntologyConstants.KnoraAdmin.ProjectAdmin)) - val expectedAdminDoapPermissions: Set[PermissionADM] = - Set( - PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.deletePermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), - PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin) - ) - assert(receivedDoapAdmin.defaultObjectAccessPermission.hasPermissions === expectedAdminDoapPermissions) - - /* Check that ProjectMember group has got administrative and default object access permissions */ // Check Administrative Permission of ProjectMember - responderManager ! PermissionByIriGetRequestADM( - permissionIri = s"http://rdfh.ch/permissions/${shortCode.toUpperCase}/defaultApForMember", - requestingUser = rootUser + val hasAPForProjectMember = receivedApAdmin.administrativePermissions.filter { + ap: AdministrativePermissionADM => + ap.forProject == received.project.id && ap.forGroup == OntologyConstants.KnoraAdmin.ProjectMember && + ap.hasPermissions.equals(Set(PermissionADM.ProjectResourceCreateAllPermission)) + } + hasAPForProjectMember.size shouldBe 1 + + // Check Default Object Access permissions + responderManager ! DefaultObjectAccessPermissionsForProjectGetRequestADM( + projectIri = received.project.id, + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() ) - val receivedApMember: AdministrativePermissionGetResponseADM = - expectMsgType[AdministrativePermissionGetResponseADM] - receivedApMember.administrativePermission.forProject should be(received.project.id) - receivedApMember.administrativePermission.forGroup should be(OntologyConstants.KnoraAdmin.ProjectMember) - val expectedMemberApPermissions: Set[PermissionADM] = - Set(PermissionADM.ProjectResourceCreateAllPermission) - assert(receivedApMember.administrativePermission.hasPermissions === expectedMemberApPermissions) + val receivedDoaps: DefaultObjectAccessPermissionsForProjectGetResponseADM = + expectMsgType[DefaultObjectAccessPermissionsForProjectGetResponseADM] + + // Check Default Object Access permission of ProjectAdmin + val hasDOAPForProjectAdmin = receivedDoaps.defaultObjectAccessPermissions.filter { + doap: DefaultObjectAccessPermissionADM => + doap.forProject == received.project.id && doap.forGroup.contains(OntologyConstants.KnoraAdmin.ProjectAdmin) && + doap.hasPermissions.equals(Set( + PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.deletePermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin), + PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin) + )) + } + hasDOAPForProjectAdmin.size shouldBe 1 // Check Default Object Access permission of ProjectMember - responderManager ! PermissionByIriGetRequestADM( - permissionIri = s"http://rdfh.ch/permissions/${shortCode.toUpperCase}/defaultDoapForMember", - requestingUser = rootUser - ) - val receivedDoapMember: DefaultObjectAccessPermissionGetResponseADM = - expectMsgType[DefaultObjectAccessPermissionGetResponseADM] - receivedDoapMember.defaultObjectAccessPermission.forProject should be(received.project.id) - receivedDoapMember.defaultObjectAccessPermission.forGroup should be( - Some(OntologyConstants.KnoraAdmin.ProjectMember)) - val expectedMemberDoapPermissions: Set[PermissionADM] = - Set( - PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember), - PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectMember), - PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectMember) - ) - assert(receivedDoapMember.defaultObjectAccessPermission.hasPermissions === expectedMemberDoapPermissions) + val hasDOAPForProjectMember = receivedDoaps.defaultObjectAccessPermissions.filter { + doap: DefaultObjectAccessPermissionADM => + doap.forProject == received.project.id && doap.forGroup.contains(OntologyConstants.KnoraAdmin.ProjectMember) && + doap.hasPermissions.equals(Set( + PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember), + PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectMember), + PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectMember) + )) + } + hasDOAPForProjectMember.size shouldBe 1 } "CREATE a project and return the project info if the supplied shortname and shortcode is unique" in { diff --git a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala index dd888228a0..3eecdbfd32 100644 --- a/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala +++ b/webapi/src/test/scala/org/knora/webapi/responders/v2/ResourcesResponderV2Spec.scala @@ -2384,7 +2384,110 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { assert(entityUsedResponse.results.bindings.isEmpty, s"Link value was not erased") } } + } + "When given a custom IRI" should { + + "create a resource with no values but a custom IRI" in { + // Create the resource. + + val resourceIri: IRI = "http://rdfh.ch/0001/55UrkgTKR2SEQgnsLWI9kk" + + val inputResource = CreateResourceV2( + resourceIri = Some(resourceIri.toSmartIri), + resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri, + label = "thing with a custom IRI", + values = Map.empty, + projectADM = SharedTestDataADM.anythingProject + ) + + responderManager ! CreateResourceRequestV2( + createResource = inputResource, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingUserProfile, + apiRequestID = UUID.randomUUID + ) + + // Check that the response contains the correct metadata. + + expectMsgPF(timeout) { + case response: ReadResourcesSequenceV2 => + val outputResource: ReadResourceV2 = response.toResource(resourceIri).toOntologySchema(ApiV2Complex) + + checkCreateResource( + inputResourceIri = resourceIri, + inputResource = inputResource, + outputResource = outputResource, + defaultResourcePermissions = defaultAnythingResourcePermissions, + defaultValuePermissions = defaultAnythingValuePermissions, + requestingUser = anythingUserProfile + ) + } + + // Get the resource from the triplestore and check it again. + + val outputResource = getResource(resourceIri, anythingUserProfile) + + checkCreateResource( + inputResourceIri = resourceIri, + inputResource = inputResource, + outputResource = outputResource, + defaultResourcePermissions = defaultAnythingResourcePermissions, + defaultValuePermissions = defaultAnythingValuePermissions, + requestingUser = anythingUserProfile + ) + } + + "create a resource with a value that has custom UUID" in { + // Create the resource. + + val resourceIri: IRI = stringFormatter.makeRandomResourceIri(SharedTestDataADM.anythingProject.shortcode) + + val inputValues: Map[SmartIri, Seq[CreateValueInNewResourceV2]] = Map( + "http://0.0.0.0:3333/ontology/0001/anything/v2#hasInteger".toSmartIri -> Seq( + CreateValueInNewResourceV2( + valueContent = IntegerValueContentV2( + ontologySchema = ApiV2Complex, + valueHasInteger = 5, + comment = Some("this is the number five") + ), + permissions = Some("CR knora-admin:Creator|V http://rdfh.ch/groups/0001/thing-searcher"), + customValueUUID = Some(stringFormatter.base64DecodeUuid("IN4R19yYR0ygi3K2VEHpUQ")) + ) + ) + ) + + val inputResource = CreateResourceV2( + resourceIri = Some(resourceIri.toSmartIri), + resourceClassIri = "http://0.0.0.0:3333/ontology/0001/anything/v2#Thing".toSmartIri, + label = "thing with custom value UUID", + values = inputValues, + projectADM = SharedTestDataADM.anythingProject + ) + responderManager ! CreateResourceRequestV2( + createResource = inputResource, + featureFactoryConfig = defaultFeatureFactoryConfig, + requestingUser = anythingUserProfile, + apiRequestID = UUID.randomUUID + ) + + expectMsgType[ReadResourcesSequenceV2](timeout) + + // Get the resource from the triplestore and check it. + + val outputResource = getResource(resourceIri, anythingUserProfile) + + checkCreateResource( + inputResourceIri = resourceIri, + inputResource = inputResource, + outputResource = outputResource, + defaultResourcePermissions = defaultAnythingResourcePermissions, + defaultValuePermissions = defaultAnythingValuePermissions, + requestingUser = anythingUserProfile + ) + } + } + "When asked for events" should { "return full history of a-thing-picture resource" in { val resourceIri = "http://rdfh.ch/0001/a-thing-picture" @@ -2461,7 +2564,7 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { "create a new value to test create value history event" in { val resourceIri = "http://rdfh.ch/0001/thing-with-history" - val newValueIri = "http://rdfh.ch/0001/thing-with-history/values/newText" + val newValueIri = "http://rdfh.ch/0001/thing-with-history/values/xZisRC3jPkcplt1hQQdb-A" val testValue = "a test value" // create new value. @@ -2511,7 +2614,7 @@ class ResourcesResponderV2Spec extends CoreSpec() with ImplicitSender { "delete the newly created value to check the delete value event of resource history" in { val resourceIri = "http://rdfh.ch/0001/thing-with-history" - val valueToDelete = "http://rdfh.ch/0001/thing-with-history/values/newText" + val valueToDelete = "http://rdfh.ch/0001/thing-with-history/values/xZisRC3jPkcplt1hQQdb-A" val deleteComment = "delete value test" // delete the new value. diff --git a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala index 66fcf3f780..b459434422 100644 --- a/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala +++ b/webapi/src/test/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala @@ -462,16 +462,16 @@ object SharedTestDataADM { /** **********************************/ val ANYTHING_PROJECT_IRI = "http://rdfh.ch/projects/0001" - val customResourceIRI: IRI = "http://rdfh.ch/0001/a-thing-with-IRI" - val customResourceIRI_resourceWithValues: IRI = "http://rdfh.ch/0001/a-thing-with-value-IRI" + val customResourceIRI: IRI = "http://rdfh.ch/0001/aUrDPcJRmFNzBHW_AlR1hw" + val customResourceIRI_resourceWithValues: IRI = "http://rdfh.ch/0001/5zCt1EMJKezFUOW_RCB0Gw" val customValueIRI_withResourceIriAndValueIRIAndValueUUID: IRI = - "http://rdfh.ch/0001/a-thing-with-value-IRI/values/a-value-with-IRI-and-UUID" - val customValueUUID = "IN4R19yYR0ygi3K2VEHpUQ" - val customValueIRI: IRI = "http://rdfh.ch/0001/a-thing-with-value-IRI/values/a-value-with-IRI" + "http://rdfh.ch/0001/5zCt1EMJKezFUOW_RCB0Gw/values/fdqCOaqT6dP19pWI84X1XQ" + val customValueUUID = "fdqCOaqT6dP19pWI84X1XQ" + val customValueIRI: IRI = "http://rdfh.ch/0001/5zCt1EMJKezFUOW_RCB0Gw/values/tdWAtnWK2qUC6tr4uQLAHA" val customResourceCreationDate: Instant = Instant.parse("2019-01-09T15:45:54.502951Z") val customValueCreationDate: Instant = Instant.parse("2020-06-09T17:04:54.502951Z") - val customListIRI: IRI = "http://rdfh.ch/lists/0001/a-list-with-IRI" + val customListIRI: IRI = "http://rdfh.ch/lists/0001/WYHQu7y6BGrTBcnRtg76Tg" def anythingAdminUser: UserADM = UserADM(