Skip to content

Commit

Permalink
[MODINV-1031] Implement endpoint to update ownership of Holdings (#731)
Browse files Browse the repository at this point in the history
* [MODINV-1031] Implement endpoint to update ownership of Holdings

* Implement updating ownership of holdings withou updating ownership of underlying items

* transfer along with all the Items attached to a selected Holdings

* Response with error message

* Add description for entityId

* Add logs on failures

* Add logs for debug

* Not set hrid for items and holdings during create

* Fix sonar errors

* Add HoldingsUpdateOwnership to ApiTestSuite

* Fix sonar issues
  • Loading branch information
RomanChernetskyi authored Jun 25, 2024
1 parent 190b661 commit 40ece08
Show file tree
Hide file tree
Showing 13 changed files with 862 additions and 169 deletions.
3 changes: 3 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@
<path>${basedir}/ramls/holdings-record.json</path>
<path>${basedir}/ramls/holdings-records-source.json</path>
<path>${basedir}/ramls/mappingMetadataDto.json</path>
<path>${basedir}/ramls/holdings_update_ownership.json</path>
<path>${basedir}/ramls/items_update_ownership.json</path>
<path>${basedir}/ramls/update_ownership_response.json</path>
<path>${basedir}/ramls/instance-ingress-event.json</path>
</sourcePaths>
<targetPackage>org.folio</targetPackage>
Expand Down
25 changes: 25 additions & 0 deletions ramls/update_ownership_response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Holder for errors during item/holdingsRecord update ownership",
"type": "object",
"properties": {
"notUpdatedEntities": {
"description": "Item/HoldingsRecord errors",
"type": "array",
"items": {
"type": "object",
"properties": {
"entityId": {
"$ref": "uuid.json",
"description": "Id of item/holdingsRecord"
},
"errorMessage": {
"type": "string",
"description": "Error message"
}
}
}
}
},
"additionalProperties": false
}
2 changes: 1 addition & 1 deletion src/main/java/org/folio/inventory/InventoryVerticle.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void start(Promise<Void> started) {
new ItemsByHoldingsRecordId(storage, client).register(router);
new InventoryConfigApi().register(router);
new TenantApi().register(router);
new UpdateOwnershipApi(storage, client).register(router);
new UpdateOwnershipApi(storage, client, consortiumService).register(router);

Handler<AsyncResult<HttpServer>> onHttpServerStart = result -> {
if (result.succeeded()) {
Expand Down
100 changes: 13 additions & 87 deletions src/main/java/org/folio/inventory/resources/MoveApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,46 @@

import static java.util.stream.Collectors.toList;
import static org.folio.inventory.support.JsonArrayHelper.toListOfStrings;
import static org.folio.inventory.support.http.server.JsonResponse.success;
import static org.folio.inventory.support.MoveApiUtil.createHoldingsRecordsFetchClient;
import static org.folio.inventory.support.MoveApiUtil.createHoldingsStorageClient;
import static org.folio.inventory.support.MoveApiUtil.createHttpClient;
import static org.folio.inventory.support.MoveApiUtil.createItemStorageClient;
import static org.folio.inventory.support.MoveApiUtil.createItemsFetchClient;
import static org.folio.inventory.support.MoveApiUtil.respond;
import static org.folio.inventory.support.http.server.JsonResponse.unprocessableEntity;
import static org.folio.inventory.validation.MoveValidator.holdingsMoveHasRequiredFields;
import static org.folio.inventory.validation.MoveValidator.itemsMoveHasRequiredFields;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import org.apache.commons.collections4.ListUtils;
import org.folio.HoldingsRecord;
import org.folio.inventory.common.WebContext;
import org.folio.inventory.domain.HoldingsRecordCollection;
import org.folio.inventory.domain.items.Item;
import org.folio.inventory.domain.items.ItemCollection;
import org.folio.inventory.storage.Storage;
import org.folio.inventory.storage.external.CollectionResourceClient;
import org.folio.inventory.storage.external.CqlQuery;
import org.folio.inventory.storage.external.MultipleRecordsFetchClient;
import org.folio.inventory.support.ItemUtil;
import org.folio.inventory.support.http.client.OkapiHttpClient;
import org.folio.inventory.support.MoveApiUtil;
import org.folio.inventory.support.http.server.JsonResponse;
import org.folio.inventory.support.http.server.ServerErrorResponse;
import org.folio.inventory.support.http.server.ValidationError;

import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.handler.BodyHandler;

public class MoveApi extends AbstractInventoryResource {
public static final String TO_HOLDINGS_RECORD_ID = "toHoldingsRecordId";
public static final String TO_INSTANCE_ID = "toInstanceId";
public static final String ITEM_IDS = "itemIds";
public static final String HOLDINGS_RECORD_IDS = "holdingsRecordIds";
public static final String ITEM_STORAGE = "/item-storage/items";
public static final String ITEMS_PROPERTY = "items";
public static final String HOLDINGS_RECORDS_PROPERTY = "holdingsRecords";
public static final String HOLDINGS_STORAGE = "/holdings-storage/holdings";
private static final String HOLDINGS_ITEMS_PROPERTY = "holdingsItems";
private static final String BARE_HOLDINGS_ITEMS_PROPERTY = "bareHoldingsItems";

public MoveApi(final Storage storage, final HttpClient client) {
super(storage, client);
Expand Down Expand Up @@ -84,10 +76,10 @@ private void moveItems(RoutingContext routingContext) {
.thenAccept(holding -> {
if (holding != null) {
try {
final var itemsStorageClient = createItemStorageClient(routingContext, context);
final var itemsStorageClient = createItemStorageClient(createHttpClient(client, routingContext, context), context);
final var itemsFetchClient = createItemsFetchClient(itemsStorageClient);

itemsFetchClient.find(itemIdsToUpdate, this::fetchByIdCql)
itemsFetchClient.find(itemIdsToUpdate, MoveApiUtil::fetchByIdCql)
.thenAccept(jsons -> {
List<Item> itemsToUpdate = updateHoldingsRecordIdForItems(toHoldingsRecordId, jsons);
updateItems(routingContext, context, itemIdsToUpdate, itemsToUpdate);
Expand Down Expand Up @@ -129,11 +121,11 @@ private void moveHoldings(RoutingContext routingContext) {
return;
}
try {
CollectionResourceClient holdingsStorageClient = createHoldingsStorageClient(createHttpClient(routingContext, context),
CollectionResourceClient holdingsStorageClient = createHoldingsStorageClient(createHttpClient(client, routingContext, context),
context);
MultipleRecordsFetchClient holdingsRecordFetchClient = createHoldingsRecordsFetchClient(holdingsStorageClient);

holdingsRecordFetchClient.find(holdingsRecordsIdsToUpdate, this::fetchByIdCql)
holdingsRecordFetchClient.find(holdingsRecordsIdsToUpdate, MoveApiUtil::fetchByIdCql)
.thenAccept(jsons -> {
List<HoldingsRecord> holdingsRecordsToUpdate = updateInstanceIdForHoldings(toInstanceId, jsons);
updateHoldings(routingContext, context, holdingsRecordsIdsToUpdate, holdingsRecordsToUpdate);
Expand Down Expand Up @@ -176,18 +168,14 @@ private void updateItems(RoutingContext routingContext, WebContext context, List
}

private List<HoldingsRecord> updateInstanceIdForHoldings(String toInstanceId, List<JsonObject> jsons) {
jsons.forEach(MoveApiUtil::removeExtraRedundantFields);

return jsons.stream()
.peek(this::removeExtraRedundantFields)
.map(json -> json.mapTo(HoldingsRecord.class))
.map(holding -> holding.withInstanceId(toInstanceId))
.collect(toList());
}

private void removeExtraRedundantFields(JsonObject json) {
json.remove(HOLDINGS_ITEMS_PROPERTY);
json.remove(BARE_HOLDINGS_ITEMS_PROPERTY);
}

private void updateHoldings(RoutingContext routingContext, WebContext context, List<String> idsToUpdate,
List<HoldingsRecord> holdingsToUpdate) {
HoldingsRecordCollection storageHoldingsRecordsCollection = storage.getHoldingsRecordCollection(context);
Expand All @@ -204,66 +192,4 @@ private void updateHoldings(RoutingContext routingContext, WebContext context, L
.collect(toList()))
.thenAccept(updatedIds -> respond(routingContext, idsToUpdate, updatedIds));
}

private void respond(RoutingContext routingContext, List<String> itemIdsToUpdate, List<String> updatedItemIds) {
List<String> nonUpdatedIds = ListUtils.subtract(itemIdsToUpdate, updatedItemIds);
HttpServerResponse response = routingContext.response();
if (nonUpdatedIds.isEmpty()) {
successWithEmptyIds(response);
} else {
successWithIds(response, nonUpdatedIds);
}
}

private OkapiHttpClient createHttpClient(RoutingContext routingContext, WebContext context) throws MalformedURLException {
return new OkapiHttpClient(WebClient.wrap(client), context,
exception -> ServerErrorResponse.internalError(routingContext.response(),
String.format("Failed to contact storage module: %s", exception.toString())));
}

private CollectionResourceClient createStorageClient(OkapiHttpClient client, WebContext context, String storageUrl)
throws MalformedURLException {

return new CollectionResourceClient(client, new URL(context.getOkapiLocation() + storageUrl));
}

private CollectionResourceClient createItemStorageClient(
RoutingContext routingContext, WebContext context) throws MalformedURLException {

return createStorageClient(createHttpClient(routingContext, context),
context, ITEM_STORAGE);
}

private CollectionResourceClient createHoldingsStorageClient(OkapiHttpClient client, WebContext context)
throws MalformedURLException {
return createStorageClient(client, context, HOLDINGS_STORAGE);
}

private CqlQuery fetchByIdCql(List<String> ids) {
return CqlQuery.exactMatchAny("id", ids);
}

private MultipleRecordsFetchClient createFetchClient(CollectionResourceClient client, String propertyName) {
return MultipleRecordsFetchClient.builder()
.withCollectionPropertyName(propertyName)
.withExpectedStatus(200)
.withCollectionResourceClient(client)
.build();
}

private MultipleRecordsFetchClient createItemsFetchClient(CollectionResourceClient client) {
return createFetchClient(client, ITEMS_PROPERTY);
}

private MultipleRecordsFetchClient createHoldingsRecordsFetchClient(CollectionResourceClient client) {
return createFetchClient(client, HOLDINGS_RECORDS_PROPERTY);
}

private void successWithIds(HttpServerResponse response, List<String> ids) {
success(response, new JsonObject().put("nonUpdatedIds", ids));
}

private void successWithEmptyIds(HttpServerResponse response) {
successWithIds(response, new ArrayList<>());
}
}
Loading

0 comments on commit 40ece08

Please sign in to comment.