diff --git a/NEWS.md b/NEWS.md index a8886a20c..b226b16ab 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,7 @@ * Facets: add support for instance classification facets ([MSEARCH-606](https://issues.folio.org/browse/MSEARCH-606)) * Return Unified List of Inventory Locations in a Consortium ([MSEARCH-681](https://folio-org.atlassian.net/browse/MSEARCH-681)) * Remove ability to match on LCCN searches without a prefix ([MSEARCH-752](https://folio-org.atlassian.net/browse/MSEARCH-752)) +* Search consolidated items/holdings data in consortium ([MSEARCH-759](https://folio-org.atlassian.net/browse/MSEARCH-759)) ### Bug fixes * Do not delete kafka topics if collection topic is enabled ([MSEARCH-725](https://folio-org.atlassian.net/browse/MSEARCH-725)) diff --git a/src/main/java/org/folio/search/controller/SearchConsortiumController.java b/src/main/java/org/folio/search/controller/SearchConsortiumController.java index 714b548e4..6b35429a9 100644 --- a/src/main/java/org/folio/search/controller/SearchConsortiumController.java +++ b/src/main/java/org/folio/search/controller/SearchConsortiumController.java @@ -1,15 +1,10 @@ package org.folio.search.controller; -import static org.apache.commons.collections4.CollectionUtils.isEmpty; - -import java.util.Objects; -import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; -import org.folio.search.domain.dto.BatchHoldingIdsDto; -import org.folio.search.domain.dto.BatchItemIdsDto; +import org.folio.search.domain.dto.BatchIdsDto; import org.folio.search.domain.dto.ConsortiumHolding; import org.folio.search.domain.dto.ConsortiumHoldingCollection; import org.folio.search.domain.dto.ConsortiumItem; @@ -22,7 +17,7 @@ import org.folio.search.model.service.CqlSearchRequest; import org.folio.search.model.types.ResourceType; import org.folio.search.rest.resource.SearchConsortiumApi; -import org.folio.search.service.SearchService; +import org.folio.search.service.consortium.ConsortiumInstanceSearchService; import org.folio.search.service.consortium.ConsortiumInstanceService; import org.folio.search.service.consortium.ConsortiumLocationService; import org.folio.search.service.consortium.ConsortiumTenantService; @@ -45,7 +40,7 @@ public class SearchConsortiumController implements SearchConsortiumApi { private final ConsortiumTenantService consortiumTenantService; private final ConsortiumInstanceService instanceService; private final ConsortiumLocationService locationService; - private final SearchService searchService; + private final ConsortiumInstanceSearchService searchService; @Override public ResponseEntity getConsortiumHoldings(String tenantHeader, String instanceId, @@ -64,34 +59,6 @@ public ResponseEntity getConsortiumHoldings(String return ResponseEntity.ok(instanceService.fetchHoldings(context)); } - @Override - public ResponseEntity getConsortiumHolding(UUID id, String tenantHeader) { - var tenant = verifyAndGetTenant(tenantHeader); - var holdingId = id.toString(); - var query = "holdings.id=" + holdingId; - var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, 1, 0, true); - var result = searchService.search(searchRequest); - - if (isEmpty(result.getRecords()) || isEmpty(result.getRecords().iterator().next().getHoldings())) { - return ResponseEntity.ok(new ConsortiumHolding()); - } - - var instance = result.getRecords().iterator().next(); - var holding = instance.getHoldings().stream() - .filter(hol -> Objects.equals(holdingId, hol.getId())) - .findFirst().orElse(null); - - if (holding == null) { - return ResponseEntity.ok(new ConsortiumHolding()); - } - - return ResponseEntity.ok(new ConsortiumHolding() - .id(holdingId) - .tenantId(holding.getTenantId()) - .instanceId(instance.getId()) - ); - } - @Override public ResponseEntity getConsortiumItems(String tenantHeader, String instanceId, String holdingsRecordId, String tenantId, @@ -126,95 +93,60 @@ public ResponseEntity getConsortiumLocations(Strin .totalRecords(result.getTotalRecords())); } + @Override + public ResponseEntity getConsortiumHolding(UUID id, String tenantHeader) { + var tenant = verifyAndGetTenant(tenantHeader); + var holdingId = id.toString(); + var query = "holdings.id=" + holdingId; + var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, 1, 0, true, false, true); + + var result = searchService.getConsortiumHolding(id.toString(), searchRequest); + return ResponseEntity.ok(result); + } + @Override public ResponseEntity getConsortiumItem(UUID itemId, String tenantHeader) { var tenant = verifyAndGetTenant(tenantHeader); var query = "items.id=" + itemId.toString(); - var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, 1, 0, true); - var result = searchService.search(searchRequest); + var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, 1, 0, true, false, true); - if (isEmpty(result.getRecords()) || isEmpty(result.getRecords().iterator().next().getItems())) { - return ResponseEntity.ok(new ConsortiumItem()); - } - - var instance = result.getRecords().iterator().next(); - var item = instance.getItems().stream() - .filter(it -> Objects.equals(itemId.toString(), it.getId())) - .findFirst().orElse(null); + var result = searchService.getConsortiumItem(itemId.toString(), searchRequest); + return ResponseEntity.ok(result); + } - if (item == null) { - return ResponseEntity.ok(new ConsortiumItem()); - } + @Override + public ResponseEntity fetchConsortiumBatchHoldings(String tenantHeader, + BatchIdsDto batchIdsDto) { + var tenant = verifyAndGetTenant(tenantHeader); + var holdingIds = batchIdsDto.getIds().stream().map(UUID::toString).collect(Collectors.toSet()); + var query = batchIdsDto.getIds().stream() + .map(UUID::toString) + .map("holdings.id = %s"::formatted) + .collect(Collectors.joining(" or ")); + var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, holdingIds.size(), 0, true, false, true); - return ResponseEntity.ok(new ConsortiumItem() - .id(itemId.toString()) - .tenantId(item.getTenantId()) - .instanceId(instance.getId()) - .holdingsRecordId(item.getHoldingsRecordId()) - ); + var result = searchService.fetchConsortiumBatchHoldings(searchRequest, holdingIds); + return ResponseEntity.ok(result); } @Override public ResponseEntity fetchConsortiumBatchItems(String tenantHeader, - BatchItemIdsDto batchItemIdsDto) { - if (batchItemIdsDto.getIds().isEmpty()) { + BatchIdsDto batchIdsDto) { + if (batchIdsDto.getIds().isEmpty()) { return ResponseEntity .ok(new ConsortiumItemCollection()); } var tenant = verifyAndGetTenant(tenantHeader); - var itemIds = batchItemIdsDto.getIds().stream().map(UUID::toString).collect(Collectors.toSet()); - var query = batchItemIdsDto.getIds().stream() + var itemIds = batchIdsDto.getIds().stream().map(UUID::toString).collect(Collectors.toSet()); + var query = batchIdsDto.getIds().stream() .map(UUID::toString) .map("items.id = %s"::formatted) .collect(Collectors.joining(" or ")); - var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, 1000, 0, true); - var result = searchService.search(searchRequest); - var consortiumItems = result.getRecords().stream() - .map(instance -> - instance.getItems().stream() - .filter(item -> itemIds.contains(item.getId())) - .findFirst() - .map(item -> new ConsortiumItem() - .id(item.getId()) - .tenantId(item.getTenantId()) - .instanceId(instance.getId()) - .holdingsRecordId(item.getHoldingsRecordId())) - ) - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - - return ResponseEntity - .ok(new ConsortiumItemCollection().items(consortiumItems).totalRecords(result.getTotalRecords())); - } + var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, itemIds.size(), 0, true, false, true); - @Override - public ResponseEntity fetchConsortiumBatchHoldings(String tenantHeader, - BatchHoldingIdsDto holdingIdsDto) { - var tenant = verifyAndGetTenant(tenantHeader); - var holdingIds = holdingIdsDto.getIds().stream().map(UUID::toString).collect(Collectors.toSet()); - var query = holdingIdsDto.getIds().stream() - .map(UUID::toString) - .map("holdings.id = %s"::formatted) - .collect(Collectors.joining(" or ")); - var searchRequest = CqlSearchRequest.of(Instance.class, tenant, query, 1000, 0, true); - var result = searchService.search(searchRequest); - var consortiumHoldings = result.getRecords().stream() - .map(instance -> - instance.getHoldings().stream() - .filter(holding -> holdingIds.contains(holding.getId())) - .findFirst() - .map(holding -> new ConsortiumHolding() - .id(holding.getId()) - .tenantId(holding.getTenantId()) - .instanceId(instance.getId())) - ) - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - return ResponseEntity - .ok(new ConsortiumHoldingCollection().holdings(consortiumHoldings).totalRecords(result.getTotalRecords())); + var result = searchService.fetchConsortiumBatchItems(searchRequest, itemIds); + return ResponseEntity.ok(result); } private String verifyAndGetTenant(String tenantHeader) { diff --git a/src/main/java/org/folio/search/converter/ConsortiumHoldingMapper.java b/src/main/java/org/folio/search/converter/ConsortiumHoldingMapper.java new file mode 100644 index 000000000..e8ea62afc --- /dev/null +++ b/src/main/java/org/folio/search/converter/ConsortiumHoldingMapper.java @@ -0,0 +1,23 @@ +package org.folio.search.converter; + +import org.folio.search.domain.dto.ConsortiumHolding; +import org.folio.search.domain.dto.Holding; +import org.springframework.stereotype.Component; + +@Component +public class ConsortiumHoldingMapper { + + public ConsortiumHolding map(String instanceId, Holding holding) { + return new ConsortiumHolding() + .id(holding.getId()) + .hrid(holding.getHrid()) + .tenantId(holding.getTenantId()) + .instanceId(instanceId) + .callNumberPrefix(holding.getCallNumberPrefix()) + .callNumber(holding.getCallNumber()) + .callNumberSuffix(holding.getCallNumberSuffix()) + .copyNumber(holding.getCopyNumber()) + .permanentLocationId(holding.getPermanentLocationId()) + .discoverySuppress(holding.getDiscoverySuppress() != null && holding.getDiscoverySuppress()); + } +} diff --git a/src/main/java/org/folio/search/converter/ConsortiumItemMapper.java b/src/main/java/org/folio/search/converter/ConsortiumItemMapper.java new file mode 100644 index 000000000..8603afee6 --- /dev/null +++ b/src/main/java/org/folio/search/converter/ConsortiumItemMapper.java @@ -0,0 +1,19 @@ +package org.folio.search.converter; + +import org.folio.search.domain.dto.ConsortiumItem; +import org.folio.search.domain.dto.Item; +import org.springframework.stereotype.Component; + +@Component +public class ConsortiumItemMapper { + + public ConsortiumItem map(String instanceId, Item item) { + return new ConsortiumItem() + .id(item.getId()) + .hrid(item.getHrid()) + .tenantId(item.getTenantId()) + .instanceId(instanceId) + .holdingsRecordId(item.getHoldingsRecordId()) + .barcode(item.getBarcode()); + } +} diff --git a/src/main/java/org/folio/search/cql/CqlSearchQueryConverter.java b/src/main/java/org/folio/search/cql/CqlSearchQueryConverter.java index cf573564f..13f90c9e6 100644 --- a/src/main/java/org/folio/search/cql/CqlSearchQueryConverter.java +++ b/src/main/java/org/folio/search/cql/CqlSearchQueryConverter.java @@ -68,9 +68,16 @@ public SearchSourceBuilder convert(String query, String resource) { * @return search source as {@link SearchSourceBuilder} object with query and sorting conditions */ public SearchSourceBuilder convertForConsortia(String query, String resource) { + return convertForConsortia(query, resource, false); + } + + public SearchSourceBuilder convertForConsortia(String query, String resource, boolean consortiumConsolidated) { var sourceBuilder = convert(query, resource); - var queryBuilder = consortiumSearchHelper.filterQueryForActiveAffiliation(sourceBuilder.query(), resource); + if (consortiumConsolidated) { + return sourceBuilder; + } + var queryBuilder = consortiumSearchHelper.filterQueryForActiveAffiliation(sourceBuilder.query(), resource); return sourceBuilder.query(queryBuilder); } diff --git a/src/main/java/org/folio/search/model/service/CqlSearchRequest.java b/src/main/java/org/folio/search/model/service/CqlSearchRequest.java index b89258e0d..a5856e87d 100644 --- a/src/main/java/org/folio/search/model/service/CqlSearchRequest.java +++ b/src/main/java/org/folio/search/model/service/CqlSearchRequest.java @@ -53,6 +53,11 @@ public class CqlSearchRequest implements ResourceRequest { */ private final Boolean includeNumberOfTitles; + /** + * Doesn't affect non-consortium. true means include all records, false means filter for active affiliation. + */ + private final Boolean consortiumConsolidated; + /** * Creates {@link CqlSearchRequest} object for given variables. * @@ -64,14 +69,22 @@ public class CqlSearchRequest implements ResourceRequest { * @param expandAll - whether to return only response properties or entire record * @param - generic type for {@link CqlSearchRequest} object. * @param includeNumberOfTitles - indicates whether the number of titles should be counted. + * @param consortiumConsolidated - indicates whether to return consortium consolidated records. * @return created {@link CqlSearchRequest} object */ public static CqlSearchRequest of(Class resourceClass, String tenantId, String query, Integer limit, Integer offset, Boolean expandAll, - Boolean includeNumberOfTitles) { + Boolean includeNumberOfTitles, Boolean consortiumConsolidated) { var resource = SearchUtils.getResourceName(resourceClass); return new CqlSearchRequest<>(resource, resourceClass, tenantId, query, limit, offset, expandAll, - includeNumberOfTitles); + includeNumberOfTitles, consortiumConsolidated); + } + + public static CqlSearchRequest of(Class resourceClass, String tenantId, String query, + Integer limit, Integer offset, Boolean expandAll, + Boolean includeNumberOfTitles) { + return CqlSearchRequest.of(resourceClass, tenantId, query, limit, offset, expandAll, + includeNumberOfTitles, false); } public static CqlSearchRequest of(Class resourceClass, String tenantId, String query, diff --git a/src/main/java/org/folio/search/service/SearchService.java b/src/main/java/org/folio/search/service/SearchService.java index 5bf28e1a7..962c258b0 100644 --- a/src/main/java/org/folio/search/service/SearchService.java +++ b/src/main/java/org/folio/search/service/SearchService.java @@ -3,6 +3,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.apache.commons.lang3.BooleanUtils.isFalse; import static org.folio.search.model.types.ResponseGroupType.SEARCH; +import static org.folio.search.utils.SearchUtils.buildPreferenceKey; import java.util.List; import java.util.Map; @@ -54,7 +55,8 @@ public SearchResult search(CqlSearchRequest request) { } var resource = request.getResource(); var requestTimeout = searchQueryConfiguration.getRequestTimeout(); - var queryBuilder = cqlSearchQueryConverter.convertForConsortia(request.getQuery(), resource) + var queryBuilder = cqlSearchQueryConverter.convertForConsortia(request.getQuery(), resource, + request.getConsortiumConsolidated()) .from(request.getOffset()) .size(request.getLimit()) .trackTotalHits(true) @@ -76,10 +78,6 @@ public SearchResult search(CqlSearchRequest request) { return searchResult; } - private String buildPreferenceKey(String tenantId, String resource, String query) { - return tenantId + "-" + resource + "-" + query; - } - private void searchResultPostProcessing(Class resourceClass, boolean includeNumberOfTitles, SearchResult searchResult) { if (Objects.isNull(resourceClass)) { diff --git a/src/main/java/org/folio/search/service/consortium/ConsortiumInstanceSearchService.java b/src/main/java/org/folio/search/service/consortium/ConsortiumInstanceSearchService.java new file mode 100644 index 000000000..5eae42e54 --- /dev/null +++ b/src/main/java/org/folio/search/service/consortium/ConsortiumInstanceSearchService.java @@ -0,0 +1,102 @@ +package org.folio.search.service.consortium; + +import static org.apache.commons.collections4.CollectionUtils.isEmpty; + +import java.util.Objects; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.folio.search.converter.ConsortiumHoldingMapper; +import org.folio.search.converter.ConsortiumItemMapper; +import org.folio.search.domain.dto.ConsortiumHolding; +import org.folio.search.domain.dto.ConsortiumHoldingCollection; +import org.folio.search.domain.dto.ConsortiumItem; +import org.folio.search.domain.dto.ConsortiumItemCollection; +import org.folio.search.domain.dto.Instance; +import org.folio.search.model.service.CqlSearchRequest; +import org.folio.search.service.SearchService; +import org.folio.spring.FolioExecutionContext; +import org.springframework.stereotype.Service; + +/** + * Class designed to be executed only in scope of consortium central tenant id. + * So, it can be expected to always have central tenant id in {@link FolioExecutionContext}. + */ +@Log4j2 +@Service +@RequiredArgsConstructor +public class ConsortiumInstanceSearchService { + + private final ConsortiumHoldingMapper holdingMapper; + private final ConsortiumItemMapper itemMapper; + private final SearchService searchService; + + public ConsortiumHolding getConsortiumHolding(String id, CqlSearchRequest searchRequest) { + var result = searchService.search(searchRequest); + + if (isEmpty(result.getRecords()) || isEmpty(result.getRecords().iterator().next().getHoldings())) { + return new ConsortiumHolding(); + } + + var instance = result.getRecords().iterator().next(); + var holding = instance.getHoldings().stream() + .filter(hol -> Objects.equals(id, hol.getId())) + .findFirst().orElse(null); + + if (holding == null) { + return new ConsortiumHolding(); + } + + return holdingMapper.map(instance.getId(), holding); + } + + public ConsortiumItem getConsortiumItem(String id, CqlSearchRequest searchRequest) { + var result = searchService.search(searchRequest); + + if (isEmpty(result.getRecords()) || isEmpty(result.getRecords().iterator().next().getItems())) { + return new ConsortiumItem(); + } + + var instance = result.getRecords().iterator().next(); + var item = instance.getItems().stream() + .filter(it -> Objects.equals(id, it.getId())) + .findFirst().orElse(null); + + if (item == null) { + return new ConsortiumItem(); + } + + return itemMapper.map(instance.getId(), item); + } + + public ConsortiumHoldingCollection fetchConsortiumBatchHoldings(CqlSearchRequest searchRequest, + Set ids) { + var result = searchService.search(searchRequest); + var consortiumHoldings = result.getRecords().stream() + .flatMap(instance -> + instance.getHoldings().stream() + .filter(holding -> ids.contains(holding.getId())) + .map(holding -> holdingMapper.map(instance.getId(), holding)) + ) + .toList(); + return new ConsortiumHoldingCollection() + .holdings(consortiumHoldings) + .totalRecords(consortiumHoldings.size()); + } + + public ConsortiumItemCollection fetchConsortiumBatchItems(CqlSearchRequest searchRequest, + Set ids) { + var result = searchService.search(searchRequest); + var consortiumItems = result.getRecords().stream() + .flatMap(instance -> + instance.getItems().stream() + .filter(item -> ids.contains(item.getId())) + .map(item -> itemMapper.map(instance.getId(), item)) + ) + .toList(); + + return new ConsortiumItemCollection() + .items(consortiumItems) + .totalRecords(consortiumItems.size()); + } +} diff --git a/src/main/java/org/folio/search/utils/SearchUtils.java b/src/main/java/org/folio/search/utils/SearchUtils.java index ebfbcaf48..2435e17e8 100644 --- a/src/main/java/org/folio/search/utils/SearchUtils.java +++ b/src/main/java/org/folio/search/utils/SearchUtils.java @@ -360,6 +360,13 @@ public static String normalizeToAlphaNumeric(String value) { .orElse(null); } + /** + * Build preference string to address similar requests to the same shard. + * */ + public static String buildPreferenceKey(String tenantId, String resource, String query) { + return tenantId + "-" + resource + "-" + query; + } + private static Object getMultilangValueObject(Object value) { return value instanceof MultilangValue v ? v.getMultilangValues() : value; } diff --git a/src/main/resources/swagger.api/mod-search.yaml b/src/main/resources/swagger.api/mod-search.yaml index 543de7a49..5a158fef0 100644 --- a/src/main/resources/swagger.api/mod-search.yaml +++ b/src/main/resources/swagger.api/mod-search.yaml @@ -83,46 +83,10 @@ paths: $ref: 'paths/search-consortium/search-consortium-batch-holdings.yaml' /search/consortium/holding/{id}: - get: - operationId: getConsortiumHolding - description: Get holding by id (only for consortium environment) - tags: - - search-consortium - parameters: - - $ref: 'parameters/record-id-param.yaml' - - $ref: 'parameters/x-okapi-tenant-header.yaml' - responses: - '200': - description: an instance holding - content: - application/json: - schema: - $ref: 'schemas/entity/consortiumHolding.yaml' - '400': - $ref: 'responses/badRequestResponse.yaml' - '500': - $ref: 'responses/internalServerErrorResponse.yaml' + $ref: 'paths/search-consortium/search-consortium-holding.yaml' /search/consortium/item/{id}: - get: - operationId: getConsortiumItem - description: Get an item (only for consortium environment) - tags: - - search-consortium - parameters: - - $ref: 'parameters/record-id-param.yaml' - - $ref: 'parameters/x-okapi-tenant-header.yaml' - responses: - '200': - description: an instance item - content: - application/json: - schema: - $ref: 'schemas/entity/consortiumItem.yaml' - '400': - $ref: 'responses/badRequestResponse.yaml' - '500': - $ref: 'responses/internalServerErrorResponse.yaml' + $ref: 'paths/search-consortium/search-consortium-item.yaml' /search/index/indices: $ref: 'paths/search-index/search-index-indices.yaml' diff --git a/src/main/resources/swagger.api/parameters/batchHoldingIdsDto.yaml b/src/main/resources/swagger.api/parameters/batchHoldingIdsDto.yaml deleted file mode 100644 index b98996216..000000000 --- a/src/main/resources/swagger.api/parameters/batchHoldingIdsDto.yaml +++ /dev/null @@ -1,11 +0,0 @@ -description: Batch Holding IDs DTO -type: object -properties: - ids: - description: Holding IDs - type: array - items: - type: string - format: uuid -required: - - ids diff --git a/src/main/resources/swagger.api/parameters/batchItemIdsDto.yaml b/src/main/resources/swagger.api/parameters/batchIdsDto.yaml similarity index 66% rename from src/main/resources/swagger.api/parameters/batchItemIdsDto.yaml rename to src/main/resources/swagger.api/parameters/batchIdsDto.yaml index 489ad4351..367ec45b8 100644 --- a/src/main/resources/swagger.api/parameters/batchItemIdsDto.yaml +++ b/src/main/resources/swagger.api/parameters/batchIdsDto.yaml @@ -1,8 +1,8 @@ -description: Batch Item IDs DTO +description: Batch IDs DTO type: object properties: ids: - description: Item IDs + description: Entity IDs type: array items: type: string diff --git a/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-holdings.yaml b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-holdings.yaml index da8c5aaf5..c60807ebf 100644 --- a/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-holdings.yaml +++ b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-holdings.yaml @@ -10,7 +10,7 @@ post: content: application/json: schema: - $ref: '../../parameters/batchHoldingIdsDto.yaml' + $ref: '../../parameters/batchIdsDto.yaml' responses: '200': description: List of holdings diff --git a/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-items.yaml b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-items.yaml index cc2a11d2c..0f2dff213 100644 --- a/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-items.yaml +++ b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-batch-items.yaml @@ -10,7 +10,7 @@ post: content: application/json: schema: - $ref: '../../parameters/batchItemIdsDto.yaml' + $ref: '../../parameters/batchIdsDto.yaml' responses: '200': description: List of items diff --git a/src/main/resources/swagger.api/paths/search-consortium/search-consortium-holding.yaml b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-holding.yaml new file mode 100644 index 000000000..00af95cc3 --- /dev/null +++ b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-holding.yaml @@ -0,0 +1,19 @@ +get: + operationId: getConsortiumHolding + description: Get holding by id (only for consortium environment) + tags: + - search-consortium + parameters: + - $ref: '../../parameters/record-id-param.yaml' + - $ref: '../../parameters/x-okapi-tenant-header.yaml' + responses: + '200': + description: an instance holding + content: + application/json: + schema: + $ref: '../../schemas/entity/consortiumHolding.yaml' + '400': + $ref: '../../responses/badRequestResponse.yaml' + '500': + $ref: '../../responses/internalServerErrorResponse.yaml' \ No newline at end of file diff --git a/src/main/resources/swagger.api/paths/search-consortium/search-consortium-item.yaml b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-item.yaml new file mode 100644 index 000000000..5d1b83b42 --- /dev/null +++ b/src/main/resources/swagger.api/paths/search-consortium/search-consortium-item.yaml @@ -0,0 +1,19 @@ +get: + operationId: getConsortiumItem + description: Get an item (only for consortium environment) + tags: + - search-consortium + parameters: + - $ref: '../../parameters/record-id-param.yaml' + - $ref: '../../parameters/x-okapi-tenant-header.yaml' + responses: + '200': + description: an instance item + content: + application/json: + schema: + $ref: '../../schemas/entity/consortiumItem.yaml' + '400': + $ref: '../../responses/badRequestResponse.yaml' + '500': + $ref: '../../responses/internalServerErrorResponse.yaml' \ No newline at end of file diff --git a/src/test/java/org/folio/search/controller/ConsortiumSearchHoldingsIT.java b/src/test/java/org/folio/search/controller/ConsortiumSearchHoldingsIT.java index ef50d933e..288143b14 100644 --- a/src/test/java/org/folio/search/controller/ConsortiumSearchHoldingsIT.java +++ b/src/test/java/org/folio/search/controller/ConsortiumSearchHoldingsIT.java @@ -5,6 +5,8 @@ import static org.folio.search.model.Pair.pair; import static org.folio.search.sample.SampleInstances.getSemanticWeb; import static org.folio.search.sample.SampleInstances.getSemanticWebId; +import static org.folio.search.support.base.ApiEndpoints.consortiumBatchHoldingsSearchPath; +import static org.folio.search.support.base.ApiEndpoints.consortiumHoldingSearchPath; import static org.folio.search.support.base.ApiEndpoints.consortiumHoldingsSearchPath; import static org.folio.search.utils.TestConstants.CENTRAL_TENANT_ID; import static org.folio.search.utils.TestConstants.MEMBER_TENANT_ID; @@ -14,7 +16,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.Arrays; import java.util.List; +import java.util.UUID; +import org.folio.search.domain.dto.BatchIdsDto; import org.folio.search.domain.dto.ConsortiumHolding; import org.folio.search.domain.dto.ConsortiumHoldingCollection; import org.folio.search.model.Pair; @@ -91,6 +96,51 @@ void tryGetConsortiumHoldings_returns400_whenOrderBySpecifiedWithoutAnyFilters() .andExpect(jsonPath("$.errors[0].code", is("validation_error"))); } + @Test + void doGetConsortiumHolding_returns200AndRecord() { + var holdings = getExpectedConsolidatedHoldings(); + var expectedHolding = holdings[0]; + var result = doGet(consortiumHoldingSearchPath(expectedHolding.getId()), CENTRAL_TENANT_ID); + var actual = parseResponse(result, ConsortiumHolding.class); + + assertThat(actual) + .isEqualTo(expectedHolding); + } + + @Test + void tryGetConsortiumHolding_returns400_whenRequestedForNotCentralTenant() throws Exception { + tryGet(consortiumHoldingSearchPath(UUID.randomUUID().toString())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0].message", is(REQUEST_NOT_ALLOWED_MSG))) + .andExpect(jsonPath("$.errors[0].type", is("RequestValidationException"))) + .andExpect(jsonPath("$.errors[0].code", is("validation_error"))) + .andExpect(jsonPath("$.errors[0].parameters[0].key", is("x-okapi-tenant"))) + .andExpect(jsonPath("$.errors[0].parameters[0].value", is(MEMBER_TENANT_ID))); + } + + @Test + void doGetConsortiumBatchHoldings_returns200AndRecords() { + var holdings = getExpectedConsolidatedHoldings(); + var request = new BatchIdsDto() + .ids(Arrays.stream(holdings).map(ConsortiumHolding::getId).map(UUID::fromString).toList()); + var result = doPost(consortiumBatchHoldingsSearchPath(), CENTRAL_TENANT_ID, request); + var actual = parseResponse(result, ConsortiumHoldingCollection.class); + + assertThat(actual.getTotalRecords()).isEqualTo(3); + assertThat(actual.getHoldings()).containsExactlyInAnyOrder(holdings); + } + + @Test + void tryGetConsortiumBatchHoldings_returns400_whenRequestedForNotCentralTenant() throws Exception { + tryPost(consortiumBatchHoldingsSearchPath(), new BatchIdsDto().ids(List.of(UUID.randomUUID()))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0].message", is(REQUEST_NOT_ALLOWED_MSG))) + .andExpect(jsonPath("$.errors[0].type", is("RequestValidationException"))) + .andExpect(jsonPath("$.errors[0].code", is("validation_error"))) + .andExpect(jsonPath("$.errors[0].parameters[0].key", is("x-okapi-tenant"))) + .andExpect(jsonPath("$.errors[0].parameters[0].value", is(MEMBER_TENANT_ID))); + } + private ConsortiumHolding[] getExpectedHoldings() { var instance = getSemanticWeb(); return instance.getHoldings().stream() @@ -107,4 +157,17 @@ private ConsortiumHolding[] getExpectedHoldings() { .discoverySuppress(holding.getDiscoverySuppress() != null && holding.getDiscoverySuppress())) .toArray(ConsortiumHolding[]::new); } + + private ConsortiumHolding[] getExpectedConsolidatedHoldings() { + var instance = getSemanticWeb(); + return instance.getHoldings().stream() + .map(holding -> new ConsortiumHolding() + .id(holding.getId()) + .hrid(holding.getHrid()) + .tenantId(MEMBER_TENANT_ID) + .instanceId(instance.getId()) + .permanentLocationId(holding.getPermanentLocationId()) + .discoverySuppress(holding.getDiscoverySuppress() != null && holding.getDiscoverySuppress())) + .toArray(ConsortiumHolding[]::new); + } } diff --git a/src/test/java/org/folio/search/controller/ConsortiumSearchItemsIT.java b/src/test/java/org/folio/search/controller/ConsortiumSearchItemsIT.java index ac20c4690..40668512d 100644 --- a/src/test/java/org/folio/search/controller/ConsortiumSearchItemsIT.java +++ b/src/test/java/org/folio/search/controller/ConsortiumSearchItemsIT.java @@ -5,6 +5,8 @@ import static org.folio.search.model.Pair.pair; import static org.folio.search.sample.SampleInstances.getSemanticWeb; import static org.folio.search.sample.SampleInstances.getSemanticWebId; +import static org.folio.search.support.base.ApiEndpoints.consortiumBatchItemsSearchPath; +import static org.folio.search.support.base.ApiEndpoints.consortiumItemSearchPath; import static org.folio.search.support.base.ApiEndpoints.consortiumItemsSearchPath; import static org.folio.search.utils.TestConstants.CENTRAL_TENANT_ID; import static org.folio.search.utils.TestConstants.MEMBER_TENANT_ID; @@ -14,7 +16,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import java.util.Arrays; import java.util.List; +import java.util.UUID; +import org.folio.search.domain.dto.BatchIdsDto; import org.folio.search.domain.dto.ConsortiumItem; import org.folio.search.domain.dto.ConsortiumItemCollection; import org.folio.search.model.Pair; @@ -95,6 +100,51 @@ void tryGetConsortiumItems_returns400_whenInstanceIdIsNotSpecified() throws Exce .andExpect(jsonPath("$.errors[0].code", is("validation_error"))); } + @Test + void doGetConsortiumItem_returns200AndRecords() { + var items = getExpectedItems(); + var expectedItem = items[0]; + var result = doGet(consortiumItemSearchPath(expectedItem.getId()), CENTRAL_TENANT_ID); + var actual = parseResponse(result, ConsortiumItem.class); + + assertThat(actual) + .isEqualTo(expectedItem); + } + + @Test + void tryGetConsortiumItem_returns400_whenRequestedForNotCentralTenant() throws Exception { + tryGet(consortiumItemSearchPath(UUID.randomUUID().toString())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0].message", is(REQUEST_NOT_ALLOWED_MSG))) + .andExpect(jsonPath("$.errors[0].type", is("RequestValidationException"))) + .andExpect(jsonPath("$.errors[0].code", is("validation_error"))) + .andExpect(jsonPath("$.errors[0].parameters[0].key", is("x-okapi-tenant"))) + .andExpect(jsonPath("$.errors[0].parameters[0].value", is(MEMBER_TENANT_ID))); + } + + @Test + void doGetConsortiumBatchItems_returns200AndRecords() { + var items = getExpectedItems(); + var request = new BatchIdsDto() + .ids(Arrays.stream(items).map(ConsortiumItem::getId).map(UUID::fromString).toList()); + var result = doPost(consortiumBatchItemsSearchPath(), CENTRAL_TENANT_ID, request); + var actual = parseResponse(result, ConsortiumItemCollection.class); + + assertThat(actual.getTotalRecords()).isEqualTo(3); + assertThat(actual.getItems()).containsExactlyInAnyOrder(items); + } + + @Test + void tryGetConsortiumBatchItems_returns400_whenRequestedForNotCentralTenant() throws Exception { + tryPost(consortiumBatchItemsSearchPath(), new BatchIdsDto().ids(List.of(UUID.randomUUID()))) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errors[0].message", is(REQUEST_NOT_ALLOWED_MSG))) + .andExpect(jsonPath("$.errors[0].type", is("RequestValidationException"))) + .andExpect(jsonPath("$.errors[0].code", is("validation_error"))) + .andExpect(jsonPath("$.errors[0].parameters[0].key", is("x-okapi-tenant"))) + .andExpect(jsonPath("$.errors[0].parameters[0].value", is(MEMBER_TENANT_ID))); + } + private ConsortiumItem[] getExpectedItems() { var instance = getSemanticWeb(); return instance.getItems().stream() diff --git a/src/test/java/org/folio/search/service/SearchServiceTest.java b/src/test/java/org/folio/search/service/SearchServiceTest.java index eef05a6fc..badc7c741 100644 --- a/src/test/java/org/folio/search/service/SearchServiceTest.java +++ b/src/test/java/org/folio/search/service/SearchServiceTest.java @@ -73,7 +73,8 @@ void search_positive() { var expectedSearchResult = searchResult(TestResource.of(RESOURCE_ID)); when(searchFieldProvider.getSourceFields(RESOURCE_NAME, SEARCH)).thenReturn(new String[] {"field1", "field2"}); - when(cqlSearchQueryConverter.convertForConsortia(SEARCH_QUERY, RESOURCE_NAME)).thenReturn(searchSourceBuilder); + when(cqlSearchQueryConverter.convertForConsortia(SEARCH_QUERY, RESOURCE_NAME, false)) + .thenReturn(searchSourceBuilder); when(searchRepository.search(eq(searchRequest), eq(expectedSourceBuilder), anyString())).thenReturn(searchResponse); when(documentConverter.convertToSearchResult(searchResponse, TestResource.class)) .thenReturn(expectedSearchResult); @@ -100,7 +101,8 @@ void search_positive_withExpandAll() { .trackTotalHits(true).timeout(new TimeValue(1000, MILLISECONDS)); var expectedSearchResult = searchResult(TestResource.of(RESOURCE_ID)); - when(cqlSearchQueryConverter.convertForConsortia(SEARCH_QUERY, RESOURCE_NAME)).thenReturn(searchSourceBuilder); + when(cqlSearchQueryConverter.convertForConsortia(SEARCH_QUERY, RESOURCE_NAME, false)) + .thenReturn(searchSourceBuilder); when(searchRepository.search(eq(searchRequest), eq(expectedSourceBuilder), anyString())).thenReturn(searchResponse); when(documentConverter.convertToSearchResult(searchResponse, TestResource.class)) .thenReturn(expectedSearchResult); diff --git a/src/test/java/org/folio/search/service/consortium/ConsortiumInstanceSearchServiceTest.java b/src/test/java/org/folio/search/service/consortium/ConsortiumInstanceSearchServiceTest.java new file mode 100644 index 000000000..06d966ff9 --- /dev/null +++ b/src/test/java/org/folio/search/service/consortium/ConsortiumInstanceSearchServiceTest.java @@ -0,0 +1,266 @@ +package org.folio.search.service.consortium; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.folio.search.utils.TestConstants.CENTRAL_TENANT_ID; +import static org.folio.search.utils.TestConstants.MEMBER_TENANT_ID; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.when; + +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import org.folio.search.converter.ConsortiumHoldingMapper; +import org.folio.search.converter.ConsortiumItemMapper; +import org.folio.search.domain.dto.ConsortiumHolding; +import org.folio.search.domain.dto.ConsortiumHoldingCollection; +import org.folio.search.domain.dto.ConsortiumItem; +import org.folio.search.domain.dto.ConsortiumItemCollection; +import org.folio.search.domain.dto.Holding; +import org.folio.search.domain.dto.Instance; +import org.folio.search.domain.dto.Item; +import org.folio.search.model.SearchResult; +import org.folio.search.model.service.CqlSearchRequest; +import org.folio.search.service.SearchService; +import org.folio.spring.testing.type.UnitTest; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@UnitTest +@ExtendWith(MockitoExtension.class) +class ConsortiumInstanceSearchServiceTest { + + private @Mock ConsortiumHoldingMapper holdingMapper; + private @Mock ConsortiumItemMapper itemMapper; + private @Mock SearchService searchService; + private @InjectMocks ConsortiumInstanceSearchService service; + + @BeforeEach + void setUpMocks() { + lenient().doAnswer(invocationOnMock -> { + var instanceId = (String) invocationOnMock.getArgument(0); + var holding = (Holding) invocationOnMock.getArgument(1); + return new ConsortiumHolding() + .id(holding.getId()) + .instanceId(instanceId) + .tenantId(holding.getTenantId()); + }).when(holdingMapper).map(anyString(), any()); + lenient().doAnswer(invocationOnMock -> { + var instanceId = (String) invocationOnMock.getArgument(0); + var item = (Item) invocationOnMock.getArgument(1); + return new ConsortiumItem() + .id(item.getId()) + .instanceId(instanceId) + .tenantId(item.getTenantId()) + .holdingsRecordId(item.getHoldingsRecordId()); + }).when(itemMapper).map(anyString(), any()); + } + + @Test + void getConsortiumHolding_positive() { + var instanceId = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var holdings = List.of(holding(UUID.randomUUID().toString(), CENTRAL_TENANT_ID), holding(id, MEMBER_TENANT_ID)); + var searchResult = SearchResult.of(1, List.of(new Instance().id(instanceId).holdings(holdings))); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumHolding(id, request); + + assertThat(result.getId()).isEqualTo(id); + assertThat(result.getTenantId()).isEqualTo(MEMBER_TENANT_ID); + assertThat(result.getInstanceId()).isEqualTo(instanceId); + } + + @Test + void getConsortiumHolding_positive_noRecords() { + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var searchResult = SearchResult.of(0, Collections.emptyList()); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumHolding(id, request); + + assertThat(result).isEqualTo(new ConsortiumHolding()); + } + + @Test + void getConsortiumHolding_positive_noHoldings() { + var instanceId = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var holdings = Collections.emptyList(); + var searchResult = SearchResult.of(1, List.of(new Instance().id(instanceId).holdings(holdings))); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumHolding(id, request); + + assertThat(result).isEqualTo(new ConsortiumHolding()); + } + + @Test + void getConsortiumHolding_positive_noDesiredHolding() { + var instanceId = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var holdings = List.of(holding(UUID.randomUUID().toString(), CENTRAL_TENANT_ID)); + var searchResult = SearchResult.of(1, List.of(new Instance().id(instanceId).holdings(holdings))); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumHolding(id, request); + + assertThat(result).isEqualTo(new ConsortiumHolding()); + } + + @Test + void getConsortiumItem_positive() { + var instanceId = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var items = List.of(item(UUID.randomUUID().toString(), CENTRAL_TENANT_ID), item(id, MEMBER_TENANT_ID)); + var searchResult = SearchResult.of(1, List.of(new Instance().id(instanceId).items(items))); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumItem(id, request); + + assertThat(result.getId()).isEqualTo(id); + assertThat(result.getTenantId()).isEqualTo(MEMBER_TENANT_ID); + assertThat(result.getInstanceId()).isEqualTo(instanceId); + assertThat(result.getHoldingsRecordId()).isNotBlank(); + } + + @Test + void getConsortiumItem_positive_noRecords() { + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var searchResult = SearchResult.of(0, Collections.emptyList()); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumItem(id, request); + + assertThat(result).isEqualTo(new ConsortiumItem()); + } + + @Test + void getConsortiumItem_positive_noItems() { + var instanceId = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var items = Collections.emptyList(); + var searchResult = SearchResult.of(1, List.of(new Instance().id(instanceId).items(items))); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumItem(id, request); + + assertThat(result).isEqualTo(new ConsortiumItem()); + } + + @Test + void getConsortiumItem_positive_noDesiredItem() { + var instanceId = UUID.randomUUID().toString(); + var id = UUID.randomUUID().toString(); + var request = Mockito.>mock(); + var items = List.of(item(UUID.randomUUID().toString(), CENTRAL_TENANT_ID)); + var searchResult = SearchResult.of(1, List.of(new Instance().id(instanceId).items(items))); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.getConsortiumItem(id, request); + + assertThat(result).isEqualTo(new ConsortiumItem()); + } + + @Test + void fetchConsortiumBatchHoldings_positive() { + var ids = List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()); + var instances = List.of( + instanceForHoldings(List.of(holding(UUID.randomUUID().toString(), CENTRAL_TENANT_ID), + holding(ids.get(0), MEMBER_TENANT_ID))), + instanceForHoldings(List.of(holding(ids.get(1), CENTRAL_TENANT_ID))), + instanceForHoldings(List.of(holding(UUID.randomUUID().toString(), CENTRAL_TENANT_ID)))); + var searchResult = SearchResult.of(instances.size(), instances); + var request = Mockito.>mock(); + var expected = new ConsortiumHoldingCollection() + .holdings(instances.subList(0, instances.size() - 1).stream().map(instance -> { + var holding = instance.getHoldings().size() > 1 ? instance.getHoldings().get(1) : instance.getHoldings().get(0); + return new ConsortiumHolding() + .id(holding.getId()) + .instanceId(instance.getId()) + .tenantId(holding.getTenantId()); + }).toList()) + .totalRecords(2); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.fetchConsortiumBatchHoldings(request, Sets.newHashSet(ids)); + + assertThat(result).isEqualTo(expected); + } + + @Test + void fetchConsortiumBatchItems_positive() { + var ids = List.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()); + var instances = List.of( + instanceForItems(List.of(item(UUID.randomUUID().toString(), CENTRAL_TENANT_ID), + item(ids.get(0), MEMBER_TENANT_ID))), + instanceForItems(List.of(item(ids.get(1), CENTRAL_TENANT_ID))), + instanceForItems(List.of(item(UUID.randomUUID().toString(), CENTRAL_TENANT_ID)))); + var searchResult = SearchResult.of(instances.size(), instances); + var request = Mockito.>mock(); + var expected = new ConsortiumItemCollection() + .items(instances.subList(0, instances.size() - 1).stream().map(instance -> { + var item = instance.getItems().size() > 1 ? instance.getItems().get(1) : instance.getItems().get(0); + return new ConsortiumItem() + .id(item.getId()) + .instanceId(instance.getId()) + .tenantId(item.getTenantId()) + .holdingsRecordId(item.getHoldingsRecordId()); + }).toList()) + .totalRecords(2); + + when(searchService.search(request)).thenReturn(searchResult); + + var result = service.fetchConsortiumBatchItems(request, Sets.newHashSet(ids)); + + assertThat(result).isEqualTo(expected); + } + + private Holding holding(String id, String tenantId) { + return new Holding() + .id(id) + .tenantId(tenantId); + } + + private Item item(String id, String tenantId) { + return new Item() + .id(id) + .tenantId(tenantId) + .holdingsRecordId(UUID.randomUUID().toString()); + } + + private Instance instanceForHoldings(List holdings) { + return new Instance() + .id(UUID.randomUUID().toString()) + .holdings(holdings); + } + + private Instance instanceForItems(List items) { + return new Instance() + .id(UUID.randomUUID().toString()) + .items(items); + } +} diff --git a/src/test/java/org/folio/search/support/base/ApiEndpoints.java b/src/test/java/org/folio/search/support/base/ApiEndpoints.java index 1309e2b69..dfc6d31c4 100644 --- a/src/test/java/org/folio/search/support/base/ApiEndpoints.java +++ b/src/test/java/org/folio/search/support/base/ApiEndpoints.java @@ -41,6 +41,22 @@ public static String consortiumItemsSearchPath(List> queryP return addQueryParams(consortiumItemsSearchPath(), queryParams); } + public static String consortiumHoldingSearchPath(String id) { + return "/search/consortium/holding/" + id; + } + + public static String consortiumItemSearchPath(String id) { + return "/search/consortium/item/" + id; + } + + public static String consortiumBatchHoldingsSearchPath() { + return "/search/consortium/batch/holdings"; + } + + public static String consortiumBatchItemsSearchPath() { + return "/search/consortium/batch/items"; + } + public static String authoritySearchPath() { return "/search/authorities"; } diff --git a/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java b/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java index 980c0ddca..b56f35b4b 100644 --- a/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java +++ b/src/test/java/org/folio/search/support/base/BaseConsortiumIntegrationTest.java @@ -103,6 +103,25 @@ public static ResultActions doGet(MockHttpServletRequestBuilder request, String .andExpect(status().isOk()); } + @SneakyThrows + public static ResultActions tryPost(String uri, Object body) { + return tryPost(uri, MEMBER_TENANT_ID, body); + } + + @SneakyThrows + public static ResultActions tryPost(String uri, String tenantHeader, Object body) { + return mockMvc.perform(post(uri) + .content(asJsonString(body)) + .headers(defaultHeaders(tenantHeader)) + .accept("application/json;charset=UTF-8")); + } + + @SneakyThrows + public static ResultActions doPost(String uri, String tenantHeader, Object body) { + return tryPost(uri, tenantHeader, body) + .andExpect(status().isOk()); + } + @SneakyThrows protected static ResultActions doSearchByInstances(String query) { return doSearch(instanceSearchPath(), MEMBER_TENANT_ID, query, null, null, null); diff --git a/src/test/resources/samples/instance-response-sample/instance-full-response.json b/src/test/resources/samples/instance-response-sample/instance-full-response.json index 7f25f4f11..03c6c3f04 100644 --- a/src/test/resources/samples/instance-response-sample/instance-full-response.json +++ b/src/test/resources/samples/instance-response-sample/instance-full-response.json @@ -220,6 +220,7 @@ "staffOnly": false } ], + "holdingsRecordId" : "e3ff6133-b9a2-4d4c-a1c9-dc1867d4df19", "metadata": { "createdDate": "2020-12-08T15:47:21.327+00:00", "updatedDate": "2021-01-18T08:17:56.752+00:00", @@ -281,6 +282,7 @@ "staffOnly": false } ], + "holdingsRecordId" : "e3ff6133-b9a2-4d4c-a1c9-dc1867d4df19", "metadata": { "createdDate": "2020-12-08T15:47:22.827+00:00", "updatedDate": "2020-12-08T15:47:22.827+00:00" @@ -312,6 +314,7 @@ "notes": [], "statisticalCodeIds": [], "circulationNotes": [], + "holdingsRecordId" : "9550c935-401a-4a85-875e-4d1fe7678870", "metadata": { "createdDate": "2024-03-14T13:05:09.406+00:00", "createdByUserId": "93fc1367-7321-4836-b2a7-cbc7ffe8c20e",