diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdiExportJobCompletionListener.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdiExportJobCompletionListener.java index 493299a39..15b9a1cd4 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdiExportJobCompletionListener.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdiExportJobCompletionListener.java @@ -1,6 +1,6 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; -import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_FILE_NAME; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; import static org.folio.dew.domain.dto.JobParameterNames.OUTPUT_FILES_IN_STORAGE; import static org.folio.dew.utils.BulkEditProcessorHelper.convertToDate; @@ -74,7 +74,7 @@ private Job createJobExecutionUpdate(String jobId, JobExecution jobExecution) { result.setFiles(Arrays.asList(outputFilesInStorage.split(PATHS_DELIMITER))); } - String ftpUploadedFile = ExecutionContextUtils.getFromJobExecutionContext(jobExecution, EDIFACT_FILE_NAME); + String ftpUploadedFile = ExecutionContextUtils.getFromJobExecutionContext(jobExecution, ACQ_EXPORT_FILE_NAME); if (StringUtils.isNotBlank(ftpUploadedFile)) { result.setFileNames(List.of(ftpUploadedFile)); } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java index ba5367604..b16386984 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/EdifactExportJobConfig.java @@ -1,11 +1,18 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import java.util.Map; + import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; + +import org.folio.dew.batch.acquisitions.edifact.jobs.decider.ExportHistoryTaskletDecider; +import org.folio.dew.batch.acquisitions.edifact.jobs.decider.ExportStepDecision; +import org.folio.dew.batch.acquisitions.edifact.jobs.decider.SaveToFileStorageTaskletDecider; import org.folio.dew.domain.dto.ExportType; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.job.builder.JobBuilder; +import org.springframework.batch.core.job.flow.JobExecutionDecider; import org.springframework.batch.core.launch.support.RunIdIncrementer; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.step.builder.StepBuilder; @@ -13,6 +20,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; +import com.fasterxml.jackson.databind.ObjectMapper; + @Configuration @Log4j2 @RequiredArgsConstructor @@ -22,31 +31,36 @@ public class EdifactExportJobConfig { private Job constructEdifactExportJob(JobBuilder jobBuilder, EdiExportJobCompletionListener ediExportJobCompletionListener, - Step mapToEdifactOrdersStep, + Step mapToFileStep, Step saveToFTPStep, Step saveToMinIOStep, - Step createExportHistoryRecordsStep) { + Step createExportHistoryRecordsStep, + Map optionalStepDeciders) { return jobBuilder.incrementer(new RunIdIncrementer()) .listener(ediExportJobCompletionListener) - .start(mapToEdifactOrdersStep) + .start(mapToFileStep) .next(saveToMinIOStep) - .next(saveToFTPStep) - .next(createExportHistoryRecordsStep) - .build(); + .next(optionalStepDeciders.get(saveToFTPStep.getName())) + .on(ExportStepDecision.PROCESS.getStatus()).to(saveToFTPStep) + .next(optionalStepDeciders.get(createExportHistoryRecordsStep.getName())) + .on(ExportStepDecision.PROCESS.getStatus()).to(createExportHistoryRecordsStep) + .end().build(); } @Bean public Job edifactOrdersExportJob(EdiExportJobCompletionListener ediExportJobCompletionListener, JobRepository jobRepository, - Step mapToEdifactOrdersStep, Step saveToFTPStep, Step saveToMinIOStep, Step createExportHistoryRecordsStep) { + Step mapToEdifactOrdersStep, Step saveToFTPStep, Step saveToMinIOStep, Step createExportHistoryRecordsStep, + Map deciders) { return constructEdifactExportJob(new JobBuilder(ExportType.EDIFACT_ORDERS_EXPORT.getValue(), jobRepository), - ediExportJobCompletionListener, mapToEdifactOrdersStep, saveToFTPStep, saveToMinIOStep, createExportHistoryRecordsStep); + ediExportJobCompletionListener, mapToEdifactOrdersStep, saveToFTPStep, saveToMinIOStep, createExportHistoryRecordsStep, deciders); } @Bean public Job edifactClaimsExportJob(EdiExportJobCompletionListener ediExportJobCompletionListener, JobRepository jobRepository, - Step mapToEdifactClaimsStep, Step saveToFTPStep, Step saveToMinIOStep, Step createExportHistoryRecordsStep) { + Step mapToEdifactClaimsStep, Step saveToFTPStep, Step saveToMinIOStep, Step createExportHistoryRecordsStep, + Map deciders) { return constructEdifactExportJob(new JobBuilder(ExportType.CLAIMS.getValue(), jobRepository), - ediExportJobCompletionListener, mapToEdifactClaimsStep, saveToFTPStep, saveToMinIOStep, createExportHistoryRecordsStep); + ediExportJobCompletionListener, mapToEdifactClaimsStep, saveToFTPStep, saveToMinIOStep, createExportHistoryRecordsStep, deciders); } @Bean @@ -89,4 +103,12 @@ public Step createExportHistoryRecordsStep(ExportHistoryTasklet exportHistoryTas .build(); } + @Bean + public Map optionalStepDeciders(Step saveToFTPStep, Step createExportHistoryRecordsStep, ObjectMapper objectMapper) { + return Map.of( + saveToFTPStep.getName(), new SaveToFileStorageTaskletDecider(objectMapper), + createExportHistoryRecordsStep.getName(), new ExportHistoryTaskletDecider(objectMapper) + ); + } + } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java index 2035f2bd6..0d50701c1 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTasklet.java @@ -1,7 +1,7 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; import static org.folio.dew.batch.acquisitions.edifact.jobs.EdifactExportJobConfig.POL_MEM_KEY; -import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_FILE_NAME; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; import java.util.Collections; @@ -11,6 +11,8 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import org.folio.dew.batch.ExecutionContextUtils; import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; @@ -27,7 +29,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @RequiredArgsConstructor @@ -60,7 +61,7 @@ ExportHistory buildExportHistory(ChunkContext chunkContext) { var vendorName = vendor.get("code").asText(); var stepExecutionContext = chunkContext.getStepContext().getStepExecution(); var poLineIds = getPoLineIdsFromExecutionContext(stepExecutionContext); - var fileName = ExecutionContextUtils.getExecutionVariable(stepExecutionContext, EDIFACT_FILE_NAME).toString(); + var fileName = ExecutionContextUtils.getExecutionVariable(stepExecutionContext, ACQ_EXPORT_FILE_NAME).toString(); var jobName = jobParameters.get(JobParameterNames.JOB_NAME).toString(); return new ExportHistory() @@ -83,4 +84,5 @@ List getPoLineIdsFromExecutionContext(StepExecution stepExecutionContext return Collections.emptyList(); } } + } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java index 23c87ad54..0d0753063 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTasklet.java @@ -1,19 +1,26 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.CLAIM_PIECE_IDS; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.LIB_EDI_TYPE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.VENDOR_EDI_TYPE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportUtils.validateField; +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.FileFormatEnum.EDI; import static org.folio.dew.utils.QueryUtils.convertIdsToCqlQuery; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import org.apache.commons.collections4.CollectionUtils; import org.folio.dew.batch.acquisitions.edifact.mapper.ExportResourceMapper; import org.folio.dew.batch.acquisitions.edifact.services.OrdersService; +import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; import org.folio.dew.domain.dto.Piece; import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; import org.folio.dew.domain.dto.acquisitions.edifact.ExportHolder; import org.folio.dew.error.NotFoundException; import org.springframework.batch.core.configuration.annotation.StepScope; -import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; @@ -22,13 +29,12 @@ @StepScope public class MapToEdifactClaimsTasklet extends MapToEdifactTasklet { - public static final String CLAIM_PIECE_IDS = "claimPieceIds"; private final ExportResourceMapper edifactMapper; private final ExportResourceMapper csvMapper; - public MapToEdifactClaimsTasklet(ObjectMapper ediObjectMapper, OrdersService ordersService, + public MapToEdifactClaimsTasklet(ObjectMapper ediObjectMapper, OrganizationsService organizationsService, OrdersService ordersService, ExportResourceMapper edifactMapper, ExportResourceMapper csvMapper) { - super(ediObjectMapper, ordersService); + super(ediObjectMapper, organizationsService, ordersService); this.edifactMapper = edifactMapper; this.csvMapper = csvMapper; } @@ -43,14 +49,20 @@ protected ExportResourceMapper getExportResourceMapper(VendorEdiOrdersExportConf @Override protected List getExportConfigMissingFields(VendorEdiOrdersExportConfig ediOrdersExportConfig) { - return CollectionUtils.isEmpty(ediOrdersExportConfig.getClaimPieceIds()) - ? List.of(CLAIM_PIECE_IDS) - : List.of(); + List missingFields = new ArrayList<>(); + validateField(CLAIM_PIECE_IDS.getName(), ediOrdersExportConfig.getClaimPieceIds(), CollectionUtils::isNotEmpty, missingFields); + + if (ediOrdersExportConfig.getFileFormat() == EDI) { + var ediConfig = ediOrdersExportConfig.getEdiConfig(); + validateField(LIB_EDI_TYPE.getName(), ediConfig.getLibEdiType(), Objects::nonNull, missingFields); + validateField(VENDOR_EDI_TYPE.getName(), ediConfig.getVendorEdiType(), Objects::nonNull, missingFields); + } + return missingFields; } @Override - protected ExportHolder buildEdifactExportHolder(ChunkContext chunkContext, VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) { - var pieces = ordersService.getPiecesByIdsAndReceivingStatus(ediExportConfig.getClaimPieceIds(), Piece.ReceivingStatusEnum.LATE); + protected ExportHolder buildEdifactExportHolder(VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) { + var pieces = ordersService.getPiecesByIdsAndReceivingStatus(ediExportConfig.getClaimPieceIds(), Piece.ReceivingStatusEnum.CLAIM_SENT); if (pieces.isEmpty()) { throw new NotFoundException(Piece.class); } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java index 0aee36e78..d468ed54c 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrdersTasklet.java @@ -1,10 +1,16 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.LIB_EDI_CODE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.LIB_EDI_TYPE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.VENDOR_EDI_CODE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.VENDOR_EDI_TYPE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportUtils.validateField; import static org.folio.dew.utils.QueryUtils.combineCqlExpressions; import static org.folio.dew.utils.QueryUtils.convertFieldListToEnclosedCqlQuery; import static org.folio.dew.utils.QueryUtils.getCqlExpressionForFieldNullValue; import static org.folio.dew.utils.QueryUtils.negateQuery; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; @@ -13,15 +19,16 @@ import java.util.stream.Collectors; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.folio.dew.batch.acquisitions.edifact.mapper.ExportResourceMapper; import org.folio.dew.batch.acquisitions.edifact.services.OrdersService; +import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; import org.folio.dew.client.DataExportSpringClient; import org.folio.dew.domain.dto.ExportConfigCollection; import org.folio.dew.domain.dto.ExportType; import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; import org.folio.dew.domain.dto.acquisitions.edifact.ExportHolder; import org.springframework.batch.core.configuration.annotation.StepScope; -import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; @@ -36,21 +43,26 @@ public class MapToEdifactOrdersTasklet extends MapToEdifactTasklet { private final DataExportSpringClient dataExportSpringClient; private final ExportResourceMapper edifactMapper; - public MapToEdifactOrdersTasklet(ObjectMapper ediObjectMapper, OrdersService ordersService, - DataExportSpringClient dataExportSpringClient, - ExportResourceMapper edifactMapper) { - super(ediObjectMapper, ordersService); - this.edifactMapper = edifactMapper; + public MapToEdifactOrdersTasklet(ObjectMapper ediObjectMapper, OrganizationsService organizationsService, OrdersService ordersService, + DataExportSpringClient dataExportSpringClient, ExportResourceMapper edifactMapper) { + super(ediObjectMapper, organizationsService, ordersService); this.dataExportSpringClient = dataExportSpringClient; + this.edifactMapper = edifactMapper; } @Override protected List getExportConfigMissingFields(VendorEdiOrdersExportConfig ediOrdersExportConfig) { - return List.of(); + List missingFields = new ArrayList<>(); + var ediConfig = ediOrdersExportConfig.getEdiConfig(); + validateField(LIB_EDI_TYPE.getName(), ediConfig.getLibEdiType(), Objects::nonNull, missingFields); + validateField(LIB_EDI_CODE.getName(), ediConfig.getLibEdiCode(), StringUtils::isNotBlank, missingFields); + validateField(VENDOR_EDI_TYPE.getName(), ediConfig.getVendorEdiType(), Objects::nonNull, missingFields); + validateField(VENDOR_EDI_CODE.getName(), ediConfig.getVendorEdiCode(), StringUtils::isNotBlank, missingFields); + return missingFields; } @Override - protected ExportHolder buildEdifactExportHolder(ChunkContext chunkContext, VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) { + protected ExportHolder buildEdifactExportHolder(VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) { var poLineQuery = getPoLineQuery(ediExportConfig); var compOrders = getCompositeOrders(poLineQuery); return new ExportHolder(compOrders, List.of()); diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java index 7f956074f..2402cb8b7 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTasklet.java @@ -3,11 +3,21 @@ import static java.util.Objects.requireNonNullElse; import static java.util.stream.Collectors.groupingBy; import static org.folio.dew.batch.acquisitions.edifact.jobs.EdifactExportJobConfig.POL_MEM_KEY; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.FILE_FORMAT; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.FTP_PORT; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.INTEGRATION_TYPE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.SERVER_ADDRESS; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.TRANSMISSION_METHOD; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportUtils.generateFileName; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportUtils.validateField; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.TransmissionMethodEnum.FTP; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.folio.dew.batch.ExecutionContextUtils; @@ -15,6 +25,7 @@ import org.folio.dew.batch.acquisitions.edifact.exceptions.EdifactException; import org.folio.dew.batch.acquisitions.edifact.mapper.ExportResourceMapper; import org.folio.dew.batch.acquisitions.edifact.services.OrdersService; +import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; import org.folio.dew.domain.dto.CompositePoLine; import org.folio.dew.domain.dto.CompositePurchaseOrder; import org.folio.dew.domain.dto.JobParameterNames; @@ -40,46 +51,47 @@ public abstract class MapToEdifactTasklet implements Tasklet { private final ObjectMapper ediObjectMapper; + private final OrganizationsService organizationsService; protected final OrdersService ordersService; @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { log.info("execute:: Executing MapToEdifactTasklet with job: {}", chunkContext.getStepContext().getJobName()); var jobParameters = chunkContext.getStepContext().getJobParameters(); - var ediExportConfig = ediObjectMapper.readValue((String)jobParameters.get(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class); + var ediExportConfig = ediObjectMapper.readValue((String) jobParameters.get(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class); validateEdiExportConfig(ediExportConfig); - var holder = buildEdifactExportHolder(chunkContext, ediExportConfig, jobParameters); + var holder = buildEdifactExportHolder(ediExportConfig, jobParameters); persistPoLineIds(chunkContext, holder.orders()); String jobName = jobParameters.get(JobParameterNames.JOB_NAME).toString(); var edifactStringResult = getExportResourceMapper(ediExportConfig).convertForExport(holder.orders(), holder.pieces(), ediExportConfig, jobName); - // save edifact file content in memory - ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(), "edifactOrderAsString", edifactStringResult, ""); + // save edifact file content and name in memory + var stepExecution = chunkContext.getStepContext().getStepExecution(); + ExecutionContextUtils.addToJobExecutionContext(stepExecution, ACQ_EXPORT_FILE, edifactStringResult, ""); + ExecutionContextUtils.addToJobExecutionContext(stepExecution, ACQ_EXPORT_FILE_NAME, getFileName(ediExportConfig), ""); return RepeatStatus.FINISHED; } private void validateEdiExportConfig(VendorEdiOrdersExportConfig ediExportConfig) { - var ediConfig = ediExportConfig.getEdiConfig(); - Optional port = Optional.ofNullable(ediExportConfig.getEdiFtp().getFtpPort()); - - if (StringUtils.isEmpty(ediConfig.getLibEdiCode()) || ediConfig.getLibEdiType() == null - || StringUtils.isEmpty(ediConfig.getVendorEdiCode()) || ediConfig.getVendorEdiType() == null) { - throw new EdifactException("Export configuration is incomplete, missing library EDI code/Vendor EDI code"); - } - - if (port.isEmpty()) { - throw new EdifactException("Export configuration is incomplete, missing FTP/SFTP Port"); + var missingFields = getExportConfigMissingFields(ediExportConfig); + validateField(INTEGRATION_TYPE.getName(), ediExportConfig.getIntegrationType(), Objects::nonNull, missingFields); + validateField(TRANSMISSION_METHOD.getName(), ediExportConfig.getTransmissionMethod(), Objects::nonNull, missingFields); + validateField(FILE_FORMAT.getName(), ediExportConfig.getFileFormat(), Objects::nonNull, missingFields); + + if (ediExportConfig.getTransmissionMethod() == FTP) { + var ftpConfig = ediExportConfig.getEdiFtp(); + validateField(FTP_PORT.getName(), ftpConfig.getFtpPort(), Objects::nonNull, missingFields); + validateField(SERVER_ADDRESS.getName(), ftpConfig.getServerAddress(), StringUtils::isNotEmpty, missingFields); } - var missingFields = getExportConfigMissingFields(ediExportConfig); if (!missingFields.isEmpty()) { throw new EdifactException("Export configuration is incomplete, missing required fields: %s".formatted(missingFields)); } } -protected List getCompositeOrders(String poLineQuery) { + protected List getCompositeOrders(String poLineQuery) { var poLines = ordersService.getPoLinesByQuery(poLineQuery); var orderIds = poLines.stream() .map(PoLine::getPurchaseOrderId) @@ -99,7 +111,7 @@ protected void persistPoLineIds(ChunkContext chunkContext, List ord.getCompositePoLines().stream()) .map(CompositePoLine::getId) .toList(); - ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(), POL_MEM_KEY, ediObjectMapper.writeValueAsString(poLineIds),""); + ExecutionContextUtils.addToJobExecutionContext(chunkContext.getStepContext().getStepExecution(), POL_MEM_KEY, ediObjectMapper.writeValueAsString(poLineIds), ""); } private List assembleCompositeOrders(List orders, List poLines) { @@ -113,6 +125,13 @@ private List assembleCompositeOrders(List .toList(); } + private String getFileName(VendorEdiOrdersExportConfig ediExportConfig) { + var vendorName = organizationsService.getOrganizationById(ediExportConfig.getVendorId().toString()).get("code").asText(); + var configName = ediExportConfig.getConfigName(); + var fileFormat = ediExportConfig.getFileFormat(); + return generateFileName(vendorName, configName, fileFormat); + } + private T convertTo(Object value, Class c) { try { return ediObjectMapper.readValue(ediObjectMapper.writeValueAsString(value), c); @@ -125,7 +144,7 @@ private T convertTo(Object value, Class c) { protected abstract List getExportConfigMissingFields(VendorEdiOrdersExportConfig ediOrdersExportConfig); - protected abstract ExportHolder buildEdifactExportHolder(ChunkContext chunkContext, VendorEdiOrdersExportConfig ediExportConfig, - Map jobParameters) throws JsonProcessingException, EDIStreamException; + protected abstract ExportHolder buildEdifactExportHolder(VendorEdiOrdersExportConfig ediExportConfig, Map jobParameters) + throws JsonProcessingException, EDIStreamException; } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTasklet.java index 0e419b227..8570ad675 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTasklet.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTasklet.java @@ -1,20 +1,20 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; -import static org.folio.dew.domain.dto.JobParameterNames.UPLOADED_FILE_PATH; -import org.apache.commons.io.FilenameUtils; +import java.nio.charset.StandardCharsets; + import org.apache.commons.lang3.StringUtils; import org.folio.dew.batch.ExecutionContextUtils; import org.folio.dew.batch.acquisitions.edifact.services.FTPStorageService; import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; -import org.folio.dew.repository.RemoteFilesStorage; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.fasterxml.jackson.databind.ObjectMapper; @@ -28,30 +28,26 @@ @StepScope @Log4j2 public class SaveToFileStorageTasklet implements Tasklet { - private final ObjectMapper ediObjectMapper; - private final FTPStorageService ftpStorageService; - - private final RemoteFilesStorage remoteFilesStorage; private static final String SFTP_PROTOCOL = "sftp://"; - @Value("#{jobParameters['edifactFileName']}") - private String edifactFileName; + + private final ObjectMapper ediObjectMapper; + private final FTPStorageService ftpStorageService; @Override @SneakyThrows public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) { - var stepExecution = chunkContext.getStepContext().getStepExecution(); - var jobParameters = chunkContext.getStepContext().getJobParameters(); - var ediExportConfig = ediObjectMapper.readValue((String)jobParameters.get(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class); - var uploadedFilePath = (String) ExecutionContextUtils.getExecutionVariable(stepExecution, UPLOADED_FILE_PATH); - - String host = ediExportConfig.getEdiFtp().getServerAddress().replace(SFTP_PROTOCOL, ""); + var exportConfig = ediObjectMapper.readValue((String) chunkContext.getStepContext().getJobParameters().get(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class); + String host = exportConfig.getEdiFtp().getServerAddress().replace(SFTP_PROTOCOL, ""); // skip ftp upload if address not specified if (StringUtils.isEmpty(host)) { return RepeatStatus.FINISHED; } - byte[] fileContent = remoteFilesStorage.readAllBytes(uploadedFilePath); - ftpStorageService.uploadToFtp(ediExportConfig, fileContent, FilenameUtils.getName(uploadedFilePath)); + + var stepExecution = chunkContext.getStepContext().getStepExecution(); + var fileName = (String) ExecutionContextUtils.getExecutionVariable(stepExecution, ACQ_EXPORT_FILE_NAME); + var edifactOrderAsString = (String) ExecutionContextUtils.getExecutionVariable(stepExecution, ACQ_EXPORT_FILE); + ftpStorageService.uploadToFtp(exportConfig, edifactOrderAsString.getBytes(StandardCharsets.UTF_8), fileName); return RepeatStatus.FINISHED; } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTasklet.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTasklet.java index 936457584..a5af973bf 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTasklet.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTasklet.java @@ -1,25 +1,19 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; -import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_FILE_NAME; -import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; import static org.folio.dew.domain.dto.JobParameterNames.OUTPUT_FILES_IN_STORAGE; -import static org.folio.dew.domain.dto.JobParameterNames.UPLOADED_FILE_PATH; import static org.folio.dew.utils.Constants.EDIFACT_EXPORT_DIR_NAME; import static org.folio.dew.utils.Constants.getWorkingDirectory; import java.nio.charset.StandardCharsets; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import org.apache.commons.io.FilenameUtils; import org.folio.dew.batch.ExecutionContextUtils; import org.folio.dew.batch.acquisitions.edifact.exceptions.EdifactException; -import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; -import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; import org.folio.dew.repository.RemoteFilesStorage; import org.folio.spring.FolioExecutionContext; import org.springframework.batch.core.StepContribution; +import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; @@ -27,8 +21,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import com.fasterxml.jackson.databind.ObjectMapper; - import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; @@ -38,12 +30,12 @@ @StepScope @Log4j2 public class SaveToMinioTasklet implements Tasklet { + + private static final String REMOTE_STORAGE_ERROR_MESSAGE = "Failed to save edifact file to remote storage"; + private static final String UPLOADED_PATH_TEMPLATE = "%s%s/%s"; + private final RemoteFilesStorage remoteFilesStorage; - private final OrganizationsService organizationsService; private final FolioExecutionContext folioExecutionContext; - private final ObjectMapper ediObjectMapper; - private final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); - private static final String REMOTE_STORAGE_ERROR_MESSAGE = "Failed to save edifact file to remote storage"; @Value("${spring.application.name}") protected String springApplicationName; @@ -53,41 +45,25 @@ public class SaveToMinioTasklet implements Tasklet { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) { // retrieve parameters from job context var stepExecution = chunkContext.getStepContext().getStepExecution(); - var jobParameters = chunkContext.getStepContext().getJobParameters(); - var ediExportConfig = ediObjectMapper.readValue((String)jobParameters.get(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class); - var edifactOrderAsString = (String) ExecutionContextUtils.getExecutionVariable(stepExecution,"edifactOrderAsString"); - - var fullFilePath = buildFullFilePath(ediExportConfig); - String edifactFileName = FilenameUtils.getName(fullFilePath); + var edifactOrderAsString = (String) ExecutionContextUtils.getExecutionVariable(stepExecution, ACQ_EXPORT_FILE); + var fullFilePath = buildFullFilePath(stepExecution); String uploadedFilePath; try { uploadedFilePath = remoteFilesStorage.write(fullFilePath, edifactOrderAsString.getBytes(StandardCharsets.UTF_8)); - } - catch (Exception e) { + } catch (Exception e) { log.error(REMOTE_STORAGE_ERROR_MESSAGE, e); throw new EdifactException(REMOTE_STORAGE_ERROR_MESSAGE); } - ExecutionContextUtils.addToJobExecutionContext(contribution.getStepExecution(), UPLOADED_FILE_PATH, fullFilePath, ""); - ExecutionContextUtils.addToJobExecutionContext(contribution.getStepExecution(), EDIFACT_FILE_NAME, edifactFileName, ""); ExecutionContextUtils.addToJobExecutionContext(contribution.getStepExecution(), OUTPUT_FILES_IN_STORAGE, uploadedFilePath, ";"); return RepeatStatus.FINISHED; } - private String generateFileName(VendorEdiOrdersExportConfig ediExportConfig) { - var vendorId = ediExportConfig.getVendorId().toString(); - var vendor = organizationsService.getOrganizationById(vendorId); - var vendorName = vendor.get("code").asText(); - var fileDate = dateFormat.format(new Date()); - // exclude restricted symbols after implementing naming convention feature - return vendorName + "_" + ediExportConfig.getConfigName() + "_" + fileDate + ".edi"; - } - - private String buildFullFilePath(VendorEdiOrdersExportConfig ediExportConfig) { + private String buildFullFilePath(StepExecution stepExecution) { var workDir = getWorkingDirectory(springApplicationName, EDIFACT_EXPORT_DIR_NAME); var tenantName = folioExecutionContext.getTenantId(); - var filename = generateFileName(ediExportConfig); - - return String.format("%s%s/%s", workDir, tenantName, filename); + var fileName = (String) ExecutionContextUtils.getExecutionVariable(stepExecution, ACQ_EXPORT_FILE_NAME); + return UPLOADED_PATH_TEMPLATE.formatted(workDir, tenantName, fileName); } + } diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportHistoryTaskletDecider.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportHistoryTaskletDecider.java new file mode 100644 index 000000000..f0bd655bd --- /dev/null +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportHistoryTaskletDecider.java @@ -0,0 +1,30 @@ +package org.folio.dew.batch.acquisitions.edifact.jobs.decider; + +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.IntegrationTypeEnum.ORDERING; + +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.extern.log4j.Log4j2; + +@Log4j2 +public class ExportHistoryTaskletDecider extends ExportStepDecider { + + public ExportHistoryTaskletDecider(ObjectMapper objectMapper) { + super(objectMapper); + } + + @Override + public ExportStepDecision decide(VendorEdiOrdersExportConfig exportConfig, JobExecution jobExecution, StepExecution stepExecution) { + // Always execute if the integration type is not ORDERING, or execute for other integration types if the transmission method is FTP + if (exportConfig.getIntegrationType() == ORDERING) { + return ExportStepDecision.PROCESS; + } + log.info("decide:: Integration type is not ORDERING, skipping the step: {}", stepExecution.getStepName()); + return ExportStepDecision.SKIP; + } + +} diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportStepDecider.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportStepDecider.java new file mode 100644 index 000000000..fdb4f7ed4 --- /dev/null +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportStepDecider.java @@ -0,0 +1,39 @@ +package org.folio.dew.batch.acquisitions.edifact.jobs.decider; + +import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; + +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.job.flow.FlowExecutionStatus; +import org.springframework.batch.core.job.flow.JobExecutionDecider; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +@RequiredArgsConstructor +public abstract class ExportStepDecider implements JobExecutionDecider { + + private final ObjectMapper objectMapper; + + @Override + @SneakyThrows + public final FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) { + var exportConfig = objectMapper.readValue(jobExecution.getJobParameters().getString(EDIFACT_ORDERS_EXPORT), VendorEdiOrdersExportConfig.class); + return new FlowExecutionStatus(decide(exportConfig, jobExecution, stepExecution).getStatus()); + } + + /** + * Evaluate the decision to execute the step or not using the provided export configuration, job execution and step + * + * @param exportConfig export configuration + * @param jobExecution job execution + * @param stepExecution step execution + * @return the decision to execute the step or not + * @throws Exception if an error occurs during the decision process + */ + protected abstract ExportStepDecision decide(VendorEdiOrdersExportConfig exportConfig, JobExecution jobExecution, StepExecution stepExecution) throws Exception; + +} diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportStepDecision.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportStepDecision.java new file mode 100644 index 000000000..1eae413b5 --- /dev/null +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportStepDecision.java @@ -0,0 +1,15 @@ +package org.folio.dew.batch.acquisitions.edifact.jobs.decider; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum ExportStepDecision { + + PROCESS("PROCESS"), + SKIP("SKIP"); + + private final String status; + +} diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/SaveToFileStorageTaskletDecider.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/SaveToFileStorageTaskletDecider.java new file mode 100644 index 000000000..1d1e5c0e8 --- /dev/null +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/SaveToFileStorageTaskletDecider.java @@ -0,0 +1,31 @@ +package org.folio.dew.batch.acquisitions.edifact.jobs.decider; + +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.IntegrationTypeEnum.ORDERING; +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.TransmissionMethodEnum.FTP; + +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.extern.log4j.Log4j2; + +@Log4j2 +public class SaveToFileStorageTaskletDecider extends ExportStepDecider { + + public SaveToFileStorageTaskletDecider(ObjectMapper objectMapper) { + super(objectMapper); + } + + @Override + public ExportStepDecision decide(VendorEdiOrdersExportConfig exportConfig, JobExecution jobExecution, StepExecution stepExecution) { + // Always execute if the integration type is ORDERING, or other integration type if the transmission method is FTP + if (exportConfig.getIntegrationType() == ORDERING || exportConfig.getTransmissionMethod() == FTP) { + return ExportStepDecision.PROCESS; + } + log.info("decide:: Transmission method is not FTP, skipping the step: {}", stepExecution.getStepName()); + return ExportStepDecision.SKIP; + } + +} diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/ConfigurationService.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/ConfigurationService.java index 17ba800fc..3888f84a6 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/ConfigurationService.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/services/ConfigurationService.java @@ -11,6 +11,7 @@ import org.apache.logging.log4j.Logger; import org.folio.dew.client.ConfigurationClient; import org.folio.dew.domain.dto.ModelConfiguration; +import org.folio.dew.error.NotFoundException; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @@ -24,7 +25,12 @@ public class ConfigurationService { private final ObjectMapper objectMapper; private ModelConfiguration getConfigById(String configId) { - return configurationClient.getConfigById(configId); + try { + return configurationClient.getConfigById(configId); + } catch (NotFoundException e) { + logger.error("getConfigById:: Couldn't fetch configuration entry by id: '{}'", configId, e); + throw new NotFoundException("Configuration entry not found for id: " + configId, e); + } } @Cacheable(cacheNames = "addressConfiguration") diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportConfigFields.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportConfigFields.java new file mode 100644 index 000000000..fdd10676e --- /dev/null +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportConfigFields.java @@ -0,0 +1,23 @@ +package org.folio.dew.batch.acquisitions.edifact.utils; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum ExportConfigFields { + + INTEGRATION_TYPE("integrationType"), + TRANSMISSION_METHOD("transmissionMethod"), + FILE_FORMAT("fileFormat"), + FTP_PORT("ftpPort"), + SERVER_ADDRESS("serverAddress"), + LIB_EDI_TYPE("libEdiType"), + LIB_EDI_CODE("libEdiCode"), + VENDOR_EDI_TYPE("vendorEdiType"), + VENDOR_EDI_CODE("vendorEdiCode"), + CLAIM_PIECE_IDS("claimPieceIds"); + + private final String name; + +} diff --git a/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportUtils.java b/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportUtils.java index 03b05d55d..7dd0d0157 100644 --- a/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportUtils.java +++ b/src/main/java/org/folio/dew/batch/acquisitions/edifact/utils/ExportUtils.java @@ -2,16 +2,22 @@ import static org.folio.dew.domain.dto.ReferenceNumberItem.RefNumberTypeEnum.ORDER_REFERENCE_NUMBER; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; import org.folio.dew.domain.dto.CompositePoLine; import org.folio.dew.domain.dto.ReferenceNumberItem; import org.folio.dew.domain.dto.VendorDetail; +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.FileFormatEnum; public class ExportUtils { + private static final String FILE_NAME_FORMAT = "%s_%s_%s.%s"; + private ExportUtils() { } public static List getVendorReferenceNumbers(CompositePoLine poLine) { @@ -39,4 +45,17 @@ public static String getVendorAccountNumber(CompositePoLine poLine) { .orElse(null); } + public static void validateField(String field, T value, Predicate validator, List missingFields) { + if (!validator.test(value)) { + missingFields.add(field); + } + } + + public static String generateFileName(String vendorName, String configName, FileFormatEnum fileFormat) { + return FILE_NAME_FORMAT.formatted(vendorName, + configName, + new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date()), + fileFormat.getValue().toLowerCase()); // Enum being EDI or CSV + } + } diff --git a/src/main/java/org/folio/dew/domain/dto/JobParameterNames.java b/src/main/java/org/folio/dew/domain/dto/JobParameterNames.java index d07803595..19c587e85 100644 --- a/src/main/java/org/folio/dew/domain/dto/JobParameterNames.java +++ b/src/main/java/org/folio/dew/domain/dto/JobParameterNames.java @@ -16,7 +16,8 @@ public class JobParameterNames { public static final String UPDATED_FILE_NAME = "updatedFileName"; public static final String PREVIEW_FILE_NAME = "previewFileName"; public static final String UPLOADED_FILE_PATH = "uploadedFilePath"; - public static final String EDIFACT_FILE_NAME = "edifactFileName"; + public static final String ACQ_EXPORT_FILE = "acqExportFile"; + public static final String ACQ_EXPORT_FILE_NAME = "acqExportFileName"; public static final String E_HOLDINGS_FILE_NAME = "eHoldingsFileName"; public static final String CIRCULATION_LOG_FILE_NAME = "circulationLogFileName"; public static final String AUTHORITY_CONTROL_FILE_NAME = "authorityControlFileName"; diff --git a/src/main/java/org/folio/dew/error/NotFoundException.java b/src/main/java/org/folio/dew/error/NotFoundException.java index 8317c2648..5edc96a31 100644 --- a/src/main/java/org/folio/dew/error/NotFoundException.java +++ b/src/main/java/org/folio/dew/error/NotFoundException.java @@ -12,4 +12,8 @@ public NotFoundException(String message) { super(message); } + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } + } diff --git a/src/main/java/org/folio/dew/service/JobCommandsReceiverService.java b/src/main/java/org/folio/dew/service/JobCommandsReceiverService.java index fbc8fe0c1..7ad94ca38 100644 --- a/src/main/java/org/folio/dew/service/JobCommandsReceiverService.java +++ b/src/main/java/org/folio/dew/service/JobCommandsReceiverService.java @@ -1,29 +1,26 @@ package org.folio.dew.service; -import static java.util.Objects.nonNull; import static org.folio.dew.domain.dto.ExportType.BULK_EDIT_IDENTIFIERS; import static org.folio.dew.domain.dto.ExportType.BULK_EDIT_QUERY; +import static org.folio.dew.domain.dto.ExportType.CLAIMS; import static org.folio.dew.domain.dto.ExportType.EDIFACT_ORDERS_EXPORT; import static org.folio.dew.utils.Constants.BULKEDIT_DIR_NAME; -import static org.folio.dew.utils.Constants.CSV_EXTENSION; -import static org.folio.dew.utils.Constants.FILE_NAME; import static org.folio.dew.utils.Constants.getWorkingDirectory; import jakarta.annotation.PostConstruct; -import java.io.FileOutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; -import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.folio.de.entity.JobCommand; import org.folio.de.entity.JobCommandType; @@ -32,7 +29,6 @@ import org.folio.dew.client.SearchClient; import org.folio.dew.config.kafka.KafkaService; import org.folio.dew.domain.dto.JobParameterNames; -import org.folio.dew.error.FileOperationException; import org.folio.dew.repository.JobCommandRepository; import org.folio.dew.repository.LocalFilesStorage; import org.folio.dew.repository.RemoteFilesStorage; @@ -43,7 +39,6 @@ import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.integration.launch.JobLaunchRequest; import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.InputStreamResource; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.messaging.handler.annotation.Headers; import org.springframework.messaging.handler.annotation.Payload; @@ -113,12 +108,16 @@ public void receiveStartJobCommand(@Payload JobCommand jobCommand, @Headers Map< } } - var jobLaunchRequest = - new JobLaunchRequest( - jobMap.get(resolveJobKey(jobCommand)), - jobCommand.getJobParameters()); + var jobKey = resolveJobKey(jobCommand); + log.info("receiveStartJobCommand:: Resolving job with key: '{}' for command: '{}'", jobKey, jobCommand.getId()); + var job = jobMap.get(jobKey); - exportJobManagerSync.launchJob(jobLaunchRequest); + if (job != null) { + log.info("receiveStartJobCommand:: Job resolved: '{}', launching...", job.getName()); + exportJobManagerSync.launchJob(new JobLaunchRequest(job, jobCommand.getJobParameters())); + } else { + log.error("Job with key '{}' not found for command: '{}'", jobKey, jobCommand.getId()); + } } catch (Exception e) { log.error(e.toString(), e); @@ -148,7 +147,7 @@ private void prepareJobParameters(JobCommand jobCommand) { } private void addOrderExportSpecificParameters(JobCommand jobCommand, JobParametersBuilder paramsBuilder) { - if (jobCommand.getExportType().equals(EDIFACT_ORDERS_EXPORT)) { + if (jobCommand.getExportType() == EDIFACT_ORDERS_EXPORT || jobCommand.getExportType() == CLAIMS) { paramsBuilder.addString(JobParameterNames.JOB_NAME, jobCommand.getName(), JOB_PARAMETER_DEFAULT_IDENTIFYING_VALUE); } } diff --git a/src/test/java/org/folio/dew/CopilotGenerated.java b/src/test/java/org/folio/dew/CopilotGenerated.java new file mode 100644 index 000000000..c272ab7bb --- /dev/null +++ b/src/test/java/org/folio/dew/CopilotGenerated.java @@ -0,0 +1,26 @@ +package org.folio.dew; + +import org.springframework.core.annotation.AliasFor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to indicate that test(s) in the class are generated by GitHub Copilot. + *

+ * Set value or partiallyGenerated attribute to true + * if the generated test(s) were significantly modified/altered by the developer. + */ +@Target({ElementType.TYPE, ElementType.METHOD}) +@Retention(RetentionPolicy.SOURCE) +public @interface CopilotGenerated { + + @AliasFor("partiallyGenerated") + boolean value() default false; + + @AliasFor("value") + boolean partiallyGenerated() default false; + +} diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java index db9a1e071..a798d4e8a 100644 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/ExportHistoryTaskletTest.java @@ -20,6 +20,11 @@ import java.util.ArrayList; import java.util.UUID; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; +import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; +import static org.folio.dew.domain.dto.JobParameterNames.JOB_ID; +import static org.folio.dew.domain.dto.JobParameterNames.JOB_NAME; import static org.folio.dew.utils.TestUtils.getMockData; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; @@ -64,19 +69,17 @@ void testCreateExportHistoryWithCompleteStatus() throws IOException { protected ExecutionContext getExecutionContext() { ExecutionContext result = new ExecutionContext(); - result.put("edifactFileName", "test_file"); - + result.put(ACQ_EXPORT_FILE_NAME, "test_file"); return result; } private JobParameters getJobParameters() throws IOException { JobParametersBuilder paramsBuilder = new JobParametersBuilder(); - paramsBuilder.addString("edifactOrdersExport", getMockData("edifact/edifactOrdersExport.json")); - paramsBuilder.addString("edifactOrderAsString", RandomStringUtils.random(100, true, true)); - paramsBuilder.addString("jobName", "TestJob00123"); - var jobId = UUID.randomUUID().toString(); - paramsBuilder.addString("jobId", jobId); + paramsBuilder.addString(EDIFACT_ORDERS_EXPORT, getMockData("edifact/edifactOrdersExport.json")); + paramsBuilder.addString(ACQ_EXPORT_FILE, RandomStringUtils.random(100, true, true)); + paramsBuilder.addString(JOB_NAME, "TestJob00123"); + paramsBuilder.addString(JOB_ID, UUID.randomUUID().toString()); return paramsBuilder.toJobParameters(); } diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java index 7fcc88db0..6a455a925 100644 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactClaimsTaskletTest.java @@ -1,6 +1,11 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; import static org.assertj.core.api.Assertions.assertThat; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.CLAIM_PIECE_IDS; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.INTEGRATION_TYPE; +import static org.folio.dew.batch.acquisitions.edifact.utils.ExportConfigFields.TRANSMISSION_METHOD; +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.IntegrationTypeEnum.CLAIMING; +import static org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.TransmissionMethodEnum.FTP; import static org.folio.dew.utils.QueryUtils.convertIdsToCqlQuery; import static org.folio.dew.utils.TestUtils.getMockData; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -56,7 +61,8 @@ protected void setUp() { pieces = objectMapper.readValue(getMockData(SAMPLE_PIECES_PATH), PieceCollection.class).getPieces(); pieceIds = pieces.stream().map(Piece::getId).toList(); - doReturn(pieces).when(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE); + doReturn(pieces).when(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.CLAIM_SENT); + doReturn(objectMapper.readTree("{\"code\": \"GOBI\"}")).when(organizationsService).getOrganizationById(anyString()); } @Test @@ -72,7 +78,7 @@ void testEdifactClaimsExport() throws Exception { JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig)); assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); - verify(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE); + verify(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.CLAIM_SENT); verify(ordersService).getPoLinesByQuery(poLineQuery); verify(ordersService).getPurchaseOrdersByIds(anyList()); } @@ -80,14 +86,14 @@ void testEdifactClaimsExport() throws Exception { @Test void testEdifactClaimsExportNoPiecesFound() throws Exception { JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); - doReturn(List.of()).when(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE); + doReturn(List.of()).when(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.CLAIM_SENT); var exportConfig = getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT, pieceIds); JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(exportConfig)); assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode()); assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Entities not found: Piece"); - verify(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.LATE); + verify(ordersService).getPiecesByIdsAndReceivingStatus(pieceIds, Piece.ReceivingStatusEnum.CLAIM_SENT); } @Test @@ -110,7 +116,9 @@ protected ObjectNode getEdifactExportConfig(String path) throws IOException { protected ObjectNode getEdifactExportConfig(String path, List pieceIds) throws IOException { var exportConfig = super.getEdifactExportConfig(path); - var arr = exportConfig.putArray("claimPieceIds"); + exportConfig.put(INTEGRATION_TYPE.getName(), CLAIMING.getValue()); + exportConfig.put(TRANSMISSION_METHOD.getName(), FTP.getValue()); + var arr = exportConfig.putArray(CLAIM_PIECE_IDS.getName()); pieceIds.forEach(arr::add); return exportConfig; } diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java index 0e94b744b..53900e4bd 100644 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactOrderTaskletTest.java @@ -1,6 +1,8 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import static org.assertj.core.api.Assertions.assertThat; import static org.folio.dew.utils.TestUtils.getMockData; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; @@ -8,6 +10,7 @@ import static org.mockito.Mockito.verify; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import org.assertj.core.api.Assertions; @@ -18,6 +21,7 @@ import org.folio.dew.domain.dto.PurchaseOrderCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; @@ -31,6 +35,7 @@ class MapToEdifactOrderTaskletTest extends MapToEdifactTaskletAbstractTest { private static final String DATA_EXPORT_CONFIGS_PATH = "edifact/dataExportConfigs.json"; + private static final String SAMPLE_EDI_ORDERS_EXPORT_MISSING_FIELDS = "edifact/edifactOrdersExportWithoutRequiredFields.json"; @Autowired Job edifactOrdersExportJob; @@ -46,6 +51,8 @@ protected void setUp() { edifactExportJob = edifactOrdersExportJob; orders = objectMapper.readValue(getMockData(SAMPLE_PURCHASE_ORDERS_PATH), PurchaseOrderCollection.class).getPurchaseOrders(); poLines = objectMapper.readValue(getMockData(SAMPLE_PO_LINES_PATH), PoLineCollection.class).getPoLines(); + + doReturn(objectMapper.readTree("{\"code\": \"GOBI\"}")).when(organizationsService).getOrganizationById(anyString()); } @Test @@ -121,6 +128,17 @@ void testEdifactOrdersExportDefaultConfigWithTwoExportConfigs() throws Exception verify(ordersService).getPurchaseOrdersByIds(anyList()); } + @Test + void testEdifactOrdersExportMissingRequiredFields() throws Exception { + JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); + JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT_MISSING_FIELDS))); + var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus(); + + assertEquals(BatchStatus.FAILED, status); + assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode()); + assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing required fields: [libEdiCode, vendorEdiType]"); + } + protected ObjectNode getEdifactExportConfig(String path, boolean isDefaultConfig) throws IOException { return getEdifactExportConfig(path).put("isDefaultConfig", isDefaultConfig); } diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java index 530d1c74d..2333c54cb 100644 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/MapToEdifactTaskletAbstractTest.java @@ -1,5 +1,8 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; +import static org.folio.dew.domain.dto.JobParameterNames.JOB_ID; +import static org.folio.dew.domain.dto.JobParameterNames.JOB_NAME; import static org.folio.dew.utils.TestUtils.getMockData; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -16,6 +19,7 @@ import org.folio.dew.BaseBatchTest; import org.folio.dew.batch.acquisitions.edifact.mapper.ExportResourceMapper; import org.folio.dew.batch.acquisitions.edifact.services.OrdersService; +import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; import org.folio.dew.client.DataExportSpringClient; import org.junit.jupiter.api.Test; import org.springframework.batch.core.BatchStatus; @@ -39,12 +43,13 @@ abstract class MapToEdifactTaskletAbstractTest extends BaseBatchTest { protected static final String MAP_TO_EDIFACT_STEP = "mapToEdifactStep"; protected static final String SAMPLE_EDI_ORDERS_EXPORT = "edifact/edifactOrdersExport.json"; - private static final String SAMPLE_EDI_ORDERS_EXPORT_MISSING_FIELDS = "edifact/edifactOrdersExportWithoutRequiredFields.json"; private static final String SAMPLE_EDI_ORDERS_EXPORT_MISSING_PORT = "edifact/edifactOrdersExportWithoutPort.json"; @MockBean protected OrdersService ordersService; @MockBean + protected OrganizationsService organizationsService; + @MockBean protected DataExportSpringClient dataExportSpringClient; @MockBean @Qualifier("edifactMapper") @@ -53,17 +58,6 @@ abstract class MapToEdifactTaskletAbstractTest extends BaseBatchTest { protected ObjectMapper objectMapper; protected Job edifactExportJob; - @Test - void testEdifactExportMissingRequiredFields() throws Exception { - JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); - JobExecution jobExecution = testLauncher.launchStep(MAP_TO_EDIFACT_STEP, getJobParameters(getEdifactExportConfig(SAMPLE_EDI_ORDERS_EXPORT_MISSING_FIELDS))); - var status = new ArrayList<>(jobExecution.getStepExecutions()).get(0).getStatus(); - - assertEquals(BatchStatus.FAILED, status); - assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode()); - assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing library EDI code/Vendor EDI code"); - } - @Test void testEdifactExportMissingFtpPort() throws Exception { JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); @@ -72,7 +66,7 @@ void testEdifactExportMissingFtpPort() throws Exception { assertEquals(BatchStatus.FAILED, status); assertThat(jobExecution.getExitStatus().getExitCode()).isEqualTo(ExitStatus.FAILED.getExitCode()); - assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing FTP/SFTP Port"); + assertThat(jobExecution.getExitStatus().getExitDescription()).contains("Export configuration is incomplete, missing required fields: [ftpPort, serverAddress]"); } @Test @@ -94,9 +88,9 @@ protected ObjectNode getEdifactExportConfig(String path) throws IOException { protected JobParameters getJobParameters(ObjectNode edifactExport) { return new JobParametersBuilder() - .addString("jobId", UUID.randomUUID().toString()) - .addString("edifactOrdersExport", edifactExport.toString()) - .addString("jobName", "000015") + .addString(JOB_ID, UUID.randomUUID().toString()) + .addString(JOB_NAME, "000015") + .addString(EDIFACT_ORDERS_EXPORT, edifactExport.toString()) .toJobParameters(); } diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java index 1bd2fcbeb..e36f463ed 100644 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToFileStorageTaskletTest.java @@ -1,9 +1,10 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; import static org.assertj.core.api.Assertions.assertThat; -import static org.folio.dew.domain.dto.JobParameterNames.UPLOADED_FILE_PATH; -import static org.folio.dew.utils.Constants.EDIFACT_EXPORT_DIR_NAME; -import static org.folio.dew.utils.Constants.getWorkingDirectory; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE_NAME; +import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; +import static org.folio.dew.domain.dto.JobParameterNames.JOB_ID; import static org.folio.dew.utils.TestUtils.getMockData; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -12,7 +13,6 @@ import static org.mockito.Mockito.doReturn; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.UUID; import org.apache.commons.lang3.RandomStringUtils; @@ -20,7 +20,9 @@ import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; import org.folio.dew.repository.FTPObjectStorageRepository; import org.folio.dew.repository.SFTPObjectStorageRepository; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobExecution; @@ -33,9 +35,10 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.annotation.DirtiesContext; -import com.fasterxml.jackson.databind.JsonNode; +import lombok.SneakyThrows; class SaveToFileStorageTaskletTest extends BaseBatchTest { + @Autowired @Qualifier("edifactOrdersExportJob") private Job edifactExportJob; @@ -46,69 +49,43 @@ class SaveToFileStorageTaskletTest extends BaseBatchTest { @MockBean private OrganizationsService organizationsService; - @Test - @DirtiesContext - void sftpUploadSuccessful() throws Exception { - JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); + @Override + @SneakyThrows + @BeforeEach + protected void setUp() { + super.setUp(); + doReturn(objectMapper.readTree("{\"code\": \"GOBI\"}")).when(organizationsService).getOrganizationById(anyString()); doReturn(true).when(sftpObjectStorageRepository).upload(anyString(), anyString(), anyString(), anyInt(), anyString(), anyString(), any()); - JsonNode vendorJson = objectMapper.readTree("{\"code\": \"GOBI\"}"); - doReturn(vendorJson).when(organizationsService).getOrganizationById(anyString()); - - JobParameters jobParameters = getSFTPJobParameters(); - ExecutionContext executionContext = getExecutionContext(jobParameters.getString(UPLOADED_FILE_PATH)); - - JobExecution jobExecution = testLauncher.launchStep("saveToFTPStep", getSFTPJobParameters(), executionContext); - - assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); - + doNothing().when(ftpObjectStorageRepository).upload(anyString(), anyString(), anyString(), anyString(), anyString(), any()); } - @Test + @ParameterizedTest(name = "{0}") + @CsvSource(value = {"SFTP Upload,edifact/edifactOrdersExport.json", "FTP Upload,edifact/edifactFTPOrdersExport.json"}, delimiter = ',') @DirtiesContext - void ftpUploadSuccessful() throws Exception { + void testUploadSuccessful(String testName, String edifactOrdersExport) throws Exception { JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); - JsonNode vendorJson = objectMapper.readTree("{\"code\": \"GOBI\"}"); - doReturn(vendorJson).when(organizationsService).getOrganizationById(anyString()); - doNothing().when(ftpObjectStorageRepository).upload(anyString(),anyString(),anyString(), anyString(), anyString(), any()); - - JobParameters jobParameters = getSFTPJobParameters(); - ExecutionContext executionContext = getExecutionContext(jobParameters.getString(UPLOADED_FILE_PATH)); - JobExecution jobExecution = testLauncher.launchStep("saveToFTPStep", getFTPJobParameters(), executionContext); + var jobParameters = getJobParameters(edifactOrdersExport); + ExecutionContext executionContext = getExecutionContext(); + JobExecution jobExecution = testLauncher.launchStep("saveToFTPStep", jobParameters, executionContext); assertThat(jobExecution.getExitStatus()).isEqualTo(ExitStatus.COMPLETED); - } - private JobParameters getSFTPJobParameters() throws IOException { - JobParametersBuilder paramsBuilder = new JobParametersBuilder(); - - paramsBuilder.addString("edifactOrdersExport", getMockData("edifact/edifactOrdersExport.json")); - paramsBuilder.addString("jobId", UUID.randomUUID().toString()); - - String workDir = getWorkingDirectory(springApplicationName, EDIFACT_EXPORT_DIR_NAME); - - // prepare local file copy - var filename = "testEdiFile.edi"; - var fileContent = RandomStringUtils.random(100); - var uploadedFilePath = remoteFilesStorage.write(workDir + filename, fileContent.getBytes(StandardCharsets.UTF_8)); - paramsBuilder.addString(UPLOADED_FILE_PATH, uploadedFilePath); - - return paramsBuilder.toJobParameters(); } - private JobParameters getFTPJobParameters() throws IOException { + private JobParameters getJobParameters(String edifactOrdersExport) throws IOException { JobParametersBuilder paramsBuilder = new JobParametersBuilder(); - - paramsBuilder.addString("edifactOrdersExport", getMockData("edifact/edifactFTPOrdersExport.json")); - paramsBuilder.addString("jobId", UUID.randomUUID().toString()); - + paramsBuilder.addString(EDIFACT_ORDERS_EXPORT, getMockData(edifactOrdersExport)); + paramsBuilder.addString(JOB_ID, UUID.randomUUID().toString()); return paramsBuilder.toJobParameters(); } - private ExecutionContext getExecutionContext(String uploadedFilePath) { + private ExecutionContext getExecutionContext() { + // Prepare file name and content ExecutionContext executionContext = new ExecutionContext(); - executionContext.put(UPLOADED_FILE_PATH, uploadedFilePath); + executionContext.put(ACQ_EXPORT_FILE_NAME, "testEdiFile.edi"); + executionContext.put(ACQ_EXPORT_FILE, RandomStringUtils.random(100)); return executionContext; } @@ -119,4 +96,5 @@ protected JobLauncherTestUtils createTestLauncher(Job job) { testLauncher.setJobRepository(jobRepository); return testLauncher; } + } diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java deleted file mode 100644 index 281569784..000000000 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletFailTest.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.folio.dew.batch.acquisitions.edifact.jobs; - -import static org.folio.dew.utils.TestUtils.getMockData; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; - -import java.io.IOException; -import java.util.UUID; - -import org.apache.commons.lang3.RandomStringUtils; -import org.folio.dew.BaseBatchTest; -import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; -import org.folio.dew.repository.RemoteFilesStorage; -import org.junit.jupiter.api.Test; -import org.springframework.batch.core.ExitStatus; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.JobParametersBuilder; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.test.JobLauncherTestUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.annotation.DirtiesContext; - -import com.fasterxml.jackson.databind.JsonNode; - -class SaveToMinioTaskletFailTest extends BaseBatchTest { - @Autowired - @Qualifier("edifactOrdersExportJob") - private Job edifactExportJob; - @MockBean - private OrganizationsService organizationsService; - - @MockBean - private RemoteFilesStorage remoteFilesStorage; - - private static final String NULL_POINTER_ERROR_TEXT = "Test null pointer exception message"; - - @Test - @DirtiesContext - void minioSaveToRemoteStorageFailed() - throws IOException { - JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); - - JsonNode vendorJson = objectMapper.readTree("{\"code\": \"GOBI\"}"); - doReturn(vendorJson).when(organizationsService).getOrganizationById(anyString()); - - doThrow(new NullPointerException(NULL_POINTER_ERROR_TEXT)).when(remoteFilesStorage).write(anyString(), any(byte[].class)); - - JobExecution jobExecution = testLauncher.launchStep("saveToMinIOStep", getJobParameters(), getExecutionContext()); - - assertEquals(ExitStatus.FAILED.getExitCode(), jobExecution.getExitStatus().getExitCode()); - } - - private JobParameters getJobParameters() throws IOException { - JobParametersBuilder paramsBuilder = new JobParametersBuilder(); - - paramsBuilder.addString("edifactOrdersExport", getMockData("edifact/edifactOrdersExport.json")); - var jobId = UUID.randomUUID().toString(); - paramsBuilder.addString("jobId", jobId); - - return paramsBuilder.toJobParameters(); - } - - private ExecutionContext getExecutionContext() { - ExecutionContext executionContext = new ExecutionContext(); - executionContext.put("edifactOrderAsString", RandomStringUtils.random(100, true, true)); - return executionContext; - } - - protected JobLauncherTestUtils createTestLauncher(Job job) { - JobLauncherTestUtils testLauncher = new JobLauncherTestUtils(); - testLauncher.setJob(job); - testLauncher.setJobLauncher(jobLauncher); - testLauncher.setJobRepository(jobRepository); - return testLauncher; - } -} diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java index daa6111ef..b01e59fa7 100644 --- a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/SaveToMinioTaskletTest.java @@ -1,9 +1,14 @@ package org.folio.dew.batch.acquisitions.edifact.jobs; +import static org.folio.dew.domain.dto.JobParameterNames.ACQ_EXPORT_FILE; +import static org.folio.dew.domain.dto.JobParameterNames.EDIFACT_ORDERS_EXPORT; +import static org.folio.dew.domain.dto.JobParameterNames.JOB_ID; import static org.folio.dew.utils.TestUtils.getMockData; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import java.io.IOException; import java.util.UUID; @@ -11,6 +16,8 @@ import org.apache.commons.lang3.RandomStringUtils; import org.folio.dew.BaseBatchTest; import org.folio.dew.batch.acquisitions.edifact.services.OrganizationsService; +import org.folio.dew.repository.RemoteFilesStorage; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.Job; @@ -22,45 +29,68 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.test.annotation.DirtiesContext; import com.fasterxml.jackson.databind.JsonNode; +import lombok.SneakyThrows; + class SaveToMinioTaskletTest extends BaseBatchTest { @Autowired @Qualifier("edifactOrdersExportJob") private Job edifactExportJob; @MockBean private OrganizationsService organizationsService; + @SpyBean + private RemoteFilesStorage remoteFilesStorage; + + @Override + @SneakyThrows + @BeforeEach + protected void setUp() { + super.setUp(); + + JsonNode vendorJson = objectMapper.readTree("{\"code\": \"GOBI\"}"); + doReturn(vendorJson).when(organizationsService).getOrganizationById(anyString()); + } @Test @DirtiesContext void minioUploadSuccessful() throws IOException { JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); - JsonNode vendorJson = objectMapper.readTree("{\"code\": \"GOBI\"}"); - doReturn(vendorJson).when(organizationsService).getOrganizationById(anyString()); - JobExecution jobExecution = testLauncher.launchStep("saveToMinIOStep", getJobParameters(), getExecutionContext()); assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); } + @Test + @DirtiesContext + void minioUploadFails() + throws IOException { + JobLauncherTestUtils testLauncher = createTestLauncher(edifactExportJob); + doThrow(new NullPointerException()).when(remoteFilesStorage).write(anyString(), any(byte[].class)); + + JobExecution jobExecution = testLauncher.launchStep("saveToMinIOStep", getJobParameters(), getExecutionContext()); + + assertEquals(ExitStatus.FAILED.getExitCode(), jobExecution.getExitStatus().getExitCode()); + } + private JobParameters getJobParameters() throws IOException { JobParametersBuilder paramsBuilder = new JobParametersBuilder(); - paramsBuilder.addString("edifactOrdersExport", getMockData("edifact/edifactOrdersExport.json")); - paramsBuilder.addString("edifactOrderAsString", RandomStringUtils.random(100, true, true)); - var jobId = UUID.randomUUID().toString(); - paramsBuilder.addString("jobId", jobId); + paramsBuilder.addString(EDIFACT_ORDERS_EXPORT, getMockData("edifact/edifactOrdersExport.json")); + paramsBuilder.addString(ACQ_EXPORT_FILE, RandomStringUtils.random(100, true, true)); + paramsBuilder.addString(JOB_ID, UUID.randomUUID().toString()); return paramsBuilder.toJobParameters(); } private ExecutionContext getExecutionContext() { ExecutionContext executionContext = new ExecutionContext(); - executionContext.put("edifactOrderAsString", RandomStringUtils.random(100, true, true)); + executionContext.put(ACQ_EXPORT_FILE, RandomStringUtils.random(100, true, true)); return executionContext; } diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportHistoryTaskletDeciderTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportHistoryTaskletDeciderTest.java new file mode 100644 index 000000000..9164c0401 --- /dev/null +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/ExportHistoryTaskletDeciderTest.java @@ -0,0 +1,50 @@ +package org.folio.dew.batch.acquisitions.edifact.jobs.decider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +import org.folio.dew.CopilotGenerated; +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.IntegrationTypeEnum; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@CopilotGenerated +class ExportHistoryTaskletDeciderTest { + + private ExportHistoryTaskletDecider decider; + + @BeforeEach + void setUp() { + decider = new ExportHistoryTaskletDecider(new ObjectMapper()); + } + + @Test + void decide_shouldProcess_whenIntegrationTypeIsOrdering() { + VendorEdiOrdersExportConfig config = new VendorEdiOrdersExportConfig(); + config.setIntegrationType(IntegrationTypeEnum.ORDERING); + JobExecution jobExecution = mock(JobExecution.class); + StepExecution stepExecution = mock(StepExecution.class); + + ExportStepDecision decision = decider.decide(config, jobExecution, stepExecution); + + assertEquals(ExportStepDecision.PROCESS, decision); + } + + @Test + void decide_shouldSkip_whenIntegrationTypeIsNotOrdering() { + VendorEdiOrdersExportConfig config = new VendorEdiOrdersExportConfig(); + config.setIntegrationType(IntegrationTypeEnum.CLAIMING); + JobExecution jobExecution = mock(JobExecution.class); + StepExecution stepExecution = mock(StepExecution.class); + + ExportStepDecision decision = decider.decide(config, jobExecution, stepExecution); + + assertEquals(ExportStepDecision.SKIP, decision); + } + +} diff --git a/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/SaveToFileStorageTaskletDeciderTest.java b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/SaveToFileStorageTaskletDeciderTest.java new file mode 100644 index 000000000..73624e599 --- /dev/null +++ b/src/test/java/org/folio/dew/batch/acquisitions/edifact/jobs/decider/SaveToFileStorageTaskletDeciderTest.java @@ -0,0 +1,64 @@ +package org.folio.dew.batch.acquisitions.edifact.jobs.decider; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.mock; + +import org.folio.dew.CopilotGenerated; +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig; +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.IntegrationTypeEnum; +import org.folio.dew.domain.dto.VendorEdiOrdersExportConfig.TransmissionMethodEnum; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; + +import com.fasterxml.jackson.databind.ObjectMapper; + +@CopilotGenerated +class SaveToFileStorageTaskletDeciderTest { + + private SaveToFileStorageTaskletDecider decider; + + @BeforeEach + void setUp() { + decider = new SaveToFileStorageTaskletDecider(new ObjectMapper()); + } + + @Test + void decide_shouldProcess_whenIntegrationTypeIsOrdering() { + VendorEdiOrdersExportConfig config = new VendorEdiOrdersExportConfig(); + config.setIntegrationType(IntegrationTypeEnum.ORDERING); + JobExecution jobExecution = mock(JobExecution.class); + StepExecution stepExecution = mock(StepExecution.class); + + ExportStepDecision decision = decider.decide(config, jobExecution, stepExecution); + + assertEquals(ExportStepDecision.PROCESS, decision); + } + + @Test + void decide_shouldProcess_whenTransmissionMethodIsFtp() { + VendorEdiOrdersExportConfig config = new VendorEdiOrdersExportConfig(); + config.setIntegrationType(IntegrationTypeEnum.CLAIMING); + config.setTransmissionMethod(TransmissionMethodEnum.FTP); + JobExecution jobExecution = mock(JobExecution.class); + StepExecution stepExecution = mock(StepExecution.class); + + ExportStepDecision decision = decider.decide(config, jobExecution, stepExecution); + + assertEquals(ExportStepDecision.PROCESS, decision); + } + + @Test + void decide_shouldSkip_whenIntegrationTypeIsNotOrderingAndTransmissionMethodIsNotFtp() { + VendorEdiOrdersExportConfig config = new VendorEdiOrdersExportConfig(); + config.setIntegrationType(IntegrationTypeEnum.CLAIMING); + config.setTransmissionMethod(TransmissionMethodEnum.FILE_DOWNLOAD); + JobExecution jobExecution = mock(JobExecution.class); + StepExecution stepExecution = mock(StepExecution.class); + + ExportStepDecision decision = decider.decide(config, jobExecution, stepExecution); + + assertEquals(ExportStepDecision.SKIP, decision); + } +} diff --git a/src/test/resources/edifact/edifactFTPOrdersExport.json b/src/test/resources/edifact/edifactFTPOrdersExport.json index 49ed96244..4b51c2943 100644 --- a/src/test/resources/edifact/edifactFTPOrdersExport.json +++ b/src/test/resources/edifact/edifactFTPOrdersExport.json @@ -2,6 +2,9 @@ "vendorId": "e54f0eb5-36c6-43c8-a219-dabbafb0176c", "configName": "EDIFACT 2", "configDescription": "EDI configuration for testing export", + "integrationType": "Ordering", + "transmissionMethod": "FTP", + "fileFormat": "EDI", "ediConfig": { "accountNoList": [ "BRXXXXX-01" diff --git a/src/test/resources/edifact/edifactOrdersExport.json b/src/test/resources/edifact/edifactOrdersExport.json index 4ac93e09c..70aaf2352 100644 --- a/src/test/resources/edifact/edifactOrdersExport.json +++ b/src/test/resources/edifact/edifactOrdersExport.json @@ -2,6 +2,8 @@ "vendorId": "d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1", "configName": "EDIFACT 1", "configDescription": "EDI configuration for testing export", + "integrationType": "Ordering", + "transmissionMethod": "FTP", "fileFormat": "EDI", "ediConfig": { "accountNoList": [ diff --git a/src/test/resources/edifact/edifactOrdersExportWithoutPort.json b/src/test/resources/edifact/edifactOrdersExportWithoutPort.json index 583f0b4ad..b76445191 100644 --- a/src/test/resources/edifact/edifactOrdersExportWithoutPort.json +++ b/src/test/resources/edifact/edifactOrdersExportWithoutPort.json @@ -2,6 +2,9 @@ "vendorId": "d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1", "configName": "EDIFACT 1", "configDescription": "EDI configuration for testing export", + "integrationType": "Ordering", + "transmissionMethod": "FTP", + "fileFormat": "EDI", "ediConfig": { "accountNoList": [ "BRXXXXX-01" @@ -27,7 +30,6 @@ "notes": "separate directories for EOCRs and cat records", "orderDirectory": "/uploads", "password": "12345", - "serverAddress": "sftp://random-address.com", "username": "user" }, "isDefaultConfig": false diff --git a/src/test/resources/edifact/edifactOrdersExportWithoutRequiredFields.json b/src/test/resources/edifact/edifactOrdersExportWithoutRequiredFields.json index 5df693a5b..91a10b38d 100644 --- a/src/test/resources/edifact/edifactOrdersExportWithoutRequiredFields.json +++ b/src/test/resources/edifact/edifactOrdersExportWithoutRequiredFields.json @@ -2,6 +2,9 @@ "vendorId": "d0fb5aa0-cdf1-11e8-a8d5-f2801f1b9fd1", "configName": "EDIFACT 1", "configDescription": "EDI configuration for testing export", + "integrationType": "Ordering", + "transmissionMethod": "FTP", + "fileFormat": "EDI", "ediConfig": { "accountNoList": [ "BRXXXXX-01"