Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MODSOURCE-854] Use deleteRecordBy id for soft delete of authority #668

Merged
merged 6 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.dao.util.IdType;
import org.folio.dao.util.RecordType;
import org.folio.kafka.AsyncRecordHandler;
import org.folio.rest.jooq.enums.RecordState;
import org.folio.services.RecordService;
import org.folio.services.util.KafkaUtil;
import org.springframework.stereotype.Component;
Expand Down Expand Up @@ -54,12 +54,12 @@ public Future<String> handle(KafkaConsumerRecord<String, String> consumerRecord)

logInput(authorityId, eventSubType, tenantId);
return (switch (eventSubType) {
case SOFT_DELETE -> performSoftDelete(authorityId, tenantId);
case SOFT_DELETE -> performSoftDelete(authorityId, tenantId, okapiHeaders);
case HARD_DELETE -> performHardDelete(authorityId, okapiHeaders);
}).onFailure(throwable -> logError(authorityId, eventSubType, tenantId));
}

private Future<String> performSoftDelete(String authorityId, String tenantId) {
private Future<String> performSoftDelete(String authorityId, String tenantId, Map<String, String> okapiHeaders) {
var condition = filterRecordByExternalId(authorityId);
return recordService.getRecords(condition, RecordType.MARC_AUTHORITY, Collections.emptyList(), 0, 1, tenantId)
.compose(recordCollection -> {
Expand All @@ -69,7 +69,7 @@ private Future<String> performSoftDelete(String authorityId, String tenantId) {
}
var matchedId = recordCollection.getRecords().get(0).getMatchedId();

return recordService.updateRecordsState(matchedId, RecordState.DELETED, RecordType.MARC_AUTHORITY, tenantId);
return recordService.deleteRecordById(matchedId, IdType.RECORD, okapiHeaders);
}).map(authorityId);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
package org.folio.services.handlers.actions;

import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.folio.ActionProfile;
import org.folio.DataImportEventPayload;
import org.folio.dao.util.RecordType;
import org.folio.dao.util.IdType;
import org.folio.processing.events.services.handler.EventHandler;
import org.folio.processing.exceptions.EventProcessingException;
import org.folio.rest.jaxrs.model.ExternalIdsHolder;
import static org.folio.rest.jaxrs.model.ProfileType.ACTION_PROFILE;
import org.folio.rest.jaxrs.model.ProfileSnapshotWrapper;
import org.folio.rest.jaxrs.model.Record;
import org.folio.rest.jooq.enums.RecordState;
import org.folio.services.RecordService;
import org.folio.services.util.TypeConnection;

import javax.ws.rs.NotFoundException;
import java.util.concurrent.CompletableFuture;

import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.folio.ActionProfile.Action.DELETE;
import static org.folio.services.util.EventHandlingUtil.toOkapiHeaders;

/**
* The abstraction handles the DELETE action
Expand Down Expand Up @@ -66,8 +68,17 @@ public CompletableFuture<DataImportEventPayload> handle(DataImportEventPayload p
/* Handles DELETE action */
private void handlePayload(DataImportEventPayload payload, CompletableFuture<DataImportEventPayload> future) {
var payloadRecord = Json.decodeValue(payload.getContext().get(getRecordKey()), Record.class);
var okapiHeaders = toOkapiHeaders(payload);
LOG.info("handlePayload:: Handling 'delete' event for the record id = {}", payloadRecord.getId());
recordService.updateRecordsState(payloadRecord.getMatchedId(), RecordState.DELETED, RecordType.MARC_AUTHORITY, payload.getTenant())
recordService.deleteRecordById(payloadRecord.getMatchedId(), IdType.RECORD, okapiHeaders)
.recover(throwable -> {
if (throwable instanceof NotFoundException) {
LOG.debug("handlePayload:: No records found, recordId: '{}'", payloadRecord.getMatchedId());
return Future.succeededFuture();
}
LOG.warn("handlePayload:: Error during record deletion", throwable);
return Future.failedFuture(throwable);
})
.onSuccess(ar -> {
payload.setEventType(getNextEventType());
payload.getContext().remove(getRecordKey());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import io.vertx.kafka.client.consumer.impl.KafkaConsumerRecordImpl;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import org.apache.kafka.clients.consumer.ConsumerRecord;
Expand All @@ -21,6 +25,7 @@
import org.folio.dao.RecordDao;
import org.folio.dao.RecordDaoImpl;
import org.folio.dao.util.IdType;
import org.folio.dao.util.ParsedRecordDaoUtil;
import org.folio.dao.util.SnapshotDaoUtil;
import org.folio.rest.jaxrs.model.ExternalIdsHolder;
import org.folio.rest.jaxrs.model.ParsedRecord;
Expand Down Expand Up @@ -55,14 +60,18 @@ public class AuthorityDomainKafkaHandlerTest extends AbstractLBServiceTest {
private RecordService recordService;
private Record record;
private AuthorityDomainKafkaHandler handler;
private static final String currentDate = "20240718132044.6";

@BeforeClass
public static void setUpClass() throws IOException {
rawRecord = new RawRecord().withId(recordId)
.withContent(
new ObjectMapper().readValue(TestUtil.readFileFromPath(RAW_MARC_RECORD_CONTENT_SAMPLE_PATH), String.class));
parsedRecord = new ParsedRecord().withId(recordId)
.withContent(TestUtil.readFileFromPath(PARSED_MARC_RECORD_CONTENT_SAMPLE_PATH));
.withContent(
new JsonObject().put("leader", "01542ccm a2200361 4500")
.put("fields", new JsonArray()
.add(new JsonObject().put("005", currentDate))));
}

@Before
Expand Down Expand Up @@ -124,6 +133,16 @@ public void shouldSoftDeleteMarcAuthorityRecordOnSoftDeleteDomainEvent(TestConte
context.assertTrue(result.result().isPresent());
SourceRecord updatedRecord = result.result().get();
context.assertTrue(updatedRecord.getDeleted());
context.assertTrue(updatedRecord.getAdditionalInfo().getSuppressDiscovery());
context.assertEquals("d", ParsedRecordDaoUtil.getLeaderStatus(updatedRecord.getParsedRecord()));

//Complex verifying "005" field is NOT empty inside parsed record.
LinkedHashMap<String, ArrayList<LinkedHashMap<String, String>>> content = (LinkedHashMap<String, ArrayList<LinkedHashMap<String, String>>>) updatedRecord.getParsedRecord().getContent();
LinkedHashMap<String, String> map = content.get("fields").get(0);
String resulted005FieldValue = map.get("005");
context.assertNotNull(resulted005FieldValue);
context.assertNotEquals(currentDate, resulted005FieldValue);

async.complete();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
import static org.folio.rest.jaxrs.model.DataImportEventTypes.DI_SRS_MARC_AUTHORITY_RECORD_DELETED;
import static org.folio.rest.jaxrs.model.ProfileType.ACTION_PROFILE;
import static org.folio.rest.jaxrs.model.Record.RecordType.MARC_AUTHORITY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

import io.vertx.core.Future;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.unit.Async;
Expand All @@ -21,6 +27,7 @@
import org.folio.ActionProfile;
import org.folio.DataImportEventPayload;
import org.folio.dao.RecordDaoImpl;
import org.folio.dao.util.IdType;
import org.folio.dao.util.SnapshotDaoUtil;
import org.folio.processing.events.services.handler.EventHandler;
import org.folio.rest.jaxrs.model.ExternalIdsHolder;
Expand Down Expand Up @@ -145,6 +152,43 @@ public void shouldCompleteExceptionallyIfNoRecordInPayload(TestContext context)
});
}

@Test
public void shouldHandleErrorDuringRecordDeletion(TestContext context) {
Async async = context.async();
// given
HashMap<String, String> payloadContext = new HashMap<>();
payloadContext.put("MATCHED_MARC_AUTHORITY", Json.encode(record));
DataImportEventPayload dataImportEventPayload = new DataImportEventPayload()
.withContext(payloadContext)
.withTenant(TENANT_ID)
.withCurrentNode(new ProfileSnapshotWrapper()
.withId(UUID.randomUUID().toString())
.withContentType(ACTION_PROFILE)
.withContent(new ActionProfile()
.withId(UUID.randomUUID().toString())
.withName("Delete Marc Authorities")
.withAction(DELETE)
.withFolioRecord(ActionProfile.FolioRecord.MARC_AUTHORITY)
)
);

RecordService spyRecordService = spy(recordService);
doReturn(Future.failedFuture(new RuntimeException("Deletion error")))
.when(spyRecordService).deleteRecordById(anyString(), any(IdType.class), anyMap());

EventHandler spyEventHandler = new MarcAuthorityDeleteEventHandler(spyRecordService);

// when
CompletableFuture<DataImportEventPayload> future = spyEventHandler.handle(dataImportEventPayload);

// then
future.whenComplete((eventPayload, throwable) -> {
context.assertNotNull(throwable);
context.assertEquals("Deletion error", throwable.getMessage());
async.complete();
});
}

@Test
public void shouldCompleteIfNoRecordStored(TestContext context) {
Async async = context.async();
Expand Down