Skip to content

Commit

Permalink
[MODINV-952] Not match on central tenant by POL or VRN during consort…
Browse files Browse the repository at this point in the history
…iua process (#674)
  • Loading branch information
RomanChernetskyi authored Jan 24, 2024
1 parent 332f679 commit 208a926
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@
import org.folio.inventory.consortium.entities.ConsortiumConfiguration;
import org.folio.inventory.consortium.services.ConsortiumService;
import org.folio.inventory.dataimport.cache.MappingMetadataCache;
import org.folio.inventory.dataimport.handlers.matching.preloaders.PreloadingFields;
import org.folio.inventory.dataimport.handlers.matching.util.MatchingParametersRelations;
import org.folio.processing.events.services.handler.EventHandler;
import org.folio.processing.exceptions.EventProcessingException;
import org.folio.processing.exceptions.MatchingException;
import org.folio.processing.matching.MatchingManager;
import org.folio.rest.jaxrs.model.EntityType;
import org.folio.MappingMetadataDto;
import org.folio.rest.jaxrs.model.MatchExpression;

import java.util.concurrent.CompletableFuture;

import static org.folio.inventory.dataimport.handlers.matching.util.EventHandlingUtil.constructContext;
import static org.folio.inventory.dataimport.handlers.matching.util.EventHandlingUtil.extractMatchProfile;
import static org.folio.inventory.dataimport.util.LoggerUtil.logParametersEventHandler;
import static org.folio.rest.jaxrs.model.ProfileSnapshotWrapper.ContentType.MATCH_PROFILE;

Expand Down Expand Up @@ -57,6 +60,7 @@ public CompletableFuture<DataImportEventPayload> handle(DataImportEventPayload d
.orElse(CompletableFuture.failedFuture(new EventProcessingException(MAPPING_METADATA_NOT_FOUND_MSG))))
.whenComplete((matched, throwable) -> {
if (throwable != null) {
LOGGER.warn("handle:: Error during matching", throwable);
future.completeExceptionally(throwable);
} else {
if (Boolean.TRUE.equals(matched)) {
Expand Down Expand Up @@ -90,7 +94,8 @@ private CompletableFuture<Boolean> matchCentralTenantIfNeeded(DataImportEventPay
return consortiumService.getConsortiumConfiguration(context)
.toCompletionStage().toCompletableFuture()
.thenCompose(consortiumConfiguration -> {
if (consortiumConfiguration.isPresent() && !consortiumConfiguration.get().getCentralTenantId().equals(context.getTenantId())) {
if (consortiumConfiguration.isPresent() && !consortiumConfiguration.get().getCentralTenantId().equals(context.getTenantId())
&& !isMatchByPolOrVrn(dataImportEventPayload)) {
LOGGER.debug("matchCentralTenantIfNeeded:: Start matching on central tenant with id: {}", consortiumConfiguration.get().getCentralTenantId());
String localMatchedInstance = dataImportEventPayload.getContext().get(getEntityType().value());
preparePayloadBeforeConsortiumProcessing(dataImportEventPayload, consortiumConfiguration.get(), mappingMetadataDto, matchingParametersRelations);
Expand All @@ -99,7 +104,7 @@ private CompletableFuture<Boolean> matchCentralTenantIfNeeded(DataImportEventPay
dataImportEventPayload.setTenant(context.getTenantId());
if (isMatchedConsortium && isMatchedLocal && !isShadowEntity(localMatchedInstance, dataImportEventPayload.getContext().get(getEntityType().value()))) {
LOGGER.warn("matchCentralTenantIfNeeded:: Found multiple results during matching on local tenant: {} and central tenant: {} ",
consortiumConfiguration.get().getCentralTenantId(), context.getTenantId());
context.getTenantId(), consortiumConfiguration.get().getCentralTenantId());
return CompletableFuture.failedFuture(new MatchingException(String.format(FOUND_MULTIPLE_ENTITIES, context.getTenantId(), consortiumConfiguration.get().getCentralTenantId())));
}
if (StringUtils.isEmpty(dataImportEventPayload.getContext().get(getEntityType().value()))) {
Expand All @@ -116,6 +121,14 @@ private CompletableFuture<Boolean> matchCentralTenantIfNeeded(DataImportEventPay
});
}

private boolean isMatchByPolOrVrn(DataImportEventPayload dataImportEventPayload) {
MatchProfile matchProfile = extractMatchProfile(dataImportEventPayload);
MatchExpression matchExpression = matchProfile.getMatchDetails().get(0).getExistingMatchExpression();
return matchExpression.getFields().stream()
.anyMatch(field -> field.getValue().endsWith("." + PreloadingFields.POL.getExistingMatchField())
|| field.getValue().endsWith("." + PreloadingFields.VRN.getExistingMatchField()));
}

private void preparePayloadBeforeConsortiumProcessing(DataImportEventPayload dataImportEventPayload, ConsortiumConfiguration consortiumConfiguration,
MappingMetadataDto mappingMetadataDto, MatchingParametersRelations matchingParametersRelations) {
dataImportEventPayload.setTenant(consortiumConfiguration.getCentralTenantId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.folio.rest.jaxrs.model.Field;
import org.folio.rest.jaxrs.model.MatchExpression;

import static org.folio.inventory.dataimport.handlers.matching.util.EventHandlingUtil.extractMatchProfile;

/**
* Preloader intended to run some logic to modify existing loading query before passing it to Loader
* It check whether match expression contains any of defined fields that imply preloading
Expand Down Expand Up @@ -58,21 +60,6 @@ public CompletableFuture<LoadQuery> preload(LoadQuery query, DataImportEventPayl
});
}

/**
* Extracts match profile from event payload
* Additional json encoding is needed to return a copy of object not to modify eventPayload
* @return MatchProfile object deep copy
* */
private MatchProfile extractMatchProfile(DataImportEventPayload dataImportEventPayload) {
if (dataImportEventPayload.getCurrentNode().getContent() instanceof Map) {
return (new JsonObject((Map)dataImportEventPayload.getCurrentNode().getContent()))
.mapTo(MatchProfile.class);
}

return new JsonObject(Json.encode(dataImportEventPayload.getCurrentNode().getContent()))
.mapTo(MatchProfile.class);
}

/**
* Reads incoming record match values from event payload
* @return record field values according to match details
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import io.vertx.core.json.Json;
import org.apache.commons.lang3.StringUtils;
import org.folio.DataImportEventPayload;
import org.folio.MatchProfile;
import org.folio.inventory.common.Context;
import org.folio.inventory.support.JsonHelper;

Expand Down Expand Up @@ -59,6 +62,21 @@ public static String getTenant(DataImportEventPayload payload) {
return payload.getTenant();
}

/**
* Extracts match profile from event payload
* Additional json encoding is needed to return a copy of object not to modify eventPayload
* @return MatchProfile object deep copy
* */
public static MatchProfile extractMatchProfile(DataImportEventPayload dataImportEventPayload) {
if (dataImportEventPayload.getCurrentNode().getContent() instanceof Map) {
return (new JsonObject((Map)dataImportEventPayload.getCurrentNode().getContent()))
.mapTo(MatchProfile.class);
}

return new JsonObject(Json.encode(dataImportEventPayload.getCurrentNode().getContent()))
.mapTo(MatchProfile.class);
}

private static boolean isExistsRequiredProperty(JsonObject representation, String propertyName, String nestedPropertyName) {
String propertyValue = StringUtils.isEmpty(nestedPropertyName)
? JsonHelper.getString(representation, propertyName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;

import static org.folio.DataImportEventTypes.DI_INVENTORY_INSTANCE_MATCHED;
import static org.folio.DataImportEventTypes.DI_INVENTORY_INSTANCE_NOT_MATCHED;
Expand Down Expand Up @@ -47,6 +48,7 @@
import io.vertx.ext.unit.junit.VertxUnitRunner;
import org.folio.inventory.consortium.entities.ConsortiumConfiguration;
import org.folio.inventory.consortium.services.ConsortiumService;
import org.folio.inventory.dataimport.handlers.matching.preloaders.PreloadingFields;
import org.folio.processing.exceptions.MatchingException;
import org.junit.Before;
import org.junit.Test;
Expand Down Expand Up @@ -355,6 +357,106 @@ public void shouldNotMatchOnLocalAndMatchOnCentralTenant(TestContext testContext
});
}

@Test
public void shouldNotTryToMatchOnCentralTenantIfMatchingByPol(TestContext testContext) throws UnsupportedEncodingException {
Async async = testContext.async();

String centralTenantId = "consortium";
String consortiumId = "consortiumId";

Instance instance = new Instance(UUID.randomUUID().toString(), "5", INSTANCE_HRID, "MARC", "Wonderful", "12334");

InstanceCollection instanceCollectionCentralTenant = Mockito.mock(InstanceCollection.class);
when(storage.getInstanceCollection(Mockito.argThat(context -> context.getTenantId().equals(centralTenantId)))).thenReturn(instanceCollectionCentralTenant);

doAnswer(ans -> {
Consumer<Success<MultipleRecords<Instance>>> callback = ans.getArgument(2);
Success<MultipleRecords<Instance>> result =
new Success<>(new MultipleRecords<>(singletonList(instance), 1));
callback.accept(result);
return null;
}).when(instanceCollectionCentralTenant)
.findByCql(eq(format("%s == \"%s\"", PreloadingFields.POL.getExistingMatchField(), INSTANCE_HRID)), any(PagingParameters.class), any(Consumer.class), any(Consumer.class));

doAnswer(ans -> {
Consumer<Success<MultipleRecords<Instance>>> callback = ans.getArgument(2);
Success<MultipleRecords<Instance>> result =
new Success<>(new MultipleRecords<>(singletonList(createInstance()), 1));
callback.accept(result);
return null;
}).when(instanceCollection)
.findByCql(eq(format("%s == \"%s\"", PreloadingFields.POL.getExistingMatchField(), INSTANCE_HRID)), any(PagingParameters.class), any(Consumer.class), any(Consumer.class));

doAnswer(invocationOnMock -> Future.succeededFuture(Optional.of(new ConsortiumConfiguration(centralTenantId, consortiumId))))
.when(consortiumService).getConsortiumConfiguration(any());

DataImportEventPayload eventPayload = createEventPayload("instance." + PreloadingFields.POL.getExistingMatchField());

eventHandler.handle(eventPayload).whenComplete((updatedEventPayload, throwable) -> {
testContext.assertNull(throwable);
testContext.assertEquals(1, updatedEventPayload.getEventsChain().size());
testContext.assertEquals(
updatedEventPayload.getEventsChain(),
singletonList(DI_INCOMING_MARC_BIB_RECORD_PARSED.value())
);
testContext.assertEquals(DI_INVENTORY_INSTANCE_MATCHED.value(), updatedEventPayload.getEventType());
JsonObject matchedInstanceAsJsonObject = new JsonObject(updatedEventPayload.getContext().get(INSTANCE.value()));
testContext.assertEquals(matchedInstanceAsJsonObject.getString("id"), INSTANCE_ID);
verify(storage, times(0)).getInstanceCollection(Mockito.argThat(context -> context.getTenantId().equals(centralTenantId)));
async.complete();
});
}

@Test
public void shouldNotTryToMatchOnCentralTenantIfMatchingByVrn(TestContext testContext) throws UnsupportedEncodingException {
Async async = testContext.async();

String centralTenantId = "consortium";
String consortiumId = "consortiumId";

Instance instance = new Instance(UUID.randomUUID().toString(), "5", INSTANCE_HRID, "MARC", "Wonderful", "12334");

InstanceCollection instanceCollectionCentralTenant = Mockito.mock(InstanceCollection.class);
when(storage.getInstanceCollection(Mockito.argThat(context -> context.getTenantId().equals(centralTenantId)))).thenReturn(instanceCollectionCentralTenant);

doAnswer(ans -> {
Consumer<Success<MultipleRecords<Instance>>> callback = ans.getArgument(2);
Success<MultipleRecords<Instance>> result =
new Success<>(new MultipleRecords<>(singletonList(instance), 1));
callback.accept(result);
return null;
}).when(instanceCollectionCentralTenant)
.findByCql(eq(format("%s == \"%s\"", PreloadingFields.VRN.getExistingMatchField(), INSTANCE_HRID)), any(PagingParameters.class), any(Consumer.class), any(Consumer.class));

doAnswer(ans -> {
Consumer<Success<MultipleRecords<Instance>>> callback = ans.getArgument(2);
Success<MultipleRecords<Instance>> result =
new Success<>(new MultipleRecords<>(singletonList(createInstance()), 1));
callback.accept(result);
return null;
}).when(instanceCollection)
.findByCql(eq(format("%s == \"%s\"", PreloadingFields.VRN.getExistingMatchField(), INSTANCE_HRID)), any(PagingParameters.class), any(Consumer.class), any(Consumer.class));

doAnswer(invocationOnMock -> Future.succeededFuture(Optional.of(new ConsortiumConfiguration(centralTenantId, consortiumId))))
.when(consortiumService).getConsortiumConfiguration(any());

DataImportEventPayload eventPayload = createEventPayload("instance." + PreloadingFields.VRN.getExistingMatchField());

eventHandler.handle(eventPayload).whenComplete((updatedEventPayload, throwable) -> {
testContext.assertNull(throwable);
testContext.assertEquals(1, updatedEventPayload.getEventsChain().size());
testContext.assertEquals(
updatedEventPayload.getEventsChain(),
singletonList(DI_INCOMING_MARC_BIB_RECORD_PARSED.value())
);
testContext.assertEquals(DI_INVENTORY_INSTANCE_MATCHED.value(), updatedEventPayload.getEventType());
JsonObject matchedInstanceAsJsonObject = new JsonObject(updatedEventPayload.getContext().get(INSTANCE.value()));
testContext.assertEquals(matchedInstanceAsJsonObject.getString("id"), INSTANCE_ID);
verify(storage, times(0)).getInstanceCollection(Mockito.argThat(context -> context.getTenantId().equals(centralTenantId)));
async.complete();
});
}

@Test
public void shouldNotMatchOnHandleEventPayload(TestContext testContext) throws UnsupportedEncodingException {
Async async = testContext.async();
Expand Down Expand Up @@ -651,6 +753,10 @@ public void shouldReturnFailedFutureWhenFirstChildProfileIsNotMatchProfileOnHand
}

private DataImportEventPayload createEventPayload() {
return createEventPayload("instance.hrid");
}

private DataImportEventPayload createEventPayload(String matchValue) {
return new DataImportEventPayload()
.withEventType(DI_INCOMING_MARC_BIB_RECORD_PARSED.value())
.withJobExecutionId(UUID.randomUUID().toString())
Expand All @@ -670,7 +776,7 @@ private DataImportEventPayload createEventPayload() {
.withExistingMatchExpression(new MatchExpression()
.withDataValueType(VALUE_FROM_RECORD)
.withFields(singletonList(
new Field().withLabel("field").withValue("instance.hrid"))
new Field().withLabel("field").withValue(matchValue))
))))));
}

Expand Down

0 comments on commit 208a926

Please sign in to comment.