diff --git a/ramls/locations/location.json b/ramls/locations/location.json new file mode 100644 index 000000000..6f0ac1da0 --- /dev/null +++ b/ramls/locations/location.json @@ -0,0 +1,75 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "A (shelf) location, the forth-level location unit below institution, campus, and library.", + "javaType": "org.folio.rest.jaxrs.model.Location", + "type": "object", + "properties": { + "id": { + "description": "id of this (shelf) location record as UUID.", + "type": "string" + }, + "name": { + "description": "Name of the (shelf) location", + "type": "string" + }, + "code": { + "description": "Code of the (shelf) location, usually an abbreviation of the name.", + "type": "string" + }, + "description": { + "description": "Description of the (shelf) location.", + "type": "string" + }, + "discoveryDisplayName": { + "description": "Name of the (shelf) location to be shown in the discovery.", + "type": "string" + }, + "isActive": { + "description": "Whether this (shelf) location is active. Inactive (shelf) locations can no longer been used.", + "type": "boolean" + }, + "institutionId": { + "description": "The UUID of the institution, the first-level location unit, this (shelf) location belongs to.", + "type": "string" + }, + "campusId": { + "description": "The UUID of the campus, the second-level location unit, this (shelf) location belongs to.", + "type": "string" + }, + "libraryId": { + "description": "The UUID of the library, the third-level location unit, this (shelf) location belongs to.", + "type": "string" + }, + "primaryServicePoint": { + "description": "The UUID of the primary service point of this (shelf) location.", + "format": "uuid", + "type": "string" + }, + "servicePointIds": { + "description": "All service points that this (shelf) location has.", + "type": "array", + "items": { + "description": "The UUID of a service point that belongs to this (shelf) location.", + "type": "string", + "format": "uuid", + "not": { + "type": "null" + } + } + }, + "metadata": { + "type": "object", + "$ref": "../raml-util/schemas/metadata.schema", + "readonly": true + } + }, + "additionalProperties": false, + "required": [ + "name", + "code", + "institutionId", + "campusId", + "libraryId", + "primaryServicePoint" + ] +} diff --git a/ramls/locations/locations.json b/ramls/locations/locations.json new file mode 100644 index 000000000..df6bdad1d --- /dev/null +++ b/ramls/locations/locations.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "List of (shelf) locations.", + "type": "object", + "properties": { + "locations": { + "id": "locations", + "description": "List of (shelf) locations.", + "type": "array", + "items": { + "type": "object", + "$ref": "location.json" + } + }, + "totalRecords": { + "description": "Estimated or exact total number of records", + "type": "integer" + } + }, + "required": [ + "locations", + "totalRecords" + ] +} diff --git a/ramls/request-storage.raml b/ramls/request-storage.raml index cf32c0fc7..29f93e5f4 100644 --- a/ramls/request-storage.raml +++ b/ramls/request-storage.raml @@ -13,6 +13,8 @@ types: requests: !include requests.json errors: !include raml-util/schemas/errors.schema parameters: !include raml-util/schemas/parameters.schema + location: !include locations/location.json + locations: !include locations/locations.json traits: pageable: !include raml-util/traits/pageable.raml diff --git a/src/main/java/org/folio/rest/client/InventoryStorageClient.java b/src/main/java/org/folio/rest/client/InventoryStorageClient.java index b9a830755..2d607c470 100644 --- a/src/main/java/org/folio/rest/client/InventoryStorageClient.java +++ b/src/main/java/org/folio/rest/client/InventoryStorageClient.java @@ -3,6 +3,7 @@ import java.util.Collection; import java.util.Map; +import org.folio.rest.jaxrs.model.Location; import org.folio.rest.jaxrs.model.Servicepoint; import io.vertx.core.Future; @@ -12,6 +13,9 @@ public class InventoryStorageClient extends OkapiClient { private static final String SERVICE_POINTS_URL = "/service-points"; private static final String SERVICE_POINTS_COLLECTION_NAME = "servicepoints"; + private static final String LOCATION_URL = "/locations"; + private static final String LOCATION_COLLECTION_NAME = "locations"; + public InventoryStorageClient(Vertx vertx, Map okapiHeaders) { super(vertx, okapiHeaders); } @@ -20,4 +24,8 @@ public Future> getServicePoints(Collection ids) return get(SERVICE_POINTS_URL, ids, SERVICE_POINTS_COLLECTION_NAME, Servicepoint.class); } + public Future> getLocations(Collection ids) { + return get(LOCATION_URL, ids, LOCATION_COLLECTION_NAME, Location.class); + } + } diff --git a/src/main/java/org/folio/service/event/handler/ItemUpdateEventHandler.java b/src/main/java/org/folio/service/event/handler/ItemUpdateEventHandler.java index c27bc6d96..561b7cf2b 100644 --- a/src/main/java/org/folio/service/event/handler/ItemUpdateEventHandler.java +++ b/src/main/java/org/folio/service/event/handler/ItemUpdateEventHandler.java @@ -5,6 +5,7 @@ import org.apache.commons.collections4.map.CaseInsensitiveMap; import org.folio.kafka.AsyncRecordHandler; import org.folio.persist.RequestRepository; +import org.folio.rest.client.InventoryStorageClient; import org.folio.service.event.handler.processor.ItemUpdateProcessorForRequest; import io.vertx.core.Context; @@ -26,7 +27,7 @@ public Future handle(KafkaConsumerRecord kafkaConsumerRe new CaseInsensitiveMap<>(kafkaHeadersToMap(kafkaConsumerRecord.headers())); ItemUpdateProcessorForRequest itemUpdateProcessorForRequest = - new ItemUpdateProcessorForRequest(new RequestRepository(context, headers)); + new ItemUpdateProcessorForRequest(new RequestRepository(context, headers), new InventoryStorageClient(context.owner(), headers)); return itemUpdateProcessorForRequest.run(kafkaConsumerRecord.key(), payload); } diff --git a/src/main/java/org/folio/service/event/handler/processor/ItemUpdateProcessorForRequest.java b/src/main/java/org/folio/service/event/handler/processor/ItemUpdateProcessorForRequest.java index 26266f056..18d816418 100644 --- a/src/main/java/org/folio/service/event/handler/processor/ItemUpdateProcessorForRequest.java +++ b/src/main/java/org/folio/service/event/handler/processor/ItemUpdateProcessorForRequest.java @@ -1,16 +1,26 @@ package org.folio.service.event.handler.processor; +import static io.vertx.core.Future.succeededFuture; import static org.apache.commons.lang3.ObjectUtils.notEqual; import static org.folio.service.event.InventoryEventType.INVENTORY_ITEM_UPDATED; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import io.vertx.core.Future; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.persist.RequestRepository; +import org.folio.rest.client.InventoryStorageClient; import org.folio.rest.jaxrs.model.CallNumberComponents; +import org.folio.rest.jaxrs.model.Item; +import org.folio.rest.jaxrs.model.Location; import org.folio.rest.jaxrs.model.Request; +import org.folio.rest.jaxrs.model.Servicepoint; import org.folio.rest.persist.Criteria.Criteria; import org.folio.rest.persist.Criteria.Criterion; @@ -24,9 +34,11 @@ public class ItemUpdateProcessorForRequest extends UpdateEventProcessor private static final String CALL_NUMBER_KEY = "callNumber"; private static final String CALL_NUMBER_PREFIX_KEY = "prefix"; private static final String CALL_NUMBER_SUFFIX_KEY = "suffix"; + private final InventoryStorageClient inventoryStorageClient; - public ItemUpdateProcessorForRequest(RequestRepository repository) { + public ItemUpdateProcessorForRequest(RequestRepository repository, InventoryStorageClient inventoryStorageClient) { super(INVENTORY_ITEM_UPDATED, repository); + this.inventoryStorageClient = inventoryStorageClient; } protected List> collectRelevantChanges(JsonObject payload) { @@ -50,9 +62,64 @@ protected List> collectRelevantChanges(JsonObject payload) { changes.add(new Change<>(request -> request.getSearchIndex().setShelvingOrder(newShelvingOrder))); } + updateItemAndServicePoint(newObject, changes); + + return changes; } + private void updateItemAndServicePoint(JsonObject newObject, + List> changes) { + String effectiveLocationId = newObject.getString("effectiveLocationId"); + log.info("ItemUpdateProcessorForRequest :: updateItemAndServicePoint(): " + + "effectiveLocationId: {}", effectiveLocationId); + changes.add(new Change<>(request -> { + if (request.getItem() == null) { + request.setItem(new Item()); + } + request.getItem().setItemEffectiveLocationId(effectiveLocationId); + inventoryStorageClient.getLocations(Collections.singletonList(effectiveLocationId)) + .compose(locations -> setEffectiveLocationName(request, locations, + effectiveLocationId)) + .compose(primaryServicePoint -> { + if (!StringUtils.isBlank(primaryServicePoint)) { + request.getItem().setRetrievalServicePointId(primaryServicePoint); + inventoryStorageClient.getServicePoints(Collections.singletonList(effectiveLocationId)) + .compose(servicePoints -> setServicePoint(request, servicePoints, + primaryServicePoint)); + } + return succeededFuture(); + }); + })); + } + + private Future setEffectiveLocationName(Request request, + Collection locations, String effectiveLocationId) { + Location effectiveLocation = locations.stream() + .filter(l -> l.getId().equals(effectiveLocationId)) + .findFirst().orElse(null); + log.info("ItemUpdateProcessorForRequest :: setEffectiveLocationName(): " + + "locationsName: {}", effectiveLocation.getName()); + if(Objects.nonNull(effectiveLocation)) { + request.getItem().setItemEffectiveLocationName(effectiveLocation.getName()); + return succeededFuture(effectiveLocation.getPrimaryServicePoint().toString()); + } + return succeededFuture(); + } + + private Future setServicePoint(Request request, + Collection servicePoints, String primaryServicePoint) { + Servicepoint retrievalServicePoint = servicePoints.stream() + .filter(sp -> sp.getId().equals(primaryServicePoint)) + .findFirst().orElse(null); + log.info("ItemUpdateProcessorForRequest :: setServicePoint(): " + + "servicePointName: {}", retrievalServicePoint.getName()); + if(Objects.nonNull(retrievalServicePoint)) { + request.getItem().setRetrievalServicePointName(retrievalServicePoint.getName()); + } + return succeededFuture(); + } + @Override protected Criterion criterionForObjectsToBeUpdated(String oldObjectId) { log.info("criteriaForObjectsToBeUpdated:: oldObjectId: {}", oldObjectId); diff --git a/src/test/java/org/folio/rest/support/builders/LocationBuilder.java b/src/test/java/org/folio/rest/support/builders/LocationBuilder.java new file mode 100644 index 000000000..fe50b6c72 --- /dev/null +++ b/src/test/java/org/folio/rest/support/builders/LocationBuilder.java @@ -0,0 +1,30 @@ +package org.folio.rest.support.builders; + +import io.vertx.core.json.JsonObject; +import lombok.AllArgsConstructor; +import lombok.With; + +import java.util.UUID; + +@With +@AllArgsConstructor +public class LocationBuilder extends JsonBuilder implements Builder { + private final String id; + private final String name; + private final String code; + private final String primaryServicePoint; + + public LocationBuilder(String name) { + this(UUID.randomUUID().toString(), name, null, null); + } + + @Override + public JsonObject create() { + JsonObject location = new JsonObject(); + put(location, "id", this.id); + put(location, "name", this.name); + put(location, "code", this.code); + put(location, "primaryServicePoint", this.primaryServicePoint); + return location; + } +}