Skip to content

Commit

Permalink
feat(cn-browse): handle deletes/updates of call-numbers
Browse files Browse the repository at this point in the history
Closes: MSEARCH-940
  • Loading branch information
psmagin committed Jan 21, 2025
1 parent 1cc6582 commit 8a2b11c
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public class CallNumberRepository extends UploadRangeRepository implements Insta
WITH deleted_ids as (
DELETE
FROM %1$s.instance_call_number
WHERE instance_id IN (%2$s)
WHERE item_id IN (%2$s)
RETURNING call_number_id
)
UPDATE %1$s.call_number
Expand Down Expand Up @@ -183,9 +183,9 @@ protected CallNumberRepository(JdbcTemplate jdbcTemplate, JsonConverter jsonConv
}

@Override
public void deleteByInstanceIds(List<String> instanceIds) {
var sql = DELETE_QUERY.formatted(JdbcUtils.getSchemaName(context), getParamPlaceholderForUuid(instanceIds.size()));
jdbcTemplate.update(sql, instanceIds.toArray());
public void deleteByInstanceIds(List<String> itemIds) {
var sql = DELETE_QUERY.formatted(JdbcUtils.getSchemaName(context), getParamPlaceholderForUuid(itemIds.size()));
jdbcTemplate.update(sql, itemIds.toArray());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import static java.util.function.Function.identity;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.awaitility.Durations.ONE_MINUTE;
import static org.awaitility.Durations.ONE_SECOND;
import static org.awaitility.Durations.TWO_MINUTES;
import static org.awaitility.Durations.TWO_SECONDS;
import static org.folio.search.support.base.ApiEndpoints.browseConfigPath;
import static org.folio.search.support.base.ApiEndpoints.instanceCallNumberBrowsePath;
import static org.folio.search.support.base.ApiEndpoints.recordFacetsPath;
Expand Down Expand Up @@ -56,7 +56,7 @@ class BrowseCallNumberIT extends BaseIntegrationTest {
@BeforeAll
static void prepare() {
setUpTenant(CallNumberTestData.instances().toArray(Instance[]::new));
await().atMost(ONE_MINUTE).pollInterval(ONE_SECOND).untilAsserted(() -> {
await().atMost(TWO_MINUTES).pollInterval(TWO_SECONDS).untilAsserted(() -> {
var counted = countIndexDocument(ResourceType.INSTANCE_CALL_NUMBER, TENANT_ID);
assertThat(counted).isEqualTo(100);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
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_CALL_NUMBER;
import static org.folio.search.service.reindex.ReindexConstants.CALL_NUMBER_TABLE;
import static org.folio.search.service.reindex.ReindexConstants.INSTANCE_CALL_NUMBER_TABLE;
import static org.folio.search.service.reindex.ReindexConstants.ITEM_TABLE;
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.Item;
import org.folio.search.domain.dto.ItemEffectiveCallNumberComponents;
import org.folio.search.support.base.BaseIntegrationTest;
import org.folio.spring.testing.extension.DatabaseCleanup;
import org.folio.spring.testing.type.IntegrationTest;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

@IntegrationTest
@DatabaseCleanup(tenants = TENANT_ID, tables = {CALL_NUMBER_TABLE, INSTANCE_CALL_NUMBER_TABLE, ITEM_TABLE})
class IndexingInstanceCallNumberIT extends BaseIntegrationTest {

private static final String INSTANCE_ID_1 = randomId();
private static final String INSTANCE_ID_2 = randomId();

@BeforeAll
static void prepare() {
var instance1 = new Instance().id(INSTANCE_ID_1).title("test");
var instance2 = new Instance().id(INSTANCE_ID_2).title("test");
setUpTenant(instance1, instance2);
}

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

@AfterEach
void tearDown() {
deleteAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID);
}

@Test
void shouldIndexInstanceCallNumber_createNewDocument_onItemCreate() {
// given
// create items with the same call number
var item1 = getItem(randomId());
var item2 = getItem(randomId());
inventoryApi.createItem(TENANT_ID, INSTANCE_ID_1, item1);
inventoryApi.createItem(TENANT_ID, INSTANCE_ID_2, item2);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID)).hasSize(1));

// when
// fetch all documents from search index
var hits = fetchAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID);

// then
var sourceAsMap = hits[0].getSourceAsMap();
// assert that the document contains the expected fields
assertThat(sourceAsMap)
.contains(
entry("callNumber", "NS 1 .B5"),
entry("fullCallNumber", "NS 1 .B5"),
entry("callNumberTypeId", "2b94c631-fca9-4892-a730-03ee529ff6c3"),
entry("defaultShelvingOrder", "NS 1 .B5"),
entry("deweyShelvingOrder", "NS 11 B 15"),
entry("lcShelvingOrder", "NS 11 B5"),
entry("sudocShelvingOrder", "!NS 11 !B 15"),
entry("nlmShelvingOrder", "NS 11 B5")
);

// assert that the document contains the expected instances object with count 2
@SuppressWarnings("unchecked")
var instances = (List<Map<String, Object>>) sourceAsMap.get("instances");
assertThat(instances)
.hasSize(1)
.allSatisfy(map -> assertThat(map).containsEntry("shared", false))
.allSatisfy(map -> assertThat(map).containsEntry("tenantId", TENANT_ID))
.allSatisfy(map -> assertThat(map).containsEntry("count", 2));
}

@Test
void shouldIndexInstanceCallNumber_deleteDocument_onItemUpdate() {
// given
// create item with call number
var item = getItem(randomId());
inventoryApi.createItem(TENANT_ID, INSTANCE_ID_1, item);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID)).hasSize(1));

// when update item with null call number
item.setEffectiveCallNumberComponents(null);
inventoryApi.updateItem(TENANT_ID, INSTANCE_ID_1, item);

// then
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID)).isEmpty());
}

