Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- remove unused
Browse files Browse the repository at this point in the history
psmagin committed Sep 3, 2024
1 parent 6fcd484 commit 564505d
Showing 42 changed files with 460 additions and 2,335 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -404,10 +404,10 @@
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
<sourceDirectory>${project.build.testSourceDirectory}</sourceDirectory>
</sourceDirectories>
<failsOnError>false</failsOnError>
<failsOnError>true</failsOnError>
<violationSeverity>warning</violationSeverity>
<failOnViolation>false</failOnViolation>
<logViolationsToConsole>false</logViolationsToConsole>
<failOnViolation>true</failOnViolation>
<logViolationsToConsole>true</logViolationsToConsole>
<configLocation>checkstyle/checkstyle.xml</configLocation>
<cacheFile>${basedir}/target/cachefile</cacheFile>
</configuration>
69 changes: 0 additions & 69 deletions src/main/java/org/folio/search/client/InventoryViewClient.java

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/java/org/folio/search/cql/SuDocCallNumber.java
Original file line number Diff line number Diff line change
@@ -104,7 +104,7 @@ private void appendWithSymbolIfNeeded(StringBuilder key, String cnPart) {
}
var parts = cnPart.split("[./ -]");
for (String part : parts) {
if (key.length() > 0) {
if (!key.isEmpty()) {
key.append(' ');
}
part = part.trim();
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.folio.search.client.InventoryViewClient;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.folio.search.service.reindex.jdbc.UploadInstanceRepository;
@@ -23,11 +22,8 @@
@Log4j2
@Service
@RequiredArgsConstructor
public class ResourceFetchService {
public class InstanceFetchService {

private static final int BATCH_SIZE = 50;

private final InventoryViewClient inventoryClient;
private final FolioExecutionContext context;
private final UploadInstanceRepository instanceRepository;

Original file line number Diff line number Diff line change
@@ -24,7 +24,6 @@
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.logging.log4j.message.FormattedMessage;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.model.event.ConsortiumInstanceEvent;
import org.folio.search.model.types.ResourceType;
import org.folio.search.service.ResourceService;
import org.folio.search.service.config.ConfigSynchronizationService;
@@ -203,13 +202,4 @@ private static void logFailedEvent(ResourceEvent event, Exception e) {
), e);
}

private static void logFailedConsortiumEvent(ConsortiumInstanceEvent event, Exception e) {
if (event == null) {
log.warn("Failed to index resource event [event: null]", e);
return;
}

log.warn(new FormattedMessage("Failed to index consortium instance [tenantId: {}, id: {}]",
event.getTenant(), event.getInstanceId()), e);
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

5 changes: 0 additions & 5 deletions src/main/java/org/folio/search/service/IndexService.java
Original file line number Diff line number Diff line change
@@ -25,7 +25,6 @@
import org.folio.search.model.types.ResourceType;
import org.folio.search.repository.IndexNameProvider;
import org.folio.search.repository.IndexRepository;
import org.folio.search.service.consortium.ConsortiumInstanceService;
import org.folio.search.service.consortium.TenantProvider;
import org.folio.search.service.es.SearchMappingsHelper;
import org.folio.search.service.es.SearchSettingsHelper;
@@ -46,7 +45,6 @@ public class IndexService {
private final SearchSettingsHelper settingsHelper;
private final ResourceReindexClient resourceReindexClient;
private final ResourceDescriptionService resourceDescriptionService;
private final ConsortiumInstanceService consortiumInstanceService;
private final IndexNameProvider indexNameProvider;
private final TenantProvider tenantProvider;
private final LocationService locationService;
@@ -147,9 +145,6 @@ && notConsortiumMemberTenant(tenantId)) {
resources.forEach(resourceName -> {
dropIndex(resourceName, tenantId);
createIndex(resourceName, tenantId, reindexRequest.getIndexSettings());
if (ResourceType.INSTANCE.getName().equals(resource)) {
consortiumInstanceService.deleteAll();
}
});
}

4 changes: 2 additions & 2 deletions src/main/java/org/folio/search/service/ResourceService.java
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
import org.folio.search.domain.dto.FolioIndexOperationResponse;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.folio.search.integration.ResourceFetchService;
import org.folio.search.integration.InstanceFetchService;
import org.folio.search.model.index.SearchDocumentBody;
import org.folio.search.model.metadata.ResourceDescription;
import org.folio.search.model.metadata.ResourceIndexingConfiguration;
@@ -48,7 +48,7 @@ public class ResourceService {
private static final String PRIMARY_INDEXING_REPOSITORY_NAME = "primary";
private static final String INSTANCE_ID_FIELD = "instanceId";

private final ResourceFetchService resourceFetchService;
private final InstanceFetchService resourceFetchService;
private final PrimaryResourceRepository primaryResourceRepository;
private final ResourceDescriptionService resourceDescriptionService;
private final MultiTenantSearchDocumentConverter multiTenantSearchDocumentConverter;
Original file line number Diff line number Diff line change
@@ -1,22 +1,10 @@
package org.folio.search.service.consortium;

import static org.folio.search.service.consortium.ConsortiumSearchQueryBuilder.CONSORTIUM_TABLES;
import static org.folio.search.utils.JdbcUtils.getFullTableName;
import static org.folio.search.utils.JdbcUtils.getParamPlaceholder;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.search.domain.dto.ConsortiumHolding;
import org.folio.search.domain.dto.ConsortiumItem;
import org.folio.search.model.types.ResourceType;
import org.folio.spring.FolioExecutionContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@@ -26,58 +14,9 @@
@RequiredArgsConstructor
public class ConsortiumInstanceRepository {

private static final String SELECT_BY_ID_SQL = "SELECT * FROM %s WHERE instance_id IN (%s)";
private static final String DELETE_BY_TENANT_AND_ID_SQL = "DELETE FROM %s WHERE tenant_id = ? AND instance_id = ?;";
private static final String DELETE_ALL_SQL = "TRUNCATE TABLE %s;";
private static final String UPSERT_SQL = """
INSERT INTO %s (tenant_id, instance_id, json, created_date, updated_date)
VALUES (?, ?, ?::json, ?, ?)
ON CONFLICT (tenant_id, instance_id)
DO UPDATE SET json = EXCLUDED.json, updated_date = EXCLUDED.updated_date;
""";
private static final String TENANT_ID_COLUMN = "tenant_id";
private static final String INSTANCE_ID_COLUMN = "instance_id";
private static final String JSON_COLUMN = "json";

private final JdbcTemplate jdbcTemplate;
private final FolioExecutionContext context;

public List<ConsortiumInstance> fetch(List<String> instanceIds) {
log.debug("fetch::consortium instances by [ids: {}]", instanceIds);
return jdbcTemplate.query(
SELECT_BY_ID_SQL.formatted(getTableName(), getParamPlaceholder(instanceIds.size())),
(rs, rowNum) -> toConsortiumInstance(rs),
instanceIds.toArray());
}

public void save(List<ConsortiumInstance> instances) {
log.debug("save::consortium instances [number: {}]", instances.size());
jdbcTemplate.batchUpdate(
UPSERT_SQL.formatted(getTableName()),
instances,
100,
(PreparedStatement ps, ConsortiumInstance item) -> {
ps.setString(1, item.id().tenantId());
ps.setString(2, item.id().instanceId());
ps.setString(3, item.instance());
ps.setTimestamp(4, Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault())));
ps.setTimestamp(5, Timestamp.valueOf(LocalDateTime.now(ZoneId.systemDefault())));
});
}

public void delete(Set<ConsortiumInstanceId> instanceIds) {
log.debug("delete::consortium instances [tenant-instanceIds: {}]", instanceIds);
jdbcTemplate.batchUpdate(
DELETE_BY_TENANT_AND_ID_SQL.formatted(getTableName()),
instanceIds,
100,
(PreparedStatement ps, ConsortiumInstanceId id) -> {
ps.setString(1, id.tenantId());
ps.setString(2, id.instanceId());
}
);
}

public List<ConsortiumHolding> fetchHoldings(ConsortiumSearchQueryBuilder searchQueryBuilder) {
return jdbcTemplate.query(searchQueryBuilder.buildSelectQuery(context),
(rs, rowNum) -> new ConsortiumHolding()
@@ -113,17 +52,4 @@ public List<ConsortiumItem> fetchItems(ConsortiumSearchQueryBuilder searchQueryB
);
}

public void deleteAll() {
log.debug("deleteAll::consortium instances");
jdbcTemplate.execute(DELETE_ALL_SQL.formatted(getTableName()));
}

private ConsortiumInstance toConsortiumInstance(ResultSet rs) throws SQLException {
var id = new ConsortiumInstanceId(rs.getString(TENANT_ID_COLUMN), rs.getString(INSTANCE_ID_COLUMN));
return new ConsortiumInstance(id, rs.getString(JSON_COLUMN));
}

private String getTableName() {
return getFullTableName(context, CONSORTIUM_TABLES.get(ResourceType.INSTANCE));
}
}
Original file line number Diff line number Diff line change
@@ -1,161 +1,21 @@
package org.folio.search.service.consortium;

import static org.apache.commons.collections4.IterableUtils.toList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
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.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.folio.search.model.event.ConsortiumInstanceEvent;
import org.folio.search.model.service.ConsortiumSearchContext;
import org.folio.search.model.types.ResourceType;
import org.folio.search.utils.JsonConverter;
import org.folio.search.utils.SearchConverterUtils;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.tools.kafka.FolioMessageProducer;
import org.jetbrains.annotations.NotNull;
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 ConsortiumInstanceService {

private static final String ID_KEY = "id";
private static final String HOLDINGS_KEY = "holdings";
private static final String ITEMS_KEY = "items";
private static final String TENANT_ID_KEY = "tenantId";
private static final String HOLDINGS_TENANT_ID_KEY = "holdings.tenantId";
private static final String ITEMS_TENANT_ID_KEY = "items.tenantId";
private static final String SHARED_KEY = "shared";

private final JsonConverter jsonConverter;
private final ConsortiumInstanceRepository repository;
private final ConsortiumTenantExecutor consortiumTenantExecutor;
private final ConsortiumTenantProvider consortiumTenantProvider;
private final FolioMessageProducer<ConsortiumInstanceEvent> producer;
private final FolioExecutionContext context;

/**
* Saves instances to database for future indexing into consortium shared index.
*
* @param instanceEvents list of instance events
* @return events that are not related to consortium tenants
*/
public List<ResourceEvent> saveInstances(List<ResourceEvent> instanceEvents) {
log.info("Saving consortium instances to DB [size: {}]", instanceEvents.size());
if (CollectionUtils.isEmpty(instanceEvents)) {
return instanceEvents;
}
var consortiumTenantEventsMap = groupEventsByConsortiumTenant(instanceEvents);

var consortiumResourceEvents = consortiumTenantEventsMap.get(true);
if (CollectionUtils.isNotEmpty(consortiumResourceEvents)) {
var instances = consortiumResourceEvents.stream()
.map(this::prepareInstance)
.map(map -> new ConsortiumInstance(
new ConsortiumInstanceId(map.get(TENANT_ID_KEY).toString(), map.get(ID_KEY).toString()),
jsonConverter.toJson(map)))
.toList();

consortiumTenantExecutor.run(() -> {
repository.save(instances);
prepareAndSendConsortiumInstanceEvents(instances, instance -> instance.id().instanceId());
});
}
return consortiumTenantEventsMap.get(false);
}

/**
* Delete instances from database that should reflect into consortium shared index.
*
* @param instanceEvents list of instance events
* @return events that are not related to consortium tenants
*/
public List<ResourceEvent> deleteInstances(List<ResourceEvent> instanceEvents) {
if (CollectionUtils.isEmpty(instanceEvents)) {
return instanceEvents;
}
var consortiumTenantEventsMap = groupEventsByConsortiumTenant(instanceEvents);
var consortiumResourceEvents = consortiumTenantEventsMap.get(true);
if (CollectionUtils.isNotEmpty(consortiumResourceEvents)) {
var instanceIds = consortiumTenantEventsMap.get(true).stream()
.map(resourceEvent -> new ConsortiumInstanceId(resourceEvent.getTenant(), resourceEvent.getId()))
.collect(Collectors.toSet());

consortiumTenantExecutor.run(() -> {
repository.delete(instanceIds);
prepareAndSendConsortiumInstanceEvents(instanceIds, ConsortiumInstanceId::instanceId);
});
}
return consortiumTenantEventsMap.get(false);
}

public void deleteAll() {
log.info("Truncate consortium instances table");
consortiumTenantExecutor.run(repository::deleteAll);
}

public List<ResourceEvent> fetchInstances(Iterable<String> instanceIds) {
List<ResourceEvent> resourceEvents = new ArrayList<>();

var instanceIdList = toList(instanceIds).stream().distinct().toList();
var instances = consortiumTenantExecutor.execute(() -> repository.fetch(instanceIdList));
var instancesById =
instances.stream().collect(Collectors.groupingBy(instance -> instance.id().instanceId()));

var missedIds = ListUtils.subtract(instanceIdList, new ArrayList<>(instancesById.keySet()));
for (var missedId : missedIds) {
resourceEvents.add(new ResourceEvent().id(missedId.toString())
.type(ResourceEventType.DELETE)
.resourceName(ResourceType.INSTANCE.getName())
.old(Map.of(ID_KEY, missedId))
.tenant(context.getTenantId()));
}

for (var entry : instancesById.entrySet()) {
Map<String, Object> mergedInstance = new HashMap<>();
List<Map<String, Object>> mergedHoldings = new ArrayList<>();
List<Map<String, Object>> mergedItems = new ArrayList<>();
if (entry.getValue().size() == 1) {
// if only one instance returned then there is nothing to merge (local instance)
mergedInstance = jsonConverter.fromJsonToMap(entry.getValue().get(0).instance());
} else {
// if more than one instance returned then holdings/items merging required
for (var instance : entry.getValue()) {
var instanceMap = jsonConverter.fromJsonToMap(instance.instance());
if (consortiumTenantProvider.isCentralTenant(instance.id().tenantId())) {
mergedInstance = instanceMap;
}
addListItems(mergedHoldings, instanceMap, HOLDINGS_KEY);
addListItems(mergedItems, instanceMap, ITEMS_KEY);
}
}
var resourceEvent = toResourceEvent(mergedInstance, mergedHoldings, mergedItems);
resourceEvents.add(resourceEvent);
mergedInstance.clear();
mergedHoldings.clear();
mergedItems.clear();
}
return resourceEvents;
}

public ConsortiumHoldingCollection fetchHoldings(ConsortiumSearchContext context) {
var searchQueryBuilder = new ConsortiumSearchQueryBuilder(context);
@@ -169,53 +29,4 @@ public ConsortiumItemCollection fetchItems(ConsortiumSearchContext context) {
return new ConsortiumItemCollection().items(itemList).totalRecords(repository.count(searchQueryBuilder));
}

@SuppressWarnings("unchecked")
private void addListItems(List<Map<String, Object>> mergedList, Map<String, Object> instanceMap, String key) {
var items = instanceMap.get(key);
if (items instanceof List<?> list) {
mergedList.addAll((List<Map<String, Object>>) list);
}
}

private ResourceEvent toResourceEvent(Map<String, Object> mergedInstance, List<Map<String, Object>> mergedHoldings,
List<Map<String, Object>> mergedItems) {
if (!mergedHoldings.isEmpty()) {
mergedInstance.put(HOLDINGS_KEY, new ArrayList<>(mergedHoldings));
}
if (!mergedItems.isEmpty()) {
mergedInstance.put(ITEMS_KEY, new ArrayList<>(mergedItems));
}
return new ResourceEvent().id(mergedInstance.get(ID_KEY).toString())
.type(ResourceEventType.UPDATE)
.resourceName(ResourceType.INSTANCE.getName())
._new(new HashMap<>(mergedInstance))
.tenant(context.getTenantId());
}

private Map<String, Object> prepareInstance(ResourceEvent resourceEvent) {
var instance = SearchConverterUtils.getNewAsMap(resourceEvent);
var tenant = resourceEvent.getTenant();
SearchConverterUtils.setMapValueByPath(TENANT_ID_KEY, tenant, instance);
SearchConverterUtils.setMapValueByPath(HOLDINGS_TENANT_ID_KEY, tenant, instance);
SearchConverterUtils.setMapValueByPath(ITEMS_TENANT_ID_KEY, tenant, instance);
if (consortiumTenantProvider.isCentralTenant(tenant)) {
SearchConverterUtils.setMapValueByPath(SHARED_KEY, true, instance);
}
return instance;
}

@NotNull
private Map<Boolean, List<ResourceEvent>> groupEventsByConsortiumTenant(List<ResourceEvent> instanceEvents) {
return instanceEvents.stream()
.collect(Collectors.groupingBy(event -> consortiumTenantProvider.isConsortiumTenant(event.getTenant())));
}

private <T> void prepareAndSendConsortiumInstanceEvents(Collection<T> values,
Function<T, String> instanceIdFunction) {
var consortiumInstanceEvents = values.stream()
.map(instanceIdFunction)
.map(ConsortiumInstanceEvent::new)
.toList();
producer.sendMessages(consortiumInstanceEvents);
}
}
Original file line number Diff line number Diff line change
@@ -5,10 +5,10 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.folio.search.configuration.properties.ReindexConfigurationProperties;
import org.folio.search.model.types.ReindexEntityType;
import org.folio.search.service.reindex.ReindexConstants;
import org.folio.search.utils.JdbcUtils;
import org.folio.search.utils.JsonConverter;
import org.folio.spring.FolioExecutionContext;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -53,8 +53,7 @@ public List<Map<String, Object>> fetchByIds(List<String> ids) {
if (ids == null || ids.isEmpty()) {
return Collections.emptyList();
}
var whereClause = INSTANCE_IDS_WHERE_CLAUSE.formatted(ids.stream().map(v -> "?::uuid")
.collect(Collectors.joining(", ")));
var whereClause = INSTANCE_IDS_WHERE_CLAUSE.formatted(JdbcUtils.getParamPlaceholderForUuid(ids.size()));
var sql = SELECT_SQL_TEMPLATE.formatted(getFullTableName(context, entityTable()),
getFullTableName(context, "holding"),
getFullTableName(context, "item"),
7 changes: 5 additions & 2 deletions src/main/java/org/folio/search/utils/KafkaConstants.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.folio.search.utils;

public final class KafkaConstants {
import lombok.experimental.UtilityClass;

@UtilityClass
public class KafkaConstants {

public static final String AUTHORITY_LISTENER_ID = "mod-search-authorities-listener";
public static final String EVENT_LISTENER_ID = "mod-search-events-listener";
public static final String CLASSIFICATION_TYPE_LISTENER_ID = "mod-search-classification-type-listener";
@@ -9,5 +13,4 @@ public final class KafkaConstants {
public static final String REINDEX_RANGE_INDEX_LISTENER_ID = "mod-search-reindex-index-listener";
public static final String REINDEX_RECORDS_LISTENER_ID = "mod-search-reindex-records-listener";

private KafkaConstants() {}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.folio.search.controller;

import static java.util.Collections.emptyList;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static org.assertj.core.api.Assertions.assertThat;
@@ -10,33 +9,34 @@
import static org.folio.search.utils.TestUtils.getShelfKeyFromCallNumber;
import static org.folio.search.utils.TestUtils.parseResponse;
import static org.folio.search.utils.TestUtils.randomId;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.folio.search.domain.dto.CallNumberBrowseResult;
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.domain.dto.ItemEffectiveCallNumberComponents;
import org.folio.search.support.base.BaseIntegrationTest;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

@Disabled
@IntegrationTest
class BrowseAroundCallNumberIT extends BaseIntegrationTest {
class BrowseCallNumberAroundIT extends BaseIntegrationTest {

private static final Instance[] INSTANCES = instances();
private static final Map<String, Instance> INSTANCE_MAP =
Arrays.stream(INSTANCES).collect(toMap(Instance::getTitle, identity()));

@BeforeAll
static void prepare() {
setUpTenant(INSTANCES);
setUpTenant(List.of(jsonPath("sum($.instances..items.length())", is((double) INSTANCES.length))), INSTANCES);
}

@AfterAll
@@ -117,15 +117,19 @@ void browseByCallNumber_browsingAroundSucceedingRecordsWithSame10FirstSymbols()

private static Instance[] instances() {
return callNumberBrowseInstanceData().stream()
.map(BrowseAroundCallNumberIT::instance)
.map(BrowseCallNumberAroundIT::instance)
.toArray(Instance[]::new);
}

@SuppressWarnings("unchecked")
private static Instance instance(List<Object> data) {
var holding = new Holding().id(randomId()).tenantId(TENANT_ID).discoverySuppress(false);

var items = ((List<String>) data.get(1)).stream()
.map(callNumber -> new Item()
.id(randomId())
.tenantId(TENANT_ID)
.holdingsRecordId(holding.getId())
.discoverySuppress(false)
.effectiveCallNumberComponents(new ItemEffectiveCallNumberComponents().callNumber(callNumber))
.effectiveShelvingOrder(getShelfKeyFromCallNumber(callNumber)))
@@ -140,7 +144,7 @@ private static Instance instance(List<Object> data) {
.shared(false)
.tenantId(TENANT_ID)
.items(items)
.holdings(emptyList());
.holdings(List.of(holding));
}

private Instance instance(String title) {
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
import static org.folio.search.domain.dto.TenantConfiguredFeature.BROWSE_CN_INTERMEDIATE_VALUES;
import static org.folio.search.support.base.ApiEndpoints.instanceCallNumberBrowsePath;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.cleanupActual;
import static org.folio.search.utils.TestUtils.cnBrowseItem;
import static org.folio.search.utils.TestUtils.getShelfKeyFromCallNumber;
import static org.folio.search.utils.TestUtils.parseResponse;
@@ -29,14 +30,12 @@
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

@Disabled
@IntegrationTest
class BrowseCallNumberIT extends BaseIntegrationTest {

@@ -63,6 +62,7 @@ void browseByCallNumber_parameterized(String query, String anchor, Integer limit
.param("query", prepareQuery(query, '"' + anchor + '"'))
.param("limit", String.valueOf(limit));
var actual = parseResponse(doGet(request), CallNumberBrowseResult.class);
cleanupActual(actual);
assertThat(actual).isEqualTo(expected);
}

@@ -74,6 +74,7 @@ void browseByCallNumber_browsingAroundWhenPrecedingRecordsCountIsSpecified() {
.param("expandAll", "true")
.param("precedingRecordsCount", "4");
var actual = parseResponse(doGet(request), CallNumberBrowseResult.class);
cleanupActual(actual);
assertThat(actual).isEqualTo(new CallNumberBrowseResult()
.totalRecords(36).prev(null).next("CE 16 B6713 X 41993").items(List.of(
cnBrowseItem(instance("instance #31"), "AB 14 C72 NO 220"),
@@ -94,6 +95,7 @@ void browseByCallNumber_browsingVeryFirstCallNumberWithNoException() {
.param("expandAll", "true")
.param("precedingRecordsCount", "5");
var actual = parseResponse(doGet(request), CallNumberBrowseResult.class);
cleanupActual(actual);
assertThat(actual).isEqualTo(new CallNumberBrowseResult()
.totalRecords(32).prev(null).next("CE 16 B6713 X 41993").items(List.of(
cnBrowseItem(instance("instance #31"), "AB 14 C72 NO 220", true),
@@ -112,6 +114,7 @@ void browseByCallNumber_browsingAroundWhenMultipleAnchors() {
.param("expandAll", "true")
.param("precedingRecordsCount", "4");
var actual = parseResponse(doGet(request), CallNumberBrowseResult.class);
cleanupActual(actual);
var expected = new CallNumberBrowseResult()
.totalRecords(41).prev("GA 16 D64 41548A").next("J29.29:M54/990").items(List.of(
cnBrowseItem(instance("instance #39"), "GA 16 D64 41548A"),
@@ -131,7 +134,7 @@ void browseByCallNumber_browsingAroundWithoutHighlightMatch() {
.param("expandAll", "true")
.param("highlightMatch", "false");
var actual = parseResponse(doGet(request), CallNumberBrowseResult.class);

cleanupActual(actual);
assertThat(actual).isEqualTo(new CallNumberBrowseResult()
.totalRecords(36).prev("AC 11 A67 X 42000").next("CE 16 D86 X 41998").items(List.of(
cnBrowseItem(instance("instance #08"), "AC 11 A67 X 42000"),
@@ -151,7 +154,7 @@ void browseByCalNumber_browseAroundWithEnabledIntermediateValues() throws Interr
.param("limit", "7")
.param("expandAll", "true");
var actual = parseResponse(doGet(request), CallNumberBrowseResult.class);

cleanupActual(actual);
assertThat(actual).isEqualTo(new CallNumberBrowseResult()
.totalRecords(49).prev("DA 3870 B55 41868").next("DA 3880 O6 D5").items(List.of(
cnBrowseItem(instance("instance #41"), "DA 3870 B55 41868"),
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@

@Disabled
@IntegrationTest
class BrowseTypedCallNumberIT extends BaseIntegrationTest {
class BrowseCallNumberTypedIT extends BaseIntegrationTest {

private static final String LOCAL_TYPE_2 = "654d7565-b277-4dfa-8b7d-fbf306d9d0cd";
private static final Instance[] INSTANCES = instances();
@@ -222,7 +222,7 @@ void getFacets_positive_withFiltrationByCallNumberType() throws Exception {

private static Instance[] instances() {
return callNumberBrowseInstanceData().stream()
.map(BrowseTypedCallNumberIT::instance)
.map(BrowseCallNumberTypedIT::instance)
.toArray(Instance[]::new);
}

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.folio.search.controller;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.groupingBy;
@@ -13,13 +12,16 @@
import static org.folio.search.utils.TestUtils.getShelfKeyFromCallNumber;
import static org.folio.search.utils.TestUtils.parseResponse;
import static org.folio.search.utils.TestUtils.randomId;
import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.folio.search.domain.dto.CallNumberBrowseResult;
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.domain.dto.ItemEffectiveCallNumberComponents;
@@ -34,7 +36,7 @@

@Disabled
@IntegrationTest
class BrowseTypedCallNumberIrrelevantResultIT extends BaseIntegrationTest {
class BrowseCallNumberTypedIrrelevantResultIT extends BaseIntegrationTest {

private static final Instance[] INSTANCES = instances();
private static final Map<String, Instance> INSTANCE_MAP =
@@ -43,7 +45,7 @@ class BrowseTypedCallNumberIrrelevantResultIT extends BaseIntegrationTest {

@BeforeAll
static void prepare() {
setUpTenant(INSTANCES);
setUpTenant(List.of(jsonPath("sum($.instances..items.length())", is(24.0))), INSTANCES);
enableFeature(BROWSE_CN_INTERMEDIATE_VALUES);
}

@@ -77,7 +79,7 @@ void browseByCallNumber_browsingAroundWithEnabledIntermediateValues() {
cnBrowseItem(instance("instance #10"), "308 H979"),
cnBrowseItem(instance("instance #02"), "308 H980"),
cnBrowseItem(instance("instance #08"), "308 H981")
));
));
expected.setItems(CallNumberUtils.excludeIrrelevantResultItems(CONTEXT, "dewey", emptySet(), expected.getItems()));
assertThat(actual).isEqualTo(expected);
}
@@ -97,7 +99,7 @@ void browseByCallNumber_browsingAroundWithoutExpandAll() {
cnBrowseItem(instance("instance #10"), "Z669.R360 1975", 3, true),
cnBrowseItem(instance("instance #02"), "Z669.R360 1976", 1),
cnBrowseItem(instance("instance #10"), "Z669.R360 1977", 1)
));
));
expected.setItems(CallNumberUtils.excludeIrrelevantResultItems(CONTEXT, "lc", emptySet(), expected.getItems()));
leaveOnlyBasicProps(expected);
assertThat(actual).isEqualTo(expected);
@@ -180,15 +182,24 @@ private static Instance instance(String title) {
}

private static Instance instance(List<List<String>> data, String title) {
var items = data.stream().map(d -> new Item()
var items = new ArrayList<Item>();
var holdings = new ArrayList<Holding>();
for (List<String> d : data) {
var holding = new Holding()
.id(randomId())
.tenantId(TENANT_ID)
.discoverySuppress(false);
var item = new Item()
.id(randomId())
.holdingsRecordId(holding.getId())
.tenantId(TENANT_ID)
.discoverySuppress(false)
.effectiveCallNumberComponents(new ItemEffectiveCallNumberComponents()
.callNumber(d.get(1))
.typeId(d.get(0)))
.effectiveShelvingOrder(getShelfKeyFromCallNumber(d.get(1), d.get(0))))
.toList();
.effectiveShelvingOrder(getShelfKeyFromCallNumber(d.get(1), d.get(0)));
items.add(item);
}

return new Instance()
.id(randomId())
@@ -199,7 +210,7 @@ private static Instance instance(List<List<String>> data, String title) {
.shared(false)
.tenantId(TENANT_ID)
.items(items)
.holdings(emptyList());
.holdings(holdings);
}

private static List<List<String>> callNumberBrowseInstanceData() {
Original file line number Diff line number Diff line change
@@ -182,6 +182,7 @@ void updateIndexDynamicSettings_negative() throws Exception {
.andExpect(jsonPath("$.errors[0].parameters[0].value", is("unknown")));
}


private static String reindexRequestJson(String resource) {
return REINDEX_REQUEST.formatted(resource);
}
47 changes: 47 additions & 0 deletions src/test/java/org/folio/search/controller/IndexingAuthorityIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.folio.search.controller;

import static org.folio.search.domain.dto.ResourceEventType.DELETE;
import static org.folio.search.model.types.ResourceType.AUTHORITY;
import static org.folio.search.support.base.ApiEndpoints.authoritySearchPath;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestConstants.inventoryAuthorityTopic;
import static org.folio.search.utils.TestUtils.randomId;
import static org.folio.search.utils.TestUtils.resourceEvent;
import static org.folio.search.utils.TestUtils.toMap;

import java.util.List;
import org.folio.search.domain.dto.Authority;
import org.folio.search.support.base.BaseIntegrationTest;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

@IntegrationTest
class IndexingAuthorityIT extends BaseIntegrationTest {

@BeforeAll
static void prepare() {
setUpTenant();
}

@AfterAll
static void cleanUp() {
removeTenant();
}

@Test
void shouldRemoveAuthority() {
var authorityId = randomId();
var authority = new Authority().id(authorityId).personalName("personal name")
.corporateName("corporate name").uniformTitle("uniform title");
var resourceEvent = resourceEvent(authorityId, AUTHORITY, toMap(authority));
kafkaTemplate.send(inventoryAuthorityTopic(TENANT_ID), resourceEvent);
assertCountByIds(authoritySearchPath(), List.of(authorityId), 3);

var deleteEvent = resourceEvent(authorityId, AUTHORITY, null).type(DELETE).old(toMap(authority));
kafkaTemplate.send(inventoryAuthorityTopic(TENANT_ID), deleteEvent);
assertCountByIds(authoritySearchPath(), List.of(authorityId), 0);
}

}
Original file line number Diff line number Diff line change
@@ -2,49 +2,27 @@

import static org.assertj.core.api.AssertionsForClassTypes.entry;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS;
import static org.awaitility.Durations.ONE_MINUTE;
import static org.folio.search.model.client.CqlQuery.exactMatchAny;
import static org.folio.search.model.types.ResourceType.INSTANCE_CLASSIFICATION;
import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath;
import static org.folio.search.utils.SearchUtils.getIndexName;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.randomId;
import static org.hamcrest.Matchers.is;
import static org.opensearch.index.query.QueryBuilders.matchAllQuery;
import static org.opensearch.search.builder.SearchSourceBuilder.searchSource;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;

import java.util.List;
import java.util.Map;
import lombok.SneakyThrows;
import org.awaitility.Awaitility;
import org.awaitility.core.ThrowingRunnable;
import org.folio.search.domain.dto.Classification;
import org.folio.search.domain.dto.Instance;
import org.folio.search.model.client.CqlQueryParam;
import org.folio.search.model.types.ResourceType;
import org.folio.search.support.base.BaseIntegrationTest;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.opensearch.action.search.SearchRequest;
import org.opensearch.action.search.SearchResponse;
import org.opensearch.client.RequestOptions;
import org.opensearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;

@Disabled
@IntegrationTest
class IndexingInstanceClassificationIT extends BaseIntegrationTest {

@Autowired
private RestHighLevelClient restHighLevelClient;

@BeforeAll
static void prepare() {
setUpTenant(Instance.class);
setUpTenant();
}

@AfterAll
@@ -64,9 +42,9 @@ void shouldIndexInstanceClassification_createNewDocument() {
inventoryApi.createInstance(TENANT_ID, instance1);
inventoryApi.createInstance(TENANT_ID, instance2);
assertCountByIds(instanceSearchPath(), List.of(instanceId1, instanceId2), 2);
await(() -> assertThat(fetchAllInstanceClassifications(TENANT_ID).getHits().getHits()).hasSize(1));
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CLASSIFICATION, TENANT_ID)).hasSize(1));

var hits = fetchAllInstanceClassifications(TENANT_ID).getHits().getHits();
var hits = fetchAllDocuments(INSTANCE_CLASSIFICATION, TENANT_ID);
var sourceAsMap = hits[0].getSourceAsMap();
assertThat(sourceAsMap)
.contains(
@@ -93,28 +71,8 @@ void shouldIndexInstanceClassification_deleteDocument() {
var instance = new Instance().id(instanceId).addClassificationsItem(classification);
inventoryApi.createInstance(TENANT_ID, instance);
assertCountByIds(instanceSearchPath(), List.of(instanceId), 1);
await(() -> assertThat(fetchAllInstanceClassifications(TENANT_ID).getHits().getHits()).hasSize(1));
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CLASSIFICATION, TENANT_ID)).hasSize(1));
inventoryApi.updateInstance(TENANT_ID, instance.classifications(null));
await(() -> assertThat(fetchAllInstanceClassifications(TENANT_ID).getHits().getHits()).isEmpty());
}

private static void assertCountByIds(String path, List<String> ids, int expected) {
var query = exactMatchAny(CqlQueryParam.ID, ids).toString();
await(() -> doSearch(path, query).andExpect(jsonPath("$.totalRecords", is(expected))));
}

private static void await(ThrowingRunnable runnable) {
Awaitility.await()
.atMost(ONE_MINUTE)
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.untilAsserted(runnable);
}

@SneakyThrows
private SearchResponse fetchAllInstanceClassifications(String tenantId) {
var searchRequest = new SearchRequest()
.source(searchSource().query(matchAllQuery()))
.indices(getIndexName(ResourceType.INSTANCE_CLASSIFICATION, tenantId));
return restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CLASSIFICATION, TENANT_ID)).isEmpty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.folio.search.controller;

import static org.assertj.core.api.AssertionsForClassTypes.entry;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.folio.search.model.types.ResourceType.INSTANCE_CONTRIBUTOR;
import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.randomId;

import java.util.List;
import java.util.Map;
import org.folio.search.domain.dto.Contributor;
import org.folio.search.domain.dto.Instance;
import org.folio.search.support.base.BaseIntegrationTest;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

@IntegrationTest
class IndexingInstanceContributorIT extends BaseIntegrationTest {

@BeforeAll
static void prepare() {
setUpTenant();
}

@AfterAll
static void cleanUp() {
removeTenant();
}

@Test
void shouldIndexInstanceContributor_createDocument() {
var instanceId1 = randomId();
var instanceId2 = randomId();
var authorityId = "ce176ace-a53e-4b4d-aa89-725ed7b2edac";
var contributorTypeId = "13f6dc91-c2e5-4d18-a6c8-5015974454ef";
var contributorNameTypeId = "d2775d47-5e1f-4659-99ff-ccbaff84af85";
var name = "John Tolkien";
var contributor = new Contributor()
.name(name)
.contributorTypeId(contributorTypeId)
.contributorNameTypeId(contributorNameTypeId)
.authorityId(authorityId);
var instance1 = new Instance().id(instanceId1).addContributorsItem(contributor);
var instance2 = new Instance().id(instanceId2).addContributorsItem(contributor);
inventoryApi.createInstance(TENANT_ID, instance1);
inventoryApi.createInstance(TENANT_ID, instance2);
assertCountByIds(instanceSearchPath(), List.of(instanceId1, instanceId2), 2);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CONTRIBUTOR, TENANT_ID)).hasSize(1));

var hits = fetchAllDocuments(INSTANCE_CONTRIBUTOR, TENANT_ID);
var sourceAsMap = hits[0].getSourceAsMap();
assertThat(sourceAsMap)
.contains(
entry("name", name),
entry("contributorNameTypeId", contributorNameTypeId),
entry("authorityId", authorityId)
);

@SuppressWarnings("unchecked")
var instances = (List<Map<String, Object>>) sourceAsMap.get("instances");
assertThat(instances)
.allSatisfy(map -> assertThat(map).containsEntry("shared", false))
.allSatisfy(map -> assertThat(map).containsEntry("tenantId", TENANT_ID))
.allSatisfy(map -> assertThat(map).containsEntry("typeId", contributorTypeId))
.anySatisfy(map -> assertThat(map).containsEntry("instanceId", instanceId1))
.anySatisfy(map -> assertThat(map).containsEntry("instanceId", instanceId2));
}

@Test
void shouldIndexInstanceContributor_deleteDocument() {
var instanceId = randomId();
var contributor = new Contributor().name("Frodo Begins").authorityId(null);
var instance = new Instance().id(instanceId).addContributorsItem(contributor);
inventoryApi.createInstance(TENANT_ID, instance);
assertCountByIds(instanceSearchPath(), List.of(instanceId), 1);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CONTRIBUTOR, TENANT_ID)).hasSize(1));
inventoryApi.updateInstance(TENANT_ID, instance.contributors(null));
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CONTRIBUTOR, TENANT_ID)).isEmpty());
}
}
114 changes: 114 additions & 0 deletions src/test/java/org/folio/search/controller/IndexingInstanceIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package org.folio.search.controller;

import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.randomId;

import java.util.List;
import java.util.stream.IntStream;
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.support.base.BaseIntegrationTest;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

@IntegrationTest
class IndexingInstanceIT extends BaseIntegrationTest {

private static final List<String> INSTANCE_IDS = getRandomIds(3);
private static final List<String> ITEM_IDS = getRandomIds(2);
private static final List<String> HOLDING_IDS = getRandomIds(4);

@BeforeAll
static void prepare() {
setUpTenant();
}

@AfterAll
static void cleanUp() {
removeTenant();
}

@Test
void shouldRemoveItem() {
createInstances();
var itemIdToDelete = ITEM_IDS.get(1);
inventoryApi.deleteItem(TENANT_ID, itemIdToDelete);
assertCountByQuery(instanceSearchPath(), "items.id=={value}", itemIdToDelete, 0);
assertCountByQuery(instanceSearchPath(), "items.id=={value}", ITEM_IDS.get(0), 1);
}

@Test
void shouldUpdateAndDeleteInstance() {
var instanceId = randomId();
var instance = new Instance().id(instanceId).title("test-resource");

inventoryApi.createInstance(TENANT_ID, instance);
assertCountByQuery(instanceSearchPath(), "title=={value}", "test-resource", 1);

var instanceToUpdate = new Instance().id(instanceId).title("test-resource-updated");
inventoryApi.updateInstance(TENANT_ID, instanceToUpdate);
assertCountByQuery(instanceSearchPath(), "title=={value}", "test-resource-updated", 1);

inventoryApi.deleteInstance(TENANT_ID, instanceId);
assertCountByQuery(instanceSearchPath(), "id=={value}", instanceId, 0);
}

@Test
void shouldRemoveHolding() {
createInstances();
inventoryApi.deleteHolding(TENANT_ID, HOLDING_IDS.get(0));
assertCountByIds(instanceSearchPath(), List.of(HOLDING_IDS.get(0)), 0);
HOLDING_IDS.subList(1, 4).forEach(id -> assertCountByIds(instanceSearchPath(), List.of(id), 1));
}

@Test
void shouldRemoveInstance() {
createInstances();
var instanceIdToDelete = INSTANCE_IDS.get(0);

assertCountByIds(instanceSearchPath(), INSTANCE_IDS, INSTANCE_IDS.size());

inventoryApi.deleteInstance(TENANT_ID, instanceIdToDelete);
assertCountByIds(instanceSearchPath(), List.of(instanceIdToDelete), 0);
List<String> ids = INSTANCE_IDS.subList(1, 3);
assertCountByIds(instanceSearchPath(), ids, 2);
}

private static Item item(int i) {
return new Item().id(ITEM_IDS.get(i)).holdingsRecordId(HOLDING_IDS.get(i));
}

private static Holding holdingsRecord(int i) {
return new Holding().id(HOLDING_IDS.get(i));
}

private static List<String> getRandomIds(int count) {
return IntStream.range(0, count).mapToObj(index -> randomId()).toList();
}

private void createInstances() {
var instances = INSTANCE_IDS.stream()
.map(id -> new Instance().id(id).title("title-" + id))
.toList();

instances.get(0)
.holdings(List.of(holdingsRecord(0), holdingsRecord(1)))
.items(List.of(item(0), item(1)));

instances.get(1).holdings(List.of(holdingsRecord(2), holdingsRecord(3)));

instances.forEach(instance -> inventoryApi.createInstance(TENANT_ID, instance));
assertCountByIds(instanceSearchPath(), INSTANCE_IDS, 3);
for (String itemId : ITEM_IDS) {
assertCountByQuery(instanceSearchPath(), "items.id=={value}", itemId, 1);
}
for (String holdingId : HOLDING_IDS) {
assertCountByQuery(instanceSearchPath(), "holdings.id=={value}", holdingId, 1);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.folio.search.controller;

import static org.assertj.core.api.AssertionsForClassTypes.entry;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
import static org.folio.search.model.types.ResourceType.INSTANCE_SUBJECT;
import static org.folio.search.support.base.ApiEndpoints.instanceSearchPath;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.randomId;

import java.util.List;
import java.util.Map;
import org.folio.search.domain.dto.Instance;
import org.folio.search.domain.dto.Subject;
import org.folio.search.support.base.BaseIntegrationTest;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

@IntegrationTest
class IndexingInstanceSubjectIT extends BaseIntegrationTest {

@BeforeAll
static void prepare() {
setUpTenant();
}

@AfterAll
static void cleanUp() {
removeTenant();
}

@Test
void shouldIndexInstanceSubject_createDocument() {
var instanceId1 = randomId();
var instanceId2 = randomId();
var authorityId = "ce176ace-a53e-4b4d-aa89-725ed7b2edac";
var value = "Fantasy";
var subject = new Subject().value(value).authorityId(authorityId);
var instance1 = new Instance().id(instanceId1).addSubjectsItem(subject);
var instance2 = new Instance().id(instanceId2).addSubjectsItem(subject);
inventoryApi.createInstance(TENANT_ID, instance1);
inventoryApi.createInstance(TENANT_ID, instance2);
assertCountByIds(instanceSearchPath(), List.of(instanceId1, instanceId2), 2);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_SUBJECT, TENANT_ID)).hasSize(1));

var hits = fetchAllDocuments(INSTANCE_SUBJECT, TENANT_ID);
var sourceAsMap = hits[0].getSourceAsMap();
assertThat(sourceAsMap)
.contains(
entry("value", value),
entry("authorityId", authorityId)
);

@SuppressWarnings("unchecked")
var instances = (List<Map<String, Object>>) sourceAsMap.get("instances");
assertThat(instances)
.allSatisfy(map -> assertThat(map).containsEntry("shared", false))
.allSatisfy(map -> assertThat(map).containsEntry("tenantId", TENANT_ID))
.anySatisfy(map -> assertThat(map).containsEntry("instanceId", instanceId1))
.anySatisfy(map -> assertThat(map).containsEntry("instanceId", instanceId2));
}

@Test
void shouldIndexInstanceSubject_deleteDocument() {
var instanceId = randomId();
var subject = new Subject().value("Sci-Fi").authorityId(null);
var instance = new Instance().id(instanceId).addSubjectsItem(subject);
inventoryApi.createInstance(TENANT_ID, instance);
assertCountByIds(instanceSearchPath(), List.of(instanceId), 1);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_SUBJECT, TENANT_ID)).hasSize(1));
inventoryApi.updateInstance(TENANT_ID, instance.subjects(null));
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_SUBJECT, TENANT_ID)).isEmpty());
}
}
241 changes: 0 additions & 241 deletions src/test/java/org/folio/search/controller/RecordsIndexingIT.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -40,7 +40,6 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.test.web.servlet.ResultMatcher;

@IntegrationTest
class SearchInstanceFilterIT extends BaseIntegrationTest {
@@ -113,16 +112,11 @@ class SearchInstanceFilterIT extends BaseIntegrationTest {

@BeforeAll
static void prepare() {
setUpTenant(getMatchers(), instances());
}

public static List<ResultMatcher> getMatchers() {
var holdingsMatcher = jsonPath("sum($.instances..holdings.length())", is((double) HOLDINGS_IDS.length));
var itemsMatcher = jsonPath("sum($.instances..items.length())", is((double) ITEM_IDS.length));
return List.of(holdingsMatcher, itemsMatcher);
setUpTenant(List.of(holdingsMatcher, itemsMatcher), instances());
}


@AfterAll
static void cleanUp() {
removeTenant();
Original file line number Diff line number Diff line change
@@ -32,15 +32,13 @@
import java.util.function.Function;
import lombok.extern.log4j.Log4j2;
import org.folio.search.configuration.RetryTemplateConfiguration;
import org.folio.search.configuration.kafka.ConsortiumInstanceEventKafkaConfiguration;
import org.folio.search.configuration.kafka.InstanceResourceEventKafkaConfiguration;
import org.folio.search.configuration.kafka.ResourceEventKafkaConfiguration;
import org.folio.search.configuration.properties.StreamIdsProperties;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.exception.SearchOperationException;
import org.folio.search.integration.KafkaMessageListenerIT.KafkaListenerTestConfiguration;
import org.folio.search.integration.interceptor.ResourceEventBatchInterceptor;
import org.folio.search.model.event.ConsortiumInstanceEvent;
import org.folio.search.model.types.ResourceType;
import org.folio.search.service.ResourceService;
import org.folio.search.service.config.ConfigSynchronizationService;
@@ -82,10 +80,8 @@
"folio.kafka.retry-interval-ms=10",
"folio.kafka.retry-delivery-attempts=3",
"folio.kafka.listener.events.concurrency=1",
"folio.kafka.listener.contributors.concurrency=1",
"folio.kafka.listener.events.group-id=${folio.environment}-test-group",
"folio.kafka.listener.authorities.group-id=${folio.environment}-authority-test-group",
"folio.kafka.listener.contributors.group-id=${folio.environment}-contributor-test-group",
"logging.level.org.apache.kafka.clients.consumer=warn"
})
class KafkaMessageListenerIT {
@@ -96,8 +92,6 @@ class KafkaMessageListenerIT {
@Autowired
private KafkaTemplate<String, ResourceEvent> resourceKafkaTemplate;
@Autowired
private KafkaTemplate<String, ConsortiumInstanceEvent> consortiumKafkaTemplate;
@Autowired
private FolioKafkaProperties kafkaProperties;
@MockBean
private ResourceService resourceService;
@@ -264,7 +258,7 @@ private static ResourceEvent authorityEvent(String id) {
@EnableRetry(proxyTargetClass = true)
@Import({
InstanceResourceEventKafkaConfiguration.class, ResourceEventKafkaConfiguration.class,
ConsortiumInstanceEventKafkaConfiguration.class, KafkaAutoConfiguration.class, FolioMessageBatchProcessor.class,
KafkaAutoConfiguration.class, FolioMessageBatchProcessor.class,
KafkaAdminService.class, LocalFileProvider.class, JsonConverter.class, JacksonAutoConfiguration.class,
RetryTemplateConfiguration.class, ResourceEventBatchInterceptor.class
})
Original file line number Diff line number Diff line change
@@ -47,7 +47,6 @@
import org.folio.search.domain.dto.LinkedDataWork;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.folio.search.model.event.ConsortiumInstanceEvent;
import org.folio.search.model.types.ResourceType;
import org.folio.search.service.ResourceService;
import org.folio.search.service.config.ConfigSynchronizationService;
@@ -218,23 +217,6 @@ void handleAuthorityEvent_negative_logFailedEvent() {
verify(batchProcessor).consumeBatchWithFallback(eq(expectedEvents), eq(KAFKA_RETRY_TEMPLATE_NAME), any(), any());
}

@Test
void handleConsortiumInstanceEvents_negative() {
var consortiumInstanceEvent = new ConsortiumInstanceEvent(RESOURCE_ID);
consortiumInstanceEvent.setTenant(TENANT_ID);
doAnswer(inv -> {
inv.<BiConsumer<ConsortiumInstanceEvent, Exception>>getArgument(3)
.accept(consortiumInstanceEvent, new Exception("error"));
return null;
}).when(batchProcessor)
.consumeBatchWithFallback(eq(singletonList(consortiumInstanceEvent)), eq(KAFKA_RETRY_TEMPLATE_NAME), any(),
any());

verify(batchProcessor).consumeBatchWithFallback(eq(singletonList(consortiumInstanceEvent)),
eq(KAFKA_RETRY_TEMPLATE_NAME),
any(), any());
}

@Test
void handleLinkedDataWorkEvent_positive() {
var payload = toMap(new LinkedDataWork().id(RESOURCE_ID));
Original file line number Diff line number Diff line change
@@ -4,34 +4,26 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.folio.search.domain.dto.ResourceEventType.CREATE;
import static org.folio.search.domain.dto.ResourceEventType.UPDATE;
import static org.folio.search.model.client.CqlQuery.exactMatchAny;
import static org.folio.search.model.service.ResultList.asSinglePage;
import static org.folio.search.utils.JsonConverter.MAP_TYPE_REFERENCE;
import static org.folio.search.utils.JsonUtils.LIST_OF_MAPS_TYPE_REFERENCE;
import static org.folio.search.utils.TestConstants.TENANT_ID;
import static org.folio.search.utils.TestUtils.OBJECT_MAPPER;
import static org.folio.search.utils.TestUtils.mapOf;
import static org.folio.search.utils.TestUtils.randomId;
import static org.folio.search.utils.TestUtils.resourceEvent;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.folio.search.client.InventoryViewClient;
import org.folio.search.client.InventoryViewClient.InstanceView;
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.domain.dto.ResourceEvent;
import org.folio.search.model.client.CqlQueryParam;
import org.folio.search.model.service.ResultList;
import org.folio.search.model.types.ResourceType;
import org.folio.search.service.reindex.jdbc.UploadInstanceRepository;
import org.folio.spring.FolioExecutionContext;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.BeforeEach;
@@ -40,19 +32,17 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;

@Disabled //TODO fix
@UnitTest
@ExtendWith(MockitoExtension.class)
class ResourceFetchServiceTest {

@InjectMocks
private ResourceFetchService resourceFetchService;
private InstanceFetchService resourceFetchService;
@Mock
private InventoryViewClient inventoryClient;
private UploadInstanceRepository instanceRepository;
@Mock
private FolioExecutionContext context;

@@ -67,40 +57,41 @@ void fetchInstancesById_positive() {
var instanceId1 = events.get(0).getId();
var instanceId2 = events.get(1).getId();

var instance1 = instanceView(new Instance().id(instanceId1).title("inst1"), null);
var instance2 = instanceView(new Instance().id(instanceId2).title("inst2")
.holdings(List.of(new Holding().id("holdingId"))).items(List.of(new Item().id("itemId"))), true);
var instance1 = instanceMap(new Instance().id(instanceId1).title("inst1").isBoundWith(null));
var instance2 = instanceMap(new Instance().id(instanceId2).title("inst2").isBoundWith(true)
.holdings(List.of(new Holding().id("holdingId"))).items(List.of(new Item().id("itemId"))));

when(inventoryClient.getInstances(exactMatchAny(CqlQueryParam.ID, List.of(instanceId1, instanceId2)), 2))
.thenReturn(asSinglePage(List.of(instance1, instance2)));
when(instanceRepository.fetchByIds(List.of(instanceId1, instanceId2)))
.thenReturn(List.of(instance1, instance2));

var actual = resourceFetchService.fetchInstancesByIds(events);

assertThat(cleanUp(actual)).isEqualTo(List.of(
resourceEvent(instanceId1, ResourceType.INSTANCE,
mapOf("id", instanceId1, "title", "inst1", "isBoundWith", null)),
mapOf("id", instanceId1, "title", "inst1")),
resourceEvent(instanceId2, ResourceType.INSTANCE, UPDATE,
mapOf("id", instanceId2, "title", "inst2",
"holdings", List.of(mapOf("id", "holdingId")),
"items", List.of(mapOf("id", "itemId")), "isBoundWith", true),
"isBoundWith", true,
"items", List.of(mapOf("id", "itemId")),
"holdings", List.of(mapOf("id", "holdingId"))),
mapOf("id", instanceId2, "title", "old"))
));
verify(inventoryClient, times(1)).getInstances(any(), anyInt());
verify(instanceRepository, times(1)).fetchByIds(any());
}

@Test
void fetchInstancesById_negative_oneOfResourcesByIdIsNotFound() {
var events = resourceEvents();
var instanceId1 = events.get(0).getId();
var instanceId2 = events.get(1).getId();
var instanceView = instanceView(new Instance().id(instanceId1).title("inst1"), null);
var instanceMap = instanceMap(new Instance().id(instanceId1).title("inst1").isBoundWith(null));

when(inventoryClient.getInstances(exactMatchAny(CqlQueryParam.ID, List.of(instanceId1, instanceId2)), 2))
.thenReturn(asSinglePage(List.of(instanceView)));
when(instanceRepository.fetchByIds(List.of(instanceId1, instanceId2)))
.thenReturn(List.of(instanceMap));

var actual = resourceFetchService.fetchInstancesByIds(events);
assertThat(cleanUp(actual)).isEqualTo(List.of(resourceEvent(instanceId1, ResourceType.INSTANCE,
mapOf("id", instanceId1, "title", "inst1", "isBoundWith", null))));
mapOf("id", instanceId1, "title", "inst1"))));
}

@Test
@@ -109,15 +100,15 @@ void fetchInstancesById_negative_resourceReturnedWithInvalidId() {
var resourceEvent = resourceEvent(id, ResourceType.INSTANCE, UPDATE,
mapOf("id", id, "title", "new"), mapOf("id", id, "title", "old"));
var invalidId = randomId();
var instanceView = instanceView(new Instance().id(invalidId).title("inst1"), null);
var instanceMap = instanceMap(new Instance().id(invalidId).title("inst1").isBoundWith(null));

when(inventoryClient.getInstances(exactMatchAny(CqlQueryParam.ID, List.of(id)), 1))
.thenReturn(asSinglePage(List.of(instanceView)));
when(instanceRepository.fetchByIds(List.of(id)))
.thenReturn(List.of(instanceMap));

var actual = resourceFetchService.fetchInstancesByIds(List.of(resourceEvent));
assertThat(cleanUp(actual)).isEqualTo(List.of(
resourceEvent(invalidId, ResourceType.INSTANCE,
mapOf("id", invalidId, "title", "inst1", "isBoundWith", null))
mapOf("id", invalidId, "title", "inst1"))
));
}

@@ -127,37 +118,6 @@ void fetchInstancesByIds_positive_emptyListOfIds() {
assertThat(actual).isEmpty();
}

@Test
void fetchInstancesByIds_positive_withMoreThan50Resources() {
var events = generateResourceEvents();
var firstPartInstances = events.stream()
.limit(50)
.map(resourceEvent -> instanceView(new Instance().id(resourceEvent.getId()).title("test"), null))
.toList();
var secondPartInstances = List.of(instanceView(new Instance()
.id(events.get(50).getId()).title("inst2").holdings(List.of(new Holding().id("holdingId")))
.items(List.of(new Item().id("itemId"))), true));

when(inventoryClient.getInstances(any(), anyInt()))
.thenAnswer(new Answer<>() {
private int count = 0;

public ResultList<InstanceView> answer(InvocationOnMock invocation) {
if (count++ == 1) {
// it will be returned the first time the method is called
return asSinglePage(firstPartInstances);
}
// it will be returned the second time the method is called
return asSinglePage(secondPartInstances);
}
});

var actual = resourceFetchService.fetchInstancesByIds(events);

assertThat(actual).hasSize(51);
verify(inventoryClient, times(2)).getInstances(any(), anyInt());
}

private List<ResourceEvent> cleanUp(List<ResourceEvent> actual) {
for (ResourceEvent event : actual) {
if (event.getNew() instanceof Map<?, ?> map) {
@@ -197,16 +157,8 @@ private static List<ResourceEvent> resourceEvents() {
mapOf("id", updateEventId, "title", "old")));
}

private static InstanceView instanceView(Instance instance, Boolean isBoundWith) {
var instanceMap = OBJECT_MAPPER.convertValue(instance, MAP_TYPE_REFERENCE);
var itemsMap = OBJECT_MAPPER.convertValue(instance.getItems(), LIST_OF_MAPS_TYPE_REFERENCE);
var holdingsMap = OBJECT_MAPPER.convertValue(instance.getHoldings(), LIST_OF_MAPS_TYPE_REFERENCE);
return new InstanceView(instanceMap, holdingsMap, itemsMap, isBoundWith);
private static Map<String, Object> instanceMap(Instance instance) {
return OBJECT_MAPPER.convertValue(instance, MAP_TYPE_REFERENCE);
}

private static List<ResourceEvent> generateResourceEvents() {
return Stream.generate(() -> resourceEvent(randomId(), ResourceType.INSTANCE, CREATE))
.limit(51)
.toList();
}
}

This file was deleted.

This file was deleted.

6 changes: 0 additions & 6 deletions src/test/java/org/folio/search/service/IndexServiceTest.java
Original file line number Diff line number Diff line change
@@ -48,7 +48,6 @@
import org.folio.search.model.types.ResourceType;
import org.folio.search.repository.IndexNameProvider;
import org.folio.search.repository.IndexRepository;
import org.folio.search.service.consortium.ConsortiumInstanceService;
import org.folio.search.service.consortium.TenantProvider;
import org.folio.search.service.es.SearchMappingsHelper;
import org.folio.search.service.es.SearchSettingsHelper;
@@ -86,8 +85,6 @@ class IndexServiceTest {
@Mock
private IndexNameProvider indexNameProvider;
@Mock
private ConsortiumInstanceService consortiumInstanceService;
@Mock
private LocationService locationService;

@Mock
@@ -268,7 +265,6 @@ void reindexInventory_positive_recreateIndexIsTrue() {

assertThat(actual).isEqualTo(expectedResponse);
verify(indexRepository).dropIndex(indexName);
verify(consortiumInstanceService).deleteAll();
verifyNoInteractions(locationService);
}

@@ -286,7 +282,6 @@ void reindexInventory_positive_recreateIndexIsTrue_memberTenant() {

assertThat(actual).isEqualTo(expectedResponse);
verifyNoInteractions(indexRepository);
verifyNoInteractions(consortiumInstanceService);
verifyNoInteractions(locationService);
}

@@ -302,7 +297,6 @@ void reindexInventory_positive_recreateIndexIsFalse() {
var actual = indexService.reindexInventory(TENANT_ID, new ReindexRequest());
assertThat(actual).isEqualTo(expectedResponse);
verifyNoInteractions(locationService);
verifyNoInteractions(consortiumInstanceService);
}

@Test
12 changes: 2 additions & 10 deletions src/test/java/org/folio/search/service/ResourceServiceTest.java
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@
import static org.folio.search.utils.TestUtils.searchDocumentBody;
import static org.folio.search.utils.TestUtils.searchDocumentBodyToDelete;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -35,15 +34,14 @@
import java.util.concurrent.Callable;
import org.folio.search.domain.dto.FolioIndexOperationResponse;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.integration.ResourceFetchService;
import org.folio.search.integration.InstanceFetchService;
import org.folio.search.model.index.SearchDocumentBody;
import org.folio.search.model.metadata.ResourceDescription;
import org.folio.search.model.metadata.ResourceIndexingConfiguration;
import org.folio.search.repository.IndexNameProvider;
import org.folio.search.repository.IndexRepository;
import org.folio.search.repository.PrimaryResourceRepository;
import org.folio.search.repository.ResourceRepository;
import org.folio.search.service.consortium.ConsortiumInstanceService;
import org.folio.search.service.consortium.ConsortiumTenantExecutor;
import org.folio.search.service.consortium.ConsortiumTenantService;
import org.folio.search.service.converter.MultiTenantSearchDocumentConverter;
@@ -67,7 +65,7 @@ class ResourceServiceTest {
@Mock
private IndexRepository indexRepository;
@Mock
private ResourceFetchService resourceFetchService;
private InstanceFetchService resourceFetchService;
@Mock
private PrimaryResourceRepository primaryResourceRepository;
@Mock
@@ -81,8 +79,6 @@ class ResourceServiceTest {
@Mock
private ConsortiumTenantExecutor consortiumTenantExecutor;
@Mock
private ConsortiumInstanceService consortiumInstanceService;
@Mock
private IndexNameProvider indexNameProvider;
@Mock
private Map<String, ResourceRepository> resourceRepositoryBeans;
@@ -94,10 +90,6 @@ class ResourceServiceTest {
@BeforeEach
public void setUp() {
lenient().when(consortiumTenantService.getCentralTenant(any())).thenReturn(Optional.empty());
lenient().when(consortiumInstanceService.saveInstances(anyList()))
.thenAnswer(invocation -> invocation.getArgument(0));
lenient().when(consortiumInstanceService.deleteInstances(anyList()))
.thenAnswer(invocation -> invocation.getArgument(0));
lenient().when(consortiumTenantExecutor.execute(any(), any()))
.thenAnswer(invocation -> ((Callable<?>) invocation.getArgument(1)).call());
lenient().when(indexNameProvider.getIndexName(any(ResourceEvent.class)))
Loading

0 comments on commit 564505d

Please sign in to comment.