diff --git a/pom.xml b/pom.xml
index 0b3c44ae0..ac5ff7c81 100644
--- a/pom.xml
+++ b/pom.xml
@@ -447,7 +447,7 @@
${basedir}/ramls/mappingMetadataDto.json
${basedir}/ramls/holdings_update_ownership.json
${basedir}/ramls/items_update_ownership.json
- ${basedir}/ramls/move_response.json
+ ${basedir}/ramls/update_ownership_response.json
org.folio
true
diff --git a/ramls/update_ownership_response.json b/ramls/update_ownership_response.json
new file mode 100644
index 000000000..ab603298f
--- /dev/null
+++ b/ramls/update_ownership_response.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "description": "Holder for errors during holdings/item update ownership",
+ "type": "object",
+ "properties": {
+ "notUpdatedEntities": {
+ "description": "Items/holdings errors",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "entityId": {
+ "$ref": "uuid.json"
+ },
+ "errorMessage": {
+ "type": "string",
+ "description": "Error message"
+ }
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java b/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java
index 67913d140..0bca384a7 100644
--- a/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java
+++ b/src/main/java/org/folio/inventory/resources/UpdateOwnershipApi.java
@@ -5,14 +5,15 @@
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
+import org.apache.commons.collections4.ListUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.HoldingsRecord;
import org.folio.HoldingsUpdateOwnership;
+import org.folio.NotUpdatedEntity;
import org.folio.inventory.common.Context;
import org.folio.inventory.common.WebContext;
import org.folio.inventory.consortium.services.ConsortiumService;
-import org.folio.inventory.domain.AsynchronousCollection;
import org.folio.inventory.domain.HoldingsRecordCollection;
import org.folio.inventory.domain.items.Item;
import org.folio.inventory.domain.items.ItemCollection;
@@ -25,8 +26,10 @@
import org.folio.inventory.support.MoveApiUtil;
import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import static org.folio.inventory.dataimport.handlers.matching.util.EventHandlingUtil.constructContext;
import static org.folio.inventory.domain.instances.InstanceSource.CONSORTIUM_FOLIO;
@@ -46,6 +49,8 @@ public class UpdateOwnershipApi extends AbstractInventoryResource {
public static final String INSTANCE_NOT_SHARED = "Instance with id: %s is not shared";
public static final String INSTANCE_NOT_FOUND_AT_SOURCE_TENANT = "Instance with id: %s not found at source tenant, tenant: %s";
public static final String TENANT_NOT_IN_CONSORTIA = "%s tenant is not in consortia";
+ public static final String INSTANCE_NOT_FOUND = "Instance with id: %s not found on tenant: %s";
+
private final ConsortiumService consortiumService;
public UpdateOwnershipApi(Storage storage, HttpClient client, ConsortiumService consortiumService) {
@@ -74,6 +79,8 @@ private void processUpdateHoldingsOwnership(RoutingContext routingContext) {
}
var holdingsUpdateOwnership = updateOwnershipRequest.mapTo(HoldingsUpdateOwnership.class);
+ List notUpdatedEntities = new ArrayList<>();
+
LOGGER.info("updateHoldingsOwnership:: Started updating ownership of holdings record: {}, to tenant: {}", holdingsUpdateOwnership.getHoldingsRecordIds(),
holdingsUpdateOwnership.getTargetTenantId());
@@ -86,7 +93,7 @@ private void processUpdateHoldingsOwnership(RoutingContext routingContext) {
if (instance != null) {
if (instance.getSource().equals(CONSORTIUM_MARC.getValue()) || instance.getSource().equals(CONSORTIUM_FOLIO.getValue())) {
Context targetTenantContext = constructContext(holdingsUpdateOwnership.getTargetTenantId(), context.getToken(), context.getOkapiLocation());
- return updateOwnershipOfHoldingsRecords(holdingsUpdateOwnership, routingContext, context, targetTenantContext);
+ return updateOwnershipOfHoldingsRecords(holdingsUpdateOwnership, notUpdatedEntities, routingContext, context, targetTenantContext);
} else {
String instanceNotSharedErrorMessage = String.format(INSTANCE_NOT_SHARED, holdingsUpdateOwnership.getToInstanceId());
LOGGER.warn("updateHoldingsOwnership:: " + instanceNotSharedErrorMessage);
@@ -103,7 +110,7 @@ private void processUpdateHoldingsOwnership(RoutingContext routingContext) {
LOGGER.warn("updateHoldingsOwnership:: " + notInConsortiaErrorMessage, context);
return CompletableFuture.failedFuture(new BadRequestException(notInConsortiaErrorMessage));
})
- .thenAccept(updateHoldingsRecords -> respond(routingContext, holdingsUpdateOwnership.getHoldingsRecordIds(), updateHoldingsRecords))
+ .thenAccept(updateHoldingsRecords -> respond(routingContext, notUpdatedEntities))
.exceptionally(throwable -> {
LOGGER.warn("updateHoldingsOwnership:: Error during update ownership of holdings {}, to tenant: {}",
holdingsUpdateOwnership.getHoldingsRecordIds(), holdingsUpdateOwnership.getTargetTenantId(), throwable);
@@ -120,7 +127,8 @@ private void processUpdateItemsOwnership(RoutingContext routingContext) {
// should be implemented in MODINV-955
}
- private CompletableFuture> updateOwnershipOfHoldingsRecords(HoldingsUpdateOwnership holdingsUpdateOwnership, RoutingContext routingContext,
+ private CompletableFuture> updateOwnershipOfHoldingsRecords(HoldingsUpdateOwnership holdingsUpdateOwnership,
+ List notUpdatedEntities, RoutingContext routingContext,
WebContext context, Context targetTenantContext) {
try {
CollectionResourceClient holdingsStorageClient = createHoldingsStorageClient(createHttpClient(client, routingContext, context),
@@ -131,11 +139,17 @@ private CompletableFuture> updateOwnershipOfHoldingsRecords(Holding
HoldingsRecordCollection targetTenantHoldingsRecordCollection = storage.getHoldingsRecordCollection(targetTenantContext);
return holdingsRecordFetchClient.find(holdingsUpdateOwnership.getHoldingsRecordIds(), MoveApiUtil::fetchByIdCql)
- .thenCompose(jsons -> createHoldings(jsons, holdingsUpdateOwnership.getToInstanceId(), targetTenantHoldingsRecordCollection))
- .thenApply(createdHoldings -> createdHoldings.stream().map(HoldingsRecord::getId).toList())
- .thenCompose(createdHoldingsIds ->
- transferAttachedItems(createdHoldingsIds, routingContext, context, targetTenantContext)
- .thenCompose(items -> deleteCollectionItems(createdHoldingsIds, sourceTenantHoldingsRecordCollection)));
+ .thenCompose(jsons -> {
+ processNotFoundedInstances(holdingsUpdateOwnership.getHoldingsRecordIds(), notUpdatedEntities, context, jsons);
+ return createHoldings(jsons, notUpdatedEntities, holdingsUpdateOwnership.getToInstanceId(), targetTenantHoldingsRecordCollection);
+ })
+ .thenCompose(createdHoldings -> {
+ List createdHoldingsIds = createdHoldings.stream().map(HoldingsRecord::getId).toList();
+
+ return transferAttachedItems(createdHoldingsIds, notUpdatedEntities, routingContext, context, targetTenantContext)
+ .thenCompose(itemIds ->
+ deleteHoldings(getHoldingsToDelete(notUpdatedEntities, createdHoldings), notUpdatedEntities, sourceTenantHoldingsRecordCollection));
+ });
} catch (Exception e) {
LOGGER.warn("updateOwnershipOfHoldingsRecords:: Error during update ownership of holdings {}, to tenant: {}",
holdingsUpdateOwnership.getHoldingsRecordIds(), holdingsUpdateOwnership.getTargetTenantId(), e);
@@ -143,8 +157,8 @@ private CompletableFuture> updateOwnershipOfHoldingsRecords(Holding
}
}
- private CompletableFuture> transferAttachedItems(List holdingsRecordIds, RoutingContext routingContext,
- WebContext context, Context targetTenantContext) {
+ private CompletableFuture> transferAttachedItems(List holdingsRecordIds, List notUpdatedEntities,
+ RoutingContext routingContext, WebContext context, Context targetTenantContext) {
try {
CollectionResourceClient itemsStorageClient = createItemStorageClient(createHttpClient(client, routingContext, context), context);
MultipleRecordsFetchClient itemsFetchClient = createItemsFetchClient(itemsStorageClient);
@@ -153,9 +167,8 @@ private CompletableFuture> transferAttachedItems(List holdi
ItemCollection targetTenantItemCollection = storage.getItemCollection(targetTenantContext);
return itemsFetchClient.find(holdingsRecordIds, MoveApiUtil::fetchByHoldingsRecordIdCql)
- .thenCompose(jsons -> createItems(jsons, targetTenantItemCollection))
- .thenApply(items -> items.stream().map(Item::getId).toList())
- .thenCompose(itemIds -> deleteCollectionItems(itemIds, sourceTenantItemCollection));
+ .thenCompose(jsons -> createItems(jsons, notUpdatedEntities, targetTenantItemCollection))
+ .thenCompose(items -> deleteItems(items, notUpdatedEntities, sourceTenantItemCollection));
} catch (Exception e) {
LOGGER.warn("transferAttachedItems:: Error during transfer attached items for holdings {}, to tenant: {}",
holdingsRecordIds, targetTenantContext.getTenantId(), e);
@@ -163,13 +176,18 @@ private CompletableFuture> transferAttachedItems(List holdi
}
}
- private CompletableFuture> createItems(List jsons, ItemCollection itemCollection) {
+ private CompletableFuture> createItems(List jsons, List notUpdatedEntities, ItemCollection itemCollection) {
List- itemRecordsToUpdateOwnership = jsons.stream()
.map(ItemUtil::fromStoredItemRepresentation)
.toList();
List> createFutures = itemRecordsToUpdateOwnership.stream()
- .map(itemCollection::add)
+ .map(item ->
+ itemCollection.add(item)
+ .exceptionally(e -> {
+ notUpdatedEntities.add(new NotUpdatedEntity().withEntityId(item.getHoldingId()).withErrorMessage(e.getMessage()));
+ throw new CompletionException(e);
+ }))
.toList();
return CompletableFuture.allOf(createFutures.toArray(new CompletableFuture[0]))
@@ -179,7 +197,7 @@ private CompletableFuture
> createItems(List jsons, ItemCo
.toList());
}
- private CompletableFuture> createHoldings(List jsons, String instanceId,
+ private CompletableFuture> createHoldings(List jsons, List notUpdatedEntities, String instanceId,
HoldingsRecordCollection holdingsRecordCollection) {
List holdingsRecordsToUpdateOwnership = jsons.stream()
.peek(MoveApiUtil::removeExtraRedundantFields)
@@ -188,7 +206,12 @@ private CompletableFuture> createHoldings(List
.toList();
List> createFutures = holdingsRecordsToUpdateOwnership.stream()
- .map(holdingsRecordCollection::add)
+ .map(holdingRecord ->
+ holdingsRecordCollection.add(holdingRecord)
+ .exceptionally(e -> {
+ notUpdatedEntities.add(new NotUpdatedEntity().withEntityId(holdingRecord.getId()).withErrorMessage(e.getMessage()));
+ throw new CompletionException(e);
+ }))
.toList();
return CompletableFuture.allOf(createFutures.toArray(new CompletableFuture[0]))
@@ -198,11 +221,35 @@ private CompletableFuture> createHoldings(List
.toList());
}
- private CompletableFuture> deleteCollectionItems(List collectionItemIds, AsynchronousCollection> collection) {
- List> deleteFutures = collectionItemIds.stream()
- .map(collectionItemId -> {
+ private CompletableFuture> deleteHoldings(List holdingsRecords, List notUpdatedEntities,
+ HoldingsRecordCollection holdingsRecordCollection) {
+ List> deleteFutures = holdingsRecords.stream()
+ .map(holdingsRecord -> {
+ Promise promise = Promise.promise();
+ holdingsRecordCollection.delete(holdingsRecord.getId(), success -> promise.complete(holdingsRecord.getId()),
+ failure -> {
+ notUpdatedEntities.add(new NotUpdatedEntity().withEntityId(holdingsRecord.getId()).withErrorMessage(failure.getReason()));
+ promise.fail(failure.getReason());
+ });
+ return promise.future().toCompletionStage().toCompletableFuture();
+ }).toList();
+
+ return CompletableFuture.allOf(deleteFutures.toArray(new CompletableFuture[0]))
+ .handle((vVoid, throwable) -> deleteFutures.stream()
+ .filter(future -> !future.isCompletedExceptionally())
+ .map(CompletableFuture::join)
+ .toList());
+ }
+
+ private CompletableFuture> deleteItems(List- itemIds, List notUpdatedEntities, ItemCollection itemCollection) {
+ List> deleteFutures = itemIds.stream()
+ .map(item -> {
Promise promise = Promise.promise();
- collection.delete(collectionItemId, success -> promise.complete(collectionItemId), failure -> promise.fail(failure.getReason()));
+ itemCollection.delete(item.getId(), success -> promise.complete(item.getId()),
+ failure -> {
+ notUpdatedEntities.add(new NotUpdatedEntity().withEntityId(item.getHoldingId()).withErrorMessage(failure.getReason()));
+ promise.fail(failure.getReason());
+ });
return promise.future().toCompletionStage().toCompletableFuture();
}).toList();
@@ -212,4 +259,16 @@ private CompletableFuture
> deleteCollectionItems(List colle
.map(CompletableFuture::join)
.toList());
}
+
+ private void processNotFoundedInstances(List holdingsRecordIds, List notUpdatedEntities, WebContext context, List jsons) {
+ List foundedIds = jsons.stream().map(json -> json.getString("id")).toList();
+ List notFoundedIds = ListUtils.subtract(holdingsRecordIds, foundedIds);
+ notFoundedIds.forEach(id ->
+ notUpdatedEntities.add(new NotUpdatedEntity().withEntityId(id).withErrorMessage(String.format(INSTANCE_NOT_FOUND, id, context.getTenantId()))));
+ }
+
+ private List getHoldingsToDelete(List notUpdatedEntities, List createdHoldings) {
+ List notUpdatedHoldingsIds = notUpdatedEntities.stream().map(NotUpdatedEntity::getEntityId).toList();
+ return createdHoldings.stream().filter(holdingsRecord -> !notUpdatedHoldingsIds.contains(holdingsRecord.getId())).toList();
+ }
}
diff --git a/src/main/java/org/folio/inventory/support/MoveApiUtil.java b/src/main/java/org/folio/inventory/support/MoveApiUtil.java
index f416567a8..af6051a21 100644
--- a/src/main/java/org/folio/inventory/support/MoveApiUtil.java
+++ b/src/main/java/org/folio/inventory/support/MoveApiUtil.java
@@ -6,6 +6,8 @@
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
import org.apache.commons.collections4.ListUtils;
+import org.folio.NotUpdatedEntity;
+import org.folio.UpdateOwnershipResponse;
import org.folio.inventory.common.WebContext;
import org.folio.inventory.storage.external.CollectionResourceClient;
import org.folio.inventory.storage.external.CqlQuery;
@@ -101,4 +103,8 @@ public static void respond(RoutingContext routingContext, List itemIdsTo
}
}
+ public static void respond(RoutingContext routingContext, List notUpdatedEntities) {
+ HttpServerResponse response = routingContext.response();
+ success(response, JsonObject.mapFrom(new UpdateOwnershipResponse().withNotUpdatedEntities(notUpdatedEntities)));
+ }
}
diff --git a/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java b/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java
index a9f5e0935..ab8f860c1 100644
--- a/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java
+++ b/src/test/java/api/holdings/HoldingsUpdateOwnershipApiTest.java
@@ -18,7 +18,6 @@
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 support.fakes.EndpointFailureDescriptor;
@@ -73,7 +72,7 @@ public void canUpdateHoldingsOwnershipToDifferentTenant() throws MalformedURLExc
Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
- assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("nonUpdatedIds").size(), is(0));
+ assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("notUpdatedEntities").size(), is(0));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1);
@@ -82,8 +81,8 @@ public void canUpdateHoldingsOwnershipToDifferentTenant() throws MalformedURLExc
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode());
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
- Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1);
- Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord1);
+ Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord2);
+ Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord2);
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord2.getStatusCode());
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
@@ -118,7 +117,7 @@ public void canUpdateHoldingsOwnershipWithRelatedItemsToDifferentTenant() throws
Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
- assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("nonUpdatedIds").size(), is(0));
+ assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("notUpdatedEntities").size(), is(0));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
// Verify Holdings ownership updated
@@ -128,8 +127,8 @@ public void canUpdateHoldingsOwnershipWithRelatedItemsToDifferentTenant() throws
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode());
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
- Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1);
- Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord1);
+ Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord2);
+ Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord2);
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord2.getStatusCode());
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
@@ -157,7 +156,6 @@ public void canUpdateHoldingsOwnershipIfErrorUpdatingRelatedItemsToDifferentTena
InstanceApiClient.createInstance(consortiumOkapiClient, instance.put("source", FOLIO.getValue()));
final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId);
- final UUID createHoldingsRecord2 = createHoldingForInstance(instanceId);
final var firstItem = itemsClient.create(
new ItemRequestBuilder()
@@ -165,12 +163,6 @@ public void canUpdateHoldingsOwnershipIfErrorUpdatingRelatedItemsToDifferentTena
.withBarcode("645398607547")
.withStatus(ItemStatusName.AVAILABLE.value()));
- final var secondItem = itemsClient.create(
- new ItemRequestBuilder()
- .forHolding(createHoldingsRecord2)
- .withBarcode("645398607546")
- .withStatus(ItemStatusName.AVAILABLE.value()));
-
final JsonObject expectedErrorResponse = new JsonObject().put("message", "Server error");
collegeItemsClient.emulateFailure(
new EndpointFailureDescriptor()
@@ -189,19 +181,21 @@ public void canUpdateHoldingsOwnershipIfErrorUpdatingRelatedItemsToDifferentTena
.setMethod(HttpMethod.DELETE.name()));
JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId,
- new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
+ new JsonArray(List.of(createHoldingsRecord1.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
+ collegeItemsClient.disableFailureEmulation();
+ holdingsStorageClient.disableFailureEmulation();
+
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
- List nonUpdatedIdsIds = postHoldingsUpdateOwnershipResponse.getJson()
- .getJsonArray("nonUpdatedIds")
- .getList();
+ JsonArray notUpdatedEntitiesIds = postHoldingsUpdateOwnershipResponse.getJson()
+ .getJsonArray("notUpdatedEntities");
- assertThat(nonUpdatedIdsIds.size(), is(2));
- assertThat(nonUpdatedIdsIds.get(0), equalTo(createHoldingsRecord1.toString()));
- assertThat(nonUpdatedIdsIds.get(1), equalTo(createHoldingsRecord2.toString()));
+ assertThat(notUpdatedEntitiesIds.size(), is(1));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("entityId"), equalTo(createHoldingsRecord1.toString()));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("errorMessage"), containsString(expectedErrorResponse.toString()));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
@@ -212,27 +206,70 @@ public void canUpdateHoldingsOwnershipIfErrorUpdatingRelatedItemsToDifferentTena
Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
- Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1);
- Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord1);
-
- Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
- Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
-
- // Verify related Items ownership updated
+ // Verify related Item ownership not updated
Response sourceTenantItem1 = itemsClient.getById(firstItem.getId());
Response targetTenantItem1 = collegeItemsClient.getById(firstItem.getId());
assertThat(HttpStatus.SC_NOT_FOUND, is(targetTenantItem1.getStatusCode()));
assertThat(sourceTenantItem1.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord1.toString()));
+ }
- Response sourceTenantItem2 = itemsClient.getById(secondItem.getId());
- Response targetTenantItem2 = collegeItemsClient.getById(secondItem.getId());
+ @Test
+ public void canUpdateHoldingsOwnershipIfErrorDeletingRelatedItemsToDifferentTenant() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException {
+ UUID instanceId = UUID.randomUUID();
+ JsonObject instance = smallAngryPlanet(instanceId);
+
+ InstanceApiClient.createInstance(okapiClient, instance.put("source", CONSORTIUM_FOLIO.getValue()));
+ InstanceApiClient.createInstance(consortiumOkapiClient, instance.put("source", FOLIO.getValue()));
+
+ final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId);
+
+ final var firstItem = itemsClient.create(
+ new ItemRequestBuilder()
+ .forHolding(createHoldingsRecord1)
+ .withBarcode("645398607547")
+ .withStatus(ItemStatusName.AVAILABLE.value()));
+
+ final JsonObject expectedErrorResponse = new JsonObject().put("message", "Server error");
+ collegeItemsClient.emulateFailure(
+ new EndpointFailureDescriptor()
+ .setFailureExpireDate(DateTime.now().plusSeconds(2).toDate())
+ .setStatusCode(500)
+ .setContentType("application/json")
+ .setBody(expectedErrorResponse.toString())
+ .setMethod(HttpMethod.DELETE.name()));
+
+ JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId,
+ new JsonArray(List.of(createHoldingsRecord1.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
- assertThat(HttpStatus.SC_NOT_FOUND, is(targetTenantItem2.getStatusCode()));
- assertThat(sourceTenantItem2.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord2.toString()));
+ Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
collegeItemsClient.disableFailureEmulation();
- holdingsStorageClient.disableFailureEmulation();
+
+ assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
+
+ JsonArray notUpdatedEntitiesIds = postHoldingsUpdateOwnershipResponse.getJson()
+ .getJsonArray("notUpdatedEntities");
+
+ assertThat(notUpdatedEntitiesIds.size(), is(1));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("entityId"), equalTo(createHoldingsRecord1.toString()));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("errorMessage"), containsString(expectedErrorResponse.toString()));
+
+ assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
+
+ // Verify Holdings ownership updated
+ Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1);
+ Response targetTenantHoldingsRecord1 = collegeHoldingsStorageClient.getById(createHoldingsRecord1);
+
+ Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
+ Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
+
+ // Verify related Item ownership not updated
+ Response sourceTenantItem1 = itemsClient.getById(firstItem.getId());
+ Response targetTenantItem1 = collegeItemsClient.getById(firstItem.getId());
+
+ assertThat(targetTenantItem1.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord1.toString()));
+ assertThat(sourceTenantItem1.getJson().getString(HOLDINGS_RECORD_ID), is(createHoldingsRecord1.toString()));
}
@Test
@@ -256,12 +293,12 @@ public void shouldReportErrorsWhenOnlySomeRequestedHoldingsRecordsCouldNotBeUpda
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
- List notFoundIds = postHoldingsUpdateOwnershipResponse.getJson()
- .getJsonArray("nonUpdatedIds")
- .getList();
+ JsonArray notFoundIds = postHoldingsUpdateOwnershipResponse.getJson()
+ .getJsonArray("notUpdatedEntities");
assertThat(notFoundIds.size(), is(1));
- assertThat(notFoundIds.get(0), equalTo(createHoldingsRecord2.toString()));
+ assertThat(notFoundIds.getJsonObject(0).getString("entityId"), equalTo(createHoldingsRecord2.toString()));
+ assertThat(notFoundIds.getJsonObject(0).getString("errorMessage"), containsString("not found on tenant"));
Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1);
Response targetTenantHoldingsRecord1 = collegeHoldingsStorageClient.getById(createHoldingsRecord1);
@@ -407,7 +444,6 @@ public void cannotUpdateHoldingsRecordOwnershipDueToHoldingsRecordCreateError()
InstanceApiClient.createInstance(consortiumOkapiClient, instance.put("source", FOLIO.getValue()));
final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId);
- final UUID createHoldingsRecord2 = createHoldingForInstance(instanceId);
final JsonObject expectedErrorResponse = new JsonObject().put("message", "Server error");
collegeHoldingsStorageClient.emulateFailure(
@@ -419,17 +455,18 @@ public void cannotUpdateHoldingsRecordOwnershipDueToHoldingsRecordCreateError()
.setMethod(HttpMethod.POST.name()));
JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId,
- new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
+ new JsonArray(List.of(createHoldingsRecord1.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
- List nonUpdatedIdsIds = postHoldingsUpdateOwnershipResponse.getJson()
- .getJsonArray("nonUpdatedIds")
- .getList();
+ collegeHoldingsStorageClient.disableFailureEmulation();
+
+ JsonArray notUpdatedEntitiesIds = postHoldingsUpdateOwnershipResponse.getJson()
+ .getJsonArray("notUpdatedEntities");
- assertThat(nonUpdatedIdsIds.size(), is(2));
- assertThat(nonUpdatedIdsIds.get(0), equalTo(createHoldingsRecord1.toString()));
- assertThat(nonUpdatedIdsIds.get(1), equalTo(createHoldingsRecord2.toString()));
+ assertThat(notUpdatedEntitiesIds.size(), is(1));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("entityId"), equalTo(createHoldingsRecord1.toString()));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("errorMessage"), containsString(expectedErrorResponse.toString()));
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
@@ -439,14 +476,6 @@ public void cannotUpdateHoldingsRecordOwnershipDueToHoldingsRecordCreateError()
Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, targetTenantHoldingsRecord1.getStatusCode());
-
- Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord2);
- Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord2);
-
- Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
- Assert.assertEquals(HttpStatus.SC_NOT_FOUND, targetTenantHoldingsRecord2.getStatusCode());
-
- collegeHoldingsStorageClient.disableFailureEmulation();
}
@Test
@@ -458,7 +487,6 @@ public void cannotUpdateHoldingsRecordOwnershipDueToHoldingsRecordDeleteError()
InstanceApiClient.createInstance(consortiumOkapiClient, instance.put("source", FOLIO.getValue()));
final UUID createHoldingsRecord1 = createHoldingForInstance(instanceId);
- final UUID createHoldingsRecord2 = createHoldingForInstance(instanceId);
final JsonObject expectedErrorResponse = new JsonObject().put("message", "Server error");
collegeHoldingsStorageClient.emulateFailure(
@@ -470,17 +498,18 @@ public void cannotUpdateHoldingsRecordOwnershipDueToHoldingsRecordDeleteError()
.setMethod(HttpMethod.DELETE.name()));
JsonObject holdingsRecordUpdateOwnershipRequestBody = new HoldingsRecordUpdateOwnershipRequestBuilder(instanceId,
- new JsonArray(List.of(createHoldingsRecord1.toString(), createHoldingsRecord2.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
+ new JsonArray(List.of(createHoldingsRecord1.toString())), ApiTestSuite.COLLEGE_TENANT_ID).create();
Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
- List nonUpdatedIdsIds = postHoldingsUpdateOwnershipResponse.getJson()
- .getJsonArray("nonUpdatedIds")
- .getList();
+ collegeHoldingsStorageClient.disableFailureEmulation();
+
+ JsonArray notUpdatedEntitiesIds = postHoldingsUpdateOwnershipResponse.getJson()
+ .getJsonArray("notUpdatedEntities");
- assertThat(nonUpdatedIdsIds.size(), is(2));
- assertThat(nonUpdatedIdsIds.get(0), equalTo(createHoldingsRecord1.toString()));
- assertThat(nonUpdatedIdsIds.get(1), equalTo(createHoldingsRecord2.toString()));
+ assertThat(notUpdatedEntitiesIds.size(), is(1));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("entityId"), equalTo(createHoldingsRecord1.toString()));
+ assertThat(notUpdatedEntitiesIds.getJsonObject(0).getString("errorMessage"), containsString(expectedErrorResponse.toString()));
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
@@ -490,14 +519,6 @@ public void cannotUpdateHoldingsRecordOwnershipDueToHoldingsRecordDeleteError()
Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
-
- Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord2);
- Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord2);
-
- Assert.assertEquals(instanceId.toString(), sourceTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
- Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));
-
- collegeHoldingsStorageClient.disableFailureEmulation();
}
@Test
@@ -526,7 +547,7 @@ public void canUpdateHoldingsRecordOwnershipToDifferentInstanceWithExtraRedundan
Response postHoldingsUpdateOwnershipResponse = updateHoldingsRecordsOwnership(holdingsRecordUpdateOwnershipRequestBody);
assertThat(postHoldingsUpdateOwnershipResponse.getStatusCode(), is(200));
- assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("nonUpdatedIds").size(), is(0));
+ assertThat(new JsonObject(postHoldingsUpdateOwnershipResponse.getBody()).getJsonArray("notUpdatedEntities").size(), is(0));
assertThat(postHoldingsUpdateOwnershipResponse.getContentType(), containsString(APPLICATION_JSON));
Response sourceTenantHoldingsRecord1 = holdingsStorageClient.getById(createHoldingsRecord1);
@@ -535,8 +556,8 @@ public void canUpdateHoldingsRecordOwnershipToDifferentInstanceWithExtraRedundan
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord1.getStatusCode());
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord1.getJson().getString(INSTANCE_ID));
- Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord1);
- Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord1);
+ Response sourceTenantHoldingsRecord2 = holdingsStorageClient.getById(createHoldingsRecord2);
+ Response targetTenantHoldingsRecord2 = collegeHoldingsStorageClient.getById(createHoldingsRecord2);
Assert.assertEquals(HttpStatus.SC_NOT_FOUND, sourceTenantHoldingsRecord2.getStatusCode());
Assert.assertEquals(instanceId.toString(), targetTenantHoldingsRecord2.getJson().getString(INSTANCE_ID));