@Test
void shouldIndexInstanceCallNumber_deleteDocument_onItemDelete() {
// given
// create item with call number
var itemId = randomId();
var item = getItem(itemId);
inventoryApi.createItem(TENANT_ID, INSTANCE_ID_1, item);
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID)).hasSize(1));

// when
inventoryApi.deleteItem(TENANT_ID, itemId);

// then
awaitAssertion(() -> assertThat(fetchAllDocuments(INSTANCE_CALL_NUMBER, TENANT_ID)).isEmpty());
}

private Item getItem(String itemId) {
return new Item().id(itemId).holdingsRecordId(randomId()).effectiveCallNumberComponents(callNumberComponents());
}

private ItemEffectiveCallNumberComponents callNumberComponents() {
var callNumberTypeId = "2b94c631-fca9-4892-a730-03ee529ff6c3";
var callNumber = "NS 1 .B5";
return new ItemEffectiveCallNumberComponents().callNumber(callNumber).typeId(callNumberTypeId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.assertj.core.api.Assertions.assertThat;

import org.folio.search.cql.searchterm.CallNumberSearchTermProcessor;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.stream.Stream;
import org.folio.search.cql.searchterm.ClassificationNumberSearchTermProcessor;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import java.util.List;
import java.util.stream.Collectors;
import org.folio.search.cql.searchterm.EffectiveShelvingOrderTermProcessor;
import org.folio.search.cql.SuDocCallNumber;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -61,9 +60,9 @@ void getSearchTerm_nlm(String given) {

@ParameterizedTest
@ValueSource(strings = {"782", "123", "185.25", "350.21", "362.82/92 / 20",
"591.52/63 / 20", "641.5943 M68l", "12", "11", "25", "1",
"3782", "3123", "3185.25", "3350.21", "3362.82 292 220",
"3591.52 263 220", "3641.5943 M68 L", "4123", "4782", "396.300"})
"591.52/63 / 20", "641.5943 M68l", "12", "11", "25", "1",
"3782", "3123", "3185.25", "3350.21", "3362.82 292 220",
"3591.52 263 220", "3641.5943 M68 L", "4123", "4782", "396.300"})
void getSearchTerm_dewey(String given) {
var expected = new DeweyCallNumber(given).getShelfKey().trim();
var actual = searchTermProcessor.getSearchTerm(given, "dewey");
Expand Down Expand Up @@ -99,7 +98,7 @@ void getSearchTerms_getMultiple() {
var actual = searchTermProcessor.getSearchTerms(given);

assertThat(callNumbers).extracting(CallNumber::isValid)
.containsExactly(false, true, false, true);
.containsExactly(false, true, false, true);
assertThat(actual).isEqualTo(expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import static org.mockito.Mockito.when;

import java.util.List;
import org.folio.search.cql.searchterm.IsbnSearchTermProcessor;
import org.folio.search.service.setter.instance.IsbnProcessor;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.Test;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import static org.mockito.Mockito.when;

import java.util.Optional;
import org.folio.search.cql.searchterm.LccnSearchTermProcessor;
import org.folio.search.service.lccn.LccnNormalizer;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.Test;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import org.folio.search.cql.searchterm.OclcSearchTermProcessor;
import org.folio.search.service.setter.instance.OclcProcessor;
import org.folio.spring.testing.type.UnitTest;
import org.junit.jupiter.api.Test;
Expand Down
17 changes: 17 additions & 0 deletions src/test/java/org/folio/search/support/api/InventoryApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
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.types.ResourceType;
import org.springframework.kafka.core.KafkaTemplate;
Expand Down Expand Up @@ -98,6 +99,10 @@ public void deleteHolding(String tenant, String id) {
.whenComplete(onCompleteConsumer());
}

public void createItem(String tenantId, String instanceId, Item instance) {
createItem(tenantId, instanceId, toMap(instance));
}

public void createItem(String tenant, String instanceId, Map<String, Object> item) {
item.put(INSTANCE_ID_FIELD, instanceId);
var itemId = getString(item, ID_FIELD);
Expand All @@ -106,6 +111,18 @@ public void createItem(String tenant, String instanceId, Map<String, Object> ite
.whenComplete(onCompleteConsumer());
}

public void updateItem(String tenantId, String instanceId, Item instance) {
updateItem(tenantId, instanceId, toMap(instance));
}

public void updateItem(String tenant, String instanceId, Map<String, Object> item) {
item.put(INSTANCE_ID_FIELD, instanceId);
var itemId = getString(item, ID_FIELD);
ITEM_STORE.computeIfAbsent(tenant, k -> new LinkedHashMap<>()).put(itemId, item);
kafkaTemplate.send(inventoryItemTopic(tenant), itemId, kafkaResourceEvent(tenant, UPDATE, item, null))
.whenComplete(onCompleteConsumer());
}

public void deleteItem(String tenant, String id) {
var item = ITEM_STORE.get(tenant).remove(id);
var resourceEvent = kafkaResourceEvent(tenant, DELETE, null, item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,13 @@ public static SearchHit[] fetchAllDocuments(ResourceType resourceType, String te
return elasticClient.search(searchRequest, RequestOptions.DEFAULT).getHits().getHits();
}

@SneakyThrows
public static void deleteAllDocuments(ResourceType resourceType, String tenantId) {
var request = new DeleteByQueryRequest(getIndexName(resourceType, tenantId));
request.setQuery(matchAllQuery());
elasticClient.deleteByQuery(request, DEFAULT);
}

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

0 comments on commit 8a2b11c

Please sign in to comment.