diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 39b727800..d614c5b3b 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -536,6 +536,37 @@ } ] }, + { + "id": "inventory-update-ownership", + "version": "1.0", + "handlers": [ + { + "methods": ["POST"], + "pathPattern": "/inventory/items/update-ownership", + "permissionsRequired": ["inventory.items.update-ownership.item.post"], + "modulePermissions": [ + "inventory-storage.items.item.post", + "inventory-storage.items.item.delete", + "inventory-storage.items.collection.get", + "inventory-storage.holdings.item.get" + ] + }, + { + "methods": ["POST"], + "pathPattern": "/inventory/holdings/update-ownership", + "permissionsRequired": ["inventory.holdings.update-ownership.item.post"], + "modulePermissions": [ + "inventory-storage.holdings.item.post", + "inventory-storage.holdings.item.delete", + "inventory-storage.holdings.collection.get", + "inventory-storage.items.item.post", + "inventory-storage.items.item.delete", + "inventory-storage.items.collection.get", + "inventory-storage.instances.item.get" + ] + } + ] + }, { "id": "_tenant", "version": "1.2", @@ -706,6 +737,11 @@ "displayName": "Inventory - mark an item as unknown", "description": "Mark an item as unknown" }, + { + "permissionName": "inventory.items.update-ownership.item.post", + "displayName": "Inventory - update an item ownership", + "description": "Update an item ownership" + }, { "permissionName": "inventory.holdings.move.item.post", @@ -742,6 +778,11 @@ "displayName": "Inventory - modify holdings", "description": "Modify individual instance" }, + { + "permissionName": "inventory.holdings.update-ownership.item.post", + "displayName": "Inventory - update holdings ownership", + "description": "Update holdings record ownership" + }, { "permissionName": "inventory.instances.item.get", "displayName": "Inventory - get individual instance", @@ -803,7 +844,9 @@ "inventory.items.item.mark-in-process-non-requestable.post", "inventory.items.item.mark-missing.post", "inventory.items.move.item.post", - "inventory.holdings.move.item.post" + "inventory.holdings.move.item.post", + "inventory.items.update-ownership.item.post", + "inventory.holdings.update-ownership.item.post" ] }, { diff --git a/ramls/holdings_update_ownership.json b/ramls/holdings_update_ownership.json new file mode 100644 index 000000000..e185dcfbc --- /dev/null +++ b/ramls/holdings_update_ownership.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Ids holder for updating ownership of the holdings records", + "type": "object", + "properties": { + "toInstanceId": { + "description": "Id of the shared Instance, ownership of Holdings linked to this Instance will be changed.", + "$ref": "uuid.json" + }, + "holdingsRecordIds": { + "description": "Ids of the holdings to update ownership.", + "type": "array", + "items": { + "$ref": "uuid.json" + } + }, + "targetTenantId": { + "description": "Target tenant Id, where selected Holdings will be created.", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "toInstanceId", + "holdingsRecordIds", + "targetTenantId" + ] +} diff --git a/ramls/inventory-update-ownership.raml b/ramls/inventory-update-ownership.raml new file mode 100644 index 000000000..c81864eb0 --- /dev/null +++ b/ramls/inventory-update-ownership.raml @@ -0,0 +1,66 @@ +#%RAML 1.0 +title: Inventory Update Ownership API +version: v0.1 +protocols: [ HTTP, HTTPS ] +baseUri: http://localhost + +documentation: + - title: "Inventory Update Ownership API" + content: API for updating ownership of holdings records and items between ECS tenants + +types: + errors: !include raml-util/schemas/errors.schema + items_update_ownership: !include items_update_ownership.json + holdings_update_ownership: !include holdings_update_ownership.json + move_response: !include move_response.json + +traits: + language: !include raml-util/traits/language.raml + validate: !include raml-util/traits/validation.raml + +/inventory/items/update-ownership: + displayName: Items Update Ownership + post: + is: [validate] + body: + application/json: + type: items_update_ownership + responses: + 200: + description: "Items ownership updated to another tenant" + body: + application/json: + type: move_response + 422: + description: "Validation error" + body: + application/json: + type: errors + 500: + description: "Internal server error" + body: + text/plain: + example: "Internal server error" +/inventory/holdings/update-ownership: + displayName: Holdings Record Update Ownership + post: + is: [validate] + body: + application/json: + type: holdings_update_ownership + responses: + 200: + description: "Holdings record ownership updated to another tenant" + body: + application/json: + type: move_response + 422: + description: "Validation error" + body: + application/json: + type: errors + 500: + description: "Internal server error" + body: + text/plain: + example: "Internal server error" diff --git a/ramls/items_update_ownership.json b/ramls/items_update_ownership.json new file mode 100644 index 000000000..b6221e93c --- /dev/null +++ b/ramls/items_update_ownership.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Ids holder for updating ownership of the items", + "type": "object", + "properties": { + "toHoldingsRecordId": { + "description": "Id of the Holdings Record, ownership of Items linked to this Holdings Record will be changed.", + "$ref": "uuid.json" + }, + "itemIds": { + "description": "Ids of the items to update ownership.", + "type": "array", + "items": { + "$ref": "uuid.json" + } + }, + "targetTenantId": { + "description": "Target tenant Id, where selected Items will be created.", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "toHoldingsRecordId", + "itemIds", + "targetTenantId" + ] +} diff --git a/src/main/java/org/folio/inventory/InventoryVerticle.java b/src/main/java/org/folio/inventory/InventoryVerticle.java index 08a08b54b..8079714c6 100644 --- a/src/main/java/org/folio/inventory/InventoryVerticle.java +++ b/src/main/java/org/folio/inventory/InventoryVerticle.java @@ -19,6 +19,7 @@ import org.folio.inventory.resources.ItemsByHoldingsRecordId; import org.folio.inventory.resources.MoveApi; import org.folio.inventory.resources.TenantApi; +import org.folio.inventory.resources.UpdateOwnershipApi; import org.folio.inventory.storage.Storage; import io.vertx.core.AbstractVerticle; @@ -69,6 +70,7 @@ public void start(Promise started) { new ItemsByHoldingsRecordId(storage, client).register(router); new InventoryConfigApi().register(router); new TenantApi().register(router); + new UpdateOwnershipApi(storage, client).register(router); Handler> onHttpServerStart = result -> { if (result.succeeded()) { diff --git a/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java b/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java new file mode 100644 index 000000000..339fb8886 --- /dev/null +++ b/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java @@ -0,0 +1,28 @@ +package org.folio.inventory.resources; + +import io.vertx.core.http.HttpClient; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import org.folio.inventory.storage.Storage; + +public class UpdateOwnershipApi extends AbstractInventoryResource { + public UpdateOwnershipApi(Storage storage, HttpClient client) { + super(storage, client); + } + + @Override + public void register(Router router) { + router.post("/inventory/items/update-ownership") + .handler(this::updateItemsOwnership); + router.post("/inventory/holdings/update-ownership") + .handler(this::updateHoldingsOwnership); + } + + private void updateHoldingsOwnership(RoutingContext routingContext) { + // should be implemented in MODINV-1031 + } + + private void updateItemsOwnership(RoutingContext routingContext) { + // should be implemented in MODINV-1031 + } +} diff --git a/src/test/java/api/ApiTestSuite.java b/src/test/java/api/ApiTestSuite.java index ca5dc7344..2373dc0fd 100644 --- a/src/test/java/api/ApiTestSuite.java +++ b/src/test/java/api/ApiTestSuite.java @@ -79,6 +79,8 @@ public class ApiTestSuite { public static final String TOKEN = "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsInRlbmFudCI6ImRlbW9fdGVuYW50In0.29VPjLI6fLJzxQW0UhQ0jsvAn8xHz501zyXAxRflXfJ9wuDzT8TDf-V75PjzD7fe2kHjSV2dzRXbstt3BTtXIQ"; public static final String USER_ID = "7e115dfb-d1d6-46ac-b2dc-2b3e74cda694"; + public static final String CENTRAL_TENANT_ID_FIELD = "centralTenantId"; + public static final String CONSORTIUM_ID_FIELD = "consortiumId"; private static String bookMaterialTypeId; private static String dvdMaterialTypeId; @@ -218,10 +220,16 @@ public static String getBibliographyNatureOfContentTermId() { public static OkapiHttpClient createOkapiHttpClient() throws MalformedURLException { + return createOkapiHttpClient(TENANT_ID); + } + + public static OkapiHttpClient createOkapiHttpClient(String tenantId) + throws MalformedURLException { + return new OkapiHttpClient( vertxAssistant.getVertx(), - new URL(storageOkapiUrl()), TENANT_ID, TOKEN, USER_ID, null, - it -> System.out.println(String.format("Request failed: %s", it.toString()))); + new URL(storageOkapiUrl()), tenantId, TOKEN, USER_ID, null, + it -> System.out.printf("Request failed: %s%n", it.toString())); } public static String storageOkapiUrl() { @@ -449,6 +457,18 @@ private static void createNatureOfContentTerms() ); } + public static void createConsortiumTenant() throws MalformedURLException { + String expectedConsortiumId = UUID.randomUUID().toString(); + + JsonObject userTenantsCollection = new JsonObject() + .put(ApiTestSuite.CENTRAL_TENANT_ID_FIELD, ApiTestSuite.CONSORTIA_TENANT_ID) + .put(ApiTestSuite.CONSORTIUM_ID_FIELD, expectedConsortiumId); + + ResourceClient client = ResourceClient.forUserTenants(createOkapiHttpClient()); + + client.create(userTenantsCollection); + } + private static void createIdentifierTypes() throws MalformedURLException, InterruptedException, diff --git a/src/test/java/api/InstanceRelationshipsTest.java b/src/test/java/api/InstanceRelationshipsTest.java index 172cf5c66..c120a1ef2 100644 --- a/src/test/java/api/InstanceRelationshipsTest.java +++ b/src/test/java/api/InstanceRelationshipsTest.java @@ -1,5 +1,6 @@ package api; +import static api.ApiTestSuite.createConsortiumTenant; import static api.support.InstanceSamples.nod; import static api.support.InstanceSamples.smallAngryPlanet; import static io.vertx.core.json.JsonObject.mapFrom; @@ -34,8 +35,6 @@ public class InstanceRelationshipsTest extends ApiTests { private static final String PARENT_INSTANCES = "parentInstances"; - private static final String CENTRAL_TENANT_ID_FIELD = "centralTenantId"; - private static final String CONSORTIUM_ID_FIELD = "consortiumId"; @After public void disableFailureEmulationAndClearConsortia() throws Exception { @@ -490,7 +489,7 @@ public void canUpdateInstanceWithChildInstancesWhenParentInstancesAlreadySet() t public void cannotLinkLocalInstanceToSharedInstance() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { UUID parentId = UUID.randomUUID(); - initConsortiumTenant(); + createConsortiumTenant(); final JsonObject parentRelationship = createParentRelationship(parentId.toString(), instanceRelationshipTypeFixture.boundWith().getId()); @@ -503,8 +502,8 @@ public void cannotLinkLocalInstanceToSharedInstance() throws MalformedURLExcepti } @Test - public void canCreateInstanceWithParentInstancesWhenConsortiaEnabled() { - initConsortiumTenant(); + public void canCreateInstanceWithParentInstancesWhenConsortiaEnabled() throws MalformedURLException { + createConsortiumTenant(); final IndividualResource parentInstance = instancesClient.create(nod(UUID.randomUUID())); @@ -518,16 +517,6 @@ public void canCreateInstanceWithParentInstancesWhenConsortiaEnabled() { is(parentRelationship)); } - private void initConsortiumTenant() { - String expectedConsortiumId = UUID.randomUUID().toString(); - - JsonObject userTenantsCollection = new JsonObject() - .put(CENTRAL_TENANT_ID_FIELD, ApiTestSuite.CONSORTIA_TENANT_ID) - .put(CONSORTIUM_ID_FIELD, expectedConsortiumId); - - userTenantsClient.create(userTenantsCollection); - } - private JsonObject createParentRelationship(String superInstanceId, String relationshipType) { return mapFrom(new InstanceRelationshipToParent(UUID.randomUUID().toString(), superInstanceId, relationshipType)); diff --git a/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java b/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java new file mode 100644 index 000000000..28f8145e1 --- /dev/null +++ b/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java @@ -0,0 +1,297 @@ +package api.holdings; + +import api.ApiTestSuite; +import api.support.ApiRoot; +import api.support.ApiTests; +import api.support.InstanceApiClient; +import api.support.builders.HoldingRequestBuilder; +import api.support.builders.HoldingsRecordUpdateOwnershipRequestBuilder; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import junitparams.JUnitParamsRunner; +import org.apache.http.HttpStatus; +import org.folio.inventory.support.http.client.Response; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.MalformedURLException; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static api.ApiTestSuite.ID_FOR_FAILURE; +import static api.ApiTestSuite.createConsortiumTenant; +import static api.support.InstanceSamples.smallAngryPlanet; +import static org.folio.inventory.support.http.ContentType.APPLICATION_JSON; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static support.matchers.ResponseMatchers.hasValidationError; + +@Ignore +@RunWith(JUnitParamsRunner.class) +public class HoldingsUpdateOwnershipApiTest extends ApiTests { + private static final String INSTANCE_ID = "instanceId"; + + @Before + public void initConsortia() throws Exception { + createConsortiumTenant(); + } + + @After + public void clearConsortia() throws Exception { + userTenantsClient.deleteAll(); + } + + @Test + public void canUpdateHoldingsOwnershipToDifferentTenant() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + UUID instanceId = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(instanceId); + + InstanceApiClient.createInstance(okapiClient, instance); + InstanceApiClient.createInstance(consortiumOkapiClient, instance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId); + final UUID createHoldingsRecord2 = createHoldingForInstance(instanceId); + + JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId, + new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("nonUpdatedIds").size(), is(0)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord1 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode()); + Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID)); + + Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord2 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord2.getStatusCode()); + Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID)); + } + + @Test + public void shouldReportErrorsWhenOnlySomeRequestedHoldingsRecordsCouldNotBeUpdated() throws InterruptedException, MalformedURLException, TimeoutException, ExecutionException { + UUID instanceId = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(instanceId); + + InstanceApiClient.createInstance(okapiClient, instance); + InstanceApiClient.createInstance(consortiumOkapiClient, instance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId); + final UUID createHoldingsRecord2 = UUID.randomUUID(); + + Assert.assertNotEquals(createHoldingsRecord1, createHoldingsRecord2); + + JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId, + new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + List notFoundIds = postHoldingsUpdateOwnershipResponse.getJson() + .getJsonArray("nonUpdatedIds") + .getList(); + + assertThat(notFoundIds.size(), is(1)); + assertThat(notFoundIds.get(0), equalTo(createHoldingsRecord2.toString())); + + Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord1 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode()); + assertThat(instanceId.toString(), equalTo(targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID))); + + Response targetTenantHoldingsRecord2 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, targetTenantHoldingsRecord2.getStatusCode()); + } + + @Test + public void cannotUpdateHoldingsRecordsOwnershipToUnspecifiedInstance() + throws InterruptedException, MalformedURLException, TimeoutException, ExecutionException { + JsonObject holdingsRecordUpdateOwnershipWithoutToInstanceId = new HoldingsRecordUpdateOwnershipRequestBuilder(null, + new JsonArray(List.of(UUID.randomUUID())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipWithoutToInstanceId); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postHoldingsUpdateOwnershipResponse, hasValidationError( + "toInstanceId is a required field", "toInstanceId", null + )); + } + + @Test + public void cannotUpdateHoldingsRecordsOwnershipToUnspecifiedTenant() + throws InterruptedException, MalformedURLException, TimeoutException, ExecutionException { + JsonObject holdingsRecordUpdateOwnershipWithoutTenantId = new HoldingsRecordUpdateOwnershipRequestBuilder(UUID.randomUUID(), + new JsonArray(List.of(UUID.randomUUID())), null).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipWithoutTenantId); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postHoldingsUpdateOwnershipResponse, hasValidationError( + "tenantId is a required field", "toInstanceId", null + )); + } + + @Test + public void cannotUpdateUnspecifiedHoldingsRecordsOwnership() + throws MalformedURLException, InterruptedException, ExecutionException, TimeoutException { + JsonObject holdingsRecordUpdateOwnershipWithoutHoldingsRecordIds = new HoldingsRecordUpdateOwnershipRequestBuilder(UUID.randomUUID(), + new JsonArray(), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipWithoutHoldingsRecordIds); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postHoldingsUpdateOwnershipResponse, hasValidationError( + "Holdings record ids aren't specified", "holdingsRecordIds", null + )); + } + + @Test + public void cannotUpdateHoldingsRecordOwnershipOfNonExistedInstance() + throws MalformedURLException, InterruptedException, ExecutionException, TimeoutException { + createConsortiumTenant(); + + UUID instanceId = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(instanceId); + + UUID invalidInstanceId = UUID.randomUUID(); + + InstanceApiClient.createInstance(okapiClient, instance); + InstanceApiClient.createInstance(consortiumOkapiClient, instance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId); + + JsonObject holdingsRecordUpdateOwnershipWithoutHoldingsRecordIds = new HoldingsRecordUpdateOwnershipRequestBuilder(invalidInstanceId, + new JsonArray(List.of(createHoldingsRecord1)), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipWithoutHoldingsRecordIds); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postHoldingsUpdateOwnershipResponse.getBody(), containsString("errors")); + assertThat(postHoldingsUpdateOwnershipResponse.getBody(), containsString(invalidInstanceId.toString())); + } + + @Test + public void canUpdateHoldingsRecordOwnershipDueToHoldingsRecordUpdateError() throws InterruptedException, MalformedURLException, TimeoutException, ExecutionException { + UUID instanceId = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(instanceId); + + InstanceApiClient.createInstance(okapiClient, instance); + InstanceApiClient.createInstance(consortiumOkapiClient, instance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId); + final UUID createHoldingsRecord2 = createHoldingForInstance(ID_FOR_FAILURE, instanceId); + + JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId, + new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody); + + List nonUpdatedIdsIds = postHoldingsUpdateOwnershipResponse.getJson() + .getJsonArray("nonUpdatedIds") + .getList(); + + assertThat(nonUpdatedIdsIds.size(), is(1)); + assertThat(nonUpdatedIdsIds.get(0), equalTo(ID_FOR_FAILURE.toString())); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord1 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode()); + Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID)); + + Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord2 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord2.getJson().getString(INSTANCE_ID)); + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, targetTenantHoldingsRecord2.getStatusCode()); + } + + @Test + public void canUpdateHoldingsRecordOwnershipToDifferentInstanceWithExtraRedundantFields() throws InterruptedException, MalformedURLException, TimeoutException, ExecutionException { + UUID instanceId = UUID.randomUUID(); + JsonObject instance = smallAngryPlanet(instanceId); + + InstanceApiClient.createInstance(okapiClient, instance); + InstanceApiClient.createInstance(consortiumOkapiClient, instance); + + JsonObject firstJsonHoldingsAsRequest = new HoldingRequestBuilder().forInstance(instanceId).create(); + final UUID createHoldingsRecord1 = holdingsStorageClient.create(firstJsonHoldingsAsRequest + .put("holdingsItems", new JsonArray().add(new JsonObject().put("id", UUID.randomUUID())).add(new JsonObject().put("id", UUID.randomUUID()))) + .put("bareHoldingsItems", new JsonArray().add(new JsonObject().put("id", UUID.randomUUID())).add(new JsonObject().put("id", UUID.randomUUID())))) + .getId(); + + JsonObject secondJsonHoldingsAsRequest = new HoldingRequestBuilder().forInstance(instanceId).create(); + final UUID createHoldingsRecord2 = holdingsStorageClient.create(secondJsonHoldingsAsRequest + .put("holdingsItems", new JsonArray().add(new JsonObject().put("id", UUID.randomUUID())).add(new JsonObject().put("id", UUID.randomUUID()))) + .put("bareHoldingsItems", new JsonArray().add(new JsonObject().put("id", UUID.randomUUID())).add(new JsonObject().put("id", UUID.randomUUID())))) + .getId(); + + JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId, + new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody); + + assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("nonUpdatedIds").size(), is(0)); + assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord1 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode()); + Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID)); + + Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1); + Response targetTenantHoldingsRecord2 = consortiumHoldingsStorageClient.getById(createHoldingsRecord1); + + Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord2.getStatusCode()); + Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID)); + } + + private Response updateHoldingsRecordsOwnership(JsonObject holdingsRecordUpdateOwnershipRequestBody) throws MalformedURLException, InterruptedException, ExecutionException, TimeoutException { + final var postHoldingRecordsUpdateOwnershipCompleted = okapiClient.post( + ApiRoot.updateHoldingsRecordsOwnership(), holdingsRecordUpdateOwnershipRequestBody); + return postHoldingRecordsUpdateOwnershipCompleted.toCompletableFuture().get(5, TimeUnit.SECONDS); + } + + private UUID createHoldingForInstance(UUID instanceId) { + return holdingsStorageClient.create(new HoldingRequestBuilder().forInstance(instanceId)) + .getId(); + } + + private UUID createHoldingForInstance(UUID id, UUID instanceId) { + JsonObject obj = new HoldingRequestBuilder().forInstance(instanceId).create(); + obj.put("id", id.toString()); + holdingsStorageClient.create(obj); + return id; + } +} diff --git a/src/test/java/api/items/ItemUpdateOwnershipApiTest.java b/src/test/java/api/items/ItemUpdateOwnershipApiTest.java new file mode 100644 index 000000000..e6ab0eeca --- /dev/null +++ b/src/test/java/api/items/ItemUpdateOwnershipApiTest.java @@ -0,0 +1,288 @@ +package api.items; + +import api.ApiTestSuite; +import api.support.ApiRoot; +import api.support.ApiTests; +import api.support.InstanceApiClient; +import api.support.builders.HoldingRequestBuilder; +import api.support.builders.ItemRequestBuilder; +import api.support.builders.ItemsUpdateOwnershipRequestBuilder; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import junitparams.JUnitParamsRunner; +import org.apache.http.HttpStatus; +import org.folio.inventory.domain.items.ItemStatusName; +import org.folio.inventory.support.http.client.Response; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.MalformedURLException; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import static api.ApiTestSuite.ID_FOR_FAILURE; +import static api.ApiTestSuite.createConsortiumTenant; +import static api.support.InstanceSamples.smallAngryPlanet; +import static org.folio.inventory.support.ItemUtil.HOLDINGS_RECORD_ID; +import static org.folio.inventory.support.JsonArrayHelper.toList; +import static org.folio.inventory.support.JsonArrayHelper.toListOfStrings; +import static org.folio.inventory.support.http.ContentType.APPLICATION_JSON; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; + +@Ignore +@RunWith(JUnitParamsRunner.class) +public class ItemUpdateOwnershipApiTest extends ApiTests { + + @Before + public void initConsortia() throws Exception { + createConsortiumTenant(); + } + + @After + public void clearConsortia() throws Exception { + userTenantsClient.deleteAll(); + } + + @Test + public void canUpdateItemsOwnershipToDifferentTenant() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + UUID sourceInstanceId = UUID.randomUUID(); + JsonObject sourceInstance = smallAngryPlanet(sourceInstanceId); + + UUID targetInstanceId = UUID.randomUUID(); + JsonObject targetInstance = smallAngryPlanet(targetInstanceId); + + InstanceApiClient.createInstance(okapiClient, sourceInstance); + InstanceApiClient.createInstance(consortiumOkapiClient, targetInstance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(sourceInstanceId); + final UUID createHoldingsRecord2 = createHoldingForInstanceAtConsortium(targetInstanceId); + + final var firstItem = itemsClient.create( + new ItemRequestBuilder() + .forHolding(createHoldingsRecord1) + .withBarcode("645398607547") + .withStatus(ItemStatusName.AVAILABLE.value())); + + final var secondItem = itemsClient.create( + new ItemRequestBuilder() + .forHolding(createHoldingsRecord1) + .withBarcode("645398607546") + .withStatus(ItemStatusName.AVAILABLE.value())); + + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(createHoldingsRecord2, + new JsonArray(List.of(firstItem.getId().toString(), secondItem.getId().toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(toList(postItemsUpdateOwnershipResponse.getJson(), "nonUpdatedIds"), hasSize(0)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + final var sourceFirstUpdatedItem = itemsClient.getById(firstItem.getId()); + final var targetFirstUpdatedItem = consortiumItemsClient.getById(firstItem.getId()); + + final var sourceSecondUpdatedItem = itemsClient.getById(secondItem.getId()); + final var targetSecondUpdatedItem = consortiumItemsClient.getById(secondItem.getId()); + + assertThat(HttpStatus.SC_NOT_FOUND, is(sourceFirstUpdatedItem.getStatusCode())); + assertThat(targetFirstUpdatedItem.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord2.toString())); + + assertThat(HttpStatus.SC_NOT_FOUND, is(sourceSecondUpdatedItem.getStatusCode())); + assertThat(targetSecondUpdatedItem.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord2.toString())); + } + + @Test + public void shouldReportErrorsWhenOnlySomeRequestedItemsOwnershipCouldNotBeUpdated() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + UUID sourceInstanceId = UUID.randomUUID(); + JsonObject sourceInstance = smallAngryPlanet(sourceInstanceId); + + UUID targetInstanceId = UUID.randomUUID(); + JsonObject targetInstance = smallAngryPlanet(targetInstanceId); + + InstanceApiClient.createInstance(okapiClient, sourceInstance); + InstanceApiClient.createInstance(consortiumOkapiClient, targetInstance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(sourceInstanceId); + final UUID createHoldingsRecord2 = createHoldingForInstanceAtConsortium(targetInstanceId); + + final var item = itemsClient.create( + new ItemRequestBuilder() + .forHolding(createHoldingsRecord1) + .withBarcode("645398607547") + .withStatus(ItemStatusName.AVAILABLE.value())); + + final var nonExistentItemId = UUID.randomUUID(); + + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(createHoldingsRecord2, + new JsonArray(List.of(item.getId().toString(), nonExistentItemId.toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + final var notFoundIds = toListOfStrings(postItemsUpdateOwnershipResponse.getJson(), + "nonUpdatedIds"); + + assertThat(notFoundIds, hasSize(1)); + assertThat(notFoundIds.get(0), equalTo(nonExistentItemId.toString())); + + final var sourceUpdatedItem = itemsClient.getById(item.getId()); + final var targetUpdatedItem = consortiumItemsClient.getById(item.getId()); + + assertThat(HttpStatus.SC_NOT_FOUND, is(sourceUpdatedItem.getStatusCode())); + assertThat(targetUpdatedItem.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord2.toString())); + } + + @Test + public void cannotUpdateItemsOwnershipToUnspecifiedHoldingsRecord() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(null, + new JsonArray(List.of(UUID.randomUUID().toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("errors")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("toHoldingsRecordId")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("toHoldingsRecordId is a required field")); + } + + @Test + public void cannotUpdateOwnershipOfUnspecifiedItems() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(UUID.randomUUID(), + new JsonArray(List.of()), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("errors")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("itemIds")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("Item ids aren't specified")); + } + + @Test + public void cannotUpdateItemsOwnershipToUnspecifiedTenant() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(UUID.randomUUID(), + new JsonArray(List.of(UUID.randomUUID())), null).create(); + + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("errors")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("tenantId")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("tenantId is a required field")); + } + + @Test + public void cannotUpdateItemsOwnershipToNonExistedHoldingsRecord() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + UUID sourceInstanceId = UUID.randomUUID(); + JsonObject sourceInstance = smallAngryPlanet(sourceInstanceId); + + InstanceApiClient.createInstance(okapiClient, sourceInstance); + + final UUID existingHoldingsId = createHoldingForInstance(sourceInstanceId); + final UUID nonExistentHoldingsId = UUID.randomUUID(); + + final var item = itemsClient.create( + new ItemRequestBuilder() + .forHolding(existingHoldingsId) + .withBarcode("645398607547") + .withStatus(ItemStatusName.AVAILABLE.value())); + + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(nonExistentHoldingsId, + new JsonArray(List.of(item.getId().toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(422)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString("errors")); + assertThat(postItemsUpdateOwnershipResponse.getBody(), containsString(nonExistentHoldingsId.toString())); + } + + @Test + public void canUpdateItemsOwnershipDueToItemUpdateError() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + UUID sourceInstanceId = UUID.randomUUID(); + JsonObject sourceInstance = smallAngryPlanet(sourceInstanceId); + + UUID targetInstanceId = UUID.randomUUID(); + JsonObject targetInstance = smallAngryPlanet(targetInstanceId); + + InstanceApiClient.createInstance(okapiClient, sourceInstance); + InstanceApiClient.createInstance(consortiumOkapiClient, targetInstance); + + final UUID createHoldingsRecord1 = createHoldingForInstance(sourceInstanceId); + final UUID createHoldingsRecord2 = createHoldingForInstanceAtConsortium(targetInstanceId); + + final var firstItem = itemsClient.create( + new ItemRequestBuilder() + .forHolding(createHoldingsRecord1) + .withBarcode("645398607547") + .withStatus(ItemStatusName.AVAILABLE.value())); + + final var secondItem = itemsClient.create( + new ItemRequestBuilder() + .forHolding(createHoldingsRecord1) + .withBarcode("645398607546") + .withId(ID_FOR_FAILURE) + .withStatus(ItemStatusName.AVAILABLE.value())); + + JsonObject itemsUpdateOwnershipRequestBody = new ItemsUpdateOwnershipRequestBuilder(createHoldingsRecord2, + new JsonArray(List.of(firstItem.getId().toString(), secondItem.getId().toString())), ApiTestSuite.CONSORTIA_TENANT_ID).create(); + Response postItemsUpdateOwnershipResponse = updateItemsOwnership(itemsUpdateOwnershipRequestBody); + + final var nonUpdatedIdsIds = toListOfStrings(postItemsUpdateOwnershipResponse.getJson(), + "nonUpdatedIds"); + + assertThat(nonUpdatedIdsIds, hasSize(1)); + assertThat(nonUpdatedIdsIds.get(0), equalTo(ID_FOR_FAILURE.toString())); + + assertThat(postItemsUpdateOwnershipResponse.getStatusCode(), is(200)); + assertThat(postItemsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON)); + + final var sourceFirstItemUpdated = itemsClient.getById(firstItem.getId()); + final var targetFirstItemUpdated = consortiumItemsClient.getById(firstItem.getId()); + + assertThat(HttpStatus.SC_NOT_FOUND, is(sourceFirstItemUpdated.getStatusCode())); + assertThat(createHoldingsRecord2.toString(), equalTo(targetFirstItemUpdated.getJson().getString(HOLDINGS_RECORD_ID))); + + final var sourceSecondUpdatedItem = itemsClient.getById(secondItem.getId()); + final var targetSecondUpdatedItem = consortiumItemsClient.getById(secondItem.getId()); + + assertThat(createHoldingsRecord1.toString(), equalTo(sourceSecondUpdatedItem.getJson().getString(HOLDINGS_RECORD_ID))); + assertThat(HttpStatus.SC_NOT_FOUND, is(targetSecondUpdatedItem.getStatusCode())); + } + + private Response updateItemsOwnership(JsonObject itemsUpdateOwnershipRequestBody) throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException { + final var postItemsUpdateOwnershipCompleted = okapiClient.post( + ApiRoot.updateItemsOwnership(), itemsUpdateOwnershipRequestBody); + return postItemsUpdateOwnershipCompleted.toCompletableFuture().get(5, TimeUnit.SECONDS); + } + + private UUID createHoldingForInstance(UUID instanceId) { + return holdingsStorageClient.create(new HoldingRequestBuilder().forInstance(instanceId)) + .getId(); + } + + private UUID createHoldingForInstanceAtConsortium(UUID instanceId) { + return consortiumHoldingsStorageClient.create(new HoldingRequestBuilder().forInstance(instanceId)) + .getId(); + } +} diff --git a/src/test/java/api/support/ApiRoot.java b/src/test/java/api/support/ApiRoot.java index 07734f499..aa1373cc1 100644 --- a/src/test/java/api/support/ApiRoot.java +++ b/src/test/java/api/support/ApiRoot.java @@ -53,6 +53,18 @@ public static URL moveHoldingsRecords() return new URL(String.format("%s/holdings/move", inventory())); } + public static URL updateItemsOwnership() + throws MalformedURLException { + + return new URL(String.format("%s/items/update-ownership", inventory())); + } + + public static URL updateHoldingsRecordsOwnership() + throws MalformedURLException { + + return new URL(String.format("%s/holdings/update-ownership", inventory())); + } + public static URL items(String query) throws MalformedURLException { diff --git a/src/test/java/api/support/ApiTests.java b/src/test/java/api/support/ApiTests.java index d5dcb5783..2d079664f 100644 --- a/src/test/java/api/support/ApiTests.java +++ b/src/test/java/api/support/ApiTests.java @@ -18,6 +18,7 @@ public abstract class ApiTests { private static boolean runningOnOwn; protected static OkapiHttpClient okapiClient; + protected static OkapiHttpClient consortiumOkapiClient; protected final ResourceClient holdingsStorageClient; protected final ResourceClient itemsStorageClient; protected final ResourceClient itemsClient; @@ -31,6 +32,8 @@ public abstract class ApiTests { protected final ResourceClient instanceRelationshipClient; protected final ResourceClient requestStorageClient; protected final ResourceClient sourceRecordStorageClient; + protected final ResourceClient consortiumItemsClient; + protected final ResourceClient consortiumHoldingsStorageClient; protected final InstanceRelationshipTypeFixture instanceRelationshipTypeFixture; protected final MarkItemFixture markItemFixture; @@ -51,6 +54,9 @@ public ApiTests() { sourceRecordStorageClient = ResourceClient.forSourceRecordStorage(okapiClient); instanceRelationshipTypeFixture = new InstanceRelationshipTypeFixture(okapiClient); markItemFixture = new MarkItemFixture(okapiClient); + + consortiumHoldingsStorageClient = ResourceClient.forHoldingsStorage(consortiumOkapiClient); + consortiumItemsClient = ResourceClient.forItemsStorage(consortiumOkapiClient); } @BeforeClass @@ -67,6 +73,7 @@ public static void before() } okapiClient = ApiTestSuite.createOkapiHttpClient(); + consortiumOkapiClient = ApiTestSuite.createOkapiHttpClient(ApiTestSuite.CONSORTIA_TENANT_ID); } @AfterClass diff --git a/src/test/java/api/support/builders/HoldingsRecordUpdateOwnershipRequestBuilder.java b/src/test/java/api/support/builders/HoldingsRecordUpdateOwnershipRequestBuilder.java new file mode 100644 index 000000000..2c4e22e0a --- /dev/null +++ b/src/test/java/api/support/builders/HoldingsRecordUpdateOwnershipRequestBuilder.java @@ -0,0 +1,34 @@ +package api.support.builders; + + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +import java.util.UUID; + +import static api.ApiTestSuite.TENANT_ID; +import static org.folio.inventory.resources.MoveApi.HOLDINGS_RECORD_IDS; +import static org.folio.inventory.resources.MoveApi.TO_INSTANCE_ID; + +public class HoldingsRecordUpdateOwnershipRequestBuilder extends AbstractBuilder { + + private final UUID toInstanceId; + private final JsonArray holdingsRecordsIds; + private final String tenantId; + + public HoldingsRecordUpdateOwnershipRequestBuilder(UUID toInstanceId, JsonArray holdingsRecordsIds, String tenantId) { + this.toInstanceId = toInstanceId; + this.holdingsRecordsIds = holdingsRecordsIds; + this.tenantId = tenantId; + } + + public JsonObject create() { + JsonObject holdingsRecordUpdateOwnershipRequest = new JsonObject(); + + includeWhenPresent(holdingsRecordUpdateOwnershipRequest, TO_INSTANCE_ID, toInstanceId); + includeWhenPresent(holdingsRecordUpdateOwnershipRequest, HOLDINGS_RECORD_IDS, holdingsRecordsIds); + includeWhenPresent(holdingsRecordUpdateOwnershipRequest, TENANT_ID, tenantId); + + return holdingsRecordUpdateOwnershipRequest; + } +} diff --git a/src/test/java/api/support/builders/ItemsUpdateOwnershipRequestBuilder.java b/src/test/java/api/support/builders/ItemsUpdateOwnershipRequestBuilder.java new file mode 100644 index 000000000..04b07cd38 --- /dev/null +++ b/src/test/java/api/support/builders/ItemsUpdateOwnershipRequestBuilder.java @@ -0,0 +1,33 @@ +package api.support.builders; + + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +import java.util.UUID; + +import static api.ApiTestSuite.TENANT_ID; +import static org.folio.inventory.resources.MoveApi.ITEM_IDS; +import static org.folio.inventory.resources.MoveApi.TO_HOLDINGS_RECORD_ID; + +public class ItemsUpdateOwnershipRequestBuilder extends AbstractBuilder { + private final UUID toHoldingsRecordId; + private final JsonArray itemIds; + private final String tenantId; + + public ItemsUpdateOwnershipRequestBuilder(UUID toHoldingsRecordId, JsonArray itemIds, String tenantId) { + this.toHoldingsRecordId = toHoldingsRecordId; + this.itemIds = itemIds; + this.tenantId = tenantId; + } + + public JsonObject create() { + final var itemsUpdateOwnershipRequest = new JsonObject(); + + includeWhenPresent(itemsUpdateOwnershipRequest, TO_HOLDINGS_RECORD_ID, toHoldingsRecordId); + includeWhenPresent(itemsUpdateOwnershipRequest, ITEM_IDS, itemIds); + includeWhenPresent(itemsUpdateOwnershipRequest, TENANT_ID, tenantId); + + return itemsUpdateOwnershipRequest; + } +} diff --git a/src/test/java/support/fakes/FakeStorageModule.java b/src/test/java/support/fakes/FakeStorageModule.java index 33d543037..84e20583b 100644 --- a/src/test/java/support/fakes/FakeStorageModule.java +++ b/src/test/java/support/fakes/FakeStorageModule.java @@ -47,7 +47,7 @@ class FakeStorageModule extends AbstractVerticle { FakeStorageModule( String rootPath, String collectionPropertyName, - String tenantId, + List tenants, Collection requiredProperties, boolean hasCollectionDelete, String recordTypeName, @@ -69,7 +69,7 @@ class FakeStorageModule extends AbstractVerticle { this.defaultProperties = defaultPropertiesWithId; storedResourcesByTenant = new HashMap<>(); - storedResourcesByTenant.put(tenantId, new HashMap<>()); + tenants.forEach(tenant -> storedResourcesByTenant.put(tenant, new HashMap<>())); this.recordPreProcessors = recordPreProcessors; } diff --git a/src/test/java/support/fakes/FakeStorageModuleBuilder.java b/src/test/java/support/fakes/FakeStorageModuleBuilder.java index 89d8bd45e..2ed59cf63 100644 --- a/src/test/java/support/fakes/FakeStorageModuleBuilder.java +++ b/src/test/java/support/fakes/FakeStorageModuleBuilder.java @@ -15,7 +15,7 @@ public class FakeStorageModuleBuilder { private final String rootPath; private final String collectionPropertyName; - private final String tenantId; + private final List tenants; private final Collection requiredProperties; private final Collection uniqueProperties; private final Map> defaultProperties; @@ -24,14 +24,14 @@ public class FakeStorageModuleBuilder { private final List recordPreProcessors; FakeStorageModuleBuilder() { - this(null, null, ApiTestSuite.TENANT_ID, new ArrayList<>(), true, "", + this(null, null, List.of(ApiTestSuite.TENANT_ID, ApiTestSuite.CONSORTIA_TENANT_ID), new ArrayList<>(), true, "", new ArrayList<>(), new HashMap<>(), Collections.emptyList()); } private FakeStorageModuleBuilder( String rootPath, String collectionPropertyName, - String tenantId, + List tenants, Collection requiredProperties, boolean hasCollectionDelete, String recordName, @@ -41,7 +41,7 @@ private FakeStorageModuleBuilder( this.rootPath = rootPath; this.collectionPropertyName = collectionPropertyName; - this.tenantId = tenantId; + this.tenants = tenants; this.requiredProperties = requiredProperties; this.hasCollectionDelete = hasCollectionDelete; this.recordName = recordName; @@ -51,7 +51,7 @@ private FakeStorageModuleBuilder( } public FakeStorageModule create() { - return new FakeStorageModule(rootPath, collectionPropertyName, tenantId, + return new FakeStorageModule(rootPath, collectionPropertyName, tenants, requiredProperties, hasCollectionDelete, recordName, uniqueProperties, defaultProperties, recordPreProcessors); } @@ -64,7 +64,7 @@ FakeStorageModuleBuilder withRootPath(String rootPath) { return new FakeStorageModuleBuilder( rootPath, newCollectionPropertyName, - this.tenantId, + this.tenants, this.requiredProperties, this.hasCollectionDelete, this.recordName, @@ -77,7 +77,7 @@ FakeStorageModuleBuilder withCollectionPropertyName(String collectionPropertyNam return new FakeStorageModuleBuilder( this.rootPath, collectionPropertyName, - this.tenantId, + this.tenants, this.requiredProperties, this.hasCollectionDelete, this.recordName, @@ -90,7 +90,7 @@ FakeStorageModuleBuilder withRecordName(String recordName) { return new FakeStorageModuleBuilder( this.rootPath, this.collectionPropertyName, - this.tenantId, + this.tenants, this.requiredProperties, this.hasCollectionDelete, recordName, @@ -105,7 +105,7 @@ private FakeStorageModuleBuilder withRequiredProperties( return new FakeStorageModuleBuilder( this.rootPath, this.collectionPropertyName, - this.tenantId, + this.tenants, requiredProperties, this.hasCollectionDelete, this.recordName, @@ -126,7 +126,7 @@ FakeStorageModuleBuilder withDefault(String property, Object value) { return new FakeStorageModuleBuilder( this.rootPath, this.collectionPropertyName, - this.tenantId, + this.tenants, this.requiredProperties, this.hasCollectionDelete, this.recordName, @@ -139,7 +139,7 @@ final FakeStorageModuleBuilder withRecordPreProcessors(RecordPreProcessor... pre return new FakeStorageModuleBuilder( this.rootPath, this.collectionPropertyName, - this.tenantId, + this.tenants, this.requiredProperties, this.hasCollectionDelete, this.recordName,