diff --git a/NEWS.md b/NEWS.md index c7e6066c7..a528e358e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,9 @@ -## XXXX-XX-XX v3.11.0-SNAPSHOT +## XXXX-XX-XX v4.0.0-SNAPSHOT * [MODSOURMAN-1304](https://folio-org.atlassian.net/browse/MODSOURMAN-1304) Handle journal records for MARC on Instance errors and COMPLETED INSTANCE/MARC_BIB events * [MODSOURMAN-1323](https://folio-org.atlassian.net/browse/MODSOURMAN-1323) Ensure holdings validation to check central ECS tenant * [MODDATAIMP-1214](https://folio-org.atlassian.net/browse/MODDATAIMP-1214) Ensure permission that allows update of shared instance and MARC * [MODSOURMAN-1136](https://folio-org.atlassian.net/browse/MODSOURMAN-1136) Send Kafka event when JobExecution is cancelled +* [MODSOURMAN-1353](https://folio-org.atlassian.net/browse/MODSOURMAN-1353) Remove GET /change-manager/parsedRecords endpoint ## 2025-03-13 v3.10.0 * [MODSOURMAN-1246](https://folio-org.atlassian.net/browse/MODSOURMAN-1246) Added data import completion notifications diff --git a/descriptors/ModuleDescriptor-template.json b/descriptors/ModuleDescriptor-template.json index f52e0b9be..01ab3431b 100644 --- a/descriptors/ModuleDescriptor-template.json +++ b/descriptors/ModuleDescriptor-template.json @@ -397,20 +397,8 @@ }, { "id": "source-manager-parsed-records", - "version": "3.0", + "version": "4.0", "handlers": [ - { - "methods": [ - "GET" - ], - "pathPattern": "/change-manager/parsedRecords", - "permissionsRequired": [ - "change-manager.parsedrecords.get" - ], - "modulePermissions": [ - "source-storage.source-records.item.get" - ] - }, { "methods": [ "PUT" diff --git a/mod-source-record-manager-client/pom.xml b/mod-source-record-manager-client/pom.xml index 5d57d3f5f..71024c3c7 100644 --- a/mod-source-record-manager-client/pom.xml +++ b/mod-source-record-manager-client/pom.xml @@ -6,7 +6,7 @@ org.folio mod-source-record-manager - 3.11.0-SNAPSHOT + 4.0.0-SNAPSHOT diff --git a/mod-source-record-manager-server/pom.xml b/mod-source-record-manager-server/pom.xml index c8be3f126..1048797ad 100644 --- a/mod-source-record-manager-server/pom.xml +++ b/mod-source-record-manager-server/pom.xml @@ -5,7 +5,7 @@ org.folio mod-source-record-manager - 3.11.0-SNAPSHOT + 4.0.0-SNAPSHOT diff --git a/mod-source-record-manager-server/src/main/java/org/folio/dao/SourceRecordStateDao.java b/mod-source-record-manager-server/src/main/java/org/folio/dao/SourceRecordStateDao.java deleted file mode 100644 index e2b5926a9..000000000 --- a/mod-source-record-manager-server/src/main/java/org/folio/dao/SourceRecordStateDao.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.folio.dao; - -import io.vertx.core.Future; -import org.folio.rest.jaxrs.model.SourceRecordState; - -import java.util.Optional; - -/** - * Source Record state in QM DAO - */ -public interface SourceRecordStateDao { - - /** - * Returns SourceRecordState by sourceRecordId for given tenant - * - * @param tenantId tenant - * @return optional of rules - */ - Future> get(String sourceRecordId, String tenantId); - - /** - * Saves SourceRecordState - * - * @param state state - * @param tenantId tenant - * @return rules id - */ - Future save(SourceRecordState state, String tenantId); - - /** - * Updates state if exist - * - * @param sourceId id of source record - * @param recordState records state in QM workflow - * @param tenantId tenant - * @return updated rules - */ - Future updateState(String sourceId, SourceRecordState.RecordState recordState, String tenantId); -} diff --git a/mod-source-record-manager-server/src/main/java/org/folio/dao/SourceRecordStateDaoImpl.java b/mod-source-record-manager-server/src/main/java/org/folio/dao/SourceRecordStateDaoImpl.java deleted file mode 100644 index 9bb3343ae..000000000 --- a/mod-source-record-manager-server/src/main/java/org/folio/dao/SourceRecordStateDaoImpl.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.folio.dao; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Promise; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.folio.dao.util.DbUtil; -import org.folio.dao.util.PostgresClientFactory; -import org.folio.rest.jaxrs.model.SourceRecordState; -import org.folio.rest.persist.Criteria.Criteria; -import org.folio.rest.persist.Criteria.Criterion; -import org.folio.rest.persist.PostgresClient; -import org.folio.rest.persist.SQLConnection; -import org.folio.rest.persist.cql.CQLWrapper; -import org.folio.rest.persist.interfaces.Results; -import org.folio.rest.tools.utils.ValidationHelper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; - -import javax.ws.rs.NotFoundException; -import java.util.Optional; -import java.util.UUID; - -import static org.folio.dataimport.util.DaoUtil.constructCriteria; - -@Repository -public class SourceRecordStateDaoImpl implements SourceRecordStateDao { - private static final Logger LOGGER = LogManager.getLogger(); - - private static final String TABLE_NAME = "source_records_state"; - private static final String ID_FIELD = "'sourceRecordId'"; - - @Autowired - private PostgresClientFactory pgClientFactory; - - - @Override - public Future> get(String sourceRecordId, String tenantId) { - Promise> promise = Promise.promise(); - try { - Criteria idCrit = constructCriteria(ID_FIELD, sourceRecordId); - pgClientFactory.createInstance(tenantId).get(TABLE_NAME, SourceRecordState.class, new Criterion(idCrit), true, false, promise); - } catch (Exception e) { - LOGGER.warn("get:: Error getting sourceRecord state by source id", e); - promise.fail(e); - } - return promise.future() - .map(Results::getResults) - .map(sourceRecordStates -> sourceRecordStates.isEmpty() ? Optional.empty() : Optional.of(sourceRecordStates.get(0))); - } - - private Future> get(String sourceRecordId, AsyncResult conn, String tenantId) { - Promise> promise = Promise.promise(); - try { - Criteria idCrit = constructCriteria(ID_FIELD, sourceRecordId); - pgClientFactory.createInstance(tenantId).get(conn, TABLE_NAME, SourceRecordState.class, new Criterion(idCrit), true, false, promise); - } catch (Exception e) { - LOGGER.warn("get:: Error getting sourceRecord state by source id", e); - promise.fail(e); - } - return promise.future() - .map(Results::getResults) - .map(sourceRecordStates -> sourceRecordStates.isEmpty() ? Optional.empty() : Optional.of(sourceRecordStates.get(0))); - } - - @Override - public Future save(SourceRecordState state, String tenantId) { - state.withId(UUID.randomUUID().toString()); - PostgresClient client = pgClientFactory.createInstance(tenantId); - return DbUtil.executeInTransaction(client, sqlConnection -> get(state.getSourceRecordId(), sqlConnection, tenantId) - .compose(sourceRecordStateOptional -> { - if (sourceRecordStateOptional.isPresent()) { - return update(state, sqlConnection, tenantId) - .map(state.getId()); - } else { - return save(state, sqlConnection, tenantId); - } - })); - } - - public Future save(SourceRecordState state, AsyncResult conn, String tenantId) { - Promise promise = Promise.promise(); - state.withId(UUID.randomUUID().toString()); - pgClientFactory.createInstance(tenantId).save(conn, TABLE_NAME, state.getId(), state, ar -> { - if (ar.succeeded()) { - promise.complete(state.getId()); - } else { - if (ValidationHelper.isDuplicate(ar.cause().getMessage())) { - promise.complete(state.getId()); - } else { - LOGGER.warn("save:: Error saving SourceRecordState entity", ar.cause()); - promise.fail(ar.cause()); - } - } - }); - return promise.future(); - } - - @Override - public Future updateState(String sourceId, SourceRecordState.RecordState recordState, String tenantId) { - PostgresClient client = pgClientFactory.createInstance(tenantId); - return DbUtil.executeInTransaction(client, sqlConnection -> get(sourceId, sqlConnection, tenantId) - .map(sourceRecordState -> sourceRecordState.orElseThrow(NotFoundException::new)) - .compose(sourceRecordState -> update(sourceRecordState.withRecordState(recordState), sqlConnection, tenantId))); - } - - private Future update(SourceRecordState state, AsyncResult conn, String tenantId) { - Promise promise = Promise.promise(); - try { - Criteria idCrit = constructCriteria(ID_FIELD, state.getSourceRecordId()); - CQLWrapper wrapper = new CQLWrapper(new Criterion(idCrit)); - pgClientFactory.createInstance(tenantId).update(conn, TABLE_NAME, state, wrapper, true, updateResult -> { - if (updateResult.failed()) { - LOGGER.warn("update:: Could not update state SourceRecordState sourceRecordId {}", state.getSourceRecordId(), updateResult.cause()); - promise.fail(updateResult.cause()); - } else if (updateResult.result().rowCount() != 1) { - String errorMessage = String.format("SourceRecordState with sourceRecordId '%s' was not found", state.getSourceRecordId()); - LOGGER.warn(errorMessage); - promise.fail(new NotFoundException(errorMessage)); - } else { - promise.complete(state); - } - }); - } catch (Exception e) { - LOGGER.warn("update:: Error updating SourceRecordState", e); - promise.fail(e); - } - return promise.future(); - } -} diff --git a/mod-source-record-manager-server/src/main/java/org/folio/rest/impl/ChangeManagerImpl.java b/mod-source-record-manager-server/src/main/java/org/folio/rest/impl/ChangeManagerImpl.java index 4d8351e16..4d539795f 100644 --- a/mod-source-record-manager-server/src/main/java/org/folio/rest/impl/ChangeManagerImpl.java +++ b/mod-source-record-manager-server/src/main/java/org/folio/rest/impl/ChangeManagerImpl.java @@ -241,24 +241,6 @@ public void deleteChangeManagerJobExecutionsRecordsById(String id, Map okapiHeaders, - Handler> asyncResultHandler, Context vertxContext) { - vertxContext.runOnContext(v -> { - try { - LOGGER.debug("getChangeManagerParsedRecords:: externalId {}", externalId); - parsedRecordService.getRecordByExternalId(externalId, new OkapiConnectionParams(okapiHeaders, vertxContext.owner())) - .map(GetChangeManagerParsedRecordsResponse::respond200WithApplicationJson) - .map(Response.class::cast) - .otherwise(ExceptionHelper::mapExceptionToResponse) - .onComplete(asyncResultHandler); - } catch (Exception e) { - LOGGER.warn(getMessage("getChangeManagerParsedRecords:: Failed to retrieve parsed record by externalId {}", e, externalId)); - asyncResultHandler.handle(Future.succeededFuture(ExceptionHelper.mapExceptionToResponse(e))); - } - }); - } - @Override public void putChangeManagerParsedRecordsById(String id, ParsedRecordDto entity, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { diff --git a/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordService.java b/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordService.java index 6bedb9373..7e4936517 100644 --- a/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordService.java +++ b/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordService.java @@ -9,15 +9,6 @@ */ public interface ParsedRecordService { - /** - * Get {@link ParsedRecordDto} by externalId - * - * @param externalId - externalId - * @param params - OkapiConnectionParams - * @return future with ParsedRecordDto, which was mapped from SourceRecord from SRS - */ - Future getRecordByExternalId(String externalId, OkapiConnectionParams params); - /** * Creates new Record in SRS with higher generation, updates status of the OLD Record and sends event with updated Record * diff --git a/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordServiceImpl.java b/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordServiceImpl.java index 442cb3332..9c8dd741e 100644 --- a/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordServiceImpl.java +++ b/mod-source-record-manager-server/src/main/java/org/folio/services/ParsedRecordServiceImpl.java @@ -3,46 +3,33 @@ import static io.vertx.core.Future.failedFuture; import static java.lang.String.format; -import static org.folio.HttpStatus.HTTP_NOT_FOUND; -import static org.folio.HttpStatus.HTTP_OK; import static org.folio.verticle.consumers.util.QMEventTypes.QM_RECORD_UPDATED; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; -import javax.ws.rs.NotFoundException; import io.vertx.core.Future; -import io.vertx.core.Promise; -import io.vertx.core.buffer.Buffer; import io.vertx.core.json.Json; import io.vertx.core.json.JsonObject; import lombok.extern.log4j.Log4j2; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.folio.dataimport.util.OkapiConnectionParams; -import org.folio.dataimport.util.Try; import org.folio.kafka.KafkaHeaderUtils; import org.folio.processing.mapping.defaultmapper.processor.parameters.MappingParameters; -import org.folio.rest.client.SourceStorageSourceRecordsClient; import org.folio.rest.jaxrs.model.ParsedRecordDto; -import org.folio.rest.jaxrs.model.SourceRecord; -import org.folio.rest.jaxrs.model.SourceRecordState; import org.folio.services.entity.MappingRuleCacheKey; import org.folio.services.mappers.processor.MappingParametersProvider; @Log4j2 @Service public class ParsedRecordServiceImpl implements ParsedRecordService { - private static final Logger LOGGER = LogManager.getLogger(); private static final AtomicInteger indexer = new AtomicInteger(); private final MappingParametersProvider mappingParametersProvider; private final MappingRuleCache mappingRuleCache; - private final SourceRecordStateService sourceRecordStateService; private final QuickMarcEventProducerService producerService; @Value("${srm.kafka.QuickMarcUpdateKafkaHandler.maxDistributionNum:100}") @@ -50,52 +37,12 @@ public class ParsedRecordServiceImpl implements ParsedRecordService { public ParsedRecordServiceImpl(MappingParametersProvider mappingParametersProvider, MappingRuleCache mappingRuleCache, - SourceRecordStateService sourceRecordStateService, QuickMarcEventProducerService producerService) { + QuickMarcEventProducerService producerService) { this.mappingParametersProvider = mappingParametersProvider; this.mappingRuleCache = mappingRuleCache; - this.sourceRecordStateService = sourceRecordStateService; this.producerService = producerService; } - @Override - public Future getRecordByExternalId(String externalId, OkapiConnectionParams params) { - LOGGER.debug("getRecordByExternalId:: externalId {}", externalId); - Promise promise = Promise.promise(); - var client = new SourceStorageSourceRecordsClient(params.getOkapiUrl(), params.getTenantId(), params.getToken()); - try { - client.getSourceStorageSourceRecordsById(externalId, "EXTERNAL", null, response -> { - if (HTTP_OK.toInt() == response.result().statusCode()) { - Buffer bodyAsBuffer = response.result().bodyAsBuffer(); - Try.itGet(() -> mapSourceRecordToParsedRecordDto(bodyAsBuffer)) - .compose(parsedRecordDto -> sourceRecordStateService.get(parsedRecordDto.getId(), params.getTenantId()) - .map(sourceRecordStateOptional -> sourceRecordStateOptional - .orElse(new SourceRecordState().withRecordState(SourceRecordState.RecordState.ACTUAL))) - .compose(sourceRecordState -> Future.succeededFuture(parsedRecordDto - .withRecordState(ParsedRecordDto.RecordState.valueOf(sourceRecordState.getRecordState().name()))))) - .onComplete(parsedRecordDtoAsyncResult -> { - if (parsedRecordDtoAsyncResult.succeeded()) { - promise.complete(parsedRecordDtoAsyncResult.result()); - } else { - promise.fail(parsedRecordDtoAsyncResult.cause()); - } - }); - } else { - String message = format("Error retrieving Record by externalId: '%s', response code %s, %s", - externalId, response.result().statusCode(), response.result().statusMessage()); - if (HTTP_NOT_FOUND.toInt() == response.result().statusCode()) { - promise.fail(new NotFoundException(message)); - } else { - promise.fail(message); - } - } - }); - } catch (Exception e) { - log.warn("getRecordByExternalId:: Failed to GET Record from SRS", e); - promise.fail(e); - } - return promise.future(); - } - @Override public Future updateRecord(ParsedRecordDto parsedRecordDto, OkapiConnectionParams params) { String snapshotId = UUID.randomUUID().toString(); @@ -117,27 +64,11 @@ public Future updateRecord(ParsedRecordDto parsedRecordDto, OkapiConnec private Future updateRecord(ParsedRecordDto parsedRecordDto, String snapshotId, MappingParameters mappingParameters, JsonObject mappingRules, OkapiConnectionParams params) { - var sourceRecordState = new SourceRecordState() - .withRecordState(SourceRecordState.RecordState.IN_PROGRESS) - .withSourceRecordId(parsedRecordDto.getId()); var tenantId = params.getTenantId(); var key = String.valueOf(indexer.incrementAndGet() % maxDistributionNum); var kafkaHeaders = KafkaHeaderUtils.kafkaHeadersFromMultiMap(params.getHeaders()); var eventPayload = prepareEventPayload(parsedRecordDto, mappingRules, mappingParameters, snapshotId); - return sourceRecordStateService.save(sourceRecordState, tenantId) - .compose(s -> producerService.sendEventWithZipping(eventPayload, QM_RECORD_UPDATED.name(), key, tenantId, kafkaHeaders)); - } - - private ParsedRecordDto mapSourceRecordToParsedRecordDto(Buffer body) { - SourceRecord sourceRecord = body.toJsonObject().mapTo(SourceRecord.class); - return new ParsedRecordDto() - .withId(sourceRecord.getRecordId()) - .withParsedRecord(sourceRecord.getParsedRecord()) - .withRecordType(ParsedRecordDto.RecordType.fromValue(sourceRecord.getRecordType().value())) - .withExternalIdsHolder(sourceRecord.getExternalIdsHolder()) - .withAdditionalInfo(sourceRecord.getAdditionalInfo()) - .withMetadata(sourceRecord.getMetadata()) - .withRecordState(ParsedRecordDto.RecordState.ACTUAL); + return producerService.sendEventWithZipping(eventPayload, QM_RECORD_UPDATED.name(), key, tenantId, kafkaHeaders); } private String prepareEventPayload(ParsedRecordDto parsedRecordDto, JsonObject mappingRules, diff --git a/mod-source-record-manager-server/src/main/java/org/folio/services/SourceRecordStateService.java b/mod-source-record-manager-server/src/main/java/org/folio/services/SourceRecordStateService.java deleted file mode 100644 index 4b8cfabd8..000000000 --- a/mod-source-record-manager-server/src/main/java/org/folio/services/SourceRecordStateService.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.folio.services; - -import io.vertx.core.Future; -import org.folio.rest.jaxrs.model.SourceRecordState; - -import java.util.Optional; - -/** - * Source Record state in QM DAO - */ -public interface SourceRecordStateService { - - /** - * Returns SourceRecordState by sourceRecordId for given tenant - * - * @param tenantId tenant - * @return optional of rules - */ - Future> get(String sourceRecordId, String tenantId); - - /** - * Saves SourceRecordState - * - * @param state state - * @param tenantId tenant - * @return rules id - */ - Future save(SourceRecordState state, String tenantId); - - /** - * Updates state if exist - * - * @param sourceId id of source record - * @param recordState records state in QM workflow - * @param tenantId tenant - * @return updated rules - */ - Future updateState(String sourceId, SourceRecordState.RecordState recordState, String tenantId); -} diff --git a/mod-source-record-manager-server/src/main/java/org/folio/services/SourceRecordStateServiceImpl.java b/mod-source-record-manager-server/src/main/java/org/folio/services/SourceRecordStateServiceImpl.java deleted file mode 100644 index 07590b6a8..000000000 --- a/mod-source-record-manager-server/src/main/java/org/folio/services/SourceRecordStateServiceImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.folio.services; - -import io.vertx.core.Future; -import org.folio.dao.SourceRecordStateDao; -import org.folio.rest.jaxrs.model.SourceRecordState; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import java.util.Optional; - -@Component -public class SourceRecordStateServiceImpl implements SourceRecordStateService { - - private SourceRecordStateDao sourceRecordStateDao; - - public SourceRecordStateServiceImpl(@Autowired SourceRecordStateDao sourceRecordStateDao) { - this.sourceRecordStateDao = sourceRecordStateDao; - } - - @Override - public Future> get(String sourceRecordId, String tenantId) { - return sourceRecordStateDao.get(sourceRecordId, tenantId); - } - - @Override - public Future save(SourceRecordState state, String tenantId) { - return sourceRecordStateDao.save(state, tenantId); - } - - @Override - public Future updateState(String sourceId, SourceRecordState.RecordState recordState, String tenantId) { - return sourceRecordStateDao.updateState(sourceId, recordState, tenantId); - } -} diff --git a/mod-source-record-manager-server/src/main/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandler.java b/mod-source-record-manager-server/src/main/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandler.java index 6cef24ab1..3c0d00a98 100644 --- a/mod-source-record-manager-server/src/main/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandler.java +++ b/mod-source-record-manager-server/src/main/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandler.java @@ -1,8 +1,6 @@ package org.folio.verticle.consumers; import static org.folio.kafka.KafkaHeaderUtils.kafkaHeadersToMap; -import static org.folio.rest.jaxrs.model.SourceRecordState.RecordState.ACTUAL; -import static org.folio.rest.jaxrs.model.SourceRecordState.RecordState.ERROR; import static org.folio.verticle.consumers.util.QMEventTypes.QM_COMPLETED; import java.util.HashMap; @@ -22,10 +20,7 @@ import org.folio.dataimport.util.OkapiConnectionParams; import org.folio.kafka.AsyncRecordHandler; import org.folio.rest.jaxrs.model.Event; -import org.folio.rest.jaxrs.model.SourceRecordState; import org.folio.services.QuickMarcEventProducerService; -import org.folio.services.SourceRecordStateService; -import org.folio.verticle.consumers.util.QMEventTypes; import org.folio.verticle.consumers.util.QmCompletedEventPayload; @Component @@ -35,10 +30,8 @@ public class QuickMarcUpdateKafkaHandler implements AsyncRecordHandler { private static final String RECORD_ID_KEY = "RECORD_ID"; - private static final String RECORD_DTO_ID_KEY = "RECORD_DTO_ID"; private static final String ERROR_KEY = "ERROR"; - private final SourceRecordStateService sourceRecordStateService; private final QuickMarcEventProducerService producerService; private final Vertx vertx; @@ -54,7 +47,6 @@ public Future handle(KafkaConsumerRecord record) { return getEventPayload(event) .compose(eventPayload -> sendQmCompletedEvent(eventPayload, tenantId, kafkaHeaders)) - .compose(eventPayload -> updateSourceState(eventPayload, eventType, tenantId)) .compose(s -> Future.succeededFuture(record.key()), th -> { log.warn("handle:: Update record state was failed while handle {} event", eventType); return Future.failedFuture(th); @@ -70,17 +62,6 @@ private Future> sendQmCompletedEvent(Map eve .map(v -> eventPayload); } - private Future updateSourceState(Map eventPayload, String eventType, - String tenantId) { - log.debug("Event was received for {}: {}", eventType, eventPayload); - var recordId = eventPayload.get(RECORD_DTO_ID_KEY); - if (QMEventTypes.QM_ERROR.name().equals(eventType)) { - return sourceRecordStateService.updateState(recordId, ERROR, tenantId); - } else { - return sourceRecordStateService.updateState(recordId, ACTUAL, tenantId); - } - } - @SuppressWarnings("unchecked") private Future> getEventPayload(Event event) { try { diff --git a/mod-source-record-manager-server/src/main/resources/templates/db_scripts/schema.json b/mod-source-record-manager-server/src/main/resources/templates/db_scripts/schema.json index a0301740e..cd214ef32 100644 --- a/mod-source-record-manager-server/src/main/resources/templates/db_scripts/schema.json +++ b/mod-source-record-manager-server/src/main/resources/templates/db_scripts/schema.json @@ -33,17 +33,6 @@ "tableName": "mapping_rules", "fromModuleVersion": "mod-source-record-manager-1.7.0", "withMetadata": false - }, - { - "tableName": "source_records_state", - "fromModuleVersion": "mod-source-record-manager-2.4.0", - "withMetadata": false, - "uniqueIndex": [ - { - "fieldName": "sourceRecordId", - "tOps": "ADD" - } - ] } ], "scripts": [ @@ -311,6 +300,11 @@ "run": "after", "snippetPath": "create_insert_journal_records_function.sql", "fromModuleVersion": "mod-source-record-manager-3.10.0" + }, + { + "run": "after", + "snippet": "DROP TABLE IF EXISTS source_records_state;", + "fromModuleVersion": "mod-source-record-manager-4.0.0" } ] } diff --git a/mod-source-record-manager-server/src/test/java/org/folio/rest/impl/changeManager/ChangeManagerParsedRecordsAPITest.java b/mod-source-record-manager-server/src/test/java/org/folio/rest/impl/changeManager/ChangeManagerParsedRecordsAPITest.java index 455d9a8bf..cb8f9c879 100644 --- a/mod-source-record-manager-server/src/test/java/org/folio/rest/impl/changeManager/ChangeManagerParsedRecordsAPITest.java +++ b/mod-source-record-manager-server/src/test/java/org/folio/rest/impl/changeManager/ChangeManagerParsedRecordsAPITest.java @@ -1,55 +1,40 @@ package org.folio.rest.impl.changeManager; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.notFound; -import static com.github.tomakehurst.wiremock.client.WireMock.ok; -import static com.github.tomakehurst.wiremock.client.WireMock.serverError; import static java.util.Collections.emptyList; import static org.folio.KafkaUtil.checkKafkaEventSent; import static org.folio.KafkaUtil.getKafkaHostAndPort; -import static org.hamcrest.Matchers.is; - import static org.folio.kafka.KafkaTopicNameHelper.formatTopicName; import static org.folio.kafka.KafkaTopicNameHelper.getDefaultNameSpace; -import io.vertx.core.json.Json; -import java.util.UUID; - import com.github.tomakehurst.wiremock.client.WireMock; -import com.github.tomakehurst.wiremock.matching.RegexPattern; -import com.github.tomakehurst.wiremock.matching.UrlPathPattern; import io.restassured.RestAssured; -import io.vertx.core.json.JsonObject; +import io.vertx.core.json.Json; import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; +import java.util.UUID; import org.apache.http.HttpStatus; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - import org.folio.kafka.KafkaConfig; import org.folio.rest.impl.AbstractRestTest; import org.folio.rest.jaxrs.model.ExternalIdsHolder; import org.folio.rest.jaxrs.model.ParsedRecord; import org.folio.rest.jaxrs.model.ParsedRecordDto; -import org.folio.rest.jaxrs.model.SourceRecord; import org.folio.verticle.consumers.util.QMEventTypes; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(VertxUnitRunner.class) public class ChangeManagerParsedRecordsAPITest extends AbstractRestTest { - private final String SOURCE_RECORDS_URL = "/source-storage/source-records/"; - private final String PARSED_RECORDS_URL = "/change-manager/parsedRecords"; - private final String EXTERNAL_ID_QUERY_PARAM = "externalId"; - private static final String KAFKA_HOST = "KAFKA_HOST"; private static final String KAFKA_PORT = "KAFKA_PORT"; private static final String KAFKA_ENV = "ENV"; private static final String KAFKA_ENV_ID = "test-env"; private static final String KAFKA_MAX_REQUEST_SIZE = "MAX_REQUEST_SIZE"; + private static final String PARSED_RECORDS_URL = "/change-manager/parsedRecords"; - KafkaConfig kafkaConfig; + private KafkaConfig kafkaConfig; @Before public void setUp() { @@ -67,92 +52,6 @@ public void setUp() { .build(); } - @Test - public void shouldReturnParsedRecordDtoIfSourceRecordExists(TestContext testContext) { - Async async = testContext.async(); - - String externalId = UUID.randomUUID().toString(); - SourceRecord sourceRecord = new SourceRecord() - .withRecordId(UUID.randomUUID().toString()) - .withParsedRecord(new ParsedRecord().withId(UUID.randomUUID().toString()) - .withContent("{\"leader\":\"01240cas a2200397 4500\",\"fields\":[]}")) - .withRecordType(SourceRecord.RecordType.MARC_BIB) - .withExternalIdsHolder(new ExternalIdsHolder().withInstanceId(externalId)); - - WireMock.stubFor(get(new UrlPathPattern(new RegexPattern(SOURCE_RECORDS_URL + ".*"), true)) - .willReturn(ok().withBody(JsonObject.mapFrom(sourceRecord).encode()))); - - RestAssured.given() - .spec(spec) - .queryParam(EXTERNAL_ID_QUERY_PARAM, externalId) - .when() - .get(PARSED_RECORDS_URL) - .then() - .statusCode(HttpStatus.SC_OK) - .body("id", is(sourceRecord.getRecordId())) - .body("parsedRecord.id", is(sourceRecord.getParsedRecord().getId())) - .body("recordType", is(sourceRecord.getRecordType().value())) - .body("externalIdsHolder.instanceId", is(externalId)); - async.complete(); - } - - @Test - public void shouldReturnNotFoundIfThereIsNoSourceRecord(TestContext testContext) { - Async async = testContext.async(); - - String externalId = UUID.randomUUID().toString(); - - WireMock.stubFor(get(new UrlPathPattern(new RegexPattern(SOURCE_RECORDS_URL + ".*"), true)) - .willReturn(notFound())); - - RestAssured.given() - .spec(spec) - .queryParam(EXTERNAL_ID_QUERY_PARAM, externalId) - .when() - .get(PARSED_RECORDS_URL) - .then() - .statusCode(HttpStatus.SC_NOT_FOUND); - async.complete(); - } - - @Test - public void shouldReturnErrorIfExceptionWasThrown(TestContext testContext) { - Async async = testContext.async(); - - String externalId = UUID.randomUUID().toString(); - - WireMock.stubFor(get(new UrlPathPattern(new RegexPattern(SOURCE_RECORDS_URL + ".*"), true)) - .willReturn(ok().withBody("{\"leader\":\"01240cas a2200397 4500\",\"fields\":[]}"))); - - RestAssured.given() - .spec(spec) - .queryParam(EXTERNAL_ID_QUERY_PARAM, externalId) - .when() - .get(PARSED_RECORDS_URL) - .then() - .statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); - async.complete(); - } - - @Test - public void shouldReturnErrorIfErrorResponse(TestContext testContext) { - Async async = testContext.async(); - - String externalId = UUID.randomUUID().toString(); - - WireMock.stubFor(get(new UrlPathPattern(new RegexPattern(SOURCE_RECORDS_URL + ".*"), true)) - .willReturn(serverError())); - - RestAssured.given() - .spec(spec) - .queryParam(EXTERNAL_ID_QUERY_PARAM, externalId) - .when() - .get(PARSED_RECORDS_URL) - .then() - .statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); - async.complete(); - } - @Test public void shouldUpdateParsedRecordOnPut(TestContext testContext) { Async async = testContext.async(); @@ -165,7 +64,6 @@ public void shouldUpdateParsedRecordOnPut(TestContext testContext) { .withParsedRecord(new ParsedRecord().withId(UUID.randomUUID().toString()) .withContent("{\"leader\":\"01240cas a2200397 4500\",\"fields\":[]}")) .withRecordType(ParsedRecordDto.RecordType.MARC_HOLDING) - .withRelatedRecordVersion("1") .withExternalIdsHolder(new ExternalIdsHolder().withInstanceId(UUID.randomUUID().toString())); RestAssured.given() diff --git a/mod-source-record-manager-server/src/test/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandlerTest.java b/mod-source-record-manager-server/src/test/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandlerTest.java index 2e1503562..ba4cf107d 100644 --- a/mod-source-record-manager-server/src/test/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandlerTest.java +++ b/mod-source-record-manager-server/src/test/java/org/folio/verticle/consumers/QuickMarcUpdateKafkaHandlerTest.java @@ -3,7 +3,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -33,9 +32,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.folio.rest.jaxrs.model.Event; -import org.folio.rest.jaxrs.model.SourceRecordState; import org.folio.services.QuickMarcEventProducerService; -import org.folio.services.SourceRecordStateService; import org.folio.verticle.consumers.util.QMEventTypes; import org.folio.verticle.consumers.util.QmCompletedEventPayload; @@ -44,8 +41,6 @@ public class QuickMarcUpdateKafkaHandlerTest { private static final String TENANT_ID = "test"; - @Mock - private SourceRecordStateService sourceRecordStateService; @Mock private QuickMarcEventProducerService producerService; @Mock @@ -59,8 +54,7 @@ public class QuickMarcUpdateKafkaHandlerTest { @Before public void setUp() throws Exception { - quickMarcHandler = - new QuickMarcUpdateKafkaHandler(sourceRecordStateService, producerService, vertx); + quickMarcHandler = new QuickMarcUpdateKafkaHandler(producerService, vertx); } @Test @@ -82,8 +76,6 @@ public void shouldUpdateRecordStateAndSendEventOnHandleQmInventoryInstanceUpdate when(kafkaRecord.headers()).thenReturn(kafkaHeaders); when(producerService.sendEvent(anyString(), anyString(), isNull(), anyString(), anyList())) .thenReturn(Future.succeededFuture(true)); - when(sourceRecordStateService.updateState(anyString(), any(SourceRecordState.RecordState.class), anyString())) - .thenReturn(Future.succeededFuture(new SourceRecordState())); var future = quickMarcHandler.handle(kafkaRecord); assertTrue(future.succeeded()); @@ -91,9 +83,6 @@ public void shouldUpdateRecordStateAndSendEventOnHandleQmInventoryInstanceUpdate .sendEvent(qmCompletedEventCaptor.capture(), eq(QMEventTypes.QM_COMPLETED.name()), isNull(), eq(TENANT_ID), eq(kafkaHeaders)); - verify(sourceRecordStateService, times(1)) - .updateState(recordDtoId, SourceRecordState.RecordState.ACTUAL, TENANT_ID); - var actualEventPayload = Json.decodeValue(qmCompletedEventCaptor.getValue(), QmCompletedEventPayload.class); assertEquals(recordId, actualEventPayload.getRecordId()); assertNull(actualEventPayload.getErrorMessage()); @@ -120,8 +109,6 @@ public void shouldUpdateRecordStateAndSendEventOnHandleQmError() { when(kafkaRecord.headers()).thenReturn(kafkaHeaders); when(producerService.sendEvent(anyString(), anyString(), isNull(), anyString(), anyList())) .thenReturn(Future.succeededFuture(true)); - when(sourceRecordStateService.updateState(anyString(), any(SourceRecordState.RecordState.class), anyString())) - .thenReturn(Future.succeededFuture(new SourceRecordState())); var future = quickMarcHandler.handle(kafkaRecord); assertTrue(future.succeeded()); @@ -129,9 +116,6 @@ public void shouldUpdateRecordStateAndSendEventOnHandleQmError() { .sendEvent(qmCompletedEventCaptor.capture(), eq(QMEventTypes.QM_COMPLETED.name()), isNull(), eq(TENANT_ID), eq(kafkaHeaders)); - verify(sourceRecordStateService, times(1)) - .updateState(recordDtoId, SourceRecordState.RecordState.ERROR, TENANT_ID); - var actualEventPayload = Json.decodeValue(qmCompletedEventCaptor.getValue(), QmCompletedEventPayload.class); assertEquals(recordId, actualEventPayload.getRecordId()); assertEquals(errorMessage, actualEventPayload.getErrorMessage()); diff --git a/pom.xml b/pom.xml index 8a2c80d6a..19a8e4a73 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.folio mod-source-record-manager - 3.11.0-SNAPSHOT + 4.0.0-SNAPSHOT pom diff --git a/ramls/change-manager.raml b/ramls/change-manager.raml index fdd3c292e..79669dc38 100644 --- a/ramls/change-manager.raml +++ b/ramls/change-manager.raml @@ -30,7 +30,6 @@ types: dataImportInitConfig: !include raml-storage/schemas/common/dataImportInitConfig.json jobExecutionProgress: !include raml-storage/schemas/mod-source-record-manager/jobExecutionProgress.json dataImportEventPayload: !include raml-storage/schemas/common/dataImportEventPayload.json - sourceRecordState: !include sourceRecordState.json deleteJobExecutionsReq: !include raml-storage/schemas/mod-source-record-manager/deleteJobExecutionsReq.json deleteJobExecutionsResp: !include raml-storage/schemas/mod-source-record-manager/deleteJobExecutionsResp.json incomingRecord: !include raml-storage/schemas/mod-source-record-manager/incomingRecord.json @@ -220,28 +219,6 @@ resourceTypes: text/plain: example: "Internal server error" /parsedRecords: - get: - description: "Retrieve ParsedRecord by externalId" - queryParameters: - externalId: - description: "externalId parameter" - pattern: "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[1-5][a-fA-F0-9]{3}-[89abAB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$" - required: true - responses: - 200: - body: - application/json: - type: parsedRecordDto - 400: - description: "Bad request" - body: - text/plain: - example: "Bad request" - 500: - description: "Internal server error" - body: - text/plain: - example: "Internal server error" /{id}: description: Update ParsedRecord by id put: diff --git a/ramls/raml-storage b/ramls/raml-storage index d53c1bce7..5e5bf0a70 160000 --- a/ramls/raml-storage +++ b/ramls/raml-storage @@ -1 +1 @@ -Subproject commit d53c1bce7e86202580487289b58dc7b28b7c4fa6 +Subproject commit 5e5bf0a70542f984c6e6777ff53aec832af4c07e diff --git a/ramls/sourceRecordState.json b/ramls/sourceRecordState.json deleted file mode 100644 index abf78f429..000000000 --- a/ramls/sourceRecordState.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Source Record State for QM workflow", - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The system assigned unique ID of the instance record" - }, - "sourceRecordId": { - "type": "string", - "description": "The system assigned unique ID of the source record" - }, - "recordState": { - "description": "State of source record in QM workflow", - "type": "string", - "enum": [ - "ACTUAL", - "IN_PROGRESS", - "ERROR" - ], - "default": "ACTUAL" - } - }, - "additionalProperties": false, - "required": [ - "sourceRecordId", - "recordState" - ] -}