From d73104b4751b04b0c72fc500ebb90365b66a77b1 Mon Sep 17 00:00:00 2001 From: OleksandrVidinieiev <56632770+OleksandrVidinieiev@users.noreply.github.com> Date: Tue, 26 Dec 2023 16:19:38 +0000 Subject: [PATCH] CIRC-1933: Implementation for Search Slips API (#1391) * CIRC-1933 Implementation of search slips API * CIRC-1933 Tests * CIRC-1933 Address review comments * CIRC-1933 Fix code smells --- descriptors/ModuleDescriptor-template.json | 19 +- ramls/staff-slips.raml | 8 +- .../RequestTypeItemStatusWhiteList.java | 10 + .../domain/notice/TemplateContextUtil.java | 16 +- .../resources/PickSlipsResource.java | 135 +------- .../resources/SearchSlipsResource.java | 39 +-- .../circulation/resources/SlipsResource.java | 173 ++++++++-- ...ckSlipsTests.java => StaffSlipsTests.java} | 295 ++++++++++++------ 8 files changed, 402 insertions(+), 293 deletions(-) rename src/test/java/api/requests/{PickSlipsTests.java => StaffSlipsTests.java} (67%) diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 8afc4ad7c6..98e4237619 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -69,6 +69,7 @@ "circulation.search-slips.get" ], "modulePermissions": [ + "modperms.circulation.search-slips.get" ] } ] @@ -1557,7 +1558,8 @@ "circulation.requests.hold-shelf-clearance-report.get", "circulation.requests.allowed-service-points.get", "circulation.inventory.items-in-transit-report.get", - "circulation.pick-slips.get" + "circulation.pick-slips.get", + "circulation.search-slips.get" ] }, { @@ -2321,6 +2323,21 @@ ], "visible": false }, + { + "permissionName": "modperms.circulation.search-slips.get", + "displayName": "module permissions for one op", + "description": "to reduce X-Okapi-Token size", + "subPermissions": [ + "circulation.internal.fetch-items", + "circulation-storage.requests.item.get", + "circulation-storage.requests.collection.get", + "users.item.get", + "users.collection.get", + "addresstypes.item.get", + "addresstypes.collection.get" + ], + "visible": false + }, { "permissionName": "circulation.internal.fetch-items", "displayName" : "Fetch item(s)", diff --git a/ramls/staff-slips.raml b/ramls/staff-slips.raml index 31b5c699f3..45c1a7a023 100644 --- a/ramls/staff-slips.raml +++ b/ramls/staff-slips.raml @@ -1,5 +1,5 @@ #%RAML 1.0 -title: Stuff Slips +title: Staff Slips version: v0.3 protocols: [ HTTP, HTTPS ] baseUri: http://localhost:9130 @@ -9,7 +9,7 @@ documentation: content: API for staff slips generation types: - stuff-slips: !include staff-slips-response.json + staff-slips: !include staff-slips-response.json traits: language: !include raml-util/traits/language.raml @@ -23,10 +23,10 @@ resourceTypes: type: collection-get: exampleCollection: !include examples/staff-slips-response.json - schemaCollection: stuff-slips + schemaCollection: staff-slips /search-slips: /{servicePointId}: type: collection-get: exampleCollection: !include examples/staff-slips-response.json - schemaCollection: stuff-slips + schemaCollection: staff-slips diff --git a/src/main/java/org/folio/circulation/domain/RequestTypeItemStatusWhiteList.java b/src/main/java/org/folio/circulation/domain/RequestTypeItemStatusWhiteList.java index 50fa6ff5d5..59b30f0aa1 100644 --- a/src/main/java/org/folio/circulation/domain/RequestTypeItemStatusWhiteList.java +++ b/src/main/java/org/folio/circulation/domain/RequestTypeItemStatusWhiteList.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.EnumMap; import java.util.List; +import java.util.Map; public class RequestTypeItemStatusWhiteList { private static EnumMap recallRules; @@ -161,4 +162,13 @@ public static List getRequestTypesAllowedForItemStatus(ItemStatus i .filter(requestType -> requestsRulesMap.get(requestType).get(itemStatus)) .toList(); } + + public static List getItemStatusesAllowedForRequestType(RequestType requestType) { + return requestsRulesMap.get(requestType) + .entrySet() + .stream() + .filter(entry -> Boolean.TRUE.equals(entry.getValue())) + .map(Map.Entry::getKey) + .toList(); + } } diff --git a/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java b/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java index 35db89db2c..92ab3145b5 100644 --- a/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java +++ b/src/main/java/org/folio/circulation/domain/notice/TemplateContextUtil.java @@ -47,7 +47,6 @@ public class TemplateContextUtil { private static final String FEE_ACTION = "feeAction"; private static final String UNLIMITED = "unlimited"; public static final String CURRENT_DATE_TIME = "currentDateTime"; - private static final String PICK_SLIPS_KEY = "pickSlips"; private static final String ADDITIONAL_INFO_KEY = "additionalInfo"; private TemplateContextUtil() { @@ -120,25 +119,30 @@ public static JsonObject createStaffSlipContext(Request request) { return createStaffSlipContext(request.getItem(), request); } - public static JsonObject addPrimaryServicePointNameToStaffSlipContext(JsonObject entries, ServicePoint primaryServicePoint) { - log.debug("addPrimaryServicePointNameToStaffSlipContext:: parameters entries: {} primaryServicePoint: {}", entries, primaryServicePoint); + public static JsonObject addPrimaryServicePointNameToStaffSlipContext(JsonObject entries, + ServicePoint primaryServicePoint, String slipsCollectionName) { + + log.debug("addPrimaryServicePointNameToStaffSlipContext:: parameters entries: {}, " + + "primaryServicePoint: {}, slipsCollectionName: {}", entries, primaryServicePoint, slipsCollectionName); if (primaryServicePoint == null) { log.info("addPrimaryServicePointNameToStaffSlipContext:: primaryServicePoint object is null"); return entries; } if (entries == null) { - log.info("addPrimaryServicePointNameToStaffSlipContext:: entries JsonObject is null, primaryServicePointName: {}", primaryServicePoint.getName()); + log.info("addPrimaryServicePointNameToStaffSlipContext:: entries JsonObject is null, " + + "primaryServicePointName: {}", primaryServicePoint.getName()); return new JsonObject(); } - entries.getJsonArray(PICK_SLIPS_KEY) + entries.getJsonArray(slipsCollectionName) .stream() .map(JsonObject.class::cast) .map(pickSlip -> pickSlip.getJsonObject(ITEM)) .forEach(item -> item.put("effectiveLocationPrimaryServicePointName", primaryServicePoint.getName())); - log.info("addPrimaryServicePointNameToStaffSlipContext:: Result entries: {}, primaryServicePointName: {}", entries, primaryServicePoint.getName()); + log.debug("addPrimaryServicePointNameToStaffSlipContext:: Result entries: {}, " + + "primaryServicePointName: {}", () -> entries, primaryServicePoint::getName); return entries; } diff --git a/src/main/java/org/folio/circulation/resources/PickSlipsResource.java b/src/main/java/org/folio/circulation/resources/PickSlipsResource.java index 910cccd773..b3214175b1 100644 --- a/src/main/java/org/folio/circulation/resources/PickSlipsResource.java +++ b/src/main/java/org/folio/circulation/resources/PickSlipsResource.java @@ -1,142 +1,13 @@ package org.folio.circulation.resources; -import static java.util.Collections.emptyList; -import static java.util.concurrent.CompletableFuture.completedFuture; -import static java.util.stream.Collectors.toSet; -import static org.folio.circulation.support.fetching.MultipleCqlIndexValuesCriteria.byIndex; -import static org.folio.circulation.support.fetching.RecordFetching.findWithMultipleCqlIndexValues; -import static org.folio.circulation.support.http.client.CqlQuery.exactMatch; -import static org.folio.circulation.support.results.Result.succeeded; -import static org.folio.circulation.support.results.ResultBinding.flatMapResult; -import static org.folio.circulation.support.utils.LogUtil.multipleRecordsAsString; - -import java.lang.invoke.MethodHandles; -import java.util.Collection; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -import org.apache.commons.lang3.StringUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.folio.circulation.domain.Item; -import org.folio.circulation.domain.ItemStatus; -import org.folio.circulation.domain.Location; -import org.folio.circulation.domain.MultipleRecords; -import org.folio.circulation.domain.Request; -import org.folio.circulation.domain.RequestStatus; -import org.folio.circulation.domain.RequestType; -import org.folio.circulation.domain.notice.TemplateContextUtil; -import org.folio.circulation.infrastructure.storage.ServicePointRepository; -import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; -import org.folio.circulation.infrastructure.storage.inventory.LocationRepository; -import org.folio.circulation.infrastructure.storage.users.AddressTypeRepository; -import org.folio.circulation.infrastructure.storage.users.DepartmentRepository; -import org.folio.circulation.infrastructure.storage.users.PatronGroupRepository; -import org.folio.circulation.infrastructure.storage.users.UserRepository; -import org.folio.circulation.support.Clients; -import org.folio.circulation.support.RouteRegistration; -import org.folio.circulation.support.http.client.CqlQuery; -import org.folio.circulation.support.http.server.JsonHttpResponse; -import org.folio.circulation.support.http.server.WebContext; -import org.folio.circulation.support.results.Result; +import static org.folio.circulation.domain.ItemStatus.PAGED; +import static org.folio.circulation.domain.RequestType.PAGE; import io.vertx.core.http.HttpClient; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; public class PickSlipsResource extends SlipsResource { - private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); - private static final String PICK_SLIPS_KEY = "pickSlips"; - private final String rootPath; - public PickSlipsResource(String rootPath, HttpClient client) { - super(client); - this.rootPath = rootPath; - } - - @Override - public void register(Router router) { - RouteRegistration routeRegistration = new RouteRegistration(rootPath, router); - routeRegistration.getMany(this::getMany); - } - - protected void getMany(RoutingContext routingContext) { - final WebContext context = new WebContext(routingContext); - final Clients clients = Clients.create(context, client); - - final var userRepository = new UserRepository(clients); - final var itemRepository = new ItemRepository(clients); - final AddressTypeRepository addressTypeRepository = new AddressTypeRepository(clients); - final ServicePointRepository servicePointRepository = new ServicePointRepository(clients); - final PatronGroupRepository patronGroupRepository = new PatronGroupRepository(clients); - final DepartmentRepository departmentRepository = new DepartmentRepository(clients); - final UUID servicePointId = UUID.fromString( - routingContext.request().getParam(SERVICE_POINT_ID_PARAM)); - - fetchLocationsForServicePoint(servicePointId, clients) - .thenComposeAsync(r -> r.after(locations -> fetchPagedItemsForLocations(locations, - itemRepository, LocationRepository.using(clients, servicePointRepository)))) - .thenComposeAsync(r -> r.after(items -> fetchOpenPageRequestsForItems(items, clients))) - .thenComposeAsync(r -> r.after(userRepository::findUsersForRequests)) - .thenComposeAsync(result -> result.after(patronGroupRepository::findPatronGroupsForRequestsUsers)) - .thenComposeAsync(r -> r.after(departmentRepository::findDepartmentsForRequestUsers)) - .thenComposeAsync(r -> r.after(addressTypeRepository::findAddressTypesForRequests)) - .thenComposeAsync(r -> r.after(servicePointRepository::findServicePointsForRequests)) - .thenApply(flatMapResult(requests -> mapResultToJson(requests, PICK_SLIPS_KEY))) - .thenComposeAsync(r -> r.combineAfter(() -> servicePointRepository.getServicePointById(servicePointId), - TemplateContextUtil::addPrimaryServicePointNameToStaffSlipContext)) - .thenApply(r -> r.map(JsonHttpResponse::ok)) - .thenAccept(context::writeResultToHttpResponse); - } - - private CompletableFuture>> fetchPagedItemsForLocations( - MultipleRecords multipleLocations, - ItemRepository itemRepository, LocationRepository locationRepository) { - - log.debug("fetchPagedItemsForLocations:: parameters multipleLocations: {}", - () -> multipleRecordsAsString(multipleLocations)); - Collection locations = multipleLocations.getRecords(); - - Set locationIds = locations.stream() - .map(Location::getId) - .filter(StringUtils::isNoneBlank) - .collect(toSet()); - - if (locationIds.isEmpty()) { - log.info("fetchPagedItemsForLocations:: locationIds is empty"); - - return completedFuture(succeeded(emptyList())); - } - - Result statusQuery = exactMatch(STATUS_NAME_KEY, ItemStatus.PAGED.getValue()); - - return itemRepository.findByIndexNameAndQuery(locationIds, EFFECTIVE_LOCATION_ID_KEY, statusQuery) - .thenComposeAsync(r -> r.after(items -> fetchLocationDetailsForItems(items, locations, - locationRepository))); - } - - private CompletableFuture>> fetchOpenPageRequestsForItems( - Collection items, Clients clients) { - - Set itemIds = items.stream() - .map(Item::getItemId) - .filter(StringUtils::isNoneBlank) - .collect(toSet()); - - if (itemIds.isEmpty()) { - log.info("fetchOpenPageRequestsForItems:: itemIds is empty"); - - return completedFuture(succeeded(MultipleRecords.empty())); - } - - final Result typeQuery = exactMatch(REQUEST_TYPE_KEY, RequestType.PAGE.getValue()); - final Result statusQuery = exactMatch(STATUS_KEY, RequestStatus.OPEN_NOT_YET_FILLED.getValue()); - final Result statusAndTypeQuery = typeQuery.combine(statusQuery, CqlQuery::and); - - return findWithMultipleCqlIndexValues(clients.requestsStorage(), REQUESTS_KEY, Request::from) - .find(byIndex(ITEM_ID_KEY, itemIds).withQuery(statusAndTypeQuery)) - .thenApply(flatMapResult(requests -> matchItemsToRequests(requests, items))); + super(rootPath, client, "pickSlips", PAGE, PAGED); } } diff --git a/src/main/java/org/folio/circulation/resources/SearchSlipsResource.java b/src/main/java/org/folio/circulation/resources/SearchSlipsResource.java index 8fe58e5512..4b33587bd0 100644 --- a/src/main/java/org/folio/circulation/resources/SearchSlipsResource.java +++ b/src/main/java/org/folio/circulation/resources/SearchSlipsResource.java @@ -1,46 +1,13 @@ package org.folio.circulation.resources; -import static org.folio.circulation.support.results.Result.ofAsync; -import static org.folio.circulation.support.results.ResultBinding.flatMapResult; - -import java.util.concurrent.CompletableFuture; - -import org.folio.circulation.domain.MultipleRecords; -import org.folio.circulation.domain.Request; -import org.folio.circulation.support.RouteRegistration; -import org.folio.circulation.support.http.server.JsonHttpResponse; -import org.folio.circulation.support.http.server.WebContext; -import org.folio.circulation.support.results.Result; +import static org.folio.circulation.domain.RequestType.HOLD; +import static org.folio.circulation.domain.RequestTypeItemStatusWhiteList.getItemStatusesAllowedForRequestType; import io.vertx.core.http.HttpClient; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.RoutingContext; public class SearchSlipsResource extends SlipsResource { - private static final String SEARCH_SLIPS_KEY = "searchSlips"; - private final String rootPath; public SearchSlipsResource(String rootPath, HttpClient client) { - super(client); - this.rootPath = rootPath; - } - - @Override - public void register(Router router) { - RouteRegistration routeRegistration = new RouteRegistration(rootPath, router); - routeRegistration.getMany(this::getMany); - } - - protected void getMany(RoutingContext routingContext) { - final WebContext context = new WebContext(routingContext); - - fetchHoldRequests() - .thenApply(flatMapResult(requests -> mapResultToJson(requests, SEARCH_SLIPS_KEY))) - .thenApply(r -> r.map(JsonHttpResponse::ok)) - .thenAccept(context::writeResultToHttpResponse); - } - - private CompletableFuture>> fetchHoldRequests() { - return ofAsync(MultipleRecords.empty()); + super(rootPath, client, "searchSlips", HOLD, getItemStatusesAllowedForRequestType(HOLD)); } } diff --git a/src/main/java/org/folio/circulation/resources/SlipsResource.java b/src/main/java/org/folio/circulation/resources/SlipsResource.java index c8d8faead2..6b6d3c926c 100644 --- a/src/main/java/org/folio/circulation/resources/SlipsResource.java +++ b/src/main/java/org/folio/circulation/resources/SlipsResource.java @@ -5,8 +5,11 @@ import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; +import static org.folio.circulation.support.fetching.MultipleCqlIndexValuesCriteria.byIndex; import static org.folio.circulation.support.fetching.RecordFetching.findWithCqlQuery; +import static org.folio.circulation.support.fetching.RecordFetching.findWithMultipleCqlIndexValues; import static org.folio.circulation.support.http.client.CqlQuery.exactMatch; +import static org.folio.circulation.support.http.client.CqlQuery.exactMatchAny; import static org.folio.circulation.support.results.Result.succeeded; import static org.folio.circulation.support.results.ResultBinding.flatMapResult; import static org.folio.circulation.support.utils.LogUtil.collectionAsString; @@ -20,46 +23,110 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; +import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.folio.circulation.domain.Item; +import org.folio.circulation.domain.ItemStatus; import org.folio.circulation.domain.Location; import org.folio.circulation.domain.MultipleRecords; import org.folio.circulation.domain.Request; +import org.folio.circulation.domain.RequestStatus; +import org.folio.circulation.domain.RequestType; +import org.folio.circulation.domain.ServicePoint; import org.folio.circulation.domain.notice.TemplateContextUtil; +import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.inventory.LocationRepository; +import org.folio.circulation.infrastructure.storage.users.AddressTypeRepository; +import org.folio.circulation.infrastructure.storage.users.DepartmentRepository; +import org.folio.circulation.infrastructure.storage.users.PatronGroupRepository; +import org.folio.circulation.infrastructure.storage.users.UserRepository; import org.folio.circulation.storage.mappers.LocationMapper; import org.folio.circulation.support.Clients; +import org.folio.circulation.support.RouteRegistration; +import org.folio.circulation.support.http.client.CqlQuery; import org.folio.circulation.support.http.client.PageLimit; +import org.folio.circulation.support.http.server.JsonHttpResponse; +import org.folio.circulation.support.http.server.WebContext; import org.folio.circulation.support.results.Result; import io.vertx.core.http.HttpClient; import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; public abstract class SlipsResource extends Resource { - protected static final String LOCATIONS_KEY = "locations"; - protected static final String STATUS_KEY = "status"; - protected static final String REQUESTS_KEY = "requests"; - protected static final String ITEM_ID_KEY = "itemId"; - protected static final String STATUS_NAME_KEY = "status.name"; - protected static final String REQUEST_TYPE_KEY = "requestType"; - protected static final String TOTAL_RECORDS_KEY = "totalRecords"; - protected static final String SERVICE_POINT_ID_PARAM = "servicePointId"; - protected static final String EFFECTIVE_LOCATION_ID_KEY = "effectiveLocationId"; - protected static final String PRIMARY_SERVICE_POINT_KEY = "primaryServicePoint"; - - protected static final PageLimit LOCATIONS_LIMIT = PageLimit.oneThousand(); - protected static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); - - - protected SlipsResource(HttpClient client) { + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + private static final PageLimit LOCATIONS_LIMIT = PageLimit.oneThousand(); + private static final String LOCATIONS_KEY = "locations"; + private static final String STATUS_KEY = "status"; + private static final String REQUESTS_KEY = "requests"; + private static final String ITEM_ID_KEY = "itemId"; + private static final String STATUS_NAME_KEY = "status.name"; + private static final String REQUEST_TYPE_KEY = "requestType"; + private static final String TOTAL_RECORDS_KEY = "totalRecords"; + private static final String SERVICE_POINT_ID_PARAM = "servicePointId"; + private static final String EFFECTIVE_LOCATION_ID_KEY = "effectiveLocationId"; + private static final String PRIMARY_SERVICE_POINT_KEY = "primaryServicePoint"; + + private final String rootPath; + private final String collectionName; + private final RequestType requestType; + private final Collection itemStatuses; + + protected SlipsResource(String rootPath, HttpClient client, String collectionName, + RequestType requestType, ItemStatus itemStatus) { + + this(rootPath, client, collectionName, requestType, List.of(itemStatus)); + } + + protected SlipsResource(String rootPath, HttpClient client, String collectionName, + RequestType requestType, Collection itemStatuses) { + super(client); + this.rootPath = rootPath; + this.requestType = requestType; + this.itemStatuses = itemStatuses; + this.collectionName = collectionName; + } + + @Override + public void register(Router router) { + RouteRegistration routeRegistration = new RouteRegistration(rootPath, router); + routeRegistration.getMany(this::getMany); } - protected abstract void getMany(RoutingContext routingContext); + private void getMany(RoutingContext routingContext) { + final WebContext context = new WebContext(routingContext); + final Clients clients = Clients.create(context, client); + + final var userRepository = new UserRepository(clients); + final var itemRepository = new ItemRepository(clients); + final var addressTypeRepository = new AddressTypeRepository(clients); + final var servicePointRepository = new ServicePointRepository(clients); + final var patronGroupRepository = new PatronGroupRepository(clients); + final var departmentRepository = new DepartmentRepository(clients); + final UUID servicePointId = UUID.fromString( + routingContext.request().getParam(SERVICE_POINT_ID_PARAM)); - protected CompletableFuture>> fetchLocationsForServicePoint( + fetchLocationsForServicePoint(servicePointId, clients) + .thenComposeAsync(r -> r.after(locations -> fetchItemsForLocations(locations, + itemRepository, LocationRepository.using(clients, servicePointRepository)))) + .thenComposeAsync(r -> r.after(items -> fetchRequests(items, clients))) + .thenComposeAsync(r -> r.after(userRepository::findUsersForRequests)) + .thenComposeAsync(result -> result.after(patronGroupRepository::findPatronGroupsForRequestsUsers)) + .thenComposeAsync(r -> r.after(departmentRepository::findDepartmentsForRequestUsers)) + .thenComposeAsync(r -> r.after(addressTypeRepository::findAddressTypesForRequests)) + .thenComposeAsync(r -> r.after(servicePointRepository::findServicePointsForRequests)) + .thenApply(flatMapResult(this::mapResultToJson)) + .thenComposeAsync(r -> r.combineAfter(() -> servicePointRepository.getServicePointById(servicePointId), + this::addPrimaryServicePointNameToStaffSlipContext)) + .thenApply(r -> r.map(JsonHttpResponse::ok)) + .thenAccept(context::writeResultToHttpResponse); + } + + private CompletableFuture>> fetchLocationsForServicePoint( UUID servicePointId, Clients clients) { log.debug("fetchLocationsForServicePoint:: parameters servicePointId: {}", servicePointId); @@ -68,7 +135,58 @@ protected CompletableFuture>> fetchLocationsFor .findByQuery(exactMatch(PRIMARY_SERVICE_POINT_KEY, servicePointId.toString()), LOCATIONS_LIMIT); } - protected CompletableFuture>> fetchLocationDetailsForItems( + private CompletableFuture>> fetchItemsForLocations( + MultipleRecords multipleLocations, + ItemRepository itemRepository, LocationRepository locationRepository) { + + log.debug("fetchPagedItemsForLocations:: parameters multipleLocations: {}", + () -> multipleRecordsAsString(multipleLocations)); + Collection locations = multipleLocations.getRecords(); + Set locationIds = locations.stream() + .map(Location::getId) + .filter(StringUtils::isNoneBlank) + .collect(toSet()); + + if (locationIds.isEmpty()) { + log.info("fetchPagedItemsForLocations:: locationIds is empty"); + + return completedFuture(succeeded(emptyList())); + } + + List itemStatusValues = itemStatuses.stream() + .map(ItemStatus::getValue) + .toList(); + Result statusQuery = exactMatchAny(STATUS_NAME_KEY, itemStatusValues); + + return itemRepository.findByIndexNameAndQuery(locationIds, EFFECTIVE_LOCATION_ID_KEY, statusQuery) + .thenComposeAsync(r -> r.after(items -> fetchLocationDetailsForItems(items, locations, + locationRepository))); + } + + private CompletableFuture>> fetchRequests( + Collection items, Clients clients) { + + Set itemIds = items.stream() + .map(Item::getItemId) + .filter(StringUtils::isNoneBlank) + .collect(toSet()); + + if (itemIds.isEmpty()) { + log.info("fetchOpenPageRequestsForItems:: itemIds is empty"); + + return completedFuture(succeeded(MultipleRecords.empty())); + } + + final Result typeQuery = exactMatch(REQUEST_TYPE_KEY, requestType.getValue()); + final Result statusQuery = exactMatch(STATUS_KEY, RequestStatus.OPEN_NOT_YET_FILLED.getValue()); + final Result statusAndTypeQuery = typeQuery.combine(statusQuery, CqlQuery::and); + + return findWithMultipleCqlIndexValues(clients.requestsStorage(), REQUESTS_KEY, Request::from) + .find(byIndex(ITEM_ID_KEY, itemIds).withQuery(statusAndTypeQuery)) + .thenApply(flatMapResult(requests -> matchItemsToRequests(requests, items))); + } + + private CompletableFuture>> fetchLocationDetailsForItems( MultipleRecords items, Collection locationsForServicePoint, LocationRepository locationRepository) { @@ -93,7 +211,7 @@ protected CompletableFuture>> fetchLocationDetailsForIte .thenApply(flatMapResult(locations -> matchLocationsToItems(items, locations))); } - protected Result> matchLocationsToItems( + private Result> matchLocationsToItems( MultipleRecords items, Collection locations) { log.debug("matchLocationsToItems:: parameters items: {}, locations: {}", @@ -108,7 +226,7 @@ protected Result> matchLocationsToItems( .getRecords()); } - protected Result> matchItemsToRequests( + private Result> matchItemsToRequests( MultipleRecords requests, Collection items) { Map itemMap = items.stream() @@ -118,17 +236,22 @@ protected Result> matchItemsToRequests( itemMap.getOrDefault(request.getItemId(), null)))); } - protected Result mapResultToJson(MultipleRecords requests, - String slipsKey) { - + private Result mapResultToJson(MultipleRecords requests) { log.debug("mapResultToJson:: parameters requests: {}", () -> multipleRecordsAsString(requests)); List representations = requests.getRecords().stream() .map(TemplateContextUtil::createStaffSlipContext) .toList(); JsonObject jsonRepresentations = new JsonObject() - .put(slipsKey, representations) + .put(collectionName, representations) .put(TOTAL_RECORDS_KEY, representations.size()); return succeeded(jsonRepresentations); } + + private JsonObject addPrimaryServicePointNameToStaffSlipContext(JsonObject context, + ServicePoint servicePoint) { + + return TemplateContextUtil.addPrimaryServicePointNameToStaffSlipContext( + context, servicePoint, collectionName); + } } diff --git a/src/test/java/api/requests/PickSlipsTests.java b/src/test/java/api/requests/StaffSlipsTests.java similarity index 67% rename from src/test/java/api/requests/PickSlipsTests.java rename to src/test/java/api/requests/StaffSlipsTests.java index f8b77b7e41..22fb0cb198 100644 --- a/src/test/java/api/requests/PickSlipsTests.java +++ b/src/test/java/api/requests/StaffSlipsTests.java @@ -4,6 +4,8 @@ import static java.net.HttpURLConnection.HTTP_OK; import static java.time.ZoneOffset.UTC; import static java.util.stream.Collectors.joining; +import static org.folio.circulation.domain.RequestType.HOLD; +import static org.folio.circulation.domain.RequestType.PAGE; import static org.folio.circulation.domain.notice.TemplateContextUtil.CURRENT_DATE_TIME; import static org.folio.circulation.support.json.JsonPropertyFetcher.getDateTimeProperty; import static org.folio.circulation.support.json.JsonPropertyFetcher.getNestedStringProperty; @@ -19,6 +21,7 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -29,6 +32,8 @@ import org.folio.circulation.domain.ItemStatus; import org.folio.circulation.domain.Location; import org.folio.circulation.domain.RequestStatus; +import org.folio.circulation.domain.RequestType; +import org.folio.circulation.domain.RequestTypeItemStatusWhiteList; import org.folio.circulation.domain.User; import org.folio.circulation.storage.mappers.InstanceMapper; import org.folio.circulation.storage.mappers.LocationMapper; @@ -38,6 +43,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; import api.support.APITests; import api.support.builders.Address; @@ -50,17 +57,19 @@ import api.support.matchers.UUIDMatcher; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; +import lombok.AllArgsConstructor; +import lombok.Getter; import lombok.val; -class PickSlipsTests extends APITests { +class StaffSlipsTests extends APITests { private static final String TOTAL_RECORDS = "totalRecords"; - private static final String PICK_SLIPS_KEY = "pickSlips"; private static final String ITEM_KEY = "item"; private static final String REQUEST_KEY = "request"; private static final String REQUESTER_KEY = "requester"; - @Test - void responseContainsNoPickSlipsForNonExistentServicePointId() { + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseContainsNoSlipsForNonExistentServicePointId(SlipsType slipsType) { UUID servicePointId = servicePointsFixture.cd1().getId(); ItemResource item = itemsFixture.basedUponSmallAngryPlanet(); @@ -71,13 +80,14 @@ void responseContainsNoPickSlipsForNonExistentServicePointId() { .forItem(item) .by(usersFixture.james())); - Response response = ResourceClient.forPickSlips().getById(UUID.randomUUID()); + Response response = slipsType.get(UUID.randomUUID()); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 0); + assertResponseHasItems(response, 0, slipsType); } - @Test - void responseContainsNoPickSlipsForWrongServicePointId() { + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseContainsNoSlipsForWrongServicePointId(SlipsType slipsType) { UUID servicePointId = servicePointsFixture.cd1().getId(); ItemResource item = itemsFixture.basedUponSmallAngryPlanet(); @@ -89,51 +99,89 @@ void responseContainsNoPickSlipsForWrongServicePointId() { .by(usersFixture.james())); UUID differentServicePointId = servicePointsFixture.cd2().getId(); - Response response = ResourceClient.forPickSlips() - .getById(differentServicePointId); + Response response = slipsType.get(differentServicePointId); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 0); + assertResponseHasItems(response, 0, slipsType); } - @Test - void responseContainsNoPickSlipsWhenThereAreNoPagedItems() { + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseContainsNoSlipsWhenThereAreNoItems(SlipsType slipsType) { UUID servicePointId = servicePointsFixture.cd1().getId(); - Response response = ResourceClient.forPickSlips().getById(servicePointId); + Response response = slipsType.get(servicePointId); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 0); + assertResponseHasItems(response, 0, slipsType); } - @Test - void responseContainsNoPickSlipsWhenItemHasOpenPageRequestWithWrongStatus() { + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseContainsNoPickSlipsWhenItemHasOpenRequestWithWrongStatus(SlipsType slipsType) { UUID servicePointId = servicePointsFixture.cd1().getId(); ItemResource item = itemsFixture.basedUponSmallAngryPlanet(); + if (slipsType == SlipsType.SEARCH_SLIPS) { + checkOutFixture.checkOutByBarcode(item); + } + requestsFixture.place(new RequestBuilder() - .page() + .withRequestType(slipsType.getRequestType().getValue()) + .withStatus(RequestStatus.OPEN_AWAITING_PICKUP.getValue()) + .withPickupServicePointId(servicePointId) + .forItem(item) + .by(usersFixture.james())); + + Response response = slipsType.get(servicePointId); + + assertThat(response.getStatusCode(), is(HTTP_OK)); + assertResponseHasItems(response, 0, slipsType); + } + + @ParameterizedTest + @MethodSource(value = "getAllowedStatusesForHoldRequest") + void responseContainsSearchSlipsForItemWithAllowedStatus(ItemStatus itemStatus) { + UUID servicePointId = servicePointsFixture.cd1().getId(); + ItemResource item = itemsFixture.basedUponNod(b -> b.withStatus(itemStatus.getValue())); + + requestsFixture.place(new RequestBuilder() + .hold() .withStatus(RequestStatus.OPEN_AWAITING_PICKUP.getValue()) .withPickupServicePointId(servicePointId) .forItem(item) .by(usersFixture.james())); - Response response = ResourceClient.forPickSlips().getById(servicePointId); + Response response = SlipsType.SEARCH_SLIPS.get(servicePointId); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 0); + assertResponseHasItems(response, 0, SlipsType.SEARCH_SLIPS); } + private static Collection getAllowedStatusesForHoldRequest() { + return RequestTypeItemStatusWhiteList.getItemStatusesAllowedForRequestType(HOLD) + .stream() + .filter(status -> status != ItemStatus.NONE) + .toList(); + } @ParameterizedTest @CsvSource({ - "US, false", - ", false", - "XX, false", - "US, true", - ", true", - "XX, true" + "US, false, PICK_SLIPS", + "US, false, SEARCH_SLIPS", + ", false, PICK_SLIPS", + ", false, SEARCH_SLIPS", + "XX, false, PICK_SLIPS", + "XX, false, SEARCH_SLIPS", + "US, true, PICK_SLIPS", + "US, true, SEARCH_SLIPS", + ", true, PICK_SLIPS", + ", true, SEARCH_SLIPS", + "XX, true, PICK_SLIPS", + "XX, true, SEARCH_SLIPS" }) - void responseContainsPickSlipWithAllAvailableTokens(String countryCode, String primaryAddress) { + void responseContainsSlipWithAllAvailableTokens(String countryCode, String primaryAddress, + String slipsTypeName) { + SlipsType slipsType = SlipsType.valueOf(slipsTypeName); IndividualResource servicePoint = servicePointsFixture.cd1(); UUID servicePointId = servicePoint.getId(); IndividualResource locationResource = locationsFixture.thirdFloor(); @@ -170,10 +218,17 @@ void responseContainsPickSlipWithAllAvailableTokens(String countryCode, String p JsonObject lastCheckIn = itemsClient.get(itemResource.getId()) .getJson().getJsonObject("lastCheckIn"); ZonedDateTime actualCheckinDateTime = getDateTimeProperty(lastCheckIn, "dateTime"); + + ItemStatus expectedItemStatus = ItemStatus.PAGED; + if (slipsType == SlipsType.SEARCH_SLIPS) { + checkOutFixture.checkOutByBarcode(itemResource); + expectedItemStatus = ItemStatus.CHECKED_OUT; + } + IndividualResource requestResource = requestsFixture.place(new RequestBuilder() .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) .open() - .page() + .withRequestType(slipsType.getRequestType().getValue()) .withRequestDate(requestDate) .withRequestExpiration(requestExpiration) .withHoldShelfExpiration(holdShelfExpiration) @@ -183,14 +238,14 @@ void responseContainsPickSlipWithAllAvailableTokens(String countryCode, String p .withPatronComments("I need the book") .by(requesterResource)); - Response response = ResourceClient.forPickSlips().getById(servicePointId); + Response response = slipsType.get(servicePointId); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 1); + assertResponseHasItems(response, 1, slipsType); - JsonObject pickSlip = getPickSlipsList(response).get(0); - JsonObject itemContext = pickSlip.getJsonObject(ITEM_KEY); - assertNotNull(pickSlip.getString(CURRENT_DATE_TIME)); + JsonObject slip = getPickSlipsList(response, slipsType).get(0); + JsonObject itemContext = slip.getJsonObject(ITEM_KEY); + assertNotNull(slip.getString(CURRENT_DATE_TIME)); ZonedDateTime requestCheckinDateTime = getDateTimeProperty(itemContext, "lastCheckedInDateTime"); @@ -207,7 +262,7 @@ void responseContainsPickSlipWithAllAvailableTokens(String countryCode, String p assertEquals(item.getTitle(), itemContext.getString("title")); assertEquals(item.getBarcode(), itemContext.getString("barcode")); - assertEquals(ItemStatus.PAGED.getValue(), itemContext.getString("status")); + assertEquals(expectedItemStatus.getValue(), itemContext.getString("status")); assertEquals(item.getPrimaryContributorName(), itemContext.getString("primaryContributor")); assertEquals(contributorNames, itemContext.getString("allContributors")); assertEquals(item.getEnumeration(), itemContext.getString("enumeration")); @@ -228,7 +283,7 @@ void responseContainsPickSlipWithAllAvailableTokens(String countryCode, String p assertEquals(callNumberComponents.getSuffix(), itemContext.getString("callNumberSuffix")); User requester = new User(requesterResource.getJson()); - JsonObject requesterContext = pickSlip.getJsonObject("requester"); + JsonObject requesterContext = slip.getJsonObject("requester"); assertThat(requesterContext.getString("firstName"), is(requester.getFirstName())); assertThat(requesterContext.getString("lastName"), is(requester.getLastName())); @@ -248,7 +303,7 @@ void responseContainsPickSlipWithAllAvailableTokens(String countryCode, String p assertThat(requesterContext.getString("departments").split("; "), arrayContainingInAnyOrder(equalTo("test department1"),equalTo("test department2"))); - JsonObject requestContext = pickSlip.getJsonObject("request"); + JsonObject requestContext = slip.getJsonObject("request"); assertThat(requestContext.getString("deliveryAddressType"), is(addressTypeResource.getJson().getString("addressType"))); @@ -288,15 +343,46 @@ void responseContainsPickSlipsForRequestsOfTypePageOnly() { IndividualResource firstRequest = requestsClient.create(firstRequestBuilder); requestsClient.create(secondRequestBuilder); - Response response = ResourceClient.forPickSlips().getById(servicePointId); + Response response = SlipsType.PICK_SLIPS.get(servicePointId); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 1); - assertResponseContains(response, item, firstRequest, james); + assertResponseHasItems(response, 1, SlipsType.PICK_SLIPS); + assertResponseContains(response, SlipsType.PICK_SLIPS, item, firstRequest, james); } @Test - void responseIncludesItemsFromDifferentLocationsForSameServicePoint() { + void responseContainsSearchSlipsForRequestsOfTypeHoldOnly() { + UUID servicePointId = servicePointsFixture.cd1().getId(); + val item = itemsFixture.basedUponSmallAngryPlanet(); + UserResource steve = usersFixture.steve(); + + RequestBuilder pageRequestBuilder = new RequestBuilder() + .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) + .page() + .withPickupServicePointId(servicePointId) + .forItem(item) + .by(usersFixture.james()); + + RequestBuilder holdRequestBuilder = new RequestBuilder() + .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) + .hold() + .withPickupServicePointId(servicePointId) + .forItem(item) + .by(steve); + + requestsClient.create(pageRequestBuilder); + IndividualResource holdRequest = requestsClient.create(holdRequestBuilder); + + Response response = SlipsType.SEARCH_SLIPS.get(servicePointId); + + assertThat(response.getStatusCode(), is(HTTP_OK)); + assertResponseHasItems(response, 1, SlipsType.SEARCH_SLIPS); + assertResponseContains(response, SlipsType.SEARCH_SLIPS, item, holdRequest, steve); + } + + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseIncludesItemsFromDifferentLocationsForSameServicePoint(SlipsType slipsType) { UUID circDesk1 = servicePointsFixture.cd1().getId(); // Circ desk 1: Second floor @@ -309,15 +395,6 @@ void responseIncludesItemsFromDifferentLocationsForSameServicePoint() { .withNoPermanentLocation() .withNoTemporaryLocation()); - val james = usersFixture.james(); - - val temeraireRequest = requestsFixture.place(new RequestBuilder() - .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) - .page() - .withPickupServicePointId(circDesk1) - .forItem(temeraireSecondFloorCd1) - .by(james)); - // Circ desk 1: Third floor val thirdFloorCd1 = locationsFixture.thirdFloor(); val planetThirdFloorCd1 = itemsFixture.basedUponSmallAngryPlanet( @@ -328,31 +405,45 @@ void responseIncludesItemsFromDifferentLocationsForSameServicePoint() { .withNoPermanentLocation() .withNoTemporaryLocation()); + val james = usersFixture.james(); val charlotte = usersFixture.charlotte(); + if (slipsType == SlipsType.SEARCH_SLIPS) { + checkOutFixture.checkOutByBarcode(temeraireSecondFloorCd1); + checkOutFixture.checkOutByBarcode(planetThirdFloorCd1); + } + + val temeraireRequest = requestsFixture.place(new RequestBuilder() + .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) + .withRequestType(slipsType.getRequestType().getValue()) + .withPickupServicePointId(circDesk1) + .forItem(temeraireSecondFloorCd1) + .by(james)); + val planetRequest = requestsFixture.place(new RequestBuilder() .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) - .page() + .withRequestType(slipsType.getRequestType().getValue()) .withPickupServicePointId(circDesk1) .forItem(planetThirdFloorCd1) .by(charlotte)); - val response = ResourceClient.forPickSlips().getById(circDesk1); + val response = slipsType.get(circDesk1); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 2); - assertResponseContains(response, temeraireSecondFloorCd1, temeraireRequest, james); - assertResponseContains(response, planetThirdFloorCd1, planetRequest, charlotte); + assertResponseHasItems(response, 2, slipsType); + assertResponseContains(response, slipsType, temeraireSecondFloorCd1, temeraireRequest, james); + assertResponseContains(response, slipsType, planetThirdFloorCd1, planetRequest, charlotte); } - @Test - void responseDoesNotIncludePickSlipsFromDifferentServicePoint() { + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseDoesNotIncludeSlipsFromDifferentServicePoint(SlipsType slipsType) { UUID circDesk1 = servicePointsFixture.cd1().getId(); UUID circDesk4 = servicePointsFixture.cd4().getId(); // Circ desk 1: Third floor val thirdFloorCd1 = locationsFixture.thirdFloor(); - val planetThirdFloorCd1 = itemsFixture.basedUponSmallAngryPlanet( + val temeraireThirdFloorCd1 = itemsFixture.basedUponTemeraire( holdingBuilder -> holdingBuilder .withPermanentLocation(thirdFloorCd1) .withNoTemporaryLocation(), @@ -360,15 +451,6 @@ void responseDoesNotIncludePickSlipsFromDifferentServicePoint() { .withNoPermanentLocation() .withNoTemporaryLocation()); - val charlotte = usersFixture.charlotte(); - - val requestForThirdFloorCd1 = requestsFixture.place(new RequestBuilder() - .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) - .page() - .withPickupServicePointId(circDesk1) - .forItem(planetThirdFloorCd1) - .by(charlotte)); - // Circ desk 4: Second floor val secondFloorCd4 = locationsFixture.fourthServicePoint(); val planetSecondFloorCd4 = itemsFixture.basedUponSmallAngryPlanet( @@ -379,32 +461,46 @@ void responseDoesNotIncludePickSlipsFromDifferentServicePoint() { .withNoPermanentLocation() .withNoTemporaryLocation()); - val jessica = usersFixture.jessica(); + if (slipsType == SlipsType.SEARCH_SLIPS) { + checkOutFixture.checkOutByBarcode(temeraireThirdFloorCd1); + checkOutFixture.checkOutByBarcode(planetSecondFloorCd4); + } + + val charlotte = usersFixture.charlotte(); + val steve = usersFixture.steve(); + + val requestForThirdFloorCd1 = requestsFixture.place(new RequestBuilder() + .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) + .withRequestType(slipsType.getRequestType().getValue()) + .withPickupServicePointId(circDesk1) + .forItem(temeraireThirdFloorCd1) + .by(charlotte)); val requestForSecondFloorCd4 = requestsFixture.place(new RequestBuilder() .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) - .page() + .withRequestType(slipsType.getRequestType().getValue()) .withPickupServicePointId(circDesk1) .forItem(planetSecondFloorCd4) - .by(jessica)); + .by(steve)); // response for Circ Desk 1 - val responseForCd1 = ResourceClient.forPickSlips().getById(circDesk1); + val responseForCd1 = slipsType.get(circDesk1); assertThat(responseForCd1.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(responseForCd1, 1); - assertResponseContains(responseForCd1, planetThirdFloorCd1, requestForThirdFloorCd1, charlotte); + assertResponseHasItems(responseForCd1, 1, slipsType); + assertResponseContains(responseForCd1, slipsType, temeraireThirdFloorCd1, requestForThirdFloorCd1, charlotte); // response for Circ Desk 4 - val responseForCd4 = ResourceClient.forPickSlips().getById(circDesk4); + val responseForCd4 = slipsType.get(circDesk4); assertThat(responseForCd4.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(responseForCd4, 1); - assertResponseContains(responseForCd4, planetSecondFloorCd4, requestForSecondFloorCd4, jessica); + assertResponseHasItems(responseForCd4, 1, slipsType); + assertResponseContains(responseForCd4, slipsType, planetSecondFloorCd4, requestForSecondFloorCd4, steve); } - @Test - void responseContainsPickSlipsWhenServicePointHasManyLocations() { + @ParameterizedTest + @EnumSource(value = SlipsType.class) + void responseContainsSlipsWhenServicePointHasManyLocations(SlipsType slipsType) { final UUID servicePointId = servicePointsFixture.cd1().getId(); final int numberOfLocations = 100; @@ -428,34 +524,38 @@ void responseContainsPickSlipsWhenServicePointHasManyLocations() { RequestBuilder pageRequestBuilder = new RequestBuilder() .withStatus(RequestStatus.OPEN_NOT_YET_FILLED.getValue()) - .page() + .withRequestType(slipsType.getRequestType().getValue()) .withPickupServicePointId(servicePointId) .forItem(item) .by(james); + if (slipsType == SlipsType.SEARCH_SLIPS) { + checkOutFixture.checkOutByBarcode(item); + } + val pageRequest = requestsClient.create(pageRequestBuilder); - val response = ResourceClient.forPickSlips().getById(servicePointId); + val response = slipsType.get(servicePointId); assertThat(response.getStatusCode(), is(HTTP_OK)); - assertResponseHasItems(response, 1); - assertResponseContains(response, item, pageRequest, james); + assertResponseHasItems(response, 1, slipsType); + assertResponseContains(response, slipsType, item, pageRequest, james); } private void assertDatetimeEquivalent(ZonedDateTime firstDateTime, ZonedDateTime secondDateTime) { assertThat(firstDateTime.compareTo(secondDateTime), is(0)); } - private void assertResponseHasItems(Response response, int itemsCount) { + private void assertResponseHasItems(Response response, int itemsCount, SlipsType slipsType) { JsonObject responseJson = response.getJson(); - assertThat(responseJson.getJsonArray(PICK_SLIPS_KEY).size(), is(itemsCount)); + assertThat(responseJson.getJsonArray(slipsType.getCollectionName()).size(), is(itemsCount)); assertThat(responseJson.getInteger(TOTAL_RECORDS), is(itemsCount)); } - private void assertResponseContains(Response response, ItemResource item, + private void assertResponseContains(Response response, SlipsType slipsType, ItemResource item, IndividualResource request, UserResource requester) { - long count = getPickSlipsStream(response) + long count = getSlipsStream(response, slipsType) .filter(ps -> item.getBarcode().equals( getNestedStringProperty(ps, ITEM_KEY, "barcode")) @@ -476,16 +576,33 @@ private void assertResponseContains(Response response, ItemResource item, } } - private Stream getPickSlipsStream(Response response) { - return JsonObjectArrayPropertyFetcher.toStream(response.getJson(), PICK_SLIPS_KEY); + private Stream getSlipsStream(Response response, SlipsType slipsType) { + return JsonObjectArrayPropertyFetcher.toStream(response.getJson(), slipsType.getCollectionName()); } - private List getPickSlipsList(Response response) { - return getPickSlipsStream(response) + private List getPickSlipsList(Response response, SlipsType slipsType) { + return getSlipsStream(response, slipsType) .collect(Collectors.toList()); } private String getName(JsonObject jsonObject) { return jsonObject.getString("name"); } + + @AllArgsConstructor + private enum SlipsType { + PICK_SLIPS(ResourceClient.forPickSlips(), "pickSlips", PAGE), + SEARCH_SLIPS(ResourceClient.forSearchSlips(), "searchSlips", HOLD); + + private final ResourceClient client; + @Getter + private final String collectionName; + @Getter + private final RequestType requestType; + + private Response get(UUID servicePointId) { + return client.getById(servicePointId); + } + + } }