diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index 37fcbce920..c7cd96fe07 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -132,8 +132,7 @@ "methods": ["POST"], "pathPattern": "/circulation/loans/{id}/change-due-date", "permissionsRequired": [ - "circulation.loans.change-due-date.post", - "configuration.entries.collection.get" + "circulation.loans.change-due-date.post" ], "modulePermissions": [ "modperms.circulation.loans.change-due-date.post" @@ -167,6 +166,22 @@ } ] }, + { + "id": "instance-items", + "version": "0.1", + "handlers": [ + { + "methods": ["GET"], + "pathPattern": "/circulation/items-by-instance", + "permissionsRequired": [ + "circulation.items-by-instance.get" + ], + "modulePermissions": [ + "search.instances.collection.get" + ] + } + ] + }, { "id": "add-info", "version": "0.1", @@ -649,7 +664,7 @@ }, { "id": "allowed-service-points", - "version": "1.0", + "version": "1.2", "handlers": [ { "methods": [ @@ -677,7 +692,10 @@ "inventory-storage.instances.item.get", "inventory-storage.instances.collection.get", "configuration.entries.item.get", - "configuration.entries.collection.get" + "configuration.entries.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation" ] } ] @@ -1577,6 +1595,11 @@ "displayName": "circulation settings - Read configuration", "description": "To read the configuration from mod settings." }, + { + "permissionName": "circulation.items-by-instance.get", + "displayName": "circulation - get items by instance", + "description": "get items by instance" + }, { "permissionName": "circulation.settings.collection.get", "displayName": "circulation - get circulation settings", @@ -1643,7 +1666,8 @@ "circulation.requests.allowed-service-points.get", "circulation.inventory.items-in-transit-report.get", "circulation.pick-slips.get", - "circulation.search-slips.get" + "circulation.search-slips.get", + "circulation.items-by-instance.get" ] }, { @@ -1747,6 +1771,9 @@ "proxiesfor.collection.get", "patron-notice.post", "configuration.entries.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation", "calendar.endpoint.dates.get", "pubsub.publish.post", "circulation-storage.loans-history.collection.get" @@ -1800,7 +1827,8 @@ "checkout-lock-storage.checkout-locks.item.delete", "mod-settings.entries.collection.get", "mod-settings.entries.item.get", - "mod-settings.global.read.mod-circulation" + "mod-settings.global.read.mod-circulation", + "mod-settings.global.read.circulation" ], "visible": false }, @@ -1861,7 +1889,10 @@ "actual-cost-fee-fine-cancel.post", "departments.item.get", "departments.collection.get", - "circulation-storage.loans-history.collection.get" + "circulation-storage.loans-history.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation" ], "visible": false }, @@ -1893,6 +1924,9 @@ "proxiesfor.collection.get", "calendar.endpoint.dates.get", "configuration.entries.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation", "scheduled-notice-storage.scheduled-notices.collection.delete", "scheduled-notice-storage.scheduled-notices.item.post", "patron-notice.post", @@ -2077,6 +2111,9 @@ "scheduled-notice-storage.scheduled-notices.collection.delete", "scheduled-notice-storage.scheduled-notices.item.post", "configuration.entries.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation", "manualblocks.collection.get", "pubsub.publish.post", "automated-patron-blocks.collection.get", @@ -2157,6 +2194,9 @@ "proxiesfor.collection.get", "patron-notice.post", "configuration.entries.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation", "scheduled-notice-storage.scheduled-notices.collection.delete", "scheduled-notice-storage.scheduled-notices.item.post", "pubsub.publish.post", @@ -2200,7 +2240,10 @@ "addresstypes.collection.get", "usergroups.collection.get", "usergroups.item.get", - "pubsub.publish.post" + "pubsub.publish.post", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation" ], "visible": false }, @@ -2235,6 +2278,9 @@ "patron-notice.post", "calendar.endpoint.dates.get", "configuration.entries.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation", "scheduled-notice-storage.scheduled-notices.collection.delete", "scheduled-notice-storage.scheduled-notices.item.post", "manualblocks.collection.get", @@ -2293,7 +2339,10 @@ "addresstypes.collection.get", "pubsub.publish.post", "patron-notice.post", - "circulation-storage.loans-history.collection.get" + "circulation-storage.loans-history.collection.get", + "mod-settings.entries.item.get", + "mod-settings.entries.collection.get", + "mod-settings.global.read.circulation" ], "visible": false }, diff --git a/ramls/circulation.raml b/ramls/circulation.raml index ff2081a21b..1b527ed222 100644 --- a/ramls/circulation.raml +++ b/ramls/circulation.raml @@ -343,6 +343,14 @@ resourceTypes: description: "Instance ID" pattern: "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[1-5][a-fA-F0-9]{3}-[89abAB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$" required: false + useStubItem: + description: "When true, allows to apply circulation rules based on patron group only" + type: boolean + required: false + ecsRequestRouting: + description: "When true, returns only service points with ecsRequestRouting" + type: boolean + required: false responses: 200: description: "List of allowed service points was retrieved successfully" @@ -364,4 +372,4 @@ resourceTypes: description: "Internal server error" body: text/plain: - example: "Internal server error" \ No newline at end of file + example: "Internal server error" diff --git a/src/main/java/org/folio/circulation/CirculationVerticle.java b/src/main/java/org/folio/circulation/CirculationVerticle.java index f0d2371440..e1dc08ffe6 100644 --- a/src/main/java/org/folio/circulation/CirculationVerticle.java +++ b/src/main/java/org/folio/circulation/CirculationVerticle.java @@ -21,6 +21,7 @@ import org.folio.circulation.resources.FeeFineNotRealTimeScheduledNoticeProcessingResource; import org.folio.circulation.resources.FeeFineScheduledNoticeProcessingResource; import org.folio.circulation.resources.HealthResource; +import org.folio.circulation.resources.ItemsByInstanceResource; import org.folio.circulation.resources.ItemsInTransitResource; import org.folio.circulation.resources.LoanAnonymizationResource; import org.folio.circulation.resources.LoanCirculationRulesEngineResource; @@ -93,6 +94,7 @@ public void start(Promise startFuture) { new RequestCollectionResource(client).register(router); new RequestQueueResource(client).register(router); new RequestByInstanceIdResource(client).register(router); + new ItemsByInstanceResource(client).register(router); new RequestHoldShelfClearanceResource( "/circulation/requests-reports/hold-shelf-clearance/:servicePointId", client) diff --git a/src/main/java/org/folio/circulation/domain/AllowedServicePointsRequest.java b/src/main/java/org/folio/circulation/domain/AllowedServicePointsRequest.java index 1e4bc603ef..16ea46f1ea 100644 --- a/src/main/java/org/folio/circulation/domain/AllowedServicePointsRequest.java +++ b/src/main/java/org/folio/circulation/domain/AllowedServicePointsRequest.java @@ -19,6 +19,9 @@ public class AllowedServicePointsRequest { private String requesterId; private String instanceId; private String itemId; + private String requestId; + private boolean useStubItem; + private boolean ecsRequestRouting; public boolean isForTitleLevelRequest() { return instanceId != null; @@ -27,7 +30,6 @@ public boolean isForTitleLevelRequest() { public boolean isForItemLevelRequest() { return itemId != null; } - private String requestId; public AllowedServicePointsRequest updateWithRequestInformation(Request request) { log.debug("updateWithRequestInformation:: parameters request: {}", request); diff --git a/src/main/java/org/folio/circulation/domain/Item.java b/src/main/java/org/folio/circulation/domain/Item.java index c27a296ef1..926a348be1 100644 --- a/src/main/java/org/folio/circulation/domain/Item.java +++ b/src/main/java/org/folio/circulation/domain/Item.java @@ -415,4 +415,15 @@ public String getLendingLibraryCode() { public String getDcbItemTitle() { return getProperty(itemRepresentation, "instanceTitle"); } + + public String getTenantId() { + return getProperty(itemRepresentation, "tenantId"); + } + + public Item changeTenantId(String tenantId) { + if (itemRepresentation != null) { + write(itemRepresentation, "tenantId", tenantId); + } + return this; + } } diff --git a/src/main/java/org/folio/circulation/domain/MoveRequestService.java b/src/main/java/org/folio/circulation/domain/MoveRequestService.java index 3077016093..d13be99cb9 100644 --- a/src/main/java/org/folio/circulation/domain/MoveRequestService.java +++ b/src/main/java/org/folio/circulation/domain/MoveRequestService.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.Logger; import org.folio.circulation.domain.validation.RequestLoanValidator; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.requests.RequestPolicyRepository; import org.folio.circulation.infrastructure.storage.requests.RequestQueueRepository; import org.folio.circulation.infrastructure.storage.requests.RequestRepository; @@ -27,13 +28,14 @@ public class MoveRequestService { private final ConfigurationRepository configurationRepository; private final EventPublisher eventPublisher; private final RequestQueueRepository requestQueueRepository; + private final SettingsRepository settingsRepository; private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public MoveRequestService(RequestRepository requestRepository, RequestPolicyRepository requestPolicyRepository, UpdateUponRequest updateUponRequest, MoveRequestProcessAdapter moveRequestHelper, RequestLoanValidator requestLoanValidator, RequestNoticeSender requestNoticeSender, ConfigurationRepository configurationRepository, EventPublisher eventPublisher, - RequestQueueRepository requestQueueRepository) { + RequestQueueRepository requestQueueRepository, SettingsRepository settingsRepository) { this.requestRepository = requestRepository; this.requestPolicyRepository = requestPolicyRepository; @@ -44,11 +46,12 @@ public MoveRequestService(RequestRepository requestRepository, RequestPolicyRepo this.configurationRepository = configurationRepository; this.eventPublisher = eventPublisher; this.requestQueueRepository = requestQueueRepository; + this.settingsRepository = settingsRepository; } public CompletableFuture> moveRequest( RequestAndRelatedRecords requestAndRelatedRecords, Request originalRequest) { - return configurationRepository.lookupTlrSettings() + return settingsRepository.lookupTlrSettings() .thenApply(r -> r.map(requestAndRelatedRecords::withTlrSettings)) .thenApply(r -> r.next(RequestServiceUtility::refuseTlrProcessingWhenFeatureIsDisabled)) .thenApply(r -> r.next(records -> RequestServiceUtility.refuseMovingToOrFromHoldTlr(records, diff --git a/src/main/java/org/folio/circulation/domain/SearchInstance.java b/src/main/java/org/folio/circulation/domain/SearchInstance.java new file mode 100644 index 0000000000..809a121181 --- /dev/null +++ b/src/main/java/org/folio/circulation/domain/SearchInstance.java @@ -0,0 +1,50 @@ +package org.folio.circulation.domain; + +import static org.folio.circulation.support.json.JsonObjectArrayPropertyFetcher.mapToList; +import static org.folio.circulation.support.json.JsonPropertyWriter.write; + +import java.lang.invoke.MethodHandles; +import java.util.Collection; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.circulation.domain.representations.ItemSummaryRepresentation; +import org.folio.circulation.storage.mappers.ItemMapper; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import lombok.NonNull; +import lombok.ToString; +import lombok.Value; + +@Value +@ToString(onlyExplicitlyIncluded = true) +public class SearchInstance { + + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + JsonObject representation; + String id; + @NonNull Collection items; + + public static SearchInstance from(JsonObject representation) { + return new SearchInstance(representation, representation.getString("id"), mapItems(representation)); + } + + private static List mapItems(JsonObject representation) { + return mapToList(representation, "items", new ItemMapper()::toDomain); + } + + public SearchInstance changeItems(Collection items) { + JsonArray itemsArray = new JsonArray(); + for (Item item : items) { + itemsArray.add(new ItemSummaryRepresentation().createItemSummary(item)); + } + write(representation, "items", itemsArray); + return new SearchInstance(representation, id, items); + } + + public JsonObject toJson() { + return representation; + } +} diff --git a/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java b/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java index efb1db2699..3e6c89dde3 100644 --- a/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java +++ b/src/main/java/org/folio/circulation/domain/representations/ItemSummaryRepresentation.java @@ -44,6 +44,7 @@ public JsonObject createItemSummary(Item item) { write(itemSummary, "copyNumber", item.getCopyNumber()); write(itemSummary, CALL_NUMBER_COMPONENTS, createCallNumberComponents(item.getCallNumberComponents())); + write(itemSummary, "tenantId", item.getTenantId()); JsonObject status = new JsonObject() .put("name", item.getStatus().getValue()); diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java index 52a974504d..339c280513 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/ConfigurationRepository.java @@ -12,7 +12,6 @@ import org.folio.circulation.domain.ConfigurationService; import org.folio.circulation.domain.MultipleRecords; import org.folio.circulation.domain.anonymization.config.LoanAnonymizationConfiguration; -import org.folio.circulation.domain.configuration.TlrSettingsConfiguration; import org.folio.circulation.support.Clients; import org.folio.circulation.support.GetManyRecordsClient; import org.folio.circulation.support.http.client.CqlQuery; @@ -49,13 +48,6 @@ public CompletableFuture> lookupSessionTimeout() { return lookupConfigurations(otherSettingsQuery, applySessionTimeout()); } - public CompletableFuture> lookupTlrSettings() { - Result queryResult = defineModuleNameAndConfigNameFilter( - "SETTINGS", "TLR"); - - return findAndMapFirstConfiguration(queryResult, TlrSettingsConfiguration::from); - } - /** * Gets loan history tenant configuration - settings for loan anonymization * @@ -123,26 +115,4 @@ private Function, Integer> applySessionTimeout() .findSessionTimeout(configurations.getRecords()); } - /** - * Find first configuration and maps it to an object with a provided mapper - */ - private CompletableFuture> findAndMapFirstConfiguration( - Result cqlQueryResult, Function mapper) { - - return cqlQueryResult - .after(query -> configurationClient.getMany(query, DEFAULT_PAGE_LIMIT)) - .thenApply(result -> result.next(r -> from(r, Configuration::new, CONFIGS_KEY))) - .thenApply(result -> result.map(this::findFirstConfigurationAsJsonObject)) - .thenApply(result -> result.map(mapper)); - } - - private JsonObject findFirstConfigurationAsJsonObject( - MultipleRecords configurations) { - - return configurations.getRecords().stream() - .findFirst() - .map(Configuration::getValue) - .map(JsonObject::new) - .orElse(new JsonObject()); - } } diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/SearchRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/SearchRepository.java new file mode 100644 index 0000000000..a999c2158d --- /dev/null +++ b/src/main/java/org/folio/circulation/infrastructure/storage/SearchRepository.java @@ -0,0 +1,93 @@ +package org.folio.circulation.infrastructure.storage; + +import static org.folio.circulation.support.StringUtil.urlEncode; +import static org.folio.circulation.support.results.Result.failed; +import static org.folio.circulation.support.results.ResultBinding.flatMapResult; +import static org.folio.circulation.support.results.ResultBinding.mapResult; + +import java.lang.invoke.MethodHandles; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.circulation.domain.Item; +import org.folio.circulation.domain.MultipleRecords; +import org.folio.circulation.domain.SearchInstance; +import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; +import org.folio.circulation.support.AsyncCoordinationUtil; +import org.folio.circulation.support.BadRequestFailure; +import org.folio.circulation.support.Clients; +import org.folio.circulation.support.CollectionResourceClient; +import org.folio.circulation.support.http.client.Response; +import org.folio.circulation.support.http.server.WebContext; +import org.folio.circulation.support.results.Result; + +import io.vertx.core.http.HttpClient; +import lombok.AllArgsConstructor; + +@AllArgsConstructor +public class SearchRepository { + + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + private final WebContext webContext; + private final HttpClient httpClient; + private final CollectionResourceClient searchClient; + + public SearchRepository(WebContext webContext, HttpClient httpClient) { + this.webContext = webContext; + this.httpClient = httpClient; + this.searchClient = Clients.create(webContext, httpClient).searchClient(); + } + + public CompletableFuture> getInstanceWithItems(List queryParams) { + log.debug("getInstanceWithItems:: query {}", queryParams); + if (queryParams.isEmpty()) { + return CompletableFuture.completedFuture(failed(new BadRequestFailure( + "query is empty"))); + } + return searchClient.getManyWithQueryStringParameters(Map.of("expandAll", + "true", "query", urlEncode(queryParams.get(0)))) + .thenApply(flatMapResult(this::mapResponseToInstances)) + .thenApply(mapResult(MultipleRecords::firstOrNull)) + .thenCompose(r -> r.after(this::updateItemDetails)); + } + + private Result> mapResponseToInstances(Response response) { + return MultipleRecords.from(response, SearchInstance::from, "instances"); + } + + private CompletableFuture> updateItemDetails(SearchInstance searchInstance) { + log.debug("updateItemDetails:: searchInstance {}", () -> searchInstance); + if (searchInstance == null) { + return CompletableFuture.completedFuture(failed(new BadRequestFailure( + "Search result is empty"))); + } + + Map> itemsByTenant = searchInstance.getItems() + .stream() + .collect(Collectors.groupingBy(Item::getTenantId)); + + log.info("updateItemDetails:: fetching item details from tenants: {}", itemsByTenant::keySet); + + return AsyncCoordinationUtil.allOf(itemsByTenant, this::fetchItemDetails) + .thenApply(r -> r.map(lists -> lists.stream().flatMap(Collection::stream).toList())) + .thenApply(r -> r.map(searchInstance::changeItems)); + } + + private CompletableFuture>> fetchItemDetails(String tenantId, List items) { + ItemRepository itemRepository = new ItemRepository(Clients.create(webContext, httpClient, tenantId)); + + return AsyncCoordinationUtil.allOf(items, item -> fetchItemDetails(item, itemRepository)); + } + + private CompletableFuture> fetchItemDetails(Item searchItem, + ItemRepository itemRepository) { + + return itemRepository.fetchById(searchItem.getItemId()) + .thenApply(r -> r.map(item -> item.changeTenantId(searchItem.getTenantId()))); + } +} diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/ServicePointRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/ServicePointRepository.java index fe9ec5c7e1..7225491566 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/ServicePointRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/ServicePointRepository.java @@ -41,7 +41,15 @@ public class ServicePointRepository { private final CollectionResourceClient servicePointsStorageClient; public ServicePointRepository(Clients clients) { - servicePointsStorageClient = clients.servicePointsStorage(); + this(clients, false); + } + + public ServicePointRepository(Clients clients, boolean includeRoutingServicePoints) { + if (includeRoutingServicePoints) { + servicePointsStorageClient = clients.routingServicePointsStorage(); + } else { + servicePointsStorageClient = clients.servicePointsStorage(); + } } public CompletableFuture> getServicePointById(UUID id) { @@ -206,22 +214,24 @@ public CompletableFuture>> findServicePointsById .thenApply(r -> r.map(MultipleRecords::getRecords)); } - public CompletableFuture>> fetchPickupLocationServicePoints() { + public CompletableFuture>> fetchServicePointsByIndexName( + String indexName) { + return createServicePointsFetcher().find(MultipleCqlIndexValuesCriteria.builder() - .indexName("pickupLocation") + .indexName(indexName) .indexOperator(CqlQuery::matchAny) .value("true") .build()) .thenApply(r -> r.map(MultipleRecords::getRecords)); } - public CompletableFuture>> fetchPickupLocationServicePointsByIds( - Set ids) { + public CompletableFuture>> + fetchPickupLocationServicePointsByIdsAndIndexName(Set ids, String indexName) { - log.debug("filterIdsByServicePointsAndPickupLocationExistence:: parameters ids: {}", - () -> collectionAsString(ids)); + log.debug("filterIdsByServicePointsAndPickupLocationExistence:: parameters ids: {}, " + + "indexName: {}", () -> collectionAsString(ids), () -> indexName); - Result pickupLocationQuery = exactMatch("pickupLocation", "true"); + Result pickupLocationQuery = exactMatch(indexName, "true"); return createServicePointsFetcher().findByIdIndexAndQuery(ids, "id", pickupLocationQuery) .thenApply(r -> r.map(MultipleRecords::getRecords)); diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java index 89f2e845ba..d5b2e32b84 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/SettingsRepository.java @@ -6,6 +6,7 @@ import org.folio.circulation.domain.Configuration; import org.folio.circulation.domain.MultipleRecords; import org.folio.circulation.domain.configuration.CheckoutLockConfiguration; +import org.folio.circulation.domain.configuration.TlrSettingsConfiguration; import org.folio.circulation.support.Clients; import org.folio.circulation.support.GetManyRecordsClient; import org.folio.circulation.support.http.client.CqlQuery; @@ -13,9 +14,13 @@ import org.folio.circulation.support.results.Result; import java.lang.invoke.MethodHandles; +import java.util.Collection; +import java.util.List; import java.util.concurrent.CompletableFuture; +import static java.util.function.Function.identity; 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; public class SettingsRepository { @@ -27,14 +32,10 @@ public SettingsRepository(Clients clients) { } public CompletableFuture> lookUpCheckOutLockSettings() { + log.debug("lookUpCheckOutLockSettings:: fetching checkout lock settings"); try { - log.debug("lookUpCheckOutLockSettings:: fetching checkout lock settings"); - final Result moduleQuery = exactMatch("scope", "mod-circulation"); - final Result configNameQuery = exactMatch("key", "checkoutLockFeature"); - - return moduleQuery.combine(configNameQuery, CqlQuery::and) - .after(cqlQuery -> settingsClient.getMany(cqlQuery, PageLimit.noLimit())) - .thenApply(result -> result.next(response -> MultipleRecords.from(response, Configuration::new, "items"))) + return fetchSettings("mod-circulation", "checkoutLockFeature") + .thenApply(r -> r.map(records -> records.mapRecords(Configuration::new))) .thenApply(r -> r.map(r1 -> r1.getRecords().stream().findFirst() .map(Configuration::getValue) .map(JsonObject::new) @@ -49,4 +50,30 @@ public CompletableFuture> lookUpCheckOutLockSe return CompletableFuture.completedFuture(succeeded(CheckoutLockConfiguration.from(new JsonObject()))); } } + + public CompletableFuture> lookupTlrSettings() { + return fetchSettings("circulation", List.of("generalTlr", "regularTlr")) + .thenApply(r -> r.map(SettingsRepository::extractAndMergeValues)) + .thenApply(r -> r.map(TlrSettingsConfiguration::from)); + } + + private CompletableFuture>> fetchSettings(String scope, String key) { + return fetchSettings(scope, List.of(key)); + } + + private CompletableFuture>> fetchSettings(String scope, + Collection keys) { + + return exactMatch("scope", scope) + .combine(exactMatchAny("key", keys), CqlQuery::and) + .after(query -> settingsClient.getMany(query, PageLimit.noLimit())) + .thenApply(r -> r.next(response -> MultipleRecords.from(response, identity(), "items"))); + } + + private static JsonObject extractAndMergeValues(MultipleRecords entries) { + return entries.getRecords() + .stream() + .map(rec -> rec.getJsonObject("value")) + .reduce(new JsonObject(), JsonObject::mergeIn); + } } diff --git a/src/main/java/org/folio/circulation/infrastructure/storage/requests/RequestPolicyRepository.java b/src/main/java/org/folio/circulation/infrastructure/storage/requests/RequestPolicyRepository.java index 60f35c780d..7df5c56de1 100644 --- a/src/main/java/org/folio/circulation/infrastructure/storage/requests/RequestPolicyRepository.java +++ b/src/main/java/org/folio/circulation/infrastructure/storage/requests/RequestPolicyRepository.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.function.BinaryOperator; import java.util.stream.Collectors; @@ -95,6 +96,15 @@ public CompletableFuture>>> lookupRequestPol .thenCompose(r -> r.after(this::lookupRequestPolicies)); } + public CompletableFuture> lookupRequestPolicy(User user) { + // Circulation rules need to be executed with the patron group parameter only. + // All the item-related parameters should be random UUIDs. + return lookupRequestPolicyId(UUID.randomUUID().toString(), user.getPatronGroupId(), + UUID.randomUUID().toString(), UUID.randomUUID().toString()) + .thenCompose(r -> r.after(this::lookupRequestPolicy)) + .thenApply(result -> result.map(RequestPolicy::from)); + } + private BinaryOperator> itemsMergeOperator() { return (items1, items2) -> Stream.concat(items1.stream(), items2.stream()) .collect(Collectors.toSet()); diff --git a/src/main/java/org/folio/circulation/resources/AllowedServicePointsResource.java b/src/main/java/org/folio/circulation/resources/AllowedServicePointsResource.java index 97fee14762..11fa7dff36 100644 --- a/src/main/java/org/folio/circulation/resources/AllowedServicePointsResource.java +++ b/src/main/java/org/folio/circulation/resources/AllowedServicePointsResource.java @@ -54,7 +54,8 @@ private void get(RoutingContext routingContext) { ofAsync(routingContext) .thenApply(r -> r.next(AllowedServicePointsResource::buildRequest)) - .thenCompose(r -> r.after(new AllowedServicePointsService(clients)::getAllowedServicePoints)) + .thenCompose(r -> r.after(request -> new AllowedServicePointsService( + clients, request.isEcsRequestRouting()).getAllowedServicePoints(request))) .thenApply(r -> r.map(AllowedServicePointsResource::toJson)) .thenApply(r -> r.map(JsonHttpResponse::ok)) .exceptionally(CommonFailures::failedDueToServerError) @@ -68,25 +69,12 @@ private static Result buildRequest(RoutingContext r .map(String::toUpperCase) .map(Request.Operation::valueOf) .orElse(null); - - AllowedServicePointsRequest request = new AllowedServicePointsRequest(operation, - queryParams.get("requesterId"), queryParams.get("instanceId"), queryParams.get("itemId"), - queryParams.get("requestId")); - - return validateAllowedServicePointsRequest(request); - } - - private static Result validateAllowedServicePointsRequest( - AllowedServicePointsRequest allowedServicePointsRequest) { - - log.debug("validateAllowedServicePointsRequest:: parameters allowedServicePointsRequest: {}", - allowedServicePointsRequest); - - Request.Operation operation = allowedServicePointsRequest.getOperation(); - String requesterId = allowedServicePointsRequest.getRequesterId(); - String instanceId = allowedServicePointsRequest.getInstanceId(); - String itemId = allowedServicePointsRequest.getItemId(); - String requestId = allowedServicePointsRequest.getRequestId(); + String requesterId = queryParams.get("requesterId"); + String instanceId = queryParams.get("instanceId"); + String itemId = queryParams.get("itemId"); + String requestId = queryParams.get("requestId"); + String useStubItem = queryParams.get("useStubItem"); + String ecsRequestRouting = queryParams.get("ecsRequestRouting"); List errors = new ArrayList<>(); @@ -111,7 +99,8 @@ private static Result validateAllowedServicePointsR log.warn("Request ID is not a valid UUID: {}", requestId); errors.add(String.format("Request ID is not a valid UUID: %s.", requestId)); } - + validateBoolean(useStubItem, "useStubItem", errors); + validateBoolean(ecsRequestRouting, "ecsRequestRouting", errors); // Checking parameter combinations boolean allowedCombinationOfParametersDetected = false; @@ -155,7 +144,16 @@ private static Result validateAllowedServicePointsR return failed(new BadRequestFailure(errorMessage)); } - return succeeded(allowedServicePointsRequest); + return succeeded(new AllowedServicePointsRequest(operation, requesterId, instanceId, itemId, + requestId, Boolean.parseBoolean(useStubItem), Boolean.parseBoolean(ecsRequestRouting))); + } + + private static void validateBoolean(String parameter, String parameterName, List errors) { + if (parameter != null && !"true".equals(parameter) && !"false".equals(parameter)) { + log.warn("validateBoolean:: {} is not a valid boolean: {}", + parameterName, parameter); + errors.add(String.format("%s is not a valid boolean: %s.", parameterName, parameter)); + } } private static JsonObject toJson(Map> allowedServicePoints) { diff --git a/src/main/java/org/folio/circulation/resources/ChangeDueDateResource.java b/src/main/java/org/folio/circulation/resources/ChangeDueDateResource.java index ec49593d85..0e43fcbc24 100644 --- a/src/main/java/org/folio/circulation/resources/ChangeDueDateResource.java +++ b/src/main/java/org/folio/circulation/resources/ChangeDueDateResource.java @@ -3,7 +3,9 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import static org.folio.circulation.domain.representations.ChangeDueDateRequest.DUE_DATE; import static org.folio.circulation.domain.representations.LoanProperties.ITEM_ID; -import static org.folio.circulation.resources.handlers.error.CirculationErrorType.*; +import static org.folio.circulation.resources.handlers.error.CirculationErrorType.FAILED_TO_FETCH_USER; +import static org.folio.circulation.resources.handlers.error.CirculationErrorType.FAILED_TO_FIND_SINGLE_OPEN_LOAN; +import static org.folio.circulation.resources.handlers.error.CirculationErrorType.ITEM_DOES_NOT_EXIST; import static org.folio.circulation.support.ValidationErrorFailure.singleValidationError; import static org.folio.circulation.support.json.JsonPropertyFetcher.getDateTimeProperty; import static org.folio.circulation.support.results.MappingFunctions.toFixedValue; @@ -26,7 +28,7 @@ import org.folio.circulation.domain.representations.ChangeDueDateRequest; import org.folio.circulation.domain.validation.ItemStatusValidator; import org.folio.circulation.domain.validation.LoanValidator; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.loans.OverdueFinePolicyRepository; @@ -80,6 +82,8 @@ private CompletableFuture> processChangeDueDate( final var itemRepository = new ItemRepository(clients); final var userRepository = new UserRepository(clients); final var loanRepository = new LoanRepository(clients, itemRepository, userRepository); + final var settingsRepository = new SettingsRepository(clients); + final WebContext webContext = new WebContext(routingContext); final OkapiPermissions okapiPermissions = OkapiPermissions.from(webContext.getHeaders()); @@ -103,13 +107,12 @@ private CompletableFuture> processChangeDueDate( final LoanNoticeSender loanNoticeSender = LoanNoticeSender.using(clients, loanRepository); - final ConfigurationRepository configurationRepository = new ConfigurationRepository(clients); log.info("starting change due date process for loan {}", request.getLoanId()); return succeeded(request) .after(r -> getExistingLoan(loanRepository, r)) .thenApply(LoanValidator::refuseWhenLoanIsClosed) .thenApply(this::toLoanAndRelatedRecords) - .thenComposeAsync(r -> r.combineAfter(configurationRepository::lookupTlrSettings, + .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTlrSettings, LoanAndRelatedRecords::withTlrSettings)) .thenComposeAsync(r -> r.after(requestQueueRepository::get)) .thenApply(itemStatusValidator::refuseWhenItemStatusDoesNotAllowDueDateChange) diff --git a/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java b/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java index a8e75f09ee..a63e63446b 100644 --- a/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java +++ b/src/main/java/org/folio/circulation/resources/CheckInByBarcodeResource.java @@ -16,6 +16,7 @@ import org.folio.circulation.domain.representations.CheckInByBarcodeResponse; import org.folio.circulation.domain.validation.CheckInValidators; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.requests.RequestQueueRepository; @@ -78,6 +79,7 @@ private void checkIn(RoutingContext routingContext) { final RequestNoticeSender requestNoticeSender = RequestNoticeSender.using(clients); final ConfigurationRepository configurationRepository = new ConfigurationRepository(clients); + final SettingsRepository settingsRepository = new SettingsRepository(clients); refuseWhenLoggedInUserNotPresent(context) .next(notUsed -> checkInRequestResult) @@ -87,7 +89,7 @@ private void checkIn(RoutingContext routingContext) { .withItemStatusBeforeCheckIn(item.getStatus())) .thenApply(checkInValidators::refuseWhenItemIsNotAllowedForCheckIn) .thenApply(checkInValidators::refuseWhenClaimedReturnedIsNotResolved) - .thenComposeAsync(r -> r.combineAfter(configurationRepository::lookupTlrSettings, + .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTlrSettings, CheckInContext::withTlrSettings)) .thenComposeAsync(r -> r.combineAfter(configurationRepository::findTimeZoneConfiguration, CheckInContext::withTimeZone)) diff --git a/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java b/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java index a4f804b70b..94e16b6721 100644 --- a/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java +++ b/src/main/java/org/folio/circulation/resources/CheckOutByBarcodeResource.java @@ -154,7 +154,7 @@ private void checkOut(RoutingContext routingContext) { .thenApply(validators::refuseWhenItemIsAlreadyCheckedOut) .thenApply(validators::refuseWhenItemIsNotAllowedForCheckOut) .thenComposeAsync(validators::refuseWhenItemHasOpenLoans) - .thenComposeAsync(r -> r.combineAfter(configurationRepository::lookupTlrSettings, + .thenComposeAsync(r -> r.combineAfter(settingsRepository::lookupTlrSettings, LoanAndRelatedRecords::withTlrSettings)) .thenComposeAsync(r -> r.after(requestQueueRepository::get)) .thenCompose(validators::refuseWhenRequestedByAnotherPatron) diff --git a/src/main/java/org/folio/circulation/resources/ItemsByInstanceResource.java b/src/main/java/org/folio/circulation/resources/ItemsByInstanceResource.java new file mode 100644 index 0000000000..381808e951 --- /dev/null +++ b/src/main/java/org/folio/circulation/resources/ItemsByInstanceResource.java @@ -0,0 +1,46 @@ +package org.folio.circulation.resources; + +import java.lang.invoke.MethodHandles; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.circulation.domain.SearchInstance; +import org.folio.circulation.infrastructure.storage.SearchRepository; +import org.folio.circulation.support.http.server.JsonHttpResponse; +import org.folio.circulation.support.http.server.WebContext; + +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 class ItemsByInstanceResource extends Resource { + + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + public ItemsByInstanceResource(HttpClient client) { + super(client); + } + + @Override + public void register(Router router) { + router.get("/circulation/items-by-instance") + .handler(this::getInstanceItems); + } + + private void getInstanceItems(RoutingContext routingContext) { + final WebContext context = new WebContext(routingContext); + new SearchRepository(context, client).getInstanceWithItems(routingContext.queryParam("query")) + .thenApply(r -> r.map(this::toJson)) + .thenApply(r -> r.map(JsonHttpResponse::ok)) + .thenAccept(context::writeResultToHttpResponse); + } + + private JsonObject toJson(SearchInstance searchInstance) { + log.debug("toJson:: searchInstance: {}", () -> searchInstance); + if (searchInstance != null) { + return searchInstance.toJson(); + } + return new JsonObject(); + } +} diff --git a/src/main/java/org/folio/circulation/resources/RequestByInstanceIdResource.java b/src/main/java/org/folio/circulation/resources/RequestByInstanceIdResource.java index 67c8409e99..36d9ba7ab8 100644 --- a/src/main/java/org/folio/circulation/resources/RequestByInstanceIdResource.java +++ b/src/main/java/org/folio/circulation/resources/RequestByInstanceIdResource.java @@ -63,7 +63,7 @@ import org.folio.circulation.domain.validation.ProxyRelationshipValidator; import org.folio.circulation.domain.validation.RequestLoanValidator; import org.folio.circulation.domain.validation.ServicePointPickupLocationValidator; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.requests.RequestQueueRepository; import org.folio.circulation.resources.handlers.error.FailFastErrorHandler; @@ -121,7 +121,7 @@ private void createInstanceLevelRequests(RoutingContext routingContext) { final var requestBody = routingContext.getBodyAsJson(); - new ConfigurationRepository(clients).lookupTlrSettings() + new SettingsRepository(clients).lookupTlrSettings() .thenCompose(r -> r.after(config -> buildAndPlaceRequests(clients, eventPublisher, repositories, itemFinder, config, requestBody))) .thenApply(r -> r.map(RequestAndRelatedRecords::getRequest)) diff --git a/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java b/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java index aab1681c96..3f19b9241d 100644 --- a/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java +++ b/src/main/java/org/folio/circulation/resources/RequestCollectionResource.java @@ -30,6 +30,7 @@ import org.folio.circulation.infrastructure.storage.CalendarRepository; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanPolicyRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; @@ -272,6 +273,7 @@ void move(RoutingContext routingContext) { final var loanPolicyRepository = new LoanPolicyRepository(clients); final var requestPolicyRepository = new RequestPolicyRepository(clients); final var configurationRepository = new ConfigurationRepository(clients); + final var settingsRepository = new SettingsRepository(clients); final var updateUponRequest = new UpdateUponRequest(new UpdateItem(itemRepository, new RequestQueueService(requestPolicyRepository, loanPolicyRepository)), @@ -287,7 +289,7 @@ void move(RoutingContext routingContext) { requestRepository, requestPolicyRepository, updateUponRequest, moveRequestProcessAdapter, new RequestLoanValidator(new ItemByInstanceIdFinder(clients.holdingsStorage(), itemRepository), loanRepository), RequestNoticeSender.using(clients), configurationRepository, eventPublisher, - requestQueueRepository); + requestQueueRepository, settingsRepository); fromFutureResult(requestRepository.getById(id)) .map(request -> request.withOperation(Request.Operation.MOVE)) diff --git a/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java b/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java index 255b015535..04bc011498 100644 --- a/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java +++ b/src/main/java/org/folio/circulation/resources/RequestFromRepresentationService.java @@ -66,6 +66,7 @@ import org.folio.circulation.domain.validation.ServicePointPickupLocationValidator; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.HoldingsRepository; import org.folio.circulation.infrastructure.storage.inventory.InstanceRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; @@ -95,6 +96,7 @@ class RequestFromRepresentationService { private final LoanRepository loanRepository; private final ServicePointRepository servicePointRepository; private final ConfigurationRepository configurationRepository; + private final SettingsRepository settingsRepository; private final RequestPolicyRepository requestPolicyRepository; private final ProxyRelationshipValidator proxyRelationshipValidator; private final ServicePointPickupLocationValidator pickupLocationValidator; @@ -118,6 +120,7 @@ public RequestFromRepresentationService(Request.Operation operation, this.loanRepository = repositories.getLoanRepository(); this.servicePointRepository = repositories.getServicePointRepository(); this.configurationRepository = repositories.getConfigurationRepository(); + this.settingsRepository = repositories.getSettingsRepository(); this.requestPolicyRepository = repositories.getRequestPolicyRepository(); this.proxyRelationshipValidator = proxyRelationshipValidator; @@ -129,7 +132,7 @@ public RequestFromRepresentationService(Request.Operation operation, CompletableFuture> getRequestFrom(JsonObject representation) { - return configurationRepository.lookupTlrSettings() + return settingsRepository.lookupTlrSettings() .thenCompose(r -> r.after(tlrSettings -> initRequest(operation, tlrSettings, representation))) .thenApply(r -> r.next(this::validateStatus)) .thenApply(r -> r.next(this::validateRequestLevel)) diff --git a/src/main/java/org/folio/circulation/resources/RequestQueueResource.java b/src/main/java/org/folio/circulation/resources/RequestQueueResource.java index 3696483058..430684b83d 100644 --- a/src/main/java/org/folio/circulation/resources/RequestQueueResource.java +++ b/src/main/java/org/folio/circulation/resources/RequestQueueResource.java @@ -27,6 +27,7 @@ import org.folio.circulation.infrastructure.storage.CalendarRepository; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.loans.LoanRepository; import org.folio.circulation.infrastructure.storage.requests.RequestQueueRepository; @@ -125,6 +126,7 @@ private void reorderQueue(RoutingContext routingContext, RequestQueueType reques final var requestRepository = RequestRepository.using(clients, itemRepository, userRepository, loanRepository); final var configurationRepository = new ConfigurationRepository(clients); + final var settingsRepository = new SettingsRepository(clients); final var requestQueueRepository = new RequestQueueRepository(requestRepository); final UpdateRequestQueue updateRequestQueue = new UpdateRequestQueue( @@ -133,7 +135,7 @@ requestQueueRepository, requestRepository, new ServicePointRepository(clients), getRequestQueueByType(routingContext, requestQueueType, requestQueueRepository); - validateTlrFeatureStatus(configurationRepository, requestQueueType, idParamValue) + validateTlrFeatureStatus(settingsRepository, requestQueueType, idParamValue) .thenCompose(r -> r.after(tlrSettings -> getRequestQueueByType(routingContext, requestQueueType, requestQueueRepository))) .thenApply(r -> r.map(reorderContext::withRequestQueue)) @@ -152,10 +154,10 @@ requestQueueRepository, requestRepository, new ServicePointRepository(clients), } private CompletableFuture> validateTlrFeatureStatus( - ConfigurationRepository configurationRepository, RequestQueueType requestQueueType, + SettingsRepository settingsRepository, RequestQueueType requestQueueType, String idParamValue) { - return configurationRepository.lookupTlrSettings() + return settingsRepository.lookupTlrSettings() .thenApply(r -> r.failWhen( tlrSettings -> succeeded( requestQueueType == FOR_INSTANCE ^ tlrSettings.isTitleLevelRequestsFeatureEnabled()), diff --git a/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java b/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java index 4d60a026aa..e7e278c757 100644 --- a/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java +++ b/src/main/java/org/folio/circulation/resources/renewal/RenewalResource.java @@ -70,6 +70,7 @@ import org.folio.circulation.infrastructure.storage.AutomatedPatronBlocksRepository; import org.folio.circulation.infrastructure.storage.CalendarRepository; import org.folio.circulation.infrastructure.storage.ConfigurationRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineOwnerRepository; import org.folio.circulation.infrastructure.storage.feesandfines.FeeFineRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; @@ -150,6 +151,7 @@ private void renew(RoutingContext routingContext) { final LoanRepresentation loanRepresentation = new LoanRepresentation(); final ConfigurationRepository configurationRepository = new ConfigurationRepository(clients); + final SettingsRepository settingsRepository = new SettingsRepository(clients); final LoanScheduledNoticeService scheduledNoticeService = LoanScheduledNoticeService.using(clients); final ReminderFeeScheduledNoticeService scheduledRemindersService = new ReminderFeeScheduledNoticeService(clients); @@ -187,7 +189,7 @@ private void renew(RoutingContext routingContext) { .thenCompose(r -> r.after(ctx -> lookupOverdueFinePolicy(ctx, overdueFinePolicyRepository, errorHandler))) .thenComposeAsync(r -> r.after(ctx -> blockRenewalOfItemsWithReminderFees(ctx, errorHandler))) .thenCompose(r -> r.after(ctx -> lookupLoanPolicy(ctx, loanPolicyRepository, errorHandler))) - .thenCompose(r -> r.combineAfter(configurationRepository::lookupTlrSettings, + .thenCompose(r -> r.combineAfter(settingsRepository::lookupTlrSettings, RenewalContext::withTlrSettings)) .thenComposeAsync(r -> r.after( ctx -> lookupRequestQueue(ctx, requestQueueRepository, errorHandler))) diff --git a/src/main/java/org/folio/circulation/services/AllowedServicePointsService.java b/src/main/java/org/folio/circulation/services/AllowedServicePointsService.java index 45fff76324..c5ee898a43 100644 --- a/src/main/java/org/folio/circulation/services/AllowedServicePointsService.java +++ b/src/main/java/org/folio/circulation/services/AllowedServicePointsService.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -40,8 +41,8 @@ import org.folio.circulation.domain.User; import org.folio.circulation.domain.configuration.TlrSettingsConfiguration; import org.folio.circulation.domain.policy.RequestPolicy; -import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.InstanceRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; import org.folio.circulation.infrastructure.storage.requests.RequestPolicyRepository; @@ -57,24 +58,28 @@ public class AllowedServicePointsService { private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + private static final String ECS_REQUEST_ROUTING_INDEX_NAME = "ecsRequestRouting"; + private static final String PICKUP_LOCATION_INDEX_NAME = "pickupLocation"; private final ItemRepository itemRepository; private final UserRepository userRepository; private final RequestRepository requestRepository; private final RequestPolicyRepository requestPolicyRepository; private final ServicePointRepository servicePointRepository; private final ItemByInstanceIdFinder itemFinder; - private final ConfigurationRepository configurationRepository; + private final SettingsRepository settingsRepository; private final InstanceRepository instanceRepository; + private final String indexName; - public AllowedServicePointsService(Clients clients) { + public AllowedServicePointsService(Clients clients, boolean isEcsRequestRouting) { itemRepository = new ItemRepository(clients); userRepository = new UserRepository(clients); requestRepository = new RequestRepository(clients); requestPolicyRepository = new RequestPolicyRepository(clients); - servicePointRepository = new ServicePointRepository(clients); - configurationRepository = new ConfigurationRepository(clients); + servicePointRepository = new ServicePointRepository(clients, isEcsRequestRouting); + settingsRepository = new SettingsRepository(clients); instanceRepository = new InstanceRepository(clients); itemFinder = new ItemByInstanceIdFinder(clients.holdingsStorage(), itemRepository); + indexName = isEcsRequestRouting ? ECS_REQUEST_ROUTING_INDEX_NAME : PICKUP_LOCATION_INDEX_NAME; } public CompletableFuture>>> @@ -154,6 +159,12 @@ private CompletableFuture> fetchUser(AllowedServicePointsRequest re ? this::extractAllowedServicePointsIgnoringItemStatus : this::extractAllowedServicePointsConsideringItemStatus; + if (request.isUseStubItem()) { + return requestPolicyRepository.lookupRequestPolicy(user) + .thenCompose(r -> r.after(policy -> extractAllowedServicePointsIgnoringItemStatus( + policy, new HashSet<>()))); + } + return requestPolicyRepository.lookupRequestPolicies(items, user) .thenCompose(r -> r.after(policies -> allOf(policies, mappingFunction))) .thenApply(r -> r.map(this::combineAllowedServicePoints)); @@ -165,7 +176,7 @@ private CompletableFuture> fetchUser(AllowedServicePointsRequest re if (request.isForTitleLevelRequest() && request.getOperation() == CREATE) { log.info("getAllowedServicePointsForTitleWithNoItems:: checking TLR settings"); - return configurationRepository.lookupTlrSettings() + return settingsRepository.lookupTlrSettings() .thenCompose(r -> r.after(this::considerTlrSettings)); } @@ -353,7 +364,7 @@ private Map> combineAllowedServicePoints( } private CompletableFuture>> fetchAllowedServicePoints() { - return servicePointRepository.fetchPickupLocationServicePoints() + return servicePointRepository.fetchServicePointsByIndexName(indexName) .thenApply(r -> r.map(servicePoints -> servicePoints.stream() .map(AllowedServicePoint::new) .collect(Collectors.toSet()))); @@ -365,7 +376,7 @@ private CompletableFuture>> fetchPickupLocationS log.debug("filterIdsByServicePointsAndPickupLocationExistence:: parameters ids: {}", () -> collectionAsString(ids)); - return servicePointRepository.fetchPickupLocationServicePointsByIds(ids) + return servicePointRepository.fetchPickupLocationServicePointsByIdsAndIndexName(ids, indexName) .thenApply(servicePointsResult -> servicePointsResult .map(servicePoints -> servicePoints.stream() .map(AllowedServicePoint::new) diff --git a/src/main/java/org/folio/circulation/support/Clients.java b/src/main/java/org/folio/circulation/support/Clients.java index 404b433461..0dcc883a34 100644 --- a/src/main/java/org/folio/circulation/support/Clients.java +++ b/src/main/java/org/folio/circulation/support/Clients.java @@ -4,7 +4,9 @@ import org.folio.circulation.rules.CirculationRulesProcessor; import org.folio.circulation.services.PubSubPublishingService; +import org.folio.circulation.support.http.client.IncludeRoutingServicePoints; import org.folio.circulation.support.http.client.OkapiHttpClient; +import org.folio.circulation.support.http.client.QueryParameter; import org.folio.circulation.support.http.server.WebContext; import io.vertx.core.http.HttpClient; @@ -40,6 +42,7 @@ public class Clients { private final CollectionResourceClient circulationRulesStorageClient; private final CollectionResourceClient requestPoliciesStorageClient; private final CollectionResourceClient servicePointsStorageClient; + private final CollectionResourceClient routingServicePointsStorageClient; private final CollectionResourceClient calendarStorageClient; private final CollectionResourceClient patronGroupsStorageClient; private final CollectionResourceClient patronNoticePolicesStorageClient; @@ -68,6 +71,7 @@ public class Clients { private final CollectionResourceClient departmentClient; private final CollectionResourceClient checkOutLockStorageClient; private final CollectionResourceClient circulationItemClient; + private final CollectionResourceClient searchClient; private final GetManyRecordsClient settingsStorageClient; private final CollectionResourceClient circulationSettingsStorageClient; @@ -75,6 +79,10 @@ public static Clients create(WebContext context, HttpClient httpClient) { return new Clients(context.createHttpClient(httpClient), context); } + public static Clients create(WebContext context, HttpClient httpClient, String tenantId) { + return new Clients(context.createHttpClient(httpClient, tenantId), context); + } + private Clients(OkapiHttpClient client, WebContext context) { try { requestsStorageClient = createRequestsStorageClient(client, context); @@ -108,6 +116,8 @@ private Clients(OkapiHttpClient client, WebContext context) { requestPoliciesStorageClient = createRequestPoliciesStorageClient(client, context); fixedDueDateSchedulesStorageClient = createFixedDueDateSchedulesStorageClient(client, context); servicePointsStorageClient = createServicePointsStorageClient(client, context); + routingServicePointsStorageClient = createServicePointsStorageWithCustomParam(client, + context, IncludeRoutingServicePoints.enabled()); patronGroupsStorageClient = createPatronGroupsStorageClient(client, context); calendarStorageClient = createCalendarStorageClient(client, context); patronNoticePolicesStorageClient = createPatronNoticePolicesStorageClient(client, context); @@ -137,6 +147,7 @@ private Clients(OkapiHttpClient client, WebContext context) { checkOutLockStorageClient = createCheckoutLockClient(client, context); settingsStorageClient = createSettingsStorageClient(client, context); circulationItemClient = createCirculationItemClient(client, context); + searchClient = createSearchClient(client, context); circulationSettingsStorageClient = createCirculationSettingsStorageClient(client, context); } catch(MalformedURLException e) { @@ -240,6 +251,10 @@ public CollectionResourceClient servicePointsStorage() { return servicePointsStorageClient; } + public CollectionResourceClient routingServicePointsStorage() { + return routingServicePointsStorageClient; + } + public CollectionResourceClient patronGroupsStorage() { return patronGroupsStorageClient; } @@ -376,6 +391,10 @@ public CollectionResourceClient circulationItemClient() { return circulationItemClient; } + public CollectionResourceClient searchClient() { + return searchClient; + } + public CollectionResourceClient circulationSettingsStorageClient() { return circulationSettingsStorageClient; } @@ -388,6 +407,14 @@ private static CollectionResourceClient getCollectionResourceClient( return new CollectionResourceClient(client, context.getOkapiBasedUrl(path)); } + private static CollectionResourceClient getCollectionResourceClientWithCustomParam( + OkapiHttpClient client, WebContext context, String path, QueryParameter customParam) + throws MalformedURLException { + + return new CustomParamCollectionResourceClient(client, context.getOkapiBasedUrl(path), + customParam); + } + public CollectionResourceClient noticeTemplatesClient() { return noticeTemplatesClient; } @@ -630,6 +657,14 @@ private CollectionResourceClient createServicePointsStorageClient( return getCollectionResourceClient(client, context, "/service-points"); } + private CollectionResourceClient createServicePointsStorageWithCustomParam( + OkapiHttpClient client, WebContext context, QueryParameter customParam) + throws MalformedURLException { + + return getCollectionResourceClientWithCustomParam(client, context, "/service-points", + customParam); + } + private CollectionResourceClient createPatronGroupsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { @@ -807,6 +842,12 @@ private CollectionResourceClient createCirculationItemClient( return getCollectionResourceClient(client, context, "/circulation-item"); } + private CollectionResourceClient createSearchClient( + OkapiHttpClient client, WebContext context) throws MalformedURLException { + + return getCollectionResourceClient(client, context, "/search/instances"); + } + private CollectionResourceClient createCirculationSettingsStorageClient( OkapiHttpClient client, WebContext context) throws MalformedURLException { diff --git a/src/main/java/org/folio/circulation/support/CollectionResourceClient.java b/src/main/java/org/folio/circulation/support/CollectionResourceClient.java index a5c827108c..0b3db44c9b 100644 --- a/src/main/java/org/folio/circulation/support/CollectionResourceClient.java +++ b/src/main/java/org/folio/circulation/support/CollectionResourceClient.java @@ -18,8 +18,8 @@ import io.vertx.core.json.JsonObject; public class CollectionResourceClient implements GetManyRecordsClient { - private final OkapiHttpClient client; - private final URL collectionRoot; + final OkapiHttpClient client; + final URL collectionRoot; public CollectionResourceClient(OkapiHttpClient client, URL collectionRoot) { this.collectionRoot = collectionRoot; @@ -109,7 +109,7 @@ public CompletableFuture> getMany(CqlQuery cqlQuery, return client.get(collectionRoot, cqlQuery, pageLimit, offset); } - private String individualRecordUrl(String id) { + String individualRecordUrl(String id) { return format("%s/%s", collectionRoot, id); } } diff --git a/src/main/java/org/folio/circulation/support/CustomParamCollectionResourceClient.java b/src/main/java/org/folio/circulation/support/CustomParamCollectionResourceClient.java new file mode 100644 index 0000000000..2c765e3acf --- /dev/null +++ b/src/main/java/org/folio/circulation/support/CustomParamCollectionResourceClient.java @@ -0,0 +1,46 @@ +package org.folio.circulation.support; + +import java.net.URL; +import java.util.concurrent.CompletableFuture; + +import org.folio.circulation.support.http.client.CqlQuery; +import org.folio.circulation.support.http.client.Offset; +import org.folio.circulation.support.http.client.OkapiHttpClient; +import org.folio.circulation.support.http.client.PageLimit; +import org.folio.circulation.support.http.client.QueryParameter; +import org.folio.circulation.support.http.client.Response; +import org.folio.circulation.support.results.Result; + +public class CustomParamCollectionResourceClient extends CollectionResourceClient { + + private QueryParameter customQueryParameter; + + public CustomParamCollectionResourceClient(OkapiHttpClient client, URL collectionRoot, + QueryParameter customQueryParameter) { + + super(client, collectionRoot); + this.customQueryParameter = customQueryParameter; + } + + @Override + public CompletableFuture> get() { + return client.get(collectionRoot.toString(), customQueryParameter); + } + + @Override + public CompletableFuture> get(PageLimit pageLimit) { + return client.get(collectionRoot, pageLimit, customQueryParameter); + } + + @Override + public CompletableFuture> get(String id) { + return client.get(individualRecordUrl(id), customQueryParameter); + } + + @Override + public CompletableFuture> getMany(CqlQuery cqlQuery, + PageLimit pageLimit, Offset offset) { + + return client.get(collectionRoot, cqlQuery, pageLimit, offset, customQueryParameter); + } +} diff --git a/src/main/java/org/folio/circulation/support/http/client/IncludeRoutingServicePoints.java b/src/main/java/org/folio/circulation/support/http/client/IncludeRoutingServicePoints.java new file mode 100644 index 0000000000..ff9d531cad --- /dev/null +++ b/src/main/java/org/folio/circulation/support/http/client/IncludeRoutingServicePoints.java @@ -0,0 +1,33 @@ +package org.folio.circulation.support.http.client; + +import static java.lang.String.format; + +public class IncludeRoutingServicePoints implements QueryParameter { + + private static final String PARAM_NAME = "includeRoutingServicePoints"; + private final Boolean value; + + public static IncludeRoutingServicePoints enabled() { + return new IncludeRoutingServicePoints(true); + } + + private IncludeRoutingServicePoints(Boolean value) { + this.value = value; + } + + @Override + public void consume(QueryStringParameterConsumer consumer) { + if (value != null) { + consumer.consume(PARAM_NAME, value.toString()); + } + } + + @Override + public String toString() { + if (value == null) { + return format("No %s", PARAM_NAME); + } + + return format("%s = \"%s\"", PARAM_NAME, value); + } +} diff --git a/src/main/java/org/folio/circulation/support/http/client/VertxWebClientOkapiHttpClient.java b/src/main/java/org/folio/circulation/support/http/client/VertxWebClientOkapiHttpClient.java index e4815a30bf..a203f4386f 100644 --- a/src/main/java/org/folio/circulation/support/http/client/VertxWebClientOkapiHttpClient.java +++ b/src/main/java/org/folio/circulation/support/http/client/VertxWebClientOkapiHttpClient.java @@ -27,7 +27,9 @@ import io.vertx.ext.web.client.HttpResponse; import io.vertx.ext.web.client.WebClient; import io.vertx.core.http.HttpMethod; +import lombok.extern.log4j.Log4j2; +@Log4j2 public class VertxWebClientOkapiHttpClient implements OkapiHttpClient { private static final Duration DEFAULT_TIMEOUT = Duration.of(20, SECONDS); private static final String ACCEPT = HttpHeaderNames.ACCEPT.toString(); @@ -184,6 +186,7 @@ public CompletableFuture> delete(String url, } private HttpRequest withStandardHeaders(HttpRequest request) { + log.debug("withStandardHeaders:: url={}, tenantId={}", request.uri(), tenantId); return request .putHeader(ACCEPT, "application/json, text/plain") .putHeader(OKAPI_URL, okapiUrl.toString()) diff --git a/src/main/java/org/folio/circulation/support/http/server/WebContext.java b/src/main/java/org/folio/circulation/support/http/server/WebContext.java index a38173b438..df6ec49778 100644 --- a/src/main/java/org/folio/circulation/support/http/server/WebContext.java +++ b/src/main/java/org/folio/circulation/support/http/server/WebContext.java @@ -76,6 +76,10 @@ public URL getOkapiBasedUrl(String path) throws MalformedURLException { } public OkapiHttpClient createHttpClient(HttpClient httpClient) { + return createHttpClient(httpClient, getTenantId()); + } + + public OkapiHttpClient createHttpClient(HttpClient httpClient, String tenantId) { URL okapiUrl; try { @@ -86,7 +90,7 @@ public OkapiHttpClient createHttpClient(HttpClient httpClient) { } return VertxWebClientOkapiHttpClient.createClientUsing(httpClient, - okapiUrl, getTenantId(), getOkapiToken(), getUserId(), + okapiUrl, tenantId, getOkapiToken(), getUserId(), getRequestId()); } diff --git a/src/main/java/org/folio/circulation/support/request/RequestRelatedRepositories.java b/src/main/java/org/folio/circulation/support/request/RequestRelatedRepositories.java index a7360d40c4..72b4c86b3c 100644 --- a/src/main/java/org/folio/circulation/support/request/RequestRelatedRepositories.java +++ b/src/main/java/org/folio/circulation/support/request/RequestRelatedRepositories.java @@ -2,6 +2,7 @@ import org.folio.circulation.infrastructure.storage.ConfigurationRepository; import org.folio.circulation.infrastructure.storage.ServicePointRepository; +import org.folio.circulation.infrastructure.storage.SettingsRepository; import org.folio.circulation.infrastructure.storage.inventory.HoldingsRepository; import org.folio.circulation.infrastructure.storage.inventory.InstanceRepository; import org.folio.circulation.infrastructure.storage.inventory.ItemRepository; @@ -28,6 +29,7 @@ public class RequestRelatedRepositories { private RequestQueueRepository requestQueueRepository; private RequestPolicyRepository requestPolicyRepository; private ConfigurationRepository configurationRepository; + private SettingsRepository settingsRepository; private ServicePointRepository servicePointRepository; private LocationRepository locationRepository; @@ -43,6 +45,7 @@ public RequestRelatedRepositories(Clients clients) { requestQueueRepository = new RequestQueueRepository(requestRepository); requestPolicyRepository = new RequestPolicyRepository(clients); configurationRepository = new ConfigurationRepository(clients); + settingsRepository = new SettingsRepository(clients); servicePointRepository = new ServicePointRepository(clients); locationRepository = LocationRepository.using(clients); } diff --git a/src/test/java/api/ItemsByInstanceResourceTest.java b/src/test/java/api/ItemsByInstanceResourceTest.java new file mode 100644 index 0000000000..71d3ce18b6 --- /dev/null +++ b/src/test/java/api/ItemsByInstanceResourceTest.java @@ -0,0 +1,84 @@ +package api; + +import static api.support.APITestContext.clearTempTenantId; +import static api.support.APITestContext.setTempTenantId; +import static api.support.http.InterfaceUrls.itemsByInstanceUrl; +import static api.support.matchers.JsonObjectMatcher.hasJsonPath; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.iterableWithSize; +import static org.hamcrest.core.Is.is; + +import java.util.List; +import java.util.UUID; + +import org.folio.circulation.support.http.client.Response; +import org.junit.jupiter.api.Test; + +import api.support.APITests; +import api.support.builders.SearchInstanceBuilder; +import api.support.http.IndividualResource; +import api.support.http.ResourceClient; +import api.support.matchers.UUIDMatcher; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +class ItemsByInstanceResourceTest extends APITests { + + private static final String TENANT_ID_COLLEGE = "college"; + private static final String TENANT_ID_UNIVERSITY = "university"; + + @Test + void canGetInstanceById() { + IndividualResource instance = instancesFixture.basedUponDunkirk(); + UUID instanceId = instance.getId(); + + // create item in tenant "college" + setTempTenantId(TENANT_ID_COLLEGE); + IndividualResource collegeLocation = locationsFixture.mainFloor(); + IndividualResource collegeHoldings = holdingsFixture.defaultWithHoldings(instanceId); + IndividualResource collegeItem = itemsFixture.createItemWithHoldingsAndLocation( + collegeHoldings.getId(), collegeLocation.getId()); + clearTempTenantId(); + + // create item in tenant "university" + setTempTenantId(TENANT_ID_UNIVERSITY); + IndividualResource universityLocation = locationsFixture.thirdFloor(); + IndividualResource universityHoldings = holdingsFixture.defaultWithHoldings(instanceId); + IndividualResource universityItem = itemsFixture.createItemWithHoldingsAndLocation( + universityHoldings.getId(), universityLocation.getId()); + clearTempTenantId(); + + // make sure neither item exists in current tenant + assertThat(itemsFixture.getById(collegeItem.getId()).getResponse().getStatusCode(), is(404)); + assertThat(itemsFixture.getById(universityItem.getId()).getResponse().getStatusCode(), is(404)); + + List searchItems = List.of( + collegeItem.getJson().put("tenantId", TENANT_ID_COLLEGE), + universityItem.getJson().put("tenantId", TENANT_ID_UNIVERSITY)); + + JsonObject searchInstance = new SearchInstanceBuilder(instance.getJson()) + .withItems(searchItems) + .create(); + + ResourceClient.forSearchClient().create(searchInstance); + Response response = get(String.format("query=(id==%s)", instanceId), 200); + JsonObject responseJson = response.getJson(); + JsonArray items = responseJson.getJsonArray("items"); + + assertThat(responseJson.getString("id"), UUIDMatcher.is(instanceId)); + assertThat(items, iterableWithSize(2)); + assertThat(items, hasItem(allOf( + hasJsonPath("id", UUIDMatcher.is(collegeItem.getId())), + hasJsonPath("tenantId", is(TENANT_ID_COLLEGE))))); + assertThat(items, hasItem(allOf( + hasJsonPath("id", UUIDMatcher.is(universityItem.getId())), + hasJsonPath("tenantId", is(TENANT_ID_UNIVERSITY))))); + } + + private Response get(String query, int expectedStatusCode) { + return restAssuredClient.get(itemsByInstanceUrl(query), expectedStatusCode, + "items-by-instance-request"); + } +} diff --git a/src/test/java/api/loans/CheckInByBarcodeTests.java b/src/test/java/api/loans/CheckInByBarcodeTests.java index d3d97e2311..bc938086e8 100644 --- a/src/test/java/api/loans/CheckInByBarcodeTests.java +++ b/src/test/java/api/loans/CheckInByBarcodeTests.java @@ -1640,7 +1640,7 @@ void availableNoticeIsSentUponCheckInWhenRequesterBarcodeWasChanged() { @Test void linkItemToHoldTLRWithHoldShelfWhenCheckedInItemThenFulfilledWithSuccess(){ reconfigureTlrFeature(TlrFeatureStatus.NOT_CONFIGURED); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID instanceId = instancesFixture.basedUponDunkirk().getId(); IndividualResource defaultWithHoldings = holdingsFixture.defaultWithHoldings(instanceId); IndividualResource checkedOutItem = itemsClient.create(buildCheckedOutItemWithHoldingRecordsId(defaultWithHoldings.getId())); @@ -1722,7 +1722,7 @@ void checkInItemWhenServiceHasChangedToNoPickupLocation(){ @Test void linkItemToHoldTLRWithDeliveryWhenCheckedInThenFulfilledWithSuccess(){ reconfigureTlrFeature(TlrFeatureStatus.NOT_CONFIGURED); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID instanceId = instancesFixture.basedUponDunkirk().getId(); IndividualResource defaultWithHoldings = holdingsFixture.defaultWithHoldings(instanceId); IndividualResource checkedOutItem = itemsClient.create(buildCheckedOutItemWithHoldingRecordsId(defaultWithHoldings.getId())); @@ -1748,7 +1748,7 @@ void linkItemToHoldTLRWithDeliveryWhenCheckedInThenFulfilledWithSuccess(){ @Test void requestsShouldChangePositionWhenTheyGoInFulfillmentOnCheckIn() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(3); ItemResource firstItem = items.get(0); @@ -1799,7 +1799,7 @@ void requestsShouldChangePositionWhenTheyGoInFulfillmentOnCheckIn() { @Test void canCheckinItemWhenRequestForAnotherItemOfSameInstanceExists() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource firstItem = items.get(0); @@ -1820,7 +1820,7 @@ void canCheckinItemWhenRequestForAnotherItemOfSameInstanceExists() { @Test void canFulFillRecallRequestWhenCheckInAnotherItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource firstItem = items.get(0); ItemResource secondItem = items.get(1); @@ -1846,7 +1846,7 @@ void canFulFillRecallRequestWhenCheckInAnotherItemOfSameInstance() { @Test void canFulFillRecallRequestWhenCheckInAnotherItemOfSameInstanceWithMultipleRecallRequests() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(3); ItemResource firstItem = items.get(0); ItemResource secondItem = items.get(1); @@ -1887,7 +1887,7 @@ void canFulFillRecallRequestWhenCheckInAnotherItemOfSameInstanceWithMultipleReca @Test void shouldNotLinkTitleLevelHoldRequestToAnItemUponCheckInWhenItemIsNonRequestable() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = itemsFixture.basedUponNod(); checkOutFixture.checkOutByBarcode(item, usersFixture.rebecca()); IndividualResource request = requestsFixture.placeTitleLevelRequest(HOLD, item.getInstanceId(), @@ -1917,7 +1917,7 @@ void shouldNotLinkTitleLevelHoldRequestToAnItemUponCheckInWhenItemIsNonRequestab @Test void shouldNotLinkTitleLevelHoldRequestToAnItemUponCheckInWhenItemIsNonLoanable() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = itemsFixture.basedUponNod(); checkOutFixture.checkOutByBarcode(item, usersFixture.rebecca()); IndividualResource request = requestsFixture.placeTitleLevelRequest(HOLD, item.getInstanceId(), @@ -1933,7 +1933,7 @@ void shouldNotLinkTitleLevelHoldRequestToAnItemUponCheckInWhenItemIsNonLoanable( @Test void shouldNotLinkTitleLevelRecallRequestToNewItemUponCheckInWhenItemIsNonRequestable() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID canCirculateLoanTypeId = loanTypesFixture.canCirculate().getId(); UUID readingRoomLoanTypeId = loanTypesFixture.readingRoom().getId(); @@ -1979,7 +1979,7 @@ void shouldNotLinkTitleLevelRecallRequestToNewItemUponCheckInWhenItemIsNonReques @Test void shouldNotLinkTitleLevelRecallRequestToNewItemUponCheckInWhenItemIsNonLoanable() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID canCirculateLoanTypeId = loanTypesFixture.canCirculate().getId(); UUID readingRoomLoanTypeId = loanTypesFixture.readingRoom().getId(); diff --git a/src/test/java/api/loans/CheckOutByBarcodeTests.java b/src/test/java/api/loans/CheckOutByBarcodeTests.java index a827a13001..b7107e04b3 100644 --- a/src/test/java/api/loans/CheckOutByBarcodeTests.java +++ b/src/test/java/api/loans/CheckOutByBarcodeTests.java @@ -2492,7 +2492,7 @@ void canCheckOutUsingAlternateCheckoutRollingLoanPolicy() { @ParameterizedTest @EnumSource(value = TlrFeatureStatus.class, names = {"DISABLED", "NOT_CONFIGURED"}) void titleLevelRequestIsIgnoredWhenTlrFeatureIsNotEnabled(TlrFeatureStatus tlrFeatureStatus) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = itemsFixture.basedUponNod(); UserResource borrower = usersFixture.steve(); @@ -2519,7 +2519,7 @@ void titleLevelRequestIsIgnoredWhenTlrFeatureIsNotEnabled(TlrFeatureStatus tlrFe "Title, Title" }) void canFulfilPageAndHoldRequestsWithMixedLevels(String pageRequestLevel, String holdRequestLevel) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = itemsFixture.basedUponNod(); UserResource firstRequester = usersFixture.steve(); @@ -2582,7 +2582,7 @@ void canFulfilPageAndHoldRequestsWithMixedLevels(String pageRequestLevel, String @Test void canCheckoutItemWhenTitleLevelPageRequestsExistForDifferentItemsOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(4); UUID instanceId = items.stream().findAny().orElseThrow().getInstanceId(); @@ -2613,7 +2613,7 @@ void canCheckoutItemWhenTitleLevelPageRequestsExistForDifferentItemsOfSameInstan void cannotCheckoutItemWhenTitleLevelPageRequestExistsForSameItem( String firstRequestLevel, String secondRequestLevel) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource randomItem = items.stream().findAny().orElseThrow(); diff --git a/src/test/java/api/loans/scenarios/ChangeDueDateAPITests.java b/src/test/java/api/loans/scenarios/ChangeDueDateAPITests.java index 9c84c684dc..509f1229d5 100644 --- a/src/test/java/api/loans/scenarios/ChangeDueDateAPITests.java +++ b/src/test/java/api/loans/scenarios/ChangeDueDateAPITests.java @@ -436,7 +436,7 @@ void dueDateChangeShouldNotUnsetRenewalFlagValueWhenTlrFeatureEnabled() { assertThat(recalledLoan.getJson().getBoolean("dueDateChangedByRecall"), equalTo(true)); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); requestsClient.create(new RequestBuilder() .recall() .titleRequestLevel() @@ -496,7 +496,7 @@ void dueDateChangeShouldUnsetRenewalFlagValueWhenTlrFeatureDisabledOrNotConfigur assertThat(recalledLoan.getJson().getBoolean("dueDateChangedByRecall"), equalTo(true)); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); requestsClient.create(new RequestBuilder() .recall() diff --git a/src/test/java/api/loans/scenarios/CheckoutWithRequestScenarioTests.java b/src/test/java/api/loans/scenarios/CheckoutWithRequestScenarioTests.java index 6332320521..efcd458121 100644 --- a/src/test/java/api/loans/scenarios/CheckoutWithRequestScenarioTests.java +++ b/src/test/java/api/loans/scenarios/CheckoutWithRequestScenarioTests.java @@ -165,7 +165,7 @@ void checkingOutWithHoldRequestAppliesAlternatePeriodAndScheduledForFixedPolicy( @Test void alternatePeriodShouldBeAppliedWhenRequestQueueContainsHoldTlr() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); var firstItem = items.get(0); var secondItem = items.get(1); @@ -245,7 +245,7 @@ void alternatePeriodShouldBeAppliedWhenRequestQueueContainsHoldTlr() { @Test void alternatePeriodShouldNotBeAppliedWhenRequestQueueContainsHoldIlrForDifferentItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); var firstItem = items.get(0); var secondItem = items.get(1); diff --git a/src/test/java/api/queue/RequestQueueResourceTest.java b/src/test/java/api/queue/RequestQueueResourceTest.java index 98d70164b0..04fb3cffa8 100644 --- a/src/test/java/api/queue/RequestQueueResourceTest.java +++ b/src/test/java/api/queue/RequestQueueResourceTest.java @@ -303,7 +303,7 @@ void shouldGetRequestQueueForItemSuccessfully() { @Test void shouldGetRequestQueueForInstanceSuccessfully() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID isbnIdentifierId = identifierTypesFixture.isbn().getId(); String isbnValue = "9780866989427"; diff --git a/src/test/java/api/requests/AllowedServicePointsAPITests.java b/src/test/java/api/requests/AllowedServicePointsAPITests.java index e6995df3f4..2446e910e0 100644 --- a/src/test/java/api/requests/AllowedServicePointsAPITests.java +++ b/src/test/java/api/requests/AllowedServicePointsAPITests.java @@ -22,6 +22,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.iterableWithSize; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.core.Is.is; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -154,8 +155,8 @@ void shouldReturnListOfAllowedServicePointsForRequest(RequestType requestType, .collect(Collectors.toSet())); var response = requestLevel == TITLE - ? get("create", requesterId, instanceId, null, null, HttpStatus.SC_OK).getJson() - : get("create", requesterId, null, itemId, null, HttpStatus.SC_OK).getJson(); + ? get("create", requesterId, instanceId, null, null, null, null, HttpStatus.SC_OK).getJson() + : get("create", requesterId, null, itemId, null, null, null, HttpStatus.SC_OK).getJson(); assertThat(response, allowedServicePointMatcher(Map.of(requestType, allowedSpInResponse))); } @@ -202,7 +203,7 @@ void shouldReturnListOfAllowedServicePointsForRequestReplacement( .map(UUID::fromString) .collect(Collectors.toSet())); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); var pickupLocationId = allowedSpByPolicy.stream() .findFirst() .map(AllowedServicePoint::getId) @@ -223,7 +224,7 @@ void shouldReturnListOfAllowedServicePointsForRequestReplacement( var requestId = request == null ? null : request.getId().toString(); var response = - get("replace", null, null, null, requestId, HttpStatus.SC_OK).getJson(); + get("replace", null, null, null, requestId, null, null, HttpStatus.SC_OK).getJson(); assertThat(response, allowedServicePointMatcher(Map.of(requestType, allowedSpInResponse))); } @@ -494,7 +495,7 @@ void allPickupLocationsAreReturnedForTitleLevelHoldWhenItIsDisabledAndInstanceHa boolean instanceHasHoldings) { // allow TLR-holds for instances with no holdings/items - configurationsFixture.configureTlrFeature(true, false, null, null, null); + settingsFixture.configureTlrFeature(true, false, null, null, null); IndividualResource sp1 = servicePointsFixture.cd1(); // pickup location IndividualResource sp2 = servicePointsFixture.cd2(); // pickup location @@ -515,7 +516,7 @@ void allPickupLocationsAreReturnedForTitleLevelHoldWhenItIsDisabledAndInstanceHa @Test void noAllowedServicePointsAreReturnedForTitleLevelHoldWhenItIsDisabledAndInstanceHasItems() { // allow TLR-holds for instances with no holdings/items - configurationsFixture.configureTlrFeature(true, false, null, null, null); + settingsFixture.configureTlrFeature(true, false, null, null, null); IndividualResource sp1 = servicePointsFixture.cd1(); // pickup location servicePointsFixture.cd2(); // pickup location @@ -653,8 +654,8 @@ void allowedServicePointsAreSortedByName() { @Test void getReplaceFailsWhenRequestDoesNotExist() { String requestId = randomId(); - Response response = get("replace", null, null, null, requestId, - HttpStatus.SC_UNPROCESSABLE_ENTITY); + Response response = get("replace", null, null, null, requestId, null, + null, HttpStatus.SC_UNPROCESSABLE_ENTITY); assertThat(response.getJson(), hasErrorWith(hasMessage( String.format("Request with ID %s was not found", requestId)))); } @@ -663,8 +664,8 @@ void getReplaceFailsWhenRequestDoesNotExist() { void getMoveFailsWhenRequestDoesNotExist() { String requestId = randomId(); String itemId = itemsFixture.basedUponNod().getId().toString(); - Response response = get("move", null, null, itemId, requestId, - HttpStatus.SC_UNPROCESSABLE_ENTITY); + Response response = get("move", null, null, itemId, requestId, null, + null, HttpStatus.SC_UNPROCESSABLE_ENTITY); assertThat(response.getJson(), hasErrorWith(hasMessage( String.format("Request with ID %s was not found", requestId)))); } @@ -703,7 +704,7 @@ void shouldReturnListOfAllowedServicePointsForRequestMove(RequestLevel requestLe setRequestPolicyWithAllowedServicePoints(PAGE, Set.of(sp1Uuid)); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); IndividualResource request = requestsFixture.place(new RequestBuilder() .withRequestType(PAGE.toString()) @@ -725,62 +726,152 @@ void shouldReturnListOfAllowedServicePointsForRequestMove(RequestLevel requestLe // Valid "move" request var moveResponse = - get("move", null, null, itemToMoveToId, requestId, HttpStatus.SC_OK).getJson(); + get("move", null, null, itemToMoveToId, requestId, null, null, HttpStatus.SC_OK).getJson(); assertThat(moveResponse, allowedServicePointMatcher(Map.of(HOLD, List.of(sp2)))); // Invalid "move" requests var invalidMoveResponse1 = get("move", null, null, null, requestId, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidMoveResponse1.getBody(), equalTo("Invalid combination of query parameters")); var invalidMoveResponse2 = get("move", null, null, itemToMoveToId, null, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidMoveResponse2.getBody(), equalTo("Invalid combination of query parameters")); var invalidMoveResponse3 = get("move", null, null, null, null, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidMoveResponse3.getBody(), equalTo("Invalid combination of query parameters")); var invalidMoveResponse4 = get("move", requesterId, null, itemToMoveToId, requestId, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidMoveResponse4.getBody(), equalTo("Invalid combination of query parameters")); var invalidMoveResponse5 = get("move", null, instanceId, itemToMoveToId, requestId, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidMoveResponse5.getBody(), equalTo("Invalid combination of query parameters")); // Valid "replace" request var replaceResponse = - get("replace", null, null, null, requestId, HttpStatus.SC_OK).getJson(); + get("replace", null, null, null, requestId, null, null, HttpStatus.SC_OK).getJson(); assertThat(replaceResponse, allowedServicePointMatcher(Map.of(HOLD, List.of(sp2)))); // Invalid "replace" requests var invalidReplaceResponse1 = get("replace", null, null, null, null, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidReplaceResponse1.getBody(), equalTo("Invalid combination of query parameters")); var invalidReplaceResponse2 = get("replace", requesterId, null, null, requestId, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidReplaceResponse2.getBody(), equalTo("Invalid combination of query parameters")); var invalidReplaceResponse3 = get("replace", null, instanceId, null, requestId, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidReplaceResponse3.getBody(), equalTo("Invalid combination of query parameters")); var invalidReplaceResponse4 = get("replace", null, null, requestedItemId, requestId, - HttpStatus.SC_BAD_REQUEST); + null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidReplaceResponse4.getBody(), equalTo("Invalid combination of query parameters")); var invalidReplaceResponse5 = get("replace", requesterId, instanceId, - requestedItemId, requestId, HttpStatus.SC_BAD_REQUEST); + requestedItemId, requestId, null, null, HttpStatus.SC_BAD_REQUEST); assertThat(invalidReplaceResponse5.getBody(), equalTo("Invalid combination of query parameters")); } + @Test + void shouldUseStubItemParameterInCirculationRuleMatchingWhenPresent() { + var requesterId = usersFixture.steve().getId().toString(); + var instanceId = itemsFixture.createMultipleItemsForTheSameInstance(2).get(0) + .getInstanceId().toString(); + var cd1 = servicePointsFixture.cd1(); + var cd2 = servicePointsFixture.cd2(); + var cd4 = servicePointsFixture.cd4(); + var cd5 = servicePointsFixture.cd5(); + final UUID book = materialTypesFixture.book().getId(); + final UUID patronGroup = patronGroupsFixture.regular().getId(); + circulationRulesFixture.updateCirculationRules(createRules("m " + book + + "+ g " + patronGroup, "g " + patronGroup)); + + var response = getCreateOp(requesterId, instanceId, null, "true", null, HttpStatus.SC_OK) + .getJson(); + assertThat(response, hasNoJsonPath(PAGE.getValue())); + JsonArray allowedServicePoints = response.getJsonArray(HOLD.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd1, cd2, cd4, cd5)); + allowedServicePoints = response.getJsonArray(RECALL.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd1, cd2, cd4, cd5)); + + response = getCreateOp(requesterId, instanceId, null, "false", null, HttpStatus.SC_OK) + .getJson(); + assertThat(response, hasNoJsonPath(HOLD.getValue())); + assertThat(response, hasNoJsonPath(RECALL.getValue())); + allowedServicePoints = response.getJsonArray(PAGE.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd1, cd2, cd4, cd5)); + + response = getCreateOp(requesterId, instanceId, null, HttpStatus.SC_OK).getJson(); + assertThat(response, hasNoJsonPath(HOLD.getValue())); + assertThat(response, hasNoJsonPath(RECALL.getValue())); + allowedServicePoints = response.getJsonArray(PAGE.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd1, cd2, cd4, cd5)); + } + + @Test + void shouldReturnErrorIfUseStubItemIsInvalid() { + Response errorResponse = getCreateOp(UUID.randomUUID().toString(), + UUID.randomUUID().toString(), null, "invalid", null, + HttpStatus.SC_BAD_REQUEST); + assertThat(errorResponse.getBody(), is("useStubItem is not a valid boolean: invalid.")); + } + + @Test + void shouldConsiderEcsRequestRoutingParameterForAllowedServicePoints() { + var requesterId = usersFixture.steve().getId().toString(); + var instanceId = itemsFixture.createMultipleItemsForTheSameInstance(2).get(0) + .getInstanceId().toString(); + var cd1 = servicePointsFixture.cd1(); + var cd2 = servicePointsFixture.cd2(); + var cd4 = servicePointsFixture.cd4(); + var cd11 = servicePointsFixture.cd11(); + + final Map> allowedServicePointsInPolicy = new HashMap<>(); + allowedServicePointsInPolicy.put(PAGE, Set.of(cd1.getId(), cd2.getId(), cd11.getId())); + allowedServicePointsInPolicy.put(HOLD, Set.of(cd4.getId(), cd2.getId(), cd11.getId())); + var requestPolicy = requestPoliciesFixture.createRequestPolicyWithAllowedServicePoints( + allowedServicePointsInPolicy, PAGE, HOLD); + policiesActivation.use(PoliciesToActivate.builder().requestPolicy(requestPolicy)); + + var response = getCreateOp(requesterId, instanceId, null, "false", "true", + HttpStatus.SC_OK).getJson(); + JsonArray allowedServicePoints = response.getJsonArray(PAGE.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd11)); + assertThat(response, hasNoJsonPath(HOLD.getValue())); + assertThat(response, hasNoJsonPath(RECALL.getValue())); + + response = getCreateOp(requesterId, instanceId, null, "false", "false", + HttpStatus.SC_OK).getJson(); + allowedServicePoints = response.getJsonArray(PAGE.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd1, cd2)); + assertThat(response, hasNoJsonPath(HOLD.getValue())); + assertThat(response, hasNoJsonPath(RECALL.getValue())); + + response = getCreateOp(requesterId, instanceId, null, "false", null, + HttpStatus.SC_OK).getJson(); + allowedServicePoints = response.getJsonArray(PAGE.getValue()); + assertServicePointsMatch(allowedServicePoints, List.of(cd1, cd2)); + assertThat(response, hasNoJsonPath(HOLD.getValue())); + assertThat(response, hasNoJsonPath(RECALL.getValue())); + } + + @Test + void shouldReturnErrorIfEcsRequestRoutingIsInvalid() { + Response errorResponse = getCreateOp(UUID.randomUUID().toString(), + UUID.randomUUID().toString(), null, null, "invalid", HttpStatus.SC_BAD_REQUEST); + assertThat(errorResponse.getBody(), is("ecsRequestRouting is not a valid boolean: invalid.")); + } + private void assertServicePointsMatch(JsonArray response, List expectedServicePoints) { @@ -805,18 +896,25 @@ private void assertServicePointsMatch(JsonArray response, .map(sp -> sp.getJson().getString("name")).toArray(String[]::new))); } + private Response getCreateOp(String requesterId, String instanceId, String itemId, + String useStubItem, String ecsRequestRouting, int expectedStatusCode) { + + return get("create", requesterId, instanceId, itemId, null, useStubItem, + ecsRequestRouting, expectedStatusCode); + } + private Response getCreateOp(String requesterId, String instanceId, String itemId, int expectedStatusCode) { - return get("create", requesterId, instanceId, itemId, null, expectedStatusCode); + return get("create", requesterId, instanceId, itemId, null, null, null, expectedStatusCode); } private Response getReplaceOp(String requestId, int expectedStatusCode) { - return get("replace", null, null, null, requestId, expectedStatusCode); + return get("replace", null, null, null, requestId, null, null, expectedStatusCode); } private Response get(String operation, String requesterId, String instanceId, String itemId, - String requestId, int expectedStatusCode) { + String requestId, String useStubItem, String ecsRequestRouting, int expectedStatusCode) { List queryParams = new ArrayList<>(); queryParams.add(namedParameter("operation", operation)); @@ -832,6 +930,12 @@ private Response get(String operation, String requesterId, String instanceId, St if (requestId != null) { queryParams.add(namedParameter("requestId", requestId)); } + if (useStubItem != null) { + queryParams.add(namedParameter("useStubItem", useStubItem)); + } + if (ecsRequestRouting != null) { + queryParams.add(namedParameter("ecsRequestRouting", ecsRequestRouting)); + } return restAssuredClient.get(allowedServicePointsUrl(), queryParams, expectedStatusCode, "allowed-service-points"); @@ -854,4 +958,21 @@ private ServicePointBuilder servicePointBuilder() { .withPickupLocation(TRUE) .withHoldShelfExpriyPeriod(30, "Days"); } + + private String createRules(String firstRuleCondition, String secondRuleCondition) { + final var loanPolicy = loanPoliciesFixture.canCirculateRolling().getId().toString(); + final var allowAllRequestPolicy = requestPoliciesFixture.allowAllRequestPolicy() + .getId().toString(); + final var holdAndRecallRequestPolicy = requestPoliciesFixture.allowHoldAndRecallRequestPolicy() + .getId().toString(); + final var noticePolicy = noticePoliciesFixture.activeNotice().getId().toString(); + final var overdueFinePolicy = overdueFinePoliciesFixture.facultyStandard().getId().toString(); + final var lostItemFeePolicy = lostItemFeePoliciesFixture.facultyStandard().getId().toString(); + + return String.join("\n", + "priority: t, s, c, b, a, m, g", + "fallback-policy: l " + loanPolicy + " r " + allowAllRequestPolicy + " n " + noticePolicy + " o " + overdueFinePolicy + " i " + lostItemFeePolicy, + firstRuleCondition + " : l " + loanPolicy + " r " + allowAllRequestPolicy + " n " + noticePolicy + " o " + overdueFinePolicy + " i " + lostItemFeePolicy, + secondRuleCondition + " : l " + loanPolicy + " r " + holdAndRecallRequestPolicy + " n " + noticePolicy + " o " + overdueFinePolicy + " i " + lostItemFeePolicy); + } } diff --git a/src/test/java/api/requests/RequestsAPICreationTests.java b/src/test/java/api/requests/RequestsAPICreationTests.java index 0595792bd6..ac05a9f465 100644 --- a/src/test/java/api/requests/RequestsAPICreationTests.java +++ b/src/test/java/api/requests/RequestsAPICreationTests.java @@ -193,7 +193,7 @@ public class RequestsAPICreationTests extends APITests { @AfterEach public void afterEach() { mockClockManagerToReturnDefaultDateTime(); - configurationsFixture.deleteTlrFeatureConfig(); + settingsFixture.deleteTlrFeatureSettings(); } @Test @@ -472,7 +472,7 @@ void cannotCreateItemLevelRequestForUnknownInstance(String tlrFeatureStatus, @ParameterizedTest @CsvSource({"Page", "Hold", "Recall"}) void cannotCreateTitleLevelRequestForUnknownInstance(String requestType) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID patronId = usersFixture.charlotte().getId(); final UUID pickupServicePointId = servicePointsFixture.cd1().getId(); @@ -520,7 +520,7 @@ void cannotCreateTitleLevelRequestForUnknownInstance(String requestType) { }) void cannotCreateRequestForUnknownItem(String tlrFeatureEnabledString, String requestType) { if (Boolean.parseBoolean(tlrFeatureEnabledString)) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); } IndividualResource instance = instancesFixture.basedUponDunkirk(); @@ -608,7 +608,7 @@ void canCreateTitleLevelRequestWhenTlrEnabled() { final var items = itemsFixture.createMultipleItemsForTheSameInstance(2); UUID instanceId = items.get(0).getInstanceId(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); IndividualResource requestResource = requestsClient.create(new RequestBuilder() .page() @@ -635,7 +635,7 @@ void cannotCreateRequestWithNonExistentRequestLevelWhenTlrEnabled() { ItemResource item = itemsFixture.basedUponSmallAngryPlanet(); UUID instanceId = item.getInstanceId(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); Response postResponse = requestsClient.attemptCreate(new RequestBuilder() .recall() @@ -742,7 +742,7 @@ void cannotCreateTlrWhenUserAlreadyRequestedAnItemFromTheSameTitle() { @ParameterizedTest @EnumSource(value = RequestType.class, names = {"HOLD", "RECALL"}) void cannotCreateHoldTlrWhenAvailableItemForInstance(RequestType requestType) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource item = items.get(0); @@ -1376,7 +1376,7 @@ void canCreatePagedRequestWhenItemStatusIsAvailable() { void cannotCreateTitleLevelPagedRequestIfThereAreNoAvailableItems() { UUID patronId = usersFixture.charlotte().getId(); final UUID pickupServicePointId = servicePointsFixture.cd1().getId(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID instanceId = instancesFixture.basedUponDunkirk().getId(); IndividualResource defaultWithHoldings = holdingsFixture.defaultWithHoldings(instanceId); @@ -1408,7 +1408,7 @@ void canCreateTlrRecallWhenAvailableItemExistsButPageIsNotAllowedByPolicy() { overdueFinePoliciesFixture.facultyStandard().getId(), lostItemFeePoliciesFixture.facultyStandard().getId()); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final var items = itemsFixture.createMultipleItemsForTheSameInstance(2); var instanceId = items.get(0).getInstanceId(); @@ -1430,7 +1430,7 @@ void canCreateTlrRecallWhenAvailableItemExistsButPageIsNotAllowedByPolicy() { void canCreateTitleLevelPagedRequest() { UUID patronId = usersFixture.charlotte().getId(); final UUID pickupServicePointId = servicePointsFixture.cd1().getId(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); IndividualResource uponDunkirkInstance = instancesFixture.basedUponDunkirk(); UUID instanceId = uponDunkirkInstance.getId(); @@ -1462,7 +1462,7 @@ void canCreateTitleLevelPagedRequest() { void canHaveUserBarcodeInCheckInPublishedEventAfterTitleLevelRequest() { UUID patronId = usersFixture.charlotte().getId(); final UUID pickupServicePointId = servicePointsFixture.cd1().getId(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); IndividualResource uponDunkirkInstance = instancesFixture.basedUponDunkirk(); UUID instanceId = uponDunkirkInstance.getId(); @@ -1495,7 +1495,7 @@ void cannotCreateItemLevelRequestIfTitleLevelRequestForInstanceAlreadyCreated() UUID patronId = usersFixture.charlotte().getId(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); buildItem(instanceId, "111"); requestsClient.create(buildPageTitleLevelRequest(patronId, pickupServicePointId, instanceId)); @@ -1517,7 +1517,7 @@ void cannotCreateTitleLevelRequestIfItemLevelRequestAlreadyCreated() { UUID patronId = usersFixture.charlotte().getId(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); buildItem(instanceId, "111"); ItemResource secondItem = buildItem(instanceId, "222"); @@ -1539,7 +1539,7 @@ void canCreateItemLevelRequestAndTitleLevelRequestForDifferentInstances() { UUID patronId = usersFixture.charlotte().getId(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId= UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); buildItem(instanceId, "111"); requestsClient.create(buildPageTitleLevelRequest(patronId, pickupServicePointId, @@ -1559,7 +1559,7 @@ void cannotCreateTwoTitleLevelRequestsForSameInstance() { UUID userId = usersFixture.charlotte().getId(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); buildItem(instanceId, "111"); buildItem(instanceId, "222"); @@ -1581,7 +1581,7 @@ void cannotCreateTwoItemLevelRequestsForSameItem() { UUID userId = usersFixture.charlotte().getId(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = buildItem(instanceId, "111"); requestsClient.create(buildItemLevelRequest(userId, pickupServicePointId, @@ -1699,7 +1699,7 @@ void canCreateRecallRequestWhenItemIsCheckedOut() { @Test void tlrRecallShouldPickItemWithLoanWithNextClosestDueDateIfAnotherRecallRequestExists() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); var londonZoneId = ZoneId.of("Europe/London"); var items = itemsFixture.createMultipleItemsForTheSameInstance(3); var firstItem = items.get(0); @@ -1725,7 +1725,7 @@ void tlrRecallShouldPickItemWithLoanWithNextClosestDueDateIfAnotherRecallRequest @Test void tlrRecallShouldPickRecalledLoanWithClosestDueDateIfThereAreNoNotRecalledLoansAndSameAmountOfRecalls() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); var londonZoneId = ZoneId.of("Europe/London"); var items = itemsFixture.createMultipleItemsForTheSameInstance(4); var firstItem = items.get(0); @@ -1771,7 +1771,7 @@ void tlrRecallWithoutLoanShouldPickRecallableItemFromRequestedInstance(String it IndividualResource inTransitPickupServicePoint = servicePointsFixture.cd2(); UUID instanceId = instancesFixture.basedUponDunkirk().getId(); IndividualResource defaultWithHoldings = holdingsFixture.defaultWithHoldings(instanceId); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); if (itemStatus.equals("Paged")) { itemsClient.create(new ItemBuilder() .forHolding(defaultWithHoldings.getId()) @@ -1815,7 +1815,7 @@ void tlrRecallShouldFailWhenRequestHasNoLoanOrRecallableItem(String itemStatus) IndividualResource requestPickupServicePoint = servicePointsFixture.cd1(); UUID instanceId = instancesFixture.basedUponDunkirk().getId(); IndividualResource defaultWithHoldings = holdingsFixture.defaultWithHoldings(instanceId); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); itemsFixture.basedUponDunkirk(holdingBuilder -> holdingBuilder, instanceBuilder -> instanceBuilder.withId(instanceId), itemBuilder -> itemBuilder @@ -2076,7 +2076,7 @@ void canCreateHoldRequestWhenItemIsMissing() { "Lost and paid", "Paged", "In process (non-requestable)", "Intellectual item", "Unavailable", "Restricted", "Unknown", "Awaiting delivery", "Order closed"}) void canCreateTlrHoldRequestWhenInstanceHasItemsWithStatusAllowedForHold(String itemStatus) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final ItemResource item = itemsFixture.basedUponSmallAngryPlanet( builder -> builder.withStatus(itemStatus)); IndividualResource response = requestsFixture.placeTitleLevelHoldShelfRequest(item.getInstanceId(), @@ -3245,7 +3245,7 @@ void cannotCreateItemLevelRequestWithoutInstanceId() { @Test void cannotCreateTitleLevelRequestWithoutInstanceId() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = itemsFixture.basedUponNod(); @@ -3302,7 +3302,7 @@ void cannotCreateRequestWithItemIdButNoHoldingsRecordId(RequestLevel requestLeve @Test void recallTlrRequestShouldBeAppliedToLoanWithClosestDueDate() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); @@ -3377,7 +3377,7 @@ void recallTlrRequestShouldBeAppliedToLoanWithClosestDueDate() { void statusOfTlrRequestShouldBeChangedIfAssociatedItemCheckedIn() { UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource firstItem = buildItem(instanceId, "111"); ItemResource secondItem = buildItem(instanceId, "222"); @@ -3405,7 +3405,7 @@ void statusOfTlrRequestShouldBeChangedIfAssociatedItemCheckedIn() { @Test void awaitingPickupNoticeShouldBeSentDuringCheckInWhenItemCreatedAfterHoldTlr() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); NoticePolicyBuilder noticePolicy = new NoticePolicyBuilder() .withName("Policy with available notice") @@ -3437,7 +3437,7 @@ void awaitingPickupNoticeShouldBeSentDuringCheckInWhenItemCreatedAfterHoldTlr() @Test void awaitingPickupNoticeShouldBeSentDuringCheckInWhenItemIsReturnedAndTlrHoldExists() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); NoticePolicyBuilder noticePolicy = new NoticePolicyBuilder() .withName("Policy with available notice") @@ -3467,7 +3467,7 @@ void awaitingPickupNoticeShouldBeSentDuringCheckInWhenItemIsReturnedAndTlrHoldEx @Test void awaitingPickupNoticesShouldBeSentToMultiplePatronsDuringPagedItemsCheckIn() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); NoticePolicyBuilder noticePolicy = new NoticePolicyBuilder() .withName("Policy with available notice") .withLoanNotices(Collections.singletonList(new NoticeConfigurationBuilder() @@ -3583,7 +3583,7 @@ void pageRequestShouldNotBeCreatedIffulfillmentPreferenceIsNotValid(String fulfi @Test void itemCheckOutShouldNotAffectRequestAssociatedWithAnotherItemOfInstance() { UUID instanceId = UUID.randomUUID(); - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource firstItem = buildItem(instanceId, "111"); ItemResource secondItem = buildItem(instanceId, "222"); ZonedDateTime requestDate = ZonedDateTime.of(2021, 7, 22, 10, 22, 54, 0, UTC); @@ -3618,7 +3618,7 @@ void itemCheckOutShouldNotAffectRequestAssociatedWithAnotherItemOfInstance() { @Test void itemCheckOutRecallRequestCreationShouldProduceNotice() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); JsonObject recallToLoaneeConfiguration = new NoticeConfigurationBuilder() .withTemplateId(UUID.randomUUID()) .withEventType(NoticeEventType.ITEM_RECALLED.getRepresentation()) @@ -3661,7 +3661,7 @@ void itemCheckOutRecallRequestCreationShouldProduceNotice() { @Test void itemCheckOutRecallCancelAgainRecallRequestCreationShouldProduceNotice() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); JsonObject recallToLoaneeConfiguration = new NoticeConfigurationBuilder() .withTemplateId(UUID.randomUUID()) .withEventType(NoticeEventType.ITEM_RECALLED.getRepresentation()) @@ -3718,7 +3718,7 @@ private void verifyNumberOfNoticeEventsForUser(UUID userId, int expectedNoticeEv @ParameterizedTest @ValueSource(ints = {1, 2, 3, 4, 5}) void titleLevelPageRequestIsCreatedForItemClosestToPickupServicePoint(int testCase) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID pickupServicePointId = servicePointsFixture.create(new ServicePointBuilder( "Pickup service point", "PICKUP", "Display name") @@ -3826,7 +3826,7 @@ void pageTlrSucceedsWhenClosestAvailableItemIsNotPageable() { // pickup service point is not requestable. At the same time, other available and requestable // items exist. - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); circulationRulesFixture.updateCirculationRules(differentRequestPoliciesBasedOnMaterialType()); UUID pickupServicePointId = servicePointsFixture.create(new ServicePointBuilder( @@ -3925,7 +3925,7 @@ void holdTlrShouldSucceedWhenAvailableItemsExistButTheyAreNotPageable() { // Hold TLR should be created when available items of the same instance exist, but all of them // are not pageable due to the request policy - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); circulationRulesFixture.updateCirculationRules(differentRequestPoliciesBasedOnMaterialType()); IndividualResource instance = instancesFixture.basedUponDunkirk(); @@ -3970,7 +3970,7 @@ void holdAndRecallTlrShouldFailWhenAvailablePageableItemsExist(RequestType type) // Hold TLR should fail when available items of the same instance exist and some of those // items are pageable (request policy allows page requests) - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); circulationRulesFixture.updateCirculationRules(differentRequestPoliciesBasedOnMaterialType()); IndividualResource instance = instancesFixture.basedUponDunkirk(); @@ -4023,7 +4023,7 @@ void holdAndRecallTlrShouldFailWhenAvailablePageableItemsExist(RequestType type) @Test void recallTlrShouldNotBeCreatedForInstanceWithOnlyAgedToLostItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); @@ -4039,7 +4039,7 @@ void recallTlrShouldNotBeCreatedForInstanceWithOnlyAgedToLostItem() { @Test void recallTlrShouldBeCreatedForOnOrderItemIfInstanceHasOnOrderAndDeclaredLostItems() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); useLostItemPolicy(lostItemFeePoliciesFixture.chargeFee().getId()); UUID pickupServicePointId = servicePointsFixture.cd1().getId(); UUID instanceId = UUID.randomUUID(); @@ -4078,7 +4078,7 @@ void recallTlrShouldBeCreatedForOnOrderItemIfInstanceHasOnOrderAndDeclaredLostIt @Test void recallTlrShouldNotBeCreatedForInstanceWithOnlyDeclaredLostItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); useLostItemPolicy(lostItemFeePoliciesFixture.chargeFee().getId()); UUID instanceId = UUID.randomUUID(); @@ -4099,7 +4099,7 @@ void recallTlrShouldNotBeCreatedForInstanceWithOnlyDeclaredLostItem() { @Test void recallTlrShouldNotBeCreatedForInstanceWithOnlyClaimedReturnedItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); useLostItemPolicy(lostItemFeePoliciesFixture.chargeFee().getId()); UUID instanceId = UUID.randomUUID(); @@ -4120,7 +4120,7 @@ void recallTlrShouldNotBeCreatedForInstanceWithOnlyClaimedReturnedItem() { @Test void recallTlrShouldFailWhenNotAllowedByPolicy() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); circulationRulesFixture.updateCirculationRules(differentRequestPoliciesBasedOnMaterialType()); IndividualResource instance = instancesFixture.basedUponDunkirk(); @@ -4161,7 +4161,7 @@ void recallTlrShouldFailWhenNotAllowedByPolicy() { @Test void holdTlrShouldSucceedEvenWhenPolicyDoesNotAllowHolds() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); circulationRulesFixture.updateCirculationRules(differentRequestPoliciesBasedOnMaterialType()); IndividualResource instance = instancesFixture.basedUponDunkirk(); @@ -4203,7 +4203,7 @@ void holdTlrShouldSucceedEvenWhenPolicyDoesNotAllowHolds() { @Test void titleLevelHoldFailsWhenItShouldFollowCirculationRulesAndNoneOfInstanceItemsAreAllowedForHold() { // enable TLR feature and make Hold requests respect circulation rules - configurationsFixture.configureTlrFeature(true, true, null, null, null); + settingsFixture.configureTlrFeature(true, true, null, null, null); IndividualResource book = materialTypesFixture.book(); IndividualResource video = materialTypesFixture.videoRecording(); @@ -4231,7 +4231,7 @@ void titleLevelHoldFailsWhenItShouldFollowCirculationRulesAndNoneOfInstanceItems @Test void titleLevelHoldIsPlacedWhenItShouldFollowCirculationRulesAndOneOfInstanceItemsIsAllowedForHold() { // enable TLR feature and make Hold requests respect circulation rules - configurationsFixture.configureTlrFeature(true, true, null, null, null); + settingsFixture.configureTlrFeature(true, true, null, null, null); IndividualResource book = materialTypesFixture.book(); IndividualResource video = materialTypesFixture.videoRecording(); @@ -4255,7 +4255,7 @@ void titleLevelHoldIsPlacedWhenItShouldFollowCirculationRulesAndOneOfInstanceIte @Test void titleLevelHoldIsPlacedWhenItCanIgnoreCirculationRulesAndNoneOfInstanceItemsAreAllowedForHold() { // enable TLR feature and make Hold requests ignore circulation rules - configurationsFixture.configureTlrFeature(true, false, null, null, null); + settingsFixture.configureTlrFeature(true, false, null, null, null); IndividualResource book = materialTypesFixture.book(); IndividualResource video = materialTypesFixture.videoRecording(); @@ -4316,7 +4316,7 @@ void itemLevelRequestIsNotPlacedWhenRequestPolicyDisallowsRequestedPickupService void titleLevelRequestIsNotPlacedWhenRequestPolicyDisallowsRequestedPickupServicePoint( RequestType requestType) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final UUID requestPolicyId = UUID.randomUUID(); policiesActivation.use(new RequestPolicyBuilder( @@ -4612,7 +4612,7 @@ void canCreateHoldTlrForInstanceWithNoHoldingsRecords() { @Test void recallTlrShouldSucceedWhenItNeedsToPickLeastRecalledLoanAndRequestsWithNoLoansAreInTheQueueScenario1() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final var patron1 = usersFixture.charlotte(); final var patron2 = usersFixture.jessica(); @@ -4711,7 +4711,7 @@ void recallTlrShouldSucceedWhenItNeedsToPickLeastRecalledLoanAndRequestsWithNoLo @Test void recallTlrShouldSucceedWhenItNeedsToPickLeastRecalledLoanAndRequestsWithNoLoansAreInTheQueueScenario2() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final var patron1 = usersFixture.charlotte(); final var patron2 = usersFixture.jessica(); diff --git a/src/test/java/api/requests/RequestsAPILoanHistoryTests.java b/src/test/java/api/requests/RequestsAPILoanHistoryTests.java index 4767c605d4..85c129025a 100644 --- a/src/test/java/api/requests/RequestsAPILoanHistoryTests.java +++ b/src/test/java/api/requests/RequestsAPILoanHistoryTests.java @@ -45,7 +45,7 @@ void creatingRecallRequestChangesTheOpenLoanForTheSameItem() { @Test void checkOutShouldNotTruncateLoanIfRecallRequestExistsForAnotherItemOfTheSameInstanceIfTlrIsEnabled() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final var items = itemsFixture.createMultipleItemsForTheSameInstance(2); var steve = usersFixture.steve(); var charlotte = usersFixture.charlotte(); diff --git a/src/test/java/api/requests/RequestsAPILoanRenewalTests.java b/src/test/java/api/requests/RequestsAPILoanRenewalTests.java index 20434ac2be..5b82140520 100644 --- a/src/test/java/api/requests/RequestsAPILoanRenewalTests.java +++ b/src/test/java/api/requests/RequestsAPILoanRenewalTests.java @@ -302,7 +302,7 @@ void forbidRenewalLoanByIdWhenLoanProfileIsFixedFirstRequestInQueueIsHoldAndRene @Test void allowRenewalWhenFirstRequestInQueueIsItemLevelHoldForDifferentItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); loanPolicyWithRollingProfileAndRenewingIsForbiddenWhenHoldIsPending(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource itemForLoan = items.get(0); @@ -316,7 +316,7 @@ void allowRenewalWhenFirstRequestInQueueIsItemLevelHoldForDifferentItemOfSameIns @Test void allowRenewalWhenFirstRequestInQueueIsTitleLevelHoldForDifferentItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); loanPolicyWithRollingProfileAndRenewingIsForbiddenWhenHoldIsPending(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource itemForLoan = items.get(0); @@ -334,7 +334,7 @@ void allowRenewalWhenFirstRequestInQueueIsTitleLevelHoldForDifferentItemOfSameIn @Test void forbidRenewalWhenFirstRequestInQueueIsTitleLevelHoldWithoutItemId() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); loanPolicyWithRollingProfileAndRenewingIsForbiddenWhenHoldIsPending(); ItemResource item = itemsFixture.basedUponNod(); UserResource borrower = usersFixture.charlotte(); @@ -348,7 +348,7 @@ void forbidRenewalWhenFirstRequestInQueueIsTitleLevelHoldWithoutItemId() { @Test void alternateLoanPeriodIsNotUsedWhenFirstRequestInQueueIsItemLevelHoldForDifferentItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); useRollingPolicyWithRenewingAllowedForHoldingRequest(); // base loan period - 3 weeks, alternate - 4 weeks List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource itemForLoan = items.get(0); @@ -364,7 +364,7 @@ void alternateLoanPeriodIsNotUsedWhenFirstRequestInQueueIsItemLevelHoldForDiffer @Test void alternateLoanPeriodIsNotUsedForRenewalWhenFirstRequestInQueueIsTitleLevelHoldForDifferentItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); useRollingPolicyWithRenewingAllowedForHoldingRequest(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource itemForLoan = items.get(0); @@ -384,7 +384,7 @@ void alternateLoanPeriodIsNotUsedForRenewalWhenFirstRequestInQueueIsTitleLevelHo @Test void alternateLoanPeriodIsUsedForRenewalWhenFirstRequestInQueueIsTitleLevelHoldWithoutItemId() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); useRollingPolicyWithRenewingAllowedForHoldingRequest(); // base loan period - 3 weeks, alternate - 4 weeks ItemResource item = itemsFixture.basedUponNod(); UUID instanceId = item.getInstanceId(); @@ -400,7 +400,7 @@ void alternateLoanPeriodIsUsedForRenewalWhenFirstRequestInQueueIsTitleLevelHoldW @Test void forbidRenewalWhenTitleLevelRecallRequestExistsForSameItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource item = itemsFixture.basedUponNod(); UserResource borrower = usersFixture.james(); checkOutFixture.checkOutByBarcode(item, borrower); @@ -418,7 +418,7 @@ void forbidRenewalWhenTitleLevelRecallRequestExistsForSameItem() { @Test void allowRenewalWhenTitleLevelRecallRequestExistsForDifferentItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource itemForLoan = items.get(0); ItemResource itemForRequest = items.get(1); @@ -598,7 +598,7 @@ void forbidRenewalOverrideWhenRecallIsForDifferentItemOfSameInstance() { @Test void forbidRenewalOverrideWhenTitleLevelRecallRequestExistsForDifferentItemOfSameInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); List items = itemsFixture.createMultipleItemsForTheSameInstance(2); ItemResource itemForLoan = items.get(0); ItemResource itemForRequest = items.get(1); diff --git a/src/test/java/api/requests/RequestsAPIRetrievalTests.java b/src/test/java/api/requests/RequestsAPIRetrievalTests.java index 6605d72546..17fdb28469 100644 --- a/src/test/java/api/requests/RequestsAPIRetrievalTests.java +++ b/src/test/java/api/requests/RequestsAPIRetrievalTests.java @@ -244,7 +244,7 @@ void canGetARequestById() { @Test void titleLevelRequestRetrievalById() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); UUID isbnIdentifierId = identifierTypesFixture.isbn().getId(); String isbnValue = "9780866989427"; diff --git a/src/test/java/api/requests/RequestsAPIUpdatingTests.java b/src/test/java/api/requests/RequestsAPIUpdatingTests.java index a0483f1712..ba382cb4f7 100644 --- a/src/test/java/api/requests/RequestsAPIUpdatingTests.java +++ b/src/test/java/api/requests/RequestsAPIUpdatingTests.java @@ -866,7 +866,7 @@ void shouldUseCurrentUserIdAsSourceWhenSendingUpdateRequestMessage() { @Test void editingRecallTlrShouldNotChangeRecalledItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); IndividualResource patron1 = usersFixture.steve(); IndividualResource patron2 = usersFixture.rebecca(); diff --git a/src/test/java/api/requests/scenarios/HoldShelfFulfillmentTests.java b/src/test/java/api/requests/scenarios/HoldShelfFulfillmentTests.java index ede4fbf442..91dceb4c8e 100644 --- a/src/test/java/api/requests/scenarios/HoldShelfFulfillmentTests.java +++ b/src/test/java/api/requests/scenarios/HoldShelfFulfillmentTests.java @@ -24,8 +24,6 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.folio.circulation.support.http.client.Response; import org.folio.circulation.support.utils.ClockUtil; @@ -38,14 +36,13 @@ import api.support.APITests; import api.support.TlrFeatureStatus; import api.support.builders.CheckInByBarcodeRequestBuilder; -import api.support.builders.InstanceBuilder; import api.support.http.IndividualResource; import api.support.http.ItemResource; class HoldShelfFulfillmentTests extends APITests { @AfterEach public void afterEach() { - configurationsFixture.deleteTlrFeatureConfig(); + settingsFixture.deleteTlrFeatureSettings(); } @ParameterizedTest @@ -84,7 +81,7 @@ void itemIsReadyForPickUpWhenCheckedInAtPickupServicePoint(TlrFeatureStatus tlrF @ParameterizedTest @ValueSource(ints = {1, 2}) void tlrRequestIsPositionedCorrectlyInUnifiedQueue(int checkedInItemNumber) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final IndividualResource pickupServicePoint = servicePointsFixture.cd1(); @@ -222,7 +219,7 @@ void canBeCheckedOutToRequestingPatronWhenReadyForPickup(TlrFeatureStatus tlrFea @Test void canBeCheckedOutToPatronRequestingTitleWhenReadyForPickup() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final IndividualResource pickupServicePoint = servicePointsFixture.cd1(); @@ -288,7 +285,7 @@ void checkInAtDifferentServicePointPlacesItemInTransit(TlrFeatureStatus tlrFeatu @Test void checkInItemWithTlrRequestAtDifferentServicePointPlacesItemInTransit() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final IndividualResource pickupServicePoint = servicePointsFixture.cd1(); final IndividualResource checkInServicePoint = servicePointsFixture.cd2(); @@ -363,7 +360,7 @@ void canBeCheckedOutToRequestingPatronWhenInTransit(TlrFeatureStatus tlrFeatureS @Test void canCheckoutItemForTitleLevelRequestWhenInTransit() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final IndividualResource pickupServicePoint = servicePointsFixture.cd1(); final IndividualResource checkInServicePoint = servicePointsFixture.cd2(); @@ -430,7 +427,7 @@ void itemIsReadyForPickUpWhenCheckedInAtPickupServicePointAfterTransit(TlrFeatur @Test void itemWithTlrRequestIsReadyForPickUpWhenCheckedInAtPickupServicePointAfterTransit() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final IndividualResource pickupServicePoint = servicePointsFixture.cd1(); final IndividualResource checkInServicePoint = servicePointsFixture.cd2(); @@ -508,7 +505,7 @@ void cannotCheckOutToOtherPatronWhenRequestIsAwaitingPickup(TlrFeatureStatus tlr @Test void cannotCheckOutToOtherPatronWhenTlrRequestIsAwaitingPickup() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); ItemResource smallAngryPlanet = itemsFixture.basedUponSmallAngryPlanet(); IndividualResource james = usersFixture.james(); @@ -580,7 +577,7 @@ void cannotCheckOutToOtherPatronWhenRequestIsInTransitForPickup(TlrFeatureStatus @Test void cannotCheckOutToOtherPatronWhenTlrRequestIsInTransitForPickup() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); final IndividualResource requestServicePoint = servicePointsFixture.cd1(); final IndividualResource checkInServicePoint = servicePointsFixture.cd2(); diff --git a/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java b/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java index e8e3022147..8ab2ea5b64 100644 --- a/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java +++ b/src/test/java/api/requests/scenarios/LoanDueDatesAfterRecallTests.java @@ -285,7 +285,7 @@ void recallRequestWithMGDAndRDValuesChangesDueDateToMGDWithCLDDM() { setFallbackPolicies(canCirculateRollingPolicy); - servicePointsFixture.create(new ServicePointBuilder(checkOutServicePointId, "CLDDM Desk", "clddm", "CLDDM Desk Test", null, null, TRUE, null, null)); + servicePointsFixture.create(new ServicePointBuilder(checkOutServicePointId, "CLDDM Desk", "clddm", "CLDDM Desk Test", null, null, TRUE, null, null, null)); // We use the loan date to calculate the minimum guaranteed due date (MGD) final ZonedDateTime loanDate = diff --git a/src/test/java/api/requests/scenarios/MoveRequestTests.java b/src/test/java/api/requests/scenarios/MoveRequestTests.java index 7a53868f15..2f9a30856c 100644 --- a/src/test/java/api/requests/scenarios/MoveRequestTests.java +++ b/src/test/java/api/requests/scenarios/MoveRequestTests.java @@ -184,7 +184,7 @@ void canMoveRequestFromOneItemCopyToAnother() { @Test void itemShouldRemainPagedIfHoldCreatedAfterRequestHasBeenMovedToAnotherItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(2); val firstItem = items.get(0); @@ -208,7 +208,7 @@ void itemShouldRemainPagedIfHoldCreatedAfterRequestHasBeenMovedToAnotherItem() { @Test void canMovePageTlrToAvailableItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val firstItem = itemsFixture.basedUponSmallAngryPlanet("89809"); val pageIlrForFirstItem = requestsFixture.placeTitleLevelPageRequest(firstItem.getInstanceId(), @@ -227,7 +227,7 @@ void canMovePageTlrToAvailableItem() { @Test void canMovePageTlrToRecall() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val firstItem = itemsFixture.basedUponSmallAngryPlanet("89809"); val pageTlrForFirstItem = requestsFixture.placeTitleLevelPageRequest(firstItem.getInstanceId(), usersFixture.james()); @@ -249,7 +249,7 @@ void canMovePageTlrToRecall() { @Test void canMoveRecallTlrToAnotherItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(2); val firstItem = items.get(0); @@ -269,7 +269,7 @@ void canMoveRecallTlrToAnotherItem() { @Test void canMoveRecallTlrToPage() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(2); val firstItem = items.get(0); @@ -289,7 +289,7 @@ void canMoveRecallTlrToPage() { @Test void whenRequestIsMovedItemShouldBecomeAvailableIfThereAreNoRequestsInTheQueueForThisItemIfTlrIsEnabled() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(2); val firstItem = items.get(0); @@ -308,7 +308,7 @@ void whenRequestIsMovedItemShouldBecomeAvailableIfThereAreNoRequestsInTheQueueFo @Test void whenRequestIsMovedPositionsShouldBeConsistentWhenTlrIsEnabled() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(3); @@ -369,7 +369,7 @@ void whenRequestIsMovedPositionsShouldBeConsistentWhenTlrIsEnabled() { @Test void cannotMoveRequestToAnItemFromDifferentInstance() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val nod = itemsFixture.basedUponNod(); val uprooted = itemsFixture.basedUponUprooted(); @@ -387,7 +387,7 @@ void cannotMoveRequestToAnItemFromDifferentInstance() { @Test void cannotMoveToOrFromHoldTlr() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(2); val firstItem = items.get(0); @@ -416,7 +416,7 @@ void cannotMoveToOrFromHoldTlr() { @Test void cannotMoveTlrToTheSameItem() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val item = itemsFixture.basedUponNod(); val jessica = usersFixture.jessica(); @@ -435,7 +435,7 @@ void cannotMoveTlrToTheSameItem() { @Test void cannotMoveTlrWhenFeatureIsDisabled() { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); val items = itemsFixture.createMultipleItemsForTheSameInstance(2); val firstItem = items.get(0); @@ -446,7 +446,7 @@ void cannotMoveTlrWhenFeatureIsDisabled() { val firstItemHoldTlr = requestsFixture.placeTitleLevelHoldShelfRequest(firstItem.getInstanceId(), usersFixture.james()); - configurationsFixture.disableTlrFeature(); + settingsFixture.disableTlrFeature(); Response response = requestsFixture.attemptMove(new MoveRequestBuilder(firstItemHoldTlr.getId(), secondItem.getId())); diff --git a/src/test/java/api/support/APITestContext.java b/src/test/java/api/support/APITestContext.java index 167f11e91e..0f7890eb34 100644 --- a/src/test/java/api/support/APITestContext.java +++ b/src/test/java/api/support/APITestContext.java @@ -9,6 +9,7 @@ import java.net.URL; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; +import java.util.Optional; import java.util.Properties; import java.util.Random; import java.util.concurrent.CompletableFuture; @@ -40,6 +41,7 @@ public class APITestContext { private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public static final String TENANT_ID = "test_tenant"; + public static String tempTenantId; private static String USER_ID = "79ff2a8b-d9c3-5b39-ad4a-0a84025ab085"; private static final String TOKEN = "eyJhbGciOiJIUzUxMiJ9eyJzdWIiOiJhZG1pbiIsInVzZXJfaWQiOiI3OWZmMmE4Yi1kOWMzLTViMzktYWQ0YS0wYTg0MDI1YWIwODUiLCJ0ZW5hbnQiOiJ0ZXN0X3RlbmFudCJ9BShwfHcNClt5ZXJ8ImQTMQtAM1sQEnhsfWNmXGsYVDpuaDN3RVQ9"; @@ -66,7 +68,15 @@ static String getToken() { } public static String getTenantId() { - return TENANT_ID; + return Optional.ofNullable(tempTenantId).orElse(TENANT_ID); + } + + public static void setTempTenantId(String tenantId) { + tempTenantId = tenantId; + } + + public static void clearTempTenantId() { + setTempTenantId(null); } public static String getUserId() { diff --git a/src/test/java/api/support/APITests.java b/src/test/java/api/support/APITests.java index b872ce7165..ab7929fb73 100644 --- a/src/test/java/api/support/APITests.java +++ b/src/test/java/api/support/APITests.java @@ -24,6 +24,17 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +import api.support.fixtures.SearchInstanceFixture; +import org.junit.Assert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.testcontainers.containers.KafkaContainer; +import org.testcontainers.utility.DockerImageName; + +import api.support.fakes.FakeModNotify; +import api.support.fakes.FakePubSub; +import api.support.fakes.FakeStorageModule; import api.support.fixtures.AddInfoFixture; import api.support.fixtures.AddressTypesFixture; import api.support.fixtures.AgeToLostFixture; @@ -36,7 +47,6 @@ import api.support.fixtures.CirculationItemsFixture; import api.support.fixtures.CirculationRulesFixture; import api.support.fixtures.ClaimItemReturnedFixture; -import api.support.fixtures.ConfigurationsFixture; import api.support.fixtures.DeclareLostFixtures; import api.support.fixtures.DepartmentFixture; import api.support.fixtures.EndPatronSessionClient; @@ -70,16 +80,6 @@ import api.support.fixtures.TenantActivationFixture; import api.support.fixtures.UserManualBlocksFixture; import api.support.fixtures.UsersFixture; -import org.junit.Assert; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.testcontainers.containers.KafkaContainer; -import org.testcontainers.utility.DockerImageName; - -import api.support.fakes.FakeModNotify; -import api.support.fakes.FakePubSub; -import api.support.fakes.FakeStorageModule; import api.support.fixtures.policies.PoliciesActivationFixture; import api.support.http.IndividualResource; import api.support.http.ResourceClient; @@ -235,9 +235,6 @@ public abstract class APITests { protected final AddressTypesFixture addressTypesFixture = new AddressTypesFixture(ResourceClient.forAddressTypes()); - protected final ConfigurationsFixture configurationsFixture = - new ConfigurationsFixture(configClient); - protected final PatronGroupsFixture patronGroupsFixture = new PatronGroupsFixture(patronGroupsClient); @@ -303,6 +300,7 @@ public abstract class APITests { protected final DepartmentFixture departmentFixture = new DepartmentFixture(); protected final CheckOutLockFixture checkOutLockFixture = new CheckOutLockFixture(); protected final SettingsFixture settingsFixture = new SettingsFixture(); + protected final SearchInstanceFixture searchFixture = new SearchInstanceFixture(); protected APITests() { this(true, false); @@ -449,13 +447,13 @@ protected void mockClockManagerToReturnDefaultDateTime() { protected void reconfigureTlrFeature(TlrFeatureStatus tlrFeatureStatus) { if (tlrFeatureStatus == TlrFeatureStatus.ENABLED) { - configurationsFixture.enableTlrFeature(); + settingsFixture.enableTlrFeature(); } else if (tlrFeatureStatus == TlrFeatureStatus.DISABLED) { - configurationsFixture.disableTlrFeature(); + settingsFixture.disableTlrFeature(); } else { - configurationsFixture.deleteTlrFeatureConfig(); + settingsFixture.deleteTlrFeatureSettings(); } } @@ -471,15 +469,15 @@ protected void reconfigureTlrFeature(TlrFeatureStatus tlrFeatureStatus, UUID cancellationTemplateId, UUID expirationTemplateId) { if (tlrFeatureStatus == TlrFeatureStatus.ENABLED) { - configurationsFixture.configureTlrFeature(true, tlrHoldShouldFollowCirculationRules, + settingsFixture.configureTlrFeature(true, tlrHoldShouldFollowCirculationRules, confirmationTemplateId, cancellationTemplateId, expirationTemplateId); } else if (tlrFeatureStatus == TlrFeatureStatus.DISABLED) { - configurationsFixture.configureTlrFeature(false, tlrHoldShouldFollowCirculationRules, + settingsFixture.configureTlrFeature(false, tlrHoldShouldFollowCirculationRules, confirmationTemplateId, cancellationTemplateId, expirationTemplateId); } else { - configurationsFixture.deleteTlrFeatureConfig(); + settingsFixture.deleteTlrFeatureSettings(); } } diff --git a/src/test/java/api/support/RestAssuredConfiguration.java b/src/test/java/api/support/RestAssuredConfiguration.java index bae905f073..e14bf8db7f 100644 --- a/src/test/java/api/support/RestAssuredConfiguration.java +++ b/src/test/java/api/support/RestAssuredConfiguration.java @@ -27,7 +27,7 @@ public static RequestSpecification standardHeaders(OkapiHeaders okapiHeaders) { final HashMap headers = new HashMap<>(); headers.put(OKAPI_URL, okapiHeaders.getUrl().toString()); - headers.put(TENANT, okapiHeaders.getTenantId()); + headers.put(TENANT, APITestContext.getTenantId()); headers.put(TOKEN, okapiHeaders.getToken()); headers.put(REQUEST_ID, okapiHeaders.getRequestId()); headers.put(OKAPI_PERMISSIONS, okapiHeaders.getOkapiPermissions()); diff --git a/src/test/java/api/support/builders/SearchInstanceBuilder.java b/src/test/java/api/support/builders/SearchInstanceBuilder.java new file mode 100644 index 0000000000..92e67bdeb3 --- /dev/null +++ b/src/test/java/api/support/builders/SearchInstanceBuilder.java @@ -0,0 +1,24 @@ +package api.support.builders; + +import java.util.List; + +import io.vertx.core.json.JsonObject; + +public class SearchInstanceBuilder extends JsonBuilder implements Builder { + + private final JsonObject searchInstance; + + public SearchInstanceBuilder(JsonObject searchInstance) { + this.searchInstance = searchInstance; + } + + @Override + public JsonObject create() { + return searchInstance; + } + + public SearchInstanceBuilder withItems(List items) { + put(searchInstance, "items", items); + return new SearchInstanceBuilder(searchInstance); + } +} diff --git a/src/test/java/api/support/builders/ServicePointBuilder.java b/src/test/java/api/support/builders/ServicePointBuilder.java index e2fb90ec3f..480f354a67 100644 --- a/src/test/java/api/support/builders/ServicePointBuilder.java +++ b/src/test/java/api/support/builders/ServicePointBuilder.java @@ -18,6 +18,7 @@ public class ServicePointBuilder extends JsonBuilder implements Builder { private final JsonObject holdShelfExpiryPeriod; private final String holdShelfClosedLibraryDateManagement; + private final Boolean ecsRequestRouting; public ServicePointBuilder( UUID id, @@ -28,7 +29,8 @@ public ServicePointBuilder( Integer shelvingLagTime, Boolean pickupLocation, JsonObject holdShelfExpiryPeriod, - String holdShelfClosedLibraryDateManagement) { + String holdShelfClosedLibraryDateManagement, + Boolean ecsRequestRouting) { this.id = id; this.name = name; this.code = code; @@ -38,6 +40,7 @@ public ServicePointBuilder( this.pickupLocation = pickupLocation; this.holdShelfExpiryPeriod = holdShelfExpiryPeriod; this.holdShelfClosedLibraryDateManagement = holdShelfClosedLibraryDateManagement; + this.ecsRequestRouting = ecsRequestRouting; } public ServicePointBuilder(String name, String code, String discoveryDisplayName) { @@ -50,6 +53,7 @@ public ServicePointBuilder(String name, String code, String discoveryDisplayName null, false, null, + null, null); } @@ -64,8 +68,9 @@ public static ServicePointBuilder from(IndividualResource response) { getIntegerProperty(representation, "shelvingLagTime", null), getBooleanProperty(representation, "pickupLocation"), getObjectProperty(representation, "holdShelfExpiryPeriod"), - getProperty(representation, "holdShelfClosedLibraryDateManagement") - ); + getProperty(representation, "holdShelfClosedLibraryDateManagement"), + getBooleanProperty(representation, "ecsRequestRouting") + ); } @Override @@ -80,6 +85,7 @@ public JsonObject create() { put(servicePoint, "pickupLocation", this.pickupLocation); put(servicePoint, "holdShelfExpiryPeriod", this.holdShelfExpiryPeriod); put(servicePoint, "holdShelfClosedLibraryDateManagement", this.holdShelfClosedLibraryDateManagement); + put(servicePoint, "ecsRequestRouting", this.ecsRequestRouting); return servicePoint; } @@ -94,7 +100,8 @@ public ServicePointBuilder withId(UUID newId) { this.shelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withName(String newName) { @@ -107,7 +114,8 @@ public ServicePointBuilder withName(String newName) { this.shelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withCode(String newCode) { @@ -120,7 +128,8 @@ public ServicePointBuilder withCode(String newCode) { this.shelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withDiscoveryDisplayName(String newDiscoveryDisplayName) { @@ -133,7 +142,8 @@ public ServicePointBuilder withDiscoveryDisplayName(String newDiscoveryDisplayNa this.shelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withDescription(String newDescription) { @@ -146,7 +156,8 @@ public ServicePointBuilder withDescription(String newDescription) { this.shelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withShelvingLagTime(Integer newShelvingLagTime) { @@ -159,7 +170,8 @@ public ServicePointBuilder withShelvingLagTime(Integer newShelvingLagTime) { newShelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withPickupLocation(Boolean newPickupLocation) { @@ -172,7 +184,8 @@ public ServicePointBuilder withPickupLocation(Boolean newPickupLocation) { this.shelvingLagTime, newPickupLocation, this.holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withHoldShelfExpriyPeriod(int duration, String intervalId) { @@ -190,7 +203,8 @@ public ServicePointBuilder withHoldShelfExpriyPeriod(int duration, String interv this.shelvingLagTime, this.pickupLocation, holdShelfExpiryPeriod, - this.holdShelfClosedLibraryDateManagement); + this.holdShelfClosedLibraryDateManagement, + this.ecsRequestRouting); } public ServicePointBuilder withholdShelfClosedLibraryDateManagement(String expirationDateManagement) { @@ -203,6 +217,21 @@ public ServicePointBuilder withholdShelfClosedLibraryDateManagement(String expir this.shelvingLagTime, this.pickupLocation, this.holdShelfExpiryPeriod, - expirationDateManagement); + expirationDateManagement, + this.ecsRequestRouting); + } + + public ServicePointBuilder withEcsRequestRouting(Boolean ecsRequestRouting) { + return new ServicePointBuilder( + this.id, + this.name, + this.code, + this.discoveryDisplayName, + this.description, + this.shelvingLagTime, + this.pickupLocation, + this.holdShelfExpiryPeriod, + this.holdShelfClosedLibraryDateManagement, + ecsRequestRouting); } } diff --git a/src/test/java/api/support/builders/SettingsBuilder.java b/src/test/java/api/support/builders/SettingsBuilder.java index dd954c07aa..380b52fef2 100644 --- a/src/test/java/api/support/builders/SettingsBuilder.java +++ b/src/test/java/api/support/builders/SettingsBuilder.java @@ -8,7 +8,7 @@ public class SettingsBuilder implements Builder { private final JsonObject representation; - public SettingsBuilder(UUID id, String scope, String key, String value) { + public SettingsBuilder(UUID id, String scope, String key, Object value) { this.representation = new JsonObject() .put("id", id) .put("scope", scope) diff --git a/src/test/java/api/support/fakes/FakeCQLToJSONInterpreter.java b/src/test/java/api/support/fakes/FakeCQLToJSONInterpreter.java index 5f9808ea09..8abd49675e 100644 --- a/src/test/java/api/support/fakes/FakeCQLToJSONInterpreter.java +++ b/src/test/java/api/support/fakes/FakeCQLToJSONInterpreter.java @@ -14,13 +14,34 @@ import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.folio.circulation.support.http.server.WebContext; import io.vertx.core.json.JsonObject; public class FakeCQLToJSONInterpreter { private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + public List execute(Collection records, String query, + WebContext context) { + + var initiallyFilteredRecords = execute(records, query); + + // Routing SP filtering + String includeRoutingServicePointsParam = context.getStringParameter( + "includeRoutingServicePoints"); + if (Boolean.parseBoolean(includeRoutingServicePointsParam)) { + return records.stream() + .filter(json -> json.containsKey("ecsRequestRouting") + ? json.getBoolean("ecsRequestRouting") + : false) + .toList(); + } + + return initiallyFilteredRecords; + } + public List execute(Collection records, String query) { + final var queryAndSort = splitQueryAndSort(query); final var cqlPredicate = new CqlPredicate(queryAndSort.left); diff --git a/src/test/java/api/support/fakes/FakeOkapi.java b/src/test/java/api/support/fakes/FakeOkapi.java index 5cbecb8542..c9b7c96836 100644 --- a/src/test/java/api/support/fakes/FakeOkapi.java +++ b/src/test/java/api/support/fakes/FakeOkapi.java @@ -23,11 +23,11 @@ import java.util.Objects; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.folio.circulation.support.ValidationErrorFailure; import org.folio.circulation.support.http.client.OkapiHttpClient; import org.folio.circulation.support.results.Result; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; @@ -231,6 +231,12 @@ public void start(Promise startFuture) throws IOException { FakeCalendarOkapi.registerCalendarSurroundingDates(router); registerFakeStorageLoansAnonymize(router); + new FakeSearchModule().register(router); + new FakeStorageModuleBuilder() + .withRecordName(FakeSearchModule.recordTypeName) + .withRootPath("/search/instances") + .create().register(router); + new FakeStorageModuleBuilder() .withRecordName("institution") .withRootPath("/location-units/institutions") @@ -276,6 +282,7 @@ public void start(Promise startFuture) throws IOException { .withRootPath("/service-points") .withRequiredProperties("name", "code", "discoveryDisplayName") .withUniqueProperties("name") + .withQueryParameters("includeRoutingServicePoints") .withChangeMetadata() .disallowCollectionDelete() .create() @@ -406,7 +413,6 @@ public void start(Promise startFuture) throws IOException { .withRootPath("/settings/entries") .withCollectionPropertyName("items") .withChangeMetadata() - .withRecordConstraint(this::userHasAlreadyAcquiredLock) .create().register(router); new FakeStorageModuleBuilder() diff --git a/src/test/java/api/support/fakes/FakeSearchModule.java b/src/test/java/api/support/fakes/FakeSearchModule.java new file mode 100644 index 0000000000..db1e07319a --- /dev/null +++ b/src/test/java/api/support/fakes/FakeSearchModule.java @@ -0,0 +1,103 @@ +package api.support.fakes; + +import static java.lang.String.format; +import static org.folio.circulation.support.results.CommonFailures.failedDueToServerError; + +import java.lang.invoke.MethodHandles; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.folio.circulation.support.http.server.ClientErrorResponse; +import org.folio.circulation.support.http.server.WebContext; +import org.folio.circulation.support.results.Result; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.json.Json; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; +import lombok.SneakyThrows; + +public class FakeSearchModule { + + private static final Logger log = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + public static final String recordTypeName = "search-instance"; + private static final String ID_REGEXP = "id\\s*(==|!=|>|>=|<|<=|\\|=|\\|=)\\s*" + + "([a-f0-9\\-]+)"; + private final Storage storage; + private final Pattern idPattern; + private static final String ROOT_PATH = "/search/instances"; + + public FakeSearchModule() { + this.idPattern = Pattern.compile(ID_REGEXP); + this.storage = Storage.getStorage(); + } + + @SneakyThrows + public void register(Router router) { + router.get("/search/instances").handler(this::getById); + } + + private void getById(RoutingContext routingContext) { + WebContext context = new WebContext(routingContext); + + Result idParsingResult = getIdParameter(routingContext); + + if (idParsingResult.failed()) { + idParsingResult.cause().writeTo(routingContext.response()); + return; + } + + Map resourcesForTenant = getResourcesForTenant(context); + + final String id = idParsingResult.value().toString(); + + if (resourcesForTenant.containsKey(id)) { + final JsonObject resourceRepresentation = resourcesForTenant.get(id); + + final JsonObject searchResult = new JsonObject(); + + searchResult.put("totalRecords", 1); + searchResult.put("instances", List.of(resourceRepresentation)); + + log.debug("Found {} resource: {}", recordTypeName, + searchResult.encodePrettily()); + + HttpServerResponse response = routingContext.response(); + Buffer buffer = Buffer.buffer(Json.encodePrettily(searchResult), "UTF-8"); + + response.setStatusCode(200); + response.putHeader("content-type", "application/json; charset=utf-8"); + response.putHeader("content-length", Integer.toString(buffer.length())); + + response.write(buffer); + response.end(); + } + else { + log.debug("Failed to find {} resource: {}", recordTypeName, + idParsingResult); + + ClientErrorResponse.notFound(routingContext.response()); + } + } + + private Result getIdParameter(RoutingContext routingContext) { + final String query = routingContext.request().getParam("query"); + Matcher matcher = idPattern.matcher(query); + String id = matcher.find() ? matcher.group(2) : null; + + return Result.of(() -> UUID.fromString(id)) + .mapFailure(r -> failedDueToServerError(format( + "ID parameter \"%s\" is not a valid UUID", id))); + } + + private Map getResourcesForTenant(WebContext context) { + return storage.getTenantResources(ROOT_PATH, context.getTenantId()); + } +} diff --git a/src/test/java/api/support/fakes/FakeStorageModule.java b/src/test/java/api/support/fakes/FakeStorageModule.java index a3cf6cdff1..d0193bd02e 100644 --- a/src/test/java/api/support/fakes/FakeStorageModule.java +++ b/src/test/java/api/support/fakes/FakeStorageModule.java @@ -320,7 +320,7 @@ private void getById(RoutingContext routingContext) { Result idParsingResult = getIdParameter(routingContext); - if(idParsingResult.failed()) { + if (idParsingResult.failed()) { idParsingResult.cause().writeTo(routingContext.response()); return; } @@ -329,7 +329,7 @@ private void getById(RoutingContext routingContext) { final String id = idParsingResult.value().toString(); - if(resourcesForTenant.containsKey(id)) { + if (resourcesForTenant.containsKey(id)) { final JsonObject resourceRepresentation = resourcesForTenant.get(id); log.debug("Found {} resource: {}", recordTypeName, @@ -378,8 +378,8 @@ private void getMany(RoutingContext routingContext) { Map resourcesForTenant = getResourcesForTenant(context); - List filteredItems = new FakeCQLToJSONInterpreter() - .execute(resourcesForTenant.values(), query); + List filteredItems = getFakeCQLToJSONInterpreter() + .execute(resourcesForTenant.values(), query, context); List pagedItems = filteredItems.stream() .skip(offset) @@ -408,6 +408,10 @@ private void getMany(RoutingContext routingContext) { response.end(); } + FakeCQLToJSONInterpreter getFakeCQLToJSONInterpreter() { + return new FakeCQLToJSONInterpreter(); + } + private void empty(RoutingContext routingContext) { WebContext context = new WebContext(routingContext); @@ -430,7 +434,7 @@ private void deleteMany(RoutingContext routingContext) { Map resourcesForTenant = getResourcesForTenant(context); - new FakeCQLToJSONInterpreter() + getFakeCQLToJSONInterpreter() .execute(resourcesForTenant.values(), query) .forEach(item -> resourcesForTenant.remove(item.getString("id"))); @@ -444,7 +448,7 @@ private void delete(RoutingContext routingContext) { Map resourcesForTenant = getResourcesForTenant(context); - if(resourcesForTenant.containsKey(id)) { + if (resourcesForTenant.containsKey(id)) { resourcesForTenant.remove(id); noContent().writeTo(routingContext.response()); @@ -631,7 +635,8 @@ private void checkForUnexpectedQueryParameters(RoutingContext routingContext) { boolean isValidParameter = queryParameter.contains("query") || queryParameter.contains("offset") || isContainsQueryParameter(queryParameter) || - queryParameter.contains("limit"); + queryParameter.contains("limit") || + queryParameter.contains("expandAll"); return !isValidParameter; }) diff --git a/src/test/java/api/support/fixtures/ConfigurationExample.java b/src/test/java/api/support/fixtures/ConfigurationExample.java index 5f7b9c7011..e06ac904c9 100644 --- a/src/test/java/api/support/fixtures/ConfigurationExample.java +++ b/src/test/java/api/support/fixtures/ConfigurationExample.java @@ -2,10 +2,7 @@ import static org.folio.circulation.support.json.JsonPropertyWriter.write; -import java.util.UUID; - import api.support.builders.ConfigRecordBuilder; -import api.support.builders.TlrSettingsConfigurationBuilder; import io.vertx.core.json.JsonObject; public class ConfigurationExample { @@ -40,37 +37,6 @@ public static ConfigRecordBuilder schedulerNoticesLimitConfiguration(String limi DEFAULT_NOTIFICATION_SCHEDULER_CONFIG_NAME, limit); } - public static ConfigRecordBuilder tlrFeatureEnabled() { - return new ConfigRecordBuilder("SETTINGS", "TLR", - new TlrSettingsConfigurationBuilder() - .withTitleLevelRequestsFeatureEnabled(true) - .create() - .encodePrettily()); - } - - public static ConfigRecordBuilder tlrFeatureDisabled() { - return new ConfigRecordBuilder("SETTINGS", "TLR", - new TlrSettingsConfigurationBuilder() - .withTitleLevelRequestsFeatureEnabled(false) - .create() - .encodePrettily()); - } - - public static ConfigRecordBuilder tlrFeatureConfiguration(boolean isTlrEnabled, - boolean holdShouldFollowCirculationRules, UUID confirmationTemplateId, - UUID cancellationTemplateId, UUID expirationTemplateId) { - - return new ConfigRecordBuilder("SETTINGS", "TLR", - new TlrSettingsConfigurationBuilder() - .withTitleLevelRequestsFeatureEnabled(isTlrEnabled) - .withTlrHoldShouldFollowCirculationRules(holdShouldFollowCirculationRules) - .withConfirmationPatronNoticeTemplateId(confirmationTemplateId) - .withCancellationPatronNoticeTemplateId(cancellationTemplateId) - .withExpirationPatronNoticeTemplateId(expirationTemplateId) - .create() - .encodePrettily()); - } - private static JsonObject combinedTimeZoneConfig(String timezone) { final JsonObject encodedValue = new JsonObject(); write(encodedValue, "locale", US_LOCALE); diff --git a/src/test/java/api/support/fixtures/ConfigurationsFixture.java b/src/test/java/api/support/fixtures/ConfigurationsFixture.java deleted file mode 100644 index f42e903052..0000000000 --- a/src/test/java/api/support/fixtures/ConfigurationsFixture.java +++ /dev/null @@ -1,41 +0,0 @@ -package api.support.fixtures; - -import java.util.UUID; - -import api.support.http.ResourceClient; - -public class ConfigurationsFixture { - private final ResourceClient client; - private UUID tlrConfigurationEntryId = null; - - public ConfigurationsFixture(ResourceClient client) { - this.client = client; - } - - public void enableTlrFeature() { - deleteTlrFeatureConfig(); - tlrConfigurationEntryId = client.create(ConfigurationExample.tlrFeatureEnabled()).getId(); - } - - public void disableTlrFeature() { - deleteTlrFeatureConfig(); - tlrConfigurationEntryId = client.create(ConfigurationExample.tlrFeatureDisabled()).getId(); - } - - public void deleteTlrFeatureConfig() { - if (tlrConfigurationEntryId != null) { - client.delete(tlrConfigurationEntryId); - tlrConfigurationEntryId = null; - } - } - - public void configureTlrFeature(boolean isTlrFeatureEnabled, boolean tlrHoldShouldFollowCirculationRules, - UUID confirmationTemplateId, UUID cancellationTemplateId, UUID expirationTemplateId) { - - deleteTlrFeatureConfig(); - tlrConfigurationEntryId = client.create(ConfigurationExample.tlrFeatureConfiguration( - isTlrFeatureEnabled, tlrHoldShouldFollowCirculationRules, confirmationTemplateId, - cancellationTemplateId, expirationTemplateId)) - .getId(); - } -} diff --git a/src/test/java/api/support/fixtures/SearchInstanceFixture.java b/src/test/java/api/support/fixtures/SearchInstanceFixture.java new file mode 100644 index 0000000000..e1bb144753 --- /dev/null +++ b/src/test/java/api/support/fixtures/SearchInstanceFixture.java @@ -0,0 +1,26 @@ +package api.support.fixtures; + +import java.util.List; +import java.util.UUID; + +import api.support.builders.InstanceBuilder; +import api.support.builders.SearchInstanceBuilder; +import api.support.http.ItemResource; +import api.support.http.ResourceClient; + +public class SearchInstanceFixture { + + private final ResourceClient searchClient; + + public SearchInstanceFixture() { + this.searchClient = ResourceClient.forSearchClient(); + } + + public void basedUponDunkirk(UUID instanceId, ItemResource itemResource) { + SearchInstanceBuilder builder = new SearchInstanceBuilder( + new InstanceBuilder( + "Dunkirk", UUID.randomUUID()).withId(instanceId).create()) + .withItems(List.of(itemResource.getJson())); + searchClient.create(builder); + } +} diff --git a/src/test/java/api/support/fixtures/ServicePointExamples.java b/src/test/java/api/support/fixtures/ServicePointExamples.java index 05dc048a41..3267a39341 100644 --- a/src/test/java/api/support/fixtures/ServicePointExamples.java +++ b/src/test/java/api/support/fixtures/ServicePointExamples.java @@ -79,4 +79,12 @@ static ServicePointBuilder basedUponCircDesk10() { .withholdShelfClosedLibraryDateManagement(ExpirationDateManagement.MOVE_TO_THE_END_OF_THE_NEXT_OPEN_DAY.name()) .withHoldShelfExpriyPeriod(6, "Months"); } + + static ServicePointBuilder basedUponCircDesk11() { + return new ServicePointBuilder("Circ Desk 11", "cd11", + "Circulation Desk -- Back Entrance") + .withPickupLocation(FALSE) + .withEcsRequestRouting(TRUE) + .withHoldShelfExpriyPeriod(6, "Months"); + } } diff --git a/src/test/java/api/support/fixtures/ServicePointsFixture.java b/src/test/java/api/support/fixtures/ServicePointsFixture.java index 52df0996c4..7cb92a7886 100644 --- a/src/test/java/api/support/fixtures/ServicePointsFixture.java +++ b/src/test/java/api/support/fixtures/ServicePointsFixture.java @@ -2,6 +2,7 @@ import static api.support.fixtures.ServicePointExamples.basedUponCircDesk1; import static api.support.fixtures.ServicePointExamples.basedUponCircDesk10; +import static api.support.fixtures.ServicePointExamples.basedUponCircDesk11; import static api.support.fixtures.ServicePointExamples.basedUponCircDesk2; import static api.support.fixtures.ServicePointExamples.basedUponCircDesk3; import static api.support.fixtures.ServicePointExamples.basedUponCircDesk4; @@ -81,6 +82,11 @@ public IndividualResource cd10() { return create(basedUponCircDesk10()); } + public IndividualResource cd11() { + + return create(basedUponCircDesk11()); + } + public IndividualResource create(ServicePointBuilder builder) { return servicePointRecordCreator.createIfAbsent(builder); diff --git a/src/test/java/api/support/fixtures/SettingsFixture.java b/src/test/java/api/support/fixtures/SettingsFixture.java index 23166fcf83..41220f073d 100644 --- a/src/test/java/api/support/fixtures/SettingsFixture.java +++ b/src/test/java/api/support/fixtures/SettingsFixture.java @@ -1,12 +1,15 @@ package api.support.fixtures; +import java.util.List; +import java.util.UUID; + import api.support.builders.SettingsBuilder; import api.support.http.ResourceClient; import io.vertx.core.json.JsonObject; -import java.util.UUID; - public class SettingsFixture { + private static final UUID GENERAL_TLR_SETTINGS_ID = UUID.randomUUID(); + private static final UUID REGULAR_TLR_SETTINGS_ID = UUID.randomUUID(); private final ResourceClient settingsClient; @@ -27,4 +30,58 @@ private SettingsBuilder buildCheckoutLockFeatureSettings(boolean checkoutFeature .encodePrettily() ); } + + public void enableTlrFeature() { + createGeneralTlrSettings(true, false); + } + + public void disableTlrFeature() { + createGeneralTlrSettings(false, false); + } + + public void deleteTlrFeatureSettings() { + settingsClient.delete(GENERAL_TLR_SETTINGS_ID); + settingsClient.delete(REGULAR_TLR_SETTINGS_ID); + } + + public void configureTlrFeature(boolean isTlrFeatureEnabled, boolean tlrHoldShouldFollowCirculationRules, + UUID confirmationTemplateId, UUID cancellationTemplateId, UUID expirationTemplateId) { + + deleteTlrFeatureSettings(); + createGeneralTlrSettings(isTlrFeatureEnabled, tlrHoldShouldFollowCirculationRules); + createRegularTlrSettings(confirmationTemplateId, cancellationTemplateId, expirationTemplateId); + } + + private void createGeneralTlrSettings(boolean isTlrFeatureEnabled, + boolean tlrHoldShouldFollowCirculationRules) { + + JsonObject value = new JsonObject() + .put("titleLevelRequestsFeatureEnabled", isTlrFeatureEnabled) + .put("createTitleLevelRequestsByDefault", false) + .put("tlrHoldShouldFollowCirculationRules", tlrHoldShouldFollowCirculationRules); + + SettingsBuilder builder = new SettingsBuilder(GENERAL_TLR_SETTINGS_ID, "circulation", + "generalTlr", value); + + settingsClient.create(builder); + } + + private void createRegularTlrSettings(UUID confirmationTemplateId, UUID cancellationTemplateId, + UUID expirationTemplateId) { + + JsonObject regularTlrValue = new JsonObject() + .put("cancellationPatronNoticeTemplateId", cancellationTemplateId) + .put("confirmationPatronNoticeTemplateId", confirmationTemplateId) + .put("expirationPatronNoticeTemplateId", expirationTemplateId); + + SettingsBuilder builder = new SettingsBuilder(REGULAR_TLR_SETTINGS_ID, "circulation", + "regularTlr", regularTlrValue); + + settingsClient.create(builder); + } + + public List getAll() { + return settingsClient.getAll(); + } + } diff --git a/src/test/java/api/support/http/InterfaceUrls.java b/src/test/java/api/support/http/InterfaceUrls.java index 59d35de534..bbc15ab851 100644 --- a/src/test/java/api/support/http/InterfaceUrls.java +++ b/src/test/java/api/support/http/InterfaceUrls.java @@ -334,6 +334,13 @@ public static URL settingsStorageUrl() { return APITestContext.viaOkapiModuleUrl("/settings/entries"); } + public static URL searchUrl(String subPath) { + return APITestContext.viaOkapiModuleUrl("/search/instances" + subPath); + } + + public static URL itemsByInstanceUrl(String subPath) { + return circulationModuleUrl("/circulation/items-by-instance?" + subPath); + } public static URL circulationSettingsUrl(String subPath) { return circulationModuleUrl("/circulation/settings" + subPath); } diff --git a/src/test/java/api/support/http/ResourceClient.java b/src/test/java/api/support/http/ResourceClient.java index 8e9b7d63be..254682ec36 100644 --- a/src/test/java/api/support/http/ResourceClient.java +++ b/src/test/java/api/support/http/ResourceClient.java @@ -272,6 +272,10 @@ public static ResourceClient forActualCostRecordsStorage() { return new ResourceClient(InterfaceUrls::actualCostRecordsStorageUrl, "actualCostRecords"); } + public static ResourceClient forSearchClient() { + return new ResourceClient(InterfaceUrls::searchUrl, "instances"); + } + public static ResourceClient forCirculationSettings() { return new ResourceClient(InterfaceUrls::circulationSettingsUrl, "circulationSettings"); }