Search in sources :

Example 1 with RecordDaoUtil

use of org.folio.dao.util.RecordDaoUtil in project mod-source-record-storage by folio-org.

the class RecordServiceTest method getMarcSourceRecordsByListOfIdsThatAreDeleted.

private void getMarcSourceRecordsByListOfIdsThatAreDeleted(TestContext context, Record.RecordType recordType, RecordType parsedRecordType) {
    Async async = context.async();
    List<Record> records = TestMocks.getRecords().stream().map(record -> {
        Record deletedRecord = new Record().withId(record.getId()).withSnapshotId(record.getSnapshotId()).withMatchedId(record.getMatchedId()).withRecordType(record.getRecordType()).withState(State.DELETED).withGeneration(record.getGeneration()).withOrder(record.getOrder()).withLeaderRecordStatus(record.getLeaderRecordStatus()).withRawRecord(record.getRawRecord()).withParsedRecord(record.getParsedRecord()).withAdditionalInfo(record.getAdditionalInfo()).withExternalIdsHolder(record.getExternalIdsHolder());
        if (Objects.nonNull(record.getMetadata())) {
            deletedRecord.withMetadata(record.getMetadata());
        }
        if (Objects.nonNull(record.getErrorRecord())) {
            deletedRecord.withErrorRecord(record.getErrorRecord());
        }
        return deletedRecord;
    }).collect(Collectors.toList());
    RecordCollection recordCollection = new RecordCollection().withRecords(records).withTotalRecords(records.size());
    saveRecords(recordCollection.getRecords()).onComplete(batch -> {
        if (batch.failed()) {
            context.fail(batch.cause());
        }
        List<String> ids = records.stream().filter(r -> r.getRecordType().equals(recordType)).map(Record::getMatchedId).collect(Collectors.toList());
        recordService.getSourceRecords(ids, IdType.RECORD, parsedRecordType, true, TENANT_ID).onComplete(get -> {
            if (get.failed()) {
                context.fail(get.cause());
            }
            List<SourceRecord> expected = records.stream().filter(r -> r.getRecordType().equals(recordType)).map(RecordDaoUtil::toSourceRecord).sorted(comparing(SourceRecord::getRecordId)).collect(Collectors.toList());
            get.result().getSourceRecords().sort(comparing(SourceRecord::getRecordId));
            context.assertEquals(expected.size(), get.result().getTotalRecords());
            compareSourceRecords(context, expected, get.result().getSourceRecords());
            async.complete();
        });
    });
}
Also used : TestContext(io.vertx.ext.unit.TestContext) Async(io.vertx.ext.unit.Async) DSL(org.jooq.impl.DSL) RecordDaoImpl(org.folio.dao.RecordDaoImpl) RawRecord(org.folio.rest.jaxrs.model.RawRecord) RunWith(org.junit.runner.RunWith) Condition(org.jooq.Condition) ArrayList(java.util.ArrayList) CompositeFuture(io.vertx.core.CompositeFuture) RecordType(org.folio.dao.util.RecordType) RecordsBatchResponse(org.folio.rest.jaxrs.model.RecordsBatchResponse) RecordState(org.folio.rest.jooq.enums.RecordState) RECORDS_LB(org.folio.rest.jooq.Tables.RECORDS_LB) Flowable(io.reactivex.Flowable) IdType(org.folio.dao.util.IdType) ExternalIdsHolder(org.folio.rest.jaxrs.model.ExternalIdsHolder) After(org.junit.After) AsyncResult(io.vertx.core.AsyncResult) Comparator.comparing(java.util.Comparator.comparing) AdditionalInfo(org.folio.rest.jaxrs.model.AdditionalInfo) SortOrder(org.jooq.SortOrder) TestMocks(org.folio.TestMocks) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Before(org.junit.Before) GenericCompositeFuture(org.folio.okapi.common.GenericCompositeFuture) RecordDao(org.folio.dao.RecordDao) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) Record(org.folio.rest.jaxrs.model.Record) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) Test(org.junit.Test) VertxUnitRunner(io.vertx.ext.unit.junit.VertxUnitRunner) SnapshotDaoUtil(org.folio.dao.util.SnapshotDaoUtil) UUID(java.util.UUID) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) Future(io.vertx.core.Future) ParsedRecordDto(org.folio.rest.jaxrs.model.ParsedRecordDto) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) List(java.util.List) OffsetDateTime(java.time.OffsetDateTime) ChronoUnit(java.time.temporal.ChronoUnit) State(org.folio.rest.jaxrs.model.Record.State) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) OrderField(org.jooq.OrderField) Assertions(org.junit.jupiter.api.Assertions) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) Async(io.vertx.ext.unit.Async) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) RawRecord(org.folio.rest.jaxrs.model.RawRecord) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Record(org.folio.rest.jaxrs.model.Record) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord)

