Skip to content

Commit

Permalink
Merge branch 'master' into MSEARCH-635-Update-module-descriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
GeloPakDev1 authored Nov 14, 2023
2 parents 1dd1ae2 + e7c888f commit d217abe
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 0 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

### Features
* Update module descriptor with environment variables ([MSEARCH-635](https://issues.folio.org/browse/MSEARCH-635))
* Add filter to ignore hard-delete authority events ([MSEARCH-617](https://issues.folio.org/browse/MSEARCH-617))

### Bug fixes
* Fix secure setup of system users by default ([MSEARCH-608](https://issues.folio.org/browse/MSEARCH-608))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.integration.ResourceChangeFilterStrategy;
import org.folio.search.model.event.ConsortiumInstanceEvent;
import org.folio.spring.config.properties.FolioEnvironment;
import org.folio.spring.tools.kafka.FolioKafkaTopic;
Expand Down Expand Up @@ -51,6 +52,7 @@ public ConcurrentKafkaListenerContainerFactory<String, ResourceEvent> standardLi
var factory = new ConcurrentKafkaListenerContainerFactory<String, ResourceEvent>();
factory.setBatchListener(true);
factory.setConsumerFactory(resourceEventConsumerFactory());
factory.setRecordFilterStrategy(new ResourceChangeFilterStrategy());
return factory;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.folio.search.integration;

import static org.folio.search.utils.SearchUtils.AUTHORITY_RESOURCE;

import lombok.extern.log4j.Log4j2;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.folio.search.domain.dto.ResourceDeleteEventSubType;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.springframework.kafka.listener.adapter.RecordFilterStrategy;

@Log4j2
public class ResourceChangeFilterStrategy implements RecordFilterStrategy<String, ResourceEvent> {

@Override
public boolean filter(ConsumerRecord<String, ResourceEvent> consumerRecord) {
log.debug("Processing resource event [id: {}]", consumerRecord.value().getId());
var resourceEvent = consumerRecord.value();
var resourceName = resourceEvent.getResourceName();
var resourceEventType = resourceEvent.getType();
if (ResourceEventType.DELETE == resourceEventType && AUTHORITY_RESOURCE.equals(resourceName)
&& ResourceDeleteEventSubType.HARD_DELETE == resourceEvent.getDeleteEventSubType()) {
log.debug("Skip hard-delete event. No need to process event for authority resource");
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "string",
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Resource event delete operation type - one of [soft-delete, hard-delete]",
"enum": [ "SOFT_DELETE", "HARD_DELETE"]
}
4 changes: 4 additions & 0 deletions src/main/resources/swagger.api/schemas/resourceEvent.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"description": "Resource event operation type",
"$ref": "resourceEventType.json"
},
"deleteEventSubType": {
"description": "Resource event delete operation type",
"$ref": "resourceDeleteEventSubType.json"
},
"tenant": {
"description": "Tenant id",
"type": "string"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.folio.search.integration;

import static org.folio.search.utils.SearchUtils.AUTHORITY_RESOURCE;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.folio.search.domain.dto.ResourceDeleteEventSubType;
import org.folio.search.domain.dto.ResourceEvent;
import org.folio.search.domain.dto.ResourceEventType;
import org.folio.spring.test.type.UnitTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@UnitTest
@ExtendWith(MockitoExtension.class)
class ResourceChangeFilterStrategyTest {

private final ResourceChangeFilterStrategy filterStrategy = new ResourceChangeFilterStrategy();

@Mock
private ConsumerRecord<String, ResourceEvent> consumerRecord;

@Test
void shouldNotFilterResourceEventWithoutName() {
var event = createResourceEvent(ResourceEventType.DELETE, ResourceDeleteEventSubType.HARD_DELETE, null);
mockConsumerRecord(event);

var actual = filterStrategy.filter(consumerRecord);

assertFalse(actual);
}

@ValueSource(strings = {"instance", "contributor", "instance_subject"})
@ParameterizedTest
void shouldNotFilterNonAuthResourceEvent(String resourceName) {
var event = createResourceEvent(ResourceEventType.DELETE, ResourceDeleteEventSubType.HARD_DELETE, resourceName);
mockConsumerRecord(event);

var actual = filterStrategy.filter(consumerRecord);

assertFalse(actual);
}

@Test
void shouldFilterHardDeleteAuthResourceEvent() {
var event = createResourceEvent(ResourceEventType.DELETE, ResourceDeleteEventSubType.HARD_DELETE,
AUTHORITY_RESOURCE);
mockConsumerRecord(event);
var actual = filterStrategy.filter(consumerRecord);

assertTrue(actual);
}

@Test
void shouldNotFilterSoftDeleteAuthResourceEvent() {
var event = createResourceEvent(ResourceEventType.DELETE, ResourceDeleteEventSubType.SOFT_DELETE,
AUTHORITY_RESOURCE);
mockConsumerRecord(event);

var actual = filterStrategy.filter(consumerRecord);

assertFalse(actual);
}

@Test
void shouldNotFilterNonDeleteAuthResourceEvent() {
var event = createResourceEvent(ResourceEventType.CREATE, ResourceDeleteEventSubType.HARD_DELETE,
AUTHORITY_RESOURCE);
mockConsumerRecord(event);

var actual = filterStrategy.filter(consumerRecord);

assertFalse(actual);
}

private void mockConsumerRecord(ResourceEvent event) {
when(consumerRecord.value()).thenReturn(event);
}

private ResourceEvent createResourceEvent(ResourceEventType eventType, ResourceDeleteEventSubType subType,
String resourceName) {
var event = new ResourceEvent();
event.setId("1");
event.setType(eventType);
event.setDeleteEventSubType(subType);
event.setResourceName(resourceName);
return event;
}
}

0 comments on commit d217abe

Please sign in to comment.