Skip to content

Commit

Permalink
MODEXPW-470 - .mrc-file creation (#540)
Browse files Browse the repository at this point in the history
* MODEXPW-470 Added mrc
  • Loading branch information
obozhko-folio authored May 29, 2024
1 parent fb91db8 commit fa67100
Show file tree
Hide file tree
Showing 21 changed files with 481 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import static org.folio.dew.domain.dto.JobParameterNames.E_HOLDINGS_FILE_NAME;
import static org.folio.dew.domain.dto.JobParameterNames.OUTPUT_FILES_IN_STORAGE;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_LOCAL_FILE_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_LOCAL_MARC_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_OUTPUT_FILE_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_OUTPUT_MARC_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TOTAL_RECORDS;
import static org.folio.dew.domain.dto.JobParameterNames.UPDATED_FILE_NAME;
import static org.folio.dew.utils.BulkEditProcessorHelper.convertToDate;
Expand Down Expand Up @@ -104,12 +106,11 @@ private void processJobUpdate(JobExecution jobExecution, boolean after) {
if (after) {
if (isBulkEditIdentifiersJob(jobExecution)) {
moveTemporaryFilesToStorage(jobParameters);
handleProcessingErrors(jobExecution, jobId);
handleProcessingMatchedRecordsAndErrors(jobExecution, jobId);
handleProcessingMarcFile(jobExecution);
}
if (isBulkEditUpdateJob(jobExecution)) {
handleProcessingChangedRecords(jobExecution);
}
if (isBulkEditUpdateJob(jobExecution)) {
String downloadErrorLink = bulkEditProcessingErrorsService.saveErrorFileAndGetDownloadLink(jobId, jobExecution);
var isChangedRecordsLinkPresent = jobExecution.getExecutionContext().containsKey(OUTPUT_FILES_IN_STORAGE);
jobExecution.getExecutionContext().putString(OUTPUT_FILES_IN_STORAGE,
Expand Down Expand Up @@ -184,6 +185,13 @@ private void moveTemporaryFilesToStorage(JobParameters jobParameters) throws IOE
moveFileToStorage(jobParameters.getString(TEMP_OUTPUT_FILE_PATH), tmpFileName);
moveFileToStorage(jobParameters.getString(TEMP_OUTPUT_FILE_PATH) + ".json", tmpFileName + ".json");
}
var tmpMarcName = jobParameters.getString(TEMP_LOCAL_MARC_PATH);
if (nonNull(tmpMarcName)) {
tmpMarcName += ".mrc";
if (Files.exists(Path.of(tmpMarcName))) {
moveFileToStorage(jobParameters.getString(TEMP_OUTPUT_MARC_PATH) + ".mrc", tmpMarcName);
}
}

var tmpIdentifiersFileName = jobParameters.getString(TEMP_IDENTIFIERS_FILE_NAME);
if (nonNull(tmpIdentifiersFileName) && Files.deleteIfExists(Path.of(tmpIdentifiersFileName))) {
Expand All @@ -201,11 +209,15 @@ private void moveFileToStorage(String destFileName, String sourceFileName) throw
}
}

private void handleProcessingErrors(JobExecution jobExecution, String jobId) {
private void handleProcessingMatchedRecordsAndErrors(JobExecution jobExecution, String jobId) {
String downloadErrorLink = bulkEditProcessingErrorsService.saveErrorFileAndGetDownloadLink(jobId, jobExecution);
jobExecution.getExecutionContext().putString(OUTPUT_FILES_IN_STORAGE, saveResult(jobExecution, false) + PATHS_DELIMITER + (isNull(downloadErrorLink) ? EMPTY : downloadErrorLink) + PATHS_DELIMITER + saveJsonResult(jobExecution, !isBulkEditUpdateJob(jobExecution)));
}

private void handleProcessingMarcFile(JobExecution jobExecution) {
jobExecution.getExecutionContext().putString(OUTPUT_FILES_IN_STORAGE, jobExecution.getExecutionContext().getString(OUTPUT_FILES_IN_STORAGE) + PATHS_DELIMITER + saveMarcResult(jobExecution, false));
}

private void handleProcessingChangedRecords(JobExecution jobExecution) {
jobExecution.getExecutionContext().putString(OUTPUT_FILES_IN_STORAGE, saveResult(jobExecution, !isBulkEditUpdateJob(jobExecution)));
}
Expand Down Expand Up @@ -244,7 +256,7 @@ private Job createJobExecutionUpdate(String jobId, JobExecution jobExecution) {

var outputFilesInStorage = getFromJobExecutionContext(jobExecution, OUTPUT_FILES_IN_STORAGE);
if (StringUtils.isNotBlank(outputFilesInStorage)) {
result.setFiles(Arrays.asList(outputFilesInStorage.split(PATHS_DELIMITER)));
result.setFiles(Arrays.asList(outputFilesInStorage.split(PATHS_DELIMITER, 4)));
}

var jobName = jobExecution.getJobInstance().getJobName();
Expand Down Expand Up @@ -355,6 +367,20 @@ private String saveJsonResult(JobExecution jobExecution, boolean isSourceToBeDel
}
}

private String saveMarcResult(JobExecution jobExecution, boolean isSourceToBeDeleted) {
var path = jobExecution.getJobParameters().getString(TEMP_OUTPUT_MARC_PATH) + ".mrc";
try {
if (localFilesStorage.notExists(path)) {
log.info("No MARC records found.");
return EMPTY; // To prevent downloading empty file.
}
return remoteFilesStorage.objectToPresignedObjectUrl(
remoteFilesStorage.uploadObject(prepareMrcObject(jobExecution, path), path, null, "text/plain", isSourceToBeDeleted));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}

private String preparePath(JobExecution jobExecution) {
if (isBulkEditContentUpdateJob(jobExecution)) {
return jobExecution.getJobParameters().getString(UPDATED_FILE_NAME);
Expand Down Expand Up @@ -387,6 +413,10 @@ private String prepareJsonObject(JobExecution jobExecution, String path) {
return jobExecution.getJobParameters().getString(JobParameterNames.JOB_ID) + PATH_SEPARATOR + FilenameUtils.getName(path) + (!isBulkEditUpdateJob(jobExecution) ? ".json" : EMPTY);
}

private String prepareMrcObject(JobExecution jobExecution, String path) {
return jobExecution.getJobParameters().getString(JobParameterNames.JOB_ID) + PATH_SEPARATOR + FilenameUtils.getName(path);
}

private String prepareDownloadFilename(JobExecution jobExecution, String path) {
if (isBulkEditIdentifiersJob(jobExecution)) {
return null;
Expand Down
74 changes: 74 additions & 0 deletions src/main/java/org/folio/dew/batch/MarcAsListStringsWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.folio.dew.batch;

import lombok.extern.log4j.Log4j2;
import org.folio.dew.client.SrsClient;
import org.folio.dew.domain.dto.Formatable;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.Chunk;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Objects;

import static java.util.Objects.nonNull;

@Log4j2
@StepScope
public class MarcAsListStringsWriter<T, U extends Formatable<T>> extends FlatFileItemWriter<List<U>> {

private SrsClient srsClient;
private MarcAsStringWriter<String> delegateToStringWriter;

public MarcAsListStringsWriter(String outputFileName, SrsClient srsClient) {
super();
this.srsClient = srsClient;
delegateToStringWriter = new MarcAsStringWriter<>(outputFileName);
}

@Override
public void write(Chunk<? extends List<U>> items) throws Exception {
delegateToStringWriter.write(new Chunk<>(items.getItems().stream().flatMap(List::stream).filter(itm -> itm.isInstanceFormat() && itm.isSourceMarc()).map(marc -> getMarcContent(marc.getId()))
.filter(Objects::nonNull).toList()));
}

@Override
public void afterPropertiesSet() {
Assert.notNull(delegateToStringWriter, "Delegate was not set");
}

@Override
public void open(ExecutionContext executionContext) {
if (nonNull(delegateToStringWriter)) {
((ItemStream) delegateToStringWriter).open(executionContext);
}
}

@Override
public void update(ExecutionContext executionContext) {
if (nonNull(delegateToStringWriter)) {
((ItemStream) delegateToStringWriter).update(executionContext);
}
}

@Override
public void close() {
if (nonNull(delegateToStringWriter)) {
((ItemStream) delegateToStringWriter).close();
}
}

private String getMarcContent(String id) {
var srsRecords = srsClient.getMarc(id, "INSTANCE");
if (srsRecords.getSourceRecords().isEmpty()) {
log.warn("No SRS records found by instanceId = {}", id);
return null;
}
var recordId = srsRecords.getSourceRecords().get(0).getRecordId();
var marcRecord = srsClient.getMarcContent(recordId);
log.info("MARC record found by recordId = {}", recordId);
return marcRecord.getRawRecord().getContent();
}
}
21 changes: 21 additions & 0 deletions src/main/java/org/folio/dew/batch/MarcAsStringWriter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.folio.dew.batch;

import lombok.extern.log4j.Log4j2;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
import org.springframework.core.io.FileSystemResource;

import static org.apache.commons.lang3.StringUtils.EMPTY;

@Log4j2
public class MarcAsStringWriter<T> extends FlatFileItemWriter<T> {

public MarcAsStringWriter(String outputFileName) {
super();
setResource(new FileSystemResource(outputFileName + ".mrc"));
setLineSeparator(EMPTY);
setLineAggregator(new PassThroughLineAggregator<>());
setShouldDeleteIfEmpty(true);
setName("marcItemWriter");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

import lombok.RequiredArgsConstructor;
import org.folio.dew.batch.CsvListFileWriter;
import org.folio.dew.batch.JsonListFileWriter;
import org.folio.dew.batch.JobCompletionNotificationListener;
import org.folio.dew.batch.JsonListFileWriter;
import org.folio.dew.batch.MarcAsListStringsWriter;
import org.folio.dew.batch.bulkedit.jobs.BulkEditInstanceProcessor;
import org.folio.dew.client.SrsClient;
import org.folio.dew.domain.dto.ExportType;
import org.folio.dew.domain.dto.ItemIdentifier;
import org.folio.dew.domain.dto.InstanceFormat;
import org.folio.dew.domain.dto.ItemIdentifier;
import org.folio.dew.error.BulkEditException;
import org.folio.dew.error.BulkEditSkipListener;
import org.springframework.batch.core.Job;
Expand All @@ -31,6 +33,7 @@

import static org.folio.dew.domain.dto.EntityType.INSTANCE;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_LOCAL_FILE_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_LOCAL_MARC_PATH;
import static org.folio.dew.utils.Constants.CHUNKS;
import static org.folio.dew.utils.Constants.JOB_NAME_POSTFIX_SEPARATOR;

Expand All @@ -39,6 +42,7 @@
public class BulkEditInstanceIdentifiersJobConfig {
private final BulkEditInstanceProcessor bulkEditInstanceProcessor;
private final BulkEditSkipListener bulkEditSkipListener;
private final SrsClient srsClient;

@Bean
public Job bulkEditProcessInstanceIdentifiersJob(JobCompletionNotificationListener listener, Step bulkEditInstanceStep,
Expand Down Expand Up @@ -72,10 +76,11 @@ public Step bulkEditInstanceStep(FlatFileItemReader<ItemIdentifier> csvItemIdent

@Bean
@StepScope
public CompositeItemWriter<List<InstanceFormat>> compositeInstanceListWriter(@Value("#{jobParameters['" + TEMP_LOCAL_FILE_PATH + "']}") String outputFileName) {
public CompositeItemWriter<List<InstanceFormat>> compositeInstanceListWriter(@Value("#{jobParameters['" + TEMP_LOCAL_FILE_PATH + "']}") String outputFileName,
@Value("#{jobParameters['" + TEMP_LOCAL_MARC_PATH + "']}") String outputMarcName) {
var writer = new CompositeItemWriter<List<InstanceFormat>>();
writer.setDelegates(Arrays.asList(new CsvListFileWriter<>(outputFileName, InstanceFormat.getInstanceColumnHeaders(), InstanceFormat.getInstanceFieldsArray(), (field, i) -> field),
new JsonListFileWriter<>(new FileSystemResource(outputFileName + ".json"))));
new JsonListFileWriter<>(new FileSystemResource(outputFileName + ".json")), new MarcAsListStringsWriter<>(outputMarcName, srsClient)));
return writer;
}
}
19 changes: 19 additions & 0 deletions src/main/java/org/folio/dew/client/SrsClient.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.folio.dew.client;

import org.folio.dew.config.feign.FeignClientConfiguration;
import org.folio.dew.domain.dto.MarcRecord;
import org.folio.dew.domain.dto.SrsRecordCollection;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(name = "source-storage", configuration = FeignClientConfiguration.class)
public interface SrsClient {

@GetMapping(value = "/source-records")
SrsRecordCollection getMarc(@RequestParam("instanceId") String instanceId, @RequestParam("idType") String idType);

@GetMapping(value = "/records/{srsId}")
MarcRecord getMarcContent(@PathVariable String srsId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import static org.folio.dew.domain.dto.JobParameterNames.QUERY;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_LOCAL_FILE_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_OUTPUT_FILE_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_OUTPUT_MARC_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.TEMP_LOCAL_MARC_PATH;
import static org.folio.dew.domain.dto.JobParameterNames.UPDATED_FILE_NAME;
import static org.folio.dew.utils.BulkEditProcessorHelper.getMatchPattern;
import static org.folio.dew.utils.BulkEditProcessorHelper.resolveIdentifier;
Expand All @@ -24,6 +26,7 @@
import static org.folio.dew.utils.Constants.FILE_UPLOAD_ERROR;
import static org.folio.dew.utils.Constants.IDENTIFIER_TYPE;
import static org.folio.dew.utils.Constants.INITIAL_PREFIX;
import static org.folio.dew.utils.Constants.MARC_RECORDS;
import static org.folio.dew.utils.Constants.MATCHED_RECORDS;
import static org.folio.dew.utils.Constants.PATH_SEPARATOR;
import static org.folio.dew.utils.Constants.TEMP_IDENTIFIERS_FILE_NAME;
Expand Down Expand Up @@ -421,6 +424,9 @@ private void prepareJobParameters(JobCommand jobCommand, String uploadedPath, St
paramsBuilder.addString(TEMP_OUTPUT_FILE_PATH, workDir + fileName, JOB_PARAMETER_DEFAULT_IDENTIFYING_VALUE);
paramsBuilder.addString(TEMP_LOCAL_FILE_PATH, getTempDirWithSeparatorSuffix() + springApplicationName + PATH_SEPARATOR + fileName, JOB_PARAMETER_DEFAULT_IDENTIFYING_VALUE);
paramsBuilder.addString(EXPORT_TYPE, jobCommand.getExportType().getValue(), JOB_PARAMETER_DEFAULT_IDENTIFYING_VALUE);
var marcFileName = jobCommand.getId() + PATH_SEPARATOR + (isBulkEditUpdate(jobCommand) ? EMPTY : LocalDate.now() + MARC_RECORDS) + FilenameUtils.getBaseName(uploadedPath);
paramsBuilder.addString(TEMP_OUTPUT_MARC_PATH, workDir + marcFileName);
paramsBuilder.addString(TEMP_LOCAL_MARC_PATH, getTempDirWithSeparatorSuffix() + springApplicationName + PATH_SEPARATOR + marcFileName, JOB_PARAMETER_DEFAULT_IDENTIFYING_VALUE);
ofNullable(jobCommand.getIdentifierType()).ifPresent(type ->
paramsBuilder.addString("identifierType", type.getValue(), JOB_PARAMETER_DEFAULT_IDENTIFYING_VALUE));
ofNullable(jobCommand.getEntityType()).ifPresent(type ->
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/folio/dew/domain/dto/Formatable.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@

public interface Formatable<T> {
T getOriginal();
boolean isInstanceFormat();
boolean isSourceMarc();
String getId();
}
10 changes: 10 additions & 0 deletions src/main/java/org/folio/dew/domain/dto/HoldingsFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,14 @@ public String getIdentifier(String identifierType) {
return id;
}
}

@Override
public boolean isInstanceFormat() {
return false;
}

@Override
public boolean isSourceMarc() {
return source.equals("MARC");
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/folio/dew/domain/dto/InstanceFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,13 @@ public String getIdentifier(String identifierType) {
}
}

@Override
public boolean isInstanceFormat() {
return true;
}

@Override
public boolean isSourceMarc() {
return source.equals("MARC");
}
}
10 changes: 10 additions & 0 deletions src/main/java/org/folio/dew/domain/dto/ItemFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -253,5 +253,15 @@ public String getIdentifier(String identifierType) {
return id;
}
}

@Override
public boolean isInstanceFormat() {
return false;
}

@Override
public boolean isSourceMarc() {
return false;
}
}

2 changes: 2 additions & 0 deletions src/main/java/org/folio/dew/domain/dto/JobParameterNames.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ public class JobParameterNames {
public static final String AUTHORITY_CONTROL_FILE_NAME = "authorityControlFileName";
public static final String QUERY = "query";
public static final String EDIFACT_ORDERS_EXPORT = "edifactOrdersExport";
public static final String TEMP_OUTPUT_MARC_PATH = "tempOutputMarcPath";
public static final String TEMP_LOCAL_MARC_PATH = "tempLocalMarcPath";

}
10 changes: 10 additions & 0 deletions src/main/java/org/folio/dew/domain/dto/UserFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,15 @@ public String getIdentifier(String identifierType) {
return id;
}
}

@Override
public boolean isInstanceFormat() {
return false;
}

@Override
public boolean isSourceMarc() {
return false;
}
}

1 change: 1 addition & 0 deletions src/main/java/org/folio/dew/utils/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class Constants {
public static final String JOB_ID_SEPARATOR = "_";
public static final String JOB_NAME_POSTFIX_SEPARATOR = "-";
public static final String MATCHED_RECORDS = "-Matched-Records-";
public static final String MARC_RECORDS = "-Marc-Records-";
public static final String CHANGED_RECORDS = "-Changed-Records-";
public static final String UPDATED_PREFIX = "UPDATED-";
public static final String PREVIEW_PREFIX = "PREVIEW-";
Expand Down
8 changes: 8 additions & 0 deletions src/main/resources/swagger.api/bulk-edit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,14 @@ components:
$ref: '../../../../folio-export-common/schemas/inventory/identifierTypeReferenceCollection.json#/IdentifierTypeReferenceCollection'
InstanceNoteType:
$ref: '../../../../folio-export-common/schemas/inventory/instanceNoteType.json#/InstanceNoteType'
RawRecord:
$ref: '../../../../folio-export-common/schemas/srs/rawRecord.json#/RawRecord'
MarcRecord:
$ref: '../../../../folio-export-common/schemas/srs/marcRecord.json#/MarcRecord'
SrsRecord:
$ref: '../../../../folio-export-common/schemas/srs/srsRecord.json#/SrsRecord'
SrsRecordCollection:
$ref: '../../../../folio-export-common/schemas/srs/srsRecordCollection.json#/SrsRecordCollection'
examples:
errors:
value:
Expand Down
Loading

0 comments on commit fa67100

Please sign in to comment.