Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MODINV-1031] Implement endpoint to update ownership of Holdings #731

Merged
merged 14 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading