Skip to content

Commit

Permalink
CIRC-1933 Implementation of search slips API
Browse files Browse the repository at this point in the history
  • Loading branch information
OleksandrVidinieiev committed Nov 30, 2023
1 parent 4bad050 commit ce45970
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 198 deletions.
19 changes: 18 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"circulation.search-slips.get"
],
"modulePermissions": [
"modperms.circulation.search-slips.get"
]
}
]
Expand Down Expand Up @@ -1556,7 +1557,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"
]
},
{
Expand Down Expand Up @@ -2320,6 +2322,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)",
Expand Down
8 changes: 4 additions & 4 deletions ramls/staff-slips.raml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#%RAML 1.0
title: Stuff Slips
title: Staff Slips
version: v0.3
protocols: [ HTTP, HTTPS ]
baseUri: http://localhost:9130
Expand All @@ -9,7 +9,7 @@ documentation:
content: <b>API for staff slips generation</b>

types:
stuff-slips: !include staff-slips-response.json
staff-slips: !include staff-slips-response.json

traits:
language: !include raml-util/traits/language.raml
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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<ItemStatus, Boolean> recallRules;
Expand Down Expand Up @@ -161,4 +162,13 @@ public static List<RequestType> getRequestTypesAllowedForItemStatus(ItemStatus i
.filter(requestType -> requestsRulesMap.get(requestType).get(itemStatus))
.toList();
}

public static List<ItemStatus> getItemStatusesAllowedForRequestType(RequestType requestType) {
return requestsRulesMap.get(requestType)
.entrySet()
.stream()
.filter(entry -> Boolean.TRUE.equals(entry.getValue()))
.map(Map.Entry::getKey)
.toList();
}
}
135 changes: 3 additions & 132 deletions src/main/java/org/folio/circulation/resources/PickSlipsResource.java
Original file line number Diff line number Diff line change
@@ -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<Result<Collection<Item>>> fetchPagedItemsForLocations(
MultipleRecords<Location> multipleLocations,
ItemRepository itemRepository, LocationRepository locationRepository) {

log.debug("fetchPagedItemsForLocations:: parameters multipleLocations: {}",
() -> multipleRecordsAsString(multipleLocations));
Collection<Location> locations = multipleLocations.getRecords();

Set<String> 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<CqlQuery> 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<Result<MultipleRecords<Request>>> fetchOpenPageRequestsForItems(
Collection<Item> items, Clients clients) {

Set<String> 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<CqlQuery> typeQuery = exactMatch(REQUEST_TYPE_KEY, RequestType.PAGE.getValue());
final Result<CqlQuery> statusQuery = exactMatch(STATUS_KEY, RequestStatus.OPEN_NOT_YET_FILLED.getValue());
final Result<CqlQuery> 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, PAGE, PAGED, "pickSlips");
}
}
Original file line number Diff line number Diff line change
@@ -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<Result<MultipleRecords<Request>>> fetchHoldRequests() {
return ofAsync(MultipleRecords.empty());
super(rootPath, client, HOLD, getItemStatusesAllowedForRequestType(HOLD), "searchSlips");
}
}
Loading

0 comments on commit ce45970

Please sign in to comment.