Example 2 with RecordDaoUtil

use of org.folio.dao.util.RecordDaoUtil in project mod-source-record-storage by folio-org.

the class RecordServiceTest method getMarcSourceRecords.

private void getMarcSourceRecords(TestContext context, RecordType parsedRecordType, Record.RecordType recordType) {
    Async async = context.async();
    List<Record> records = TestMocks.getRecords();
    RecordCollection recordCollection = new RecordCollection().withRecords(records).withTotalRecords(records.size());
    saveRecords(recordCollection.getRecords()).onComplete(batch -> {
        if (batch.failed()) {
            context.fail(batch.cause());
        }
        Condition condition = DSL.trueCondition();
        List<OrderField<?>> orderFields = new ArrayList<>();
        recordService.getSourceRecords(condition, parsedRecordType, orderFields, 0, 10, TENANT_ID).onComplete(get -> {
            if (get.failed()) {
                context.fail(get.cause());
            }
            List<SourceRecord> expected = records.stream().filter(r -> r.getRecordType().equals(recordType)).map(RecordDaoUtil::toSourceRecord).sorted(comparing(SourceRecord::getRecordId)).collect(Collectors.toList());
            get.result().getSourceRecords().sort(comparing(SourceRecord::getRecordId));
            context.assertEquals(expected.size(), get.result().getTotalRecords());
            compareSourceRecords(context, expected, get.result().getSourceRecords());
            async.complete();
        });
    });
}
Also used : Condition(org.jooq.Condition) TestContext(io.vertx.ext.unit.TestContext) Async(io.vertx.ext.unit.Async) DSL(org.jooq.impl.DSL) RecordDaoImpl(org.folio.dao.RecordDaoImpl) RawRecord(org.folio.rest.jaxrs.model.RawRecord) RunWith(org.junit.runner.RunWith) Condition(org.jooq.Condition) ArrayList(java.util.ArrayList) CompositeFuture(io.vertx.core.CompositeFuture) RecordType(org.folio.dao.util.RecordType) RecordsBatchResponse(org.folio.rest.jaxrs.model.RecordsBatchResponse) RecordState(org.folio.rest.jooq.enums.RecordState) RECORDS_LB(org.folio.rest.jooq.Tables.RECORDS_LB) Flowable(io.reactivex.Flowable) IdType(org.folio.dao.util.IdType) ExternalIdsHolder(org.folio.rest.jaxrs.model.ExternalIdsHolder) After(org.junit.After) AsyncResult(io.vertx.core.AsyncResult) Comparator.comparing(java.util.Comparator.comparing) AdditionalInfo(org.folio.rest.jaxrs.model.AdditionalInfo) SortOrder(org.jooq.SortOrder) TestMocks(org.folio.TestMocks) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Before(org.junit.Before) GenericCompositeFuture(org.folio.okapi.common.GenericCompositeFuture) RecordDao(org.folio.dao.RecordDao) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) Record(org.folio.rest.jaxrs.model.Record) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) Test(org.junit.Test) VertxUnitRunner(io.vertx.ext.unit.junit.VertxUnitRunner) SnapshotDaoUtil(org.folio.dao.util.SnapshotDaoUtil) UUID(java.util.UUID) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) Future(io.vertx.core.Future) ParsedRecordDto(org.folio.rest.jaxrs.model.ParsedRecordDto) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) List(java.util.List) OffsetDateTime(java.time.OffsetDateTime) ChronoUnit(java.time.temporal.ChronoUnit) State(org.folio.rest.jaxrs.model.Record.State) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) OrderField(org.jooq.OrderField) Assertions(org.junit.jupiter.api.Assertions) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) Async(io.vertx.ext.unit.Async) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) ArrayList(java.util.ArrayList) RawRecord(org.folio.rest.jaxrs.model.RawRecord) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Record(org.folio.rest.jaxrs.model.Record) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) OrderField(org.jooq.OrderField)

Example 3 with RecordDaoUtil

use of org.folio.dao.util.RecordDaoUtil in project mod-source-record-storage by folio-org.

the class RecordServiceTest method getMarcSourceRecordsBetweenDates.

private void getMarcSourceRecordsBetweenDates(TestContext context, Record.RecordType recordType, RecordType parsedRecordType, OffsetDateTime earliestDate, OffsetDateTime latestDate) {
    Async async = context.async();
    List<Record> records = TestMocks.getRecords();
    RecordCollection recordCollection = new RecordCollection().withRecords(records).withTotalRecords(records.size());
    saveRecords(recordCollection.getRecords()).onComplete(batch -> {
        if (batch.failed()) {
            context.fail(batch.cause());
        }
        Condition condition = RECORDS_LB.CREATED_DATE.between(earliestDate, latestDate);
        List<OrderField<?>> orderFields = new ArrayList<>();
        recordService.getSourceRecords(condition, parsedRecordType, orderFields, 0, 10, TENANT_ID).onComplete(get -> {
            if (get.failed()) {
                context.fail(get.cause());
            }
            List<SourceRecord> expected = records.stream().filter(r -> r.getRecordType().equals(recordType)).map(RecordDaoUtil::toSourceRecord).sorted(comparing(SourceRecord::getRecordId)).collect(Collectors.toList());
            get.result().getSourceRecords().sort(comparing(SourceRecord::getRecordId));
            context.assertEquals(expected.size(), get.result().getTotalRecords());
            compareSourceRecords(context, expected, get.result().getSourceRecords());
            async.complete();
        });
    });
}
Also used : Condition(org.jooq.Condition) TestContext(io.vertx.ext.unit.TestContext) Async(io.vertx.ext.unit.Async) DSL(org.jooq.impl.DSL) RecordDaoImpl(org.folio.dao.RecordDaoImpl) RawRecord(org.folio.rest.jaxrs.model.RawRecord) RunWith(org.junit.runner.RunWith) Condition(org.jooq.Condition) ArrayList(java.util.ArrayList) CompositeFuture(io.vertx.core.CompositeFuture) RecordType(org.folio.dao.util.RecordType) RecordsBatchResponse(org.folio.rest.jaxrs.model.RecordsBatchResponse) RecordState(org.folio.rest.jooq.enums.RecordState) RECORDS_LB(org.folio.rest.jooq.Tables.RECORDS_LB) Flowable(io.reactivex.Flowable) IdType(org.folio.dao.util.IdType) ExternalIdsHolder(org.folio.rest.jaxrs.model.ExternalIdsHolder) After(org.junit.After) AsyncResult(io.vertx.core.AsyncResult) Comparator.comparing(java.util.Comparator.comparing) AdditionalInfo(org.folio.rest.jaxrs.model.AdditionalInfo) SortOrder(org.jooq.SortOrder) TestMocks(org.folio.TestMocks) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Before(org.junit.Before) GenericCompositeFuture(org.folio.okapi.common.GenericCompositeFuture) RecordDao(org.folio.dao.RecordDao) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) Record(org.folio.rest.jaxrs.model.Record) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) Test(org.junit.Test) VertxUnitRunner(io.vertx.ext.unit.junit.VertxUnitRunner) SnapshotDaoUtil(org.folio.dao.util.SnapshotDaoUtil) UUID(java.util.UUID) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) Future(io.vertx.core.Future) ParsedRecordDto(org.folio.rest.jaxrs.model.ParsedRecordDto) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) List(java.util.List) OffsetDateTime(java.time.OffsetDateTime) ChronoUnit(java.time.temporal.ChronoUnit) State(org.folio.rest.jaxrs.model.Record.State) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) OrderField(org.jooq.OrderField) Assertions(org.junit.jupiter.api.Assertions) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) Async(io.vertx.ext.unit.Async) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) ArrayList(java.util.ArrayList) RawRecord(org.folio.rest.jaxrs.model.RawRecord) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Record(org.folio.rest.jaxrs.model.Record) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) OrderField(org.jooq.OrderField)

Example 4 with RecordDaoUtil

use of org.folio.dao.util.RecordDaoUtil in project mod-source-record-storage by folio-org.

the class RecordServiceTest method getMarcSourceRecordsByListOfIds.

private void getMarcSourceRecordsByListOfIds(TestContext context, Record.RecordType recordType, RecordType parsedRecordType) {
    Async async = context.async();
    List<Record> records = TestMocks.getRecords();
    RecordCollection recordCollection = new RecordCollection().withRecords(records).withTotalRecords(records.size());
    saveRecords(recordCollection.getRecords()).onComplete(batch -> {
        if (batch.failed()) {
            context.fail(batch.cause());
        }
        List<String> ids = records.stream().filter(r -> r.getRecordType().equals(recordType)).map(Record::getMatchedId).collect(Collectors.toList());
        recordService.getSourceRecords(ids, IdType.RECORD, parsedRecordType, false, TENANT_ID).onComplete(get -> {
            if (get.failed()) {
                context.fail(get.cause());
            }
            List<SourceRecord> expected = records.stream().filter(r -> r.getRecordType().equals(recordType)).map(RecordDaoUtil::toSourceRecord).sorted(comparing(SourceRecord::getRecordId)).collect(Collectors.toList());
            sortByRecordId(get);
            context.assertEquals(expected.size(), get.result().getTotalRecords());
            compareSourceRecords(context, expected, get.result().getSourceRecords());
            async.complete();
        });
    });
}
Also used : TestContext(io.vertx.ext.unit.TestContext) Async(io.vertx.ext.unit.Async) DSL(org.jooq.impl.DSL) RecordDaoImpl(org.folio.dao.RecordDaoImpl) RawRecord(org.folio.rest.jaxrs.model.RawRecord) RunWith(org.junit.runner.RunWith) Condition(org.jooq.Condition) ArrayList(java.util.ArrayList) CompositeFuture(io.vertx.core.CompositeFuture) RecordType(org.folio.dao.util.RecordType) RecordsBatchResponse(org.folio.rest.jaxrs.model.RecordsBatchResponse) RecordState(org.folio.rest.jooq.enums.RecordState) RECORDS_LB(org.folio.rest.jooq.Tables.RECORDS_LB) Flowable(io.reactivex.Flowable) IdType(org.folio.dao.util.IdType) ExternalIdsHolder(org.folio.rest.jaxrs.model.ExternalIdsHolder) After(org.junit.After) AsyncResult(io.vertx.core.AsyncResult) Comparator.comparing(java.util.Comparator.comparing) AdditionalInfo(org.folio.rest.jaxrs.model.AdditionalInfo) SortOrder(org.jooq.SortOrder) TestMocks(org.folio.TestMocks) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Before(org.junit.Before) GenericCompositeFuture(org.folio.okapi.common.GenericCompositeFuture) RecordDao(org.folio.dao.RecordDao) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) Record(org.folio.rest.jaxrs.model.Record) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) Test(org.junit.Test) VertxUnitRunner(io.vertx.ext.unit.junit.VertxUnitRunner) SnapshotDaoUtil(org.folio.dao.util.SnapshotDaoUtil) UUID(java.util.UUID) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) Future(io.vertx.core.Future) ParsedRecordDto(org.folio.rest.jaxrs.model.ParsedRecordDto) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) List(java.util.List) OffsetDateTime(java.time.OffsetDateTime) ChronoUnit(java.time.temporal.ChronoUnit) State(org.folio.rest.jaxrs.model.Record.State) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) OrderField(org.jooq.OrderField) Assertions(org.junit.jupiter.api.Assertions) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) Async(io.vertx.ext.unit.Async) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) RawRecord(org.folio.rest.jaxrs.model.RawRecord) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) Record(org.folio.rest.jaxrs.model.Record) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord)

Example 5 with RecordDaoUtil

use of org.folio.dao.util.RecordDaoUtil in project mod-source-record-storage by folio-org.

the class RecordDaoImpl method saveRecords.

@Override
public Future<RecordsBatchResponse> saveRecords(RecordCollection recordCollection, String tenantId) {
    Promise<RecordsBatchResponse> promise = Promise.promise();
    Set<UUID> matchedIds = new HashSet<>();
    Set<String> snapshotIds = new HashSet<>();
    Set<String> recordTypes = new HashSet<>();
    List<RecordsLbRecord> dbRecords = new ArrayList<>();
    List<RawRecordsLbRecord> dbRawRecords = new ArrayList<>();
    List<Record2<UUID, JSONB>> dbParsedRecords = new ArrayList<>();
    List<ErrorRecordsLbRecord> dbErrorRecords = new ArrayList<>();
    List<String> errorMessages = new ArrayList<>();
    recordCollection.getRecords().stream().map(RecordDaoUtil::ensureRecordHasId).map(RecordDaoUtil::ensureRecordHasSuppressDiscovery).map(RecordDaoUtil::ensureRecordForeignKeys).forEach(record -> {
        // collect unique matched ids to query to determine generation
        matchedIds.add(UUID.fromString(record.getMatchedId()));
        // make sure only one snapshot id
        snapshotIds.add(record.getSnapshotId());
        if (snapshotIds.size() > 1) {
            throw new BadRequestException("Batch record collection only supports single snapshot");
        }
        if (Objects.nonNull(record.getRecordType())) {
            recordTypes.add(record.getRecordType().name());
        } else {
            throw new BadRequestException(StringUtils.defaultIfEmpty(record.getErrorRecord().getDescription(), String.format("Record with id %s has not record type", record.getId())));
        }
        // make sure only one record type
        if (recordTypes.size() > 1) {
            throw new BadRequestException("Batch record collection only supports single record type");
        }
        // if record has parsed record, validate by attempting format
        if (Objects.nonNull(record.getParsedRecord())) {
            try {
                RecordType recordType = toRecordType(record.getRecordType().name());
                recordType.formatRecord(record);
                Record2<UUID, JSONB> dbParsedRecord = recordType.toDatabaseRecord2(record.getParsedRecord());
                dbParsedRecords.add(dbParsedRecord);
            } catch (Exception e) {
                // create error record and remove from record
                Object content = Objects.nonNull(record.getParsedRecord()) ? record.getParsedRecord().getContent() : null;
                ErrorRecord errorRecord = new ErrorRecord().withId(record.getId()).withDescription(e.getMessage()).withContent(content);
                errorMessages.add(format(INVALID_PARSED_RECORD_MESSAGE_TEMPLATE, record.getId(), e.getMessage()));
                record.withErrorRecord(errorRecord).withParsedRecord(null).withLeaderRecordStatus(null);
            }
        }
        if (Objects.nonNull(record.getRawRecord())) {
            dbRawRecords.add(RawRecordDaoUtil.toDatabaseRawRecord(record.getRawRecord()));
        }
        if (Objects.nonNull(record.getErrorRecord())) {
            dbErrorRecords.add(ErrorRecordDaoUtil.toDatabaseErrorRecord(record.getErrorRecord()));
        }
        dbRecords.add(RecordDaoUtil.toDatabaseRecord(record));
    });
    UUID snapshotId = UUID.fromString(snapshotIds.stream().findFirst().orElseThrow());
    RecordType recordType = toRecordType(recordTypes.stream().findFirst().orElseThrow());
    try (Connection connection = getConnection(tenantId)) {
        DSL.using(connection).transaction(ctx -> {
            DSLContext dsl = DSL.using(ctx);
            // validate snapshot
            Optional<SnapshotsLbRecord> snapshot = DSL.using(ctx).selectFrom(SNAPSHOTS_LB).where(SNAPSHOTS_LB.ID.eq(snapshotId)).fetchOptional();
            if (snapshot.isPresent()) {
                if (Objects.isNull(snapshot.get().getProcessingStartedDate())) {
                    throw new BadRequestException(format(SNAPSHOT_NOT_STARTED_MESSAGE_TEMPLATE, snapshot.get().getStatus()));
                }
            } else {
                throw new NotFoundException(format(SNAPSHOT_NOT_FOUND_TEMPLATE, snapshotId));
            }
            List<UUID> ids = new ArrayList<>();
            Map<UUID, Integer> matchedGenerations = new HashMap<>();
            // lookup latest generation by matched id and committed snapshot updated before current snapshot
            dsl.select(RECORDS_LB.MATCHED_ID, RECORDS_LB.ID, RECORDS_LB.GENERATION).distinctOn(RECORDS_LB.MATCHED_ID).from(RECORDS_LB).innerJoin(SNAPSHOTS_LB).on(RECORDS_LB.SNAPSHOT_ID.eq(SNAPSHOTS_LB.ID)).where(RECORDS_LB.MATCHED_ID.in(matchedIds).and(SNAPSHOTS_LB.STATUS.in(JobExecutionStatus.COMMITTED, JobExecutionStatus.ERROR)).and(SNAPSHOTS_LB.UPDATED_DATE.lessThan(dsl.select(SNAPSHOTS_LB.PROCESSING_STARTED_DATE).from(SNAPSHOTS_LB).where(SNAPSHOTS_LB.ID.eq(snapshotId))))).orderBy(RECORDS_LB.MATCHED_ID.asc(), RECORDS_LB.GENERATION.desc()).fetchStream().forEach(r -> {
                UUID id = r.get(RECORDS_LB.ID);
                UUID matchedId = r.get(RECORDS_LB.MATCHED_ID);
                int generation = r.get(RECORDS_LB.GENERATION);
                ids.add(id);
                matchedGenerations.put(matchedId, generation);
            });
            // update matching records state
            dsl.update(RECORDS_LB).set(RECORDS_LB.STATE, RecordState.OLD).where(RECORDS_LB.ID.in(ids)).execute();
            // batch insert records updating generation if required
            List<LoaderError> recordsLoadingErrors = dsl.loadInto(RECORDS_LB).batchAfter(1000).bulkAfter(500).commitAfter(1000).onErrorAbort().loadRecords(dbRecords.stream().map(record -> {
                Integer generation = matchedGenerations.get(record.getMatchedId());
                if (Objects.nonNull(generation)) {
                    record.setGeneration(generation + 1);
                } else if (Objects.isNull(record.getGeneration())) {
                    record.setGeneration(0);
                }
                return record;
            }).collect(Collectors.toList())).fieldsFromSource().execute().errors();
            recordsLoadingErrors.forEach(error -> {
                if (error.exception().sqlState().equals(UNIQUE_VIOLATION_SQL_STATE)) {
                    throw new DuplicateEventException("SQL Unique constraint violation prevented repeatedly saving the record");
                }
                LOG.warn("Error occurred on batch execution: {}", error.exception().getCause().getMessage());
                LOG.debug("Failed to execute statement from batch: {}", error.query());
            });
            // batch insert raw records
            dsl.loadInto(RAW_RECORDS_LB).batchAfter(250).commitAfter(1000).onDuplicateKeyUpdate().onErrorAbort().loadRecords(dbRawRecords).fieldsFromSource().execute();
            // batch insert parsed records
            recordType.toLoaderOptionsStep(dsl).batchAfter(250).commitAfter(1000).onDuplicateKeyUpdate().onErrorAbort().loadRecords(dbParsedRecords).fieldsFromSource().execute();
            if (!dbErrorRecords.isEmpty()) {
                // batch insert error records
                dsl.loadInto(ERROR_RECORDS_LB).batchAfter(250).commitAfter(1000).onDuplicateKeyUpdate().onErrorAbort().loadRecords(dbErrorRecords).fieldsFromSource().execute();
            }
            promise.complete(new RecordsBatchResponse().withRecords(recordCollection.getRecords()).withTotalRecords(recordCollection.getRecords().size()).withErrorMessages(errorMessages));
        });
    } catch (DuplicateEventException e) {
        LOG.info("Skipped saving records due to duplicate event: {}", e.getMessage());
        promise.fail(e);
    } catch (SQLException | DataAccessException e) {
        LOG.error("Failed to save records", e);
        promise.fail(e);
    }
    return promise.future();
}
Also used : DSL(org.jooq.impl.DSL) RecordsLbRecord(org.folio.rest.jooq.tables.records.RecordsLbRecord) MarcBibCollection(org.folio.rest.jaxrs.model.MarcBibCollection) DSL.field(org.jooq.impl.DSL.field) Autowired(org.springframework.beans.factory.annotation.Autowired) RecordSearchParameters(org.folio.services.RecordSearchParameters) DSL.condition(org.jooq.impl.DSL.condition) StringUtils(org.apache.commons.lang3.StringUtils) SnapshotsLbRecord(org.folio.rest.jooq.tables.records.SnapshotsLbRecord) Condition(org.jooq.Condition) DSL.trueCondition(org.jooq.impl.DSL.trueCondition) RecordsBatchResponse(org.folio.rest.jaxrs.model.RecordsBatchResponse) RawRecordsLbRecord(org.folio.rest.jooq.tables.records.RawRecordsLbRecord) Record2(org.jooq.Record2) Map(java.util.Map) ErrorRecordsLbRecord(org.folio.rest.jooq.tables.records.ErrorRecordsLbRecord) Metadata(org.folio.rest.jaxrs.model.Metadata) ZoneOffset(java.time.ZoneOffset) RAW_RECORDS_LB(org.folio.rest.jooq.Tables.RAW_RECORDS_LB) GenericCompositeFuture(org.folio.okapi.common.GenericCompositeFuture) Set(java.util.Set) SnapshotDaoUtil(org.folio.dao.util.SnapshotDaoUtil) RecordCollection(org.folio.rest.jaxrs.model.RecordCollection) QueryResult(io.github.jklingsporn.vertx.jooq.shared.internal.QueryResult) Logger(org.apache.logging.log4j.Logger) StrSubstitutor(org.apache.commons.lang.text.StrSubstitutor) ParsedRecord(org.folio.rest.jaxrs.model.ParsedRecord) SelectJoinStep(org.jooq.SelectJoinStep) PARSED_RECORD_CONTENT(org.folio.dao.util.ParsedRecordDaoUtil.PARSED_RECORD_CONTENT) MARC_BIB(org.folio.rest.jooq.enums.RecordType.MARC_BIB) ArrayList(java.util.ArrayList) RecordState(org.folio.rest.jooq.enums.RecordState) RECORDS_LB(org.folio.rest.jooq.Tables.RECORDS_LB) UpdateSetFirstStep(org.jooq.UpdateSetFirstStep) SQLException(java.sql.SQLException) RAW_RECORD_CONTENT(org.folio.dao.util.RawRecordDaoUtil.RAW_RECORD_CONTENT) Lists(com.google.common.collect.Lists) Flowable(io.reactivex.Flowable) JobExecutionStatus(org.folio.rest.jooq.enums.JobExecutionStatus) ParsedRecordsBatchResponse(org.folio.rest.jaxrs.model.ParsedRecordsBatchResponse) LoaderError(org.jooq.LoaderError) ParseFieldsResult(org.folio.services.util.parser.ParseFieldsResult) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord) UpdateSetMoreStep(org.jooq.UpdateSetMoreStep) Row(io.vertx.sqlclient.Row) RecordDaoUtil.getExternalHrid(org.folio.dao.util.RecordDaoUtil.getExternalHrid) DSL.table(org.jooq.impl.DSL.table) Connection(java.sql.Connection) Table(org.jooq.Table) IdType(org.folio.dao.util.IdType) UpdateConditionStep(org.jooq.UpdateConditionStep) DSLContext(org.jooq.DSLContext) BadRequestException(javax.ws.rs.BadRequestException) SNAPSHOT_NOT_STARTED_MESSAGE_TEMPLATE(org.folio.dao.util.SnapshotDaoUtil.SNAPSHOT_NOT_STARTED_MESSAGE_TEMPLATE) SortOrder(org.jooq.SortOrder) RecordDaoUtil.filterRecordByType(org.folio.dao.util.RecordDaoUtil.filterRecordByType) DSL.name(org.jooq.impl.DSL.name) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) Name(org.jooq.Name) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) Collection(java.util.Collection) RecordDaoUtil.filterRecordByState(org.folio.dao.util.RecordDaoUtil.filterRecordByState) Field(org.jooq.Field) UUID(java.util.UUID) ERROR_RECORDS_LB(org.folio.rest.jooq.Tables.ERROR_RECORDS_LB) Future(io.vertx.core.Future) Collectors(java.util.stream.Collectors) RecordDaoUtil.getExternalId(org.folio.dao.util.RecordDaoUtil.getExternalId) NotFoundException(javax.ws.rs.NotFoundException) String.format(java.lang.String.format) Objects(java.util.Objects) List(java.util.List) Optional(java.util.Optional) ErrorRecordDaoUtil(org.folio.dao.util.ErrorRecordDaoUtil) MatchField(org.folio.dao.util.MatchField) RECORD_NOT_FOUND_TEMPLATE(org.folio.dao.util.RecordDaoUtil.RECORD_NOT_FOUND_TEMPLATE) QueryParamUtil.toRecordType(org.folio.rest.util.QueryParamUtil.toRecordType) RawRecord(org.folio.rest.jaxrs.model.RawRecord) SNAPSHOT_NOT_FOUND_TEMPLATE(org.folio.dao.util.SnapshotDaoUtil.SNAPSHOT_NOT_FOUND_TEMPLATE) SNAPSHOTS_LB(org.folio.rest.jooq.Tables.SNAPSHOTS_LB) HashMap(java.util.HashMap) RawRecordDaoUtil(org.folio.dao.util.RawRecordDaoUtil) ParamType(org.jooq.conf.ParamType) Function(java.util.function.Function) HashSet(java.util.HashSet) RecordType(org.folio.dao.util.RecordType) ExternalIdsHolder(org.folio.rest.jaxrs.model.ExternalIdsHolder) DSL.countDistinct(org.jooq.impl.DSL.countDistinct) AdditionalInfo(org.folio.rest.jaxrs.model.AdditionalInfo) SourceRecord(org.folio.rest.jaxrs.model.SourceRecord) DataAccessException(org.jooq.exception.DataAccessException) PgPool(io.vertx.reactivex.pgclient.PgPool) Record(org.folio.rest.jaxrs.model.Record) Promise(io.vertx.core.Promise) ParseLeaderResult(org.folio.services.util.parser.ParseLeaderResult) ReactiveClassicGenericQueryExecutor(io.github.jklingsporn.vertx.jooq.classic.reactivepg.ReactiveClassicGenericQueryExecutor) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) Component(org.springframework.stereotype.Component) ERROR_RECORD_CONTENT(org.folio.dao.util.ErrorRecordDaoUtil.ERROR_RECORD_CONTENT) TypeConnection(org.folio.services.util.TypeConnection) DSL.max(org.jooq.impl.DSL.max) OrderField(org.jooq.OrderField) JSONB(org.jooq.JSONB) LogManager(org.apache.logging.log4j.LogManager) SourceRecordCollection(org.folio.rest.jaxrs.model.SourceRecordCollection) ArrayUtils(org.apache.commons.lang.ArrayUtils) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) HashMap(java.util.HashMap) SQLException(java.sql.SQLException) ArrayList(java.util.ArrayList) NotFoundException(javax.ws.rs.NotFoundException) RecordsLbRecord(org.folio.rest.jooq.tables.records.RecordsLbRecord) RawRecordsLbRecord(org.folio.rest.jooq.tables.records.RawRecordsLbRecord) ErrorRecordsLbRecord(org.folio.rest.jooq.tables.records.ErrorRecordsLbRecord) QueryParamUtil.toRecordType(org.folio.rest.util.QueryParamUtil.toRecordType) RecordType(org.folio.dao.util.RecordType) LoaderError(org.jooq.LoaderError) UUID(java.util.UUID) Record2(org.jooq.Record2) SnapshotsLbRecord(org.folio.rest.jooq.tables.records.SnapshotsLbRecord) DataAccessException(org.jooq.exception.DataAccessException) HashSet(java.util.HashSet) JSONB(org.jooq.JSONB) Connection(java.sql.Connection) TypeConnection(org.folio.services.util.TypeConnection) DSLContext(org.jooq.DSLContext) RawRecordsLbRecord(org.folio.rest.jooq.tables.records.RawRecordsLbRecord) SQLException(java.sql.SQLException) BadRequestException(javax.ws.rs.BadRequestException) DuplicateEventException(org.folio.kafka.exception.DuplicateEventException) NotFoundException(javax.ws.rs.NotFoundException) DataAccessException(org.jooq.exception.DataAccessException) RecordsBatchResponse(org.folio.rest.jaxrs.model.RecordsBatchResponse) ParsedRecordsBatchResponse(org.folio.rest.jaxrs.model.ParsedRecordsBatchResponse) ErrorRecordsLbRecord(org.folio.rest.jooq.tables.records.ErrorRecordsLbRecord) BadRequestException(javax.ws.rs.BadRequestException) RecordDaoUtil(org.folio.dao.util.RecordDaoUtil) ErrorRecordDaoUtil(org.folio.dao.util.ErrorRecordDaoUtil) RawRecordDaoUtil(org.folio.dao.util.RawRecordDaoUtil) ParsedRecordDaoUtil(org.folio.dao.util.ParsedRecordDaoUtil) ErrorRecord(org.folio.rest.jaxrs.model.ErrorRecord)

Aggregations

Flowable (io.reactivex.Flowable)5 Future (io.vertx.core.Future)5 ArrayList (java.util.ArrayList)5 List (java.util.List)5 Objects (java.util.Objects)5 UUID (java.util.UUID)5 Collectors (java.util.stream.Collectors)5 IdType (org.folio.dao.util.IdType)5 ParsedRecordDaoUtil (org.folio.dao.util.ParsedRecordDaoUtil)5 RecordDaoUtil (org.folio.dao.util.RecordDaoUtil)5 RecordType (org.folio.dao.util.RecordType)5 SnapshotDaoUtil (org.folio.dao.util.SnapshotDaoUtil)5 DuplicateEventException (org.folio.kafka.exception.DuplicateEventException)5 GenericCompositeFuture (org.folio.okapi.common.GenericCompositeFuture)5 AdditionalInfo (org.folio.rest.jaxrs.model.AdditionalInfo)5 ErrorRecord (org.folio.rest.jaxrs.model.ErrorRecord)5 ExternalIdsHolder (org.folio.rest.jaxrs.model.ExternalIdsHolder)5 ParsedRecord (org.folio.rest.jaxrs.model.ParsedRecord)5 RawRecord (org.folio.rest.jaxrs.model.RawRecord)5 Record (org.folio.rest.jaxrs.model.Record)5