From da1c6f57f53bd46c49cf7b43bc8c9823b802d928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Wed, 1 Feb 2023 10:00:51 +0100 Subject: [PATCH 01/11] Config port update --- src/main/resources/application.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 73ca2af..857f44e 100755 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -17,6 +17,9 @@ spring: data.mongodb.uri: mongodb://localhost:27017/EmbalsesCHG +server: + port : 9090 + batch: jdbc: testWhileIdle: true From 18649a5912f2e94d0931628a7fb9f8895affdc3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Wed, 1 Feb 2023 23:50:25 +0100 Subject: [PATCH 02/11] test coverage --- .../controller/JobController.java | 29 ++++++++ .../chronos/chguadalquivir/model/Status.java | 32 +++++++++ .../reader/DailyRegisterItemReader.java | 8 +-- .../reader/StatExecutionsFileItemReader.java | 35 ++++----- .../writer/StatsExecutionWriter.java | 2 - src/main/resources/application.yml | 3 +- src/main/resources/logback.xml | 4 +- .../CHGuadalquivirApplicationTests.java | 66 +++++++++++++++++ .../chguadalquivir/TestComponents.java | 11 +++ .../chronos/chguadalquivir/TestListeners.java | 31 ++++++++ .../chronos/chguadalquivir/TestModels.java | 72 +++++++++++++------ .../chronos/chguadalquivir/TestReaders.java | 3 + .../chronos/chguadalquivir/TestWriters.java | 69 ++++++++++++++++++ src/test/resources/application.yml | 4 +- 14 files changed, 313 insertions(+), 56 deletions(-) create mode 100644 src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java create mode 100644 src/main/java/net/bounceme/chronos/chguadalquivir/model/Status.java create mode 100644 src/test/java/net/bounceme/chronos/chguadalquivir/TestWriters.java diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java new file mode 100644 index 0000000..dcdeb20 --- /dev/null +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java @@ -0,0 +1,29 @@ +package net.bounceme.chronos.chguadalquivir.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import net.bounceme.chronos.chguadalquivir.model.Status; + +@RestController +@RequestMapping("/api") +public class JobController { + + /** + * @return + */ + @GetMapping("/") + public String index() { + return "Hello world"; + } + + /** + * @return + */ + @GetMapping("/status") + public Status status() { + return Status.builder().version(System.getProperty("java.version")) + .platform(System.getProperty("os.name")).response("OK").build(); + } +} diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/model/Status.java b/src/main/java/net/bounceme/chronos/chguadalquivir/model/Status.java new file mode 100644 index 0000000..b7f89f4 --- /dev/null +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/model/Status.java @@ -0,0 +1,32 @@ +package net.bounceme.chronos.chguadalquivir.model; + +import java.io.Serializable; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Builder(toBuilder = true) +public class Status implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -6605836896150803431L; + + @Getter + @Setter + private String version; + + @Getter + @Setter + private String platform; + + @Getter + @Setter + private String response; +} diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java index 0b3a32f..02dc416 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java @@ -30,9 +30,6 @@ public class DailyRegisterItemReader implements ItemReader, Initializin @Value("${application.importJob.url}") private String url; - @Value("${application.importJob.excludeHeader}") - private Boolean excludeHeader; - @Autowired private ObjectMapper mapper; @@ -50,7 +47,6 @@ public class DailyRegisterItemReader implements ItemReader, Initializin @Override public void afterPropertiesSet() { Assert.notNull(url, "Must provide the url"); - Assert.notNull(excludeHeader, "excludeHeader must be [true/false]"); Assert.notNull(elementMapper, "Must provide a elementMapper"); initialize(); @@ -76,9 +72,7 @@ private void initialize() { Elements elements = doc.select("table#ContentPlaceHolder1_GridNivelesEmbalses > tbody > tr"); - Integer inicio = (excludeHeader) ? 1 : 0; - - for (int i = inicio; i < elements.size(); i++) { + for (int i = 1; i < elements.size(); i++) { ZonaElement ze = new ZonaElement(); ze.setZona(zona); ze.setElement(elements.get(i)); diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/StatExecutionsFileItemReader.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/StatExecutionsFileItemReader.java index 6237c03..9894c01 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/StatExecutionsFileItemReader.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/StatExecutionsFileItemReader.java @@ -11,12 +11,10 @@ import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; -import lombok.extern.slf4j.Slf4j; import net.bounceme.chronos.chguadalquivir.model.Execution; import net.bounceme.chronos.chguadalquivir.reader.mapping.StatExecutionsLineMapper; @Component -@Slf4j public class StatExecutionsFileItemReader extends FlatFileItemReader implements InitializingBean { @Autowired @@ -24,7 +22,7 @@ public class StatExecutionsFileItemReader extends FlatFileItemReader @Autowired private SimpleDateFormat dateFormat; - + @Autowired private StatExecutionsLineMapper lineMapper; @@ -37,23 +35,18 @@ public void afterPropertiesSet() { * */ private void initialize() { - try { - String exportFilePath = environment.getRequiredProperty("application.lastExecutions.export.file.path"); - String suffix = environment.getRequiredProperty("application.lastExecutions.export.file.suffix"); - - Date date = new Date(); - String sDate = dateFormat.format(date); - Resource exportFileResource = new FileSystemResource(exportFilePath + "-" + sDate + "." + suffix); - setResource(exportFileResource); - - //Set number of lines to skips. Use it if file has header rows. - setLinesToSkip(1); - - //Configure how each line will be parsed and mapped to different values - setLineMapper(lineMapper); - - } catch (Exception e) { - log.error("ERROR: ", e); - } + String exportFilePath = environment.getRequiredProperty("application.lastExecutions.export.file.path"); + String suffix = environment.getRequiredProperty("application.lastExecutions.export.file.suffix"); + + Date date = new Date(); + String sDate = dateFormat.format(date); + Resource exportFileResource = new FileSystemResource(exportFilePath + "-" + sDate + "." + suffix); + setResource(exportFileResource); + + // Set number of lines to skips. Use it if file has header rows. + setLinesToSkip(1); + + // Configure how each line will be parsed and mapped to different values + setLineMapper(lineMapper); } } diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/writer/StatsExecutionWriter.java b/src/main/java/net/bounceme/chronos/chguadalquivir/writer/StatsExecutionWriter.java index faea784..9e0615e 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/writer/StatsExecutionWriter.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/writer/StatsExecutionWriter.java @@ -5,7 +5,6 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.annotation.BeforeStep; -import org.springframework.batch.core.configuration.annotation.StepScope; import org.springframework.batch.item.ItemWriter; import org.springframework.stereotype.Component; @@ -13,7 +12,6 @@ import net.bounceme.chronos.chguadalquivir.model.Execution; @Component -@StepScope @Slf4j public class StatsExecutionWriter implements ItemWriter { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 857f44e..9bcad2e 100755 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -18,7 +18,7 @@ spring: data.mongodb.uri: mongodb://localhost:27017/EmbalsesCHG server: - port : 9090 + port : 9091 batch: jdbc: @@ -40,7 +40,6 @@ application: importJob: cron: 0 45 9 ? * 1,2,3,4,5 url: https://www.chguadalquivir.es/saih/EmbalNiv.aspx - excludeHeader: true lastExecutions: cron: 0 0 10 ? * 5 diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index 6306e1d..1250f83 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -57,11 +57,11 @@ - + - + \ No newline at end of file diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java b/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java index a778ed5..0384b74 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java @@ -1,12 +1,78 @@ package net.bounceme.chronos.chguadalquivir; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import net.bounceme.chronos.chguadalquivir.controller.JobController; +import net.bounceme.chronos.chguadalquivir.model.Status; @SpringBootTest +@AutoConfigureMockMvc public class CHGuadalquivirApplicationTests { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private JobController controller; + private Status status; + + @BeforeEach + public void setup(){ + status = Status.builder().version("1.8.0_202").platform("Linux").response("OK").build(); + + } + + @AfterEach + void tearDown() { + status = null; + } + + @Test void contextLoads() { + assertThat(controller).isNotNull(); + } + + @Test + public void testIndex() throws Exception { + mockMvc.perform(get("/api/")).andDo(print()).andExpect(status().isOk()) + .andExpect(content().string(containsString("Hello world"))); } + + @Test + public void testStatus() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/api/status"). + contentType(MediaType.APPLICATION_JSON). + content(asJsonString(status))). + andExpect(MockMvcResultMatchers.status().isOk()). + andDo(MockMvcResultHandlers.print()); + } + + public static String asJsonString(final Object obj){ + try{ + return new ObjectMapper().writeValueAsString(obj); + }catch (Exception e){ + throw new RuntimeException(e); + } + } } diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java index f0f3d41..88f3f11 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java @@ -1,6 +1,7 @@ package net.bounceme.chronos.chguadalquivir; import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import java.util.List; @@ -10,6 +11,7 @@ import org.springframework.boot.test.context.SpringBootTest; import net.bounceme.chronos.chguadalquivir.model.Execution; +import net.bounceme.chronos.chguadalquivir.repository.impl.RepositoryCollectionCustomImpl; import net.bounceme.chronos.chguadalquivir.support.StatsCalculations; @SpringBootTest @@ -17,6 +19,9 @@ public class TestComponents { @Autowired private StatsCalculations statsCalculations; + + @Autowired + private RepositoryCollectionCustomImpl repositoryCollectionCustomImpl; @Test public void testStatCalculations() { @@ -31,6 +36,12 @@ public void testStatCalculations() { assertNotNull(variation); } + @Test + public void testRepositoryCollectionCustomImpl() { + repositoryCollectionCustomImpl.setCollectionName("collection"); + assertEquals("collection", repositoryCollectionCustomImpl.getCollectionName()); + } + private List buildExecutions() { List executions = new ArrayList<>(); diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java index 336c245..80b3c6a 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java @@ -4,15 +4,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; +import java.util.Map; import org.junit.jupiter.api.Test; import org.springframework.batch.core.ExitStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.scope.context.ChunkContext; +import org.springframework.batch.core.scope.context.StepContext; import org.springframework.batch.test.MetaDataInstanceFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import net.bounceme.chronos.chguadalquivir.listener.CustomChunkListener; import net.bounceme.chronos.chguadalquivir.listener.ImportJobListener; import net.bounceme.chronos.chguadalquivir.listener.LastExecutionsListener; import net.bounceme.chronos.chguadalquivir.listener.StatExecutionsListener; @@ -32,6 +36,9 @@ public class TestListeners { @Autowired private TimeStepListener timeStepListener; + + @Autowired + private CustomChunkListener customChunkListener; @Test public void testLastExecutionsListener() { @@ -72,4 +79,28 @@ public void testTimeStepListener() { assertEquals(ExitStatus.COMPLETED, status); } + + @Test + public void testCustomChunkListener() { + ChunkContext chunkContext = createChunkContext("sampleStep"); + customChunkListener.beforeChunk(chunkContext); + customChunkListener.afterChunk(chunkContext); + customChunkListener.afterChunkError(chunkContext); + } + + private ChunkContext createChunkContext(String nameStep) { + JobExecution jobExecution = createJobExecution(); + + StepContext stepContext = new StepContext(createStepExecution(nameStep, jobExecution)); + ChunkContext chunkContext = new ChunkContext(stepContext); + return chunkContext; + } + + private JobExecution createJobExecution() { + return MetaDataInstanceFactory.createJobExecution(); + } + + private StepExecution createStepExecution(String name, JobExecution jobExecution) { + return new StepExecution(name, jobExecution); + } } diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java index 1eaa52b..4774ca6 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java @@ -13,40 +13,41 @@ import net.bounceme.chronos.chguadalquivir.model.Embalse; import net.bounceme.chronos.chguadalquivir.model.Execution; import net.bounceme.chronos.chguadalquivir.model.ExecutionStats; +import net.bounceme.chronos.chguadalquivir.model.Status; import net.bounceme.chronos.chguadalquivir.model.Zona; import net.bounceme.chronos.chguadalquivir.model.ZonaElement; @SpringBootTest @Slf4j public class TestModels { - + @Test public void testExecutionStatsModel() { ExecutionStats executionStats = ExecutionStats.builder().average(1.0).deviation(1.0).variation(1.0).build(); assertNotNull(executionStats); log.info(executionStats.toString()); - + executionStats = new ExecutionStats(1L, new Date(), 1.0, 1.0, 1.0); assertNotNull(executionStats); - + executionStats = new ExecutionStats(); executionStats.setId(1L); executionStats.setAverage(1.0); executionStats.setDeviation(1.0); executionStats.setVariation(1.0); - + Date d = new Date(); executionStats.setInitDate(d); - + assertEquals(1L, executionStats.getId()); assertEquals(1.0, executionStats.getAverage()); assertEquals(1.0, executionStats.getDeviation()); assertEquals(1.0, executionStats.getVariation()); assertEquals(d, executionStats.getInitDate()); - + log.info("{}, {}", executionStats.toString(), executionStats.hashCode()); } - + @Test public void testExecutionModel() { Execution execution = new Execution(); @@ -54,13 +55,13 @@ public void testExecutionModel() { execution.setValue(1); execution.setExecutionTime(1L); assertNotNull(execution); - + assertEquals("2023-01-23", execution.getId()); assertEquals(1, execution.getValue()); assertEquals(1L, execution.getExecutionTime()); log.info("{}, {}", execution.toString(), execution.hashCode()); } - + @Test public void testEmbalseModel() { Embalse embalse = new Embalse(); @@ -74,12 +75,12 @@ public void testEmbalseModel() { embalse.setVolumen(150F); embalse.setMEN(150F); embalse.setNivel(150F); - + Date d = new Date(); embalse.setFecha(d); - + assertNotNull(embalse); - + assertEquals("id", embalse.getId()); assertEquals("Embalse", embalse.getEmbalse()); assertEquals("cod_zona", embalse.getCod_zona()); @@ -91,43 +92,72 @@ public void testEmbalseModel() { assertEquals(150F, embalse.getMEN()); assertEquals(150F, embalse.getNivel()); assertEquals(d, embalse.getFecha()); - + log.info("{}, {}", embalse.toString(), embalse.hashCode()); } - + @Test public void testZonaModel() { Zona zona = new Zona(); zona.setCodigo("codigo"); zona.setDescripcion("descripcion"); zona.setNombre("nombre"); - + assertNotNull(zona); assertEquals("codigo", zona.getCodigo()); assertEquals("descripcion", zona.getDescripcion()); assertEquals("nombre", zona.getNombre()); - + zona = Zona.builder().codigo("codigo").descripcion("descripcion").nombre("nombre").build(); assertNotNull(zona); - + zona = new Zona("codigo", "descripcion", "nombre"); assertNotNull(zona); log.info("{}, {}", zona.toString(), zona.hashCode()); } - + @Test public void testZonaElementModel() { Zona zona = new Zona(); assertNotNull(zona); - + ZonaElement ze = new ZonaElement(); ze.setZona(zona); - + Element element = new Element("
"); ze.setElement(element); - + assertEquals(zona, ze.getZona()); assertEquals(element, ze.getElement()); log.info("{}, {}", ze.toString(), ze.hashCode()); } + + @Test + public void testStatus() { + Status status = Status.builder().version(System.getProperty("java.version")) + .platform(System.getProperty("os.name")).response("OK").build(); + assertNotNull(status); + + assertEquals(System.getProperty("java.version"), status.getVersion()); + assertEquals(System.getProperty("os.name"), status.getPlatform()); + assertEquals("OK", status.getResponse()); + + status = new Status(System.getProperty("java.version"), System.getProperty("os.name"), "OK"); + assertNotNull(status); + + assertEquals(System.getProperty("java.version"), status.getVersion()); + assertEquals(System.getProperty("os.name"), status.getPlatform()); + assertEquals("OK", status.getResponse()); + + status = new Status(); + status.setVersion(System.getProperty("java.version")); + status.setPlatform(System.getProperty("os.name")); + status.setResponse("OK"); + + assertNotNull(status); + + assertEquals(System.getProperty("java.version"), status.getVersion()); + assertEquals(System.getProperty("os.name"), status.getPlatform()); + assertEquals("OK", status.getResponse()); + } } diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestReaders.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestReaders.java index 5bbe0ac..5b3dc67 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestReaders.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestReaders.java @@ -1,5 +1,7 @@ package net.bounceme.chronos.chguadalquivir; +import static org.junit.jupiter.api.Assertions.assertNotNull; + import java.util.ArrayList; import java.util.List; @@ -53,6 +55,7 @@ public void testExecutionsItemReader() { @Test public void testDailyRegisterItemReader() { try { + assertNotNull(dailyRegisterItemReader.getElementMapper()); dailyRegisterItemReader.afterPropertiesSet(); Embalse embalse; diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestWriters.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestWriters.java new file mode 100644 index 0000000..ada0364 --- /dev/null +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestWriters.java @@ -0,0 +1,69 @@ +package net.bounceme.chronos.chguadalquivir; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.test.MetaDataInstanceFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import net.bounceme.chronos.chguadalquivir.model.Execution; +import net.bounceme.chronos.chguadalquivir.writer.StatsExecutionWriter; + +@SpringBootTest +public class TestWriters { + + @Autowired + private StatsExecutionWriter statsExecutionWriter; + + @Test + public void testStatsExecutionWriter() throws Exception { + List executions = new ArrayList<>(); + StepExecution stepExecution = createStepExecution("statExecutionsStep", executions); + + statsExecutionWriter.beforeStep(stepExecution); + statsExecutionWriter.write(buildExecutions()); + + assertTrue(true); + } + + private StepExecution createStepExecution(String name, List executions) { + JobExecution jobExecution = createJobExecution(); + jobExecution.getExecutionContext().put("EXECUTIONS", executions); + + return new StepExecution(name, jobExecution); + } + + private JobExecution createJobExecution() { + return MetaDataInstanceFactory.createJobExecution(); + } + + private List buildExecutions() { + List executions = new ArrayList<>(); + + Execution execution = Execution.builder().id("2023-01-23").value(1).executionTime(1896L).build(); + executions.add(execution); + + execution = Execution.builder().id("2023-01-24").value(1).executionTime(1923L).build(); + executions.add(execution); + + execution = Execution.builder().id("2023-01-25").value(1).executionTime(1642L).build(); + executions.add(execution); + + execution = Execution.builder().id("2023-01-26").value(1).executionTime(1828L).build(); + executions.add(execution); + + execution = Execution.builder().id("2023-01-27").value(1).executionTime(1805L).build(); + executions.add(execution); + + execution = Execution.builder().id("2023-01-28").value(1).executionTime(1665L).build(); + executions.add(execution); + + return executions; + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index ca91793..d1ea947 100755 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -17,6 +17,9 @@ spring: data.mongodb.uri: mongodb://localhost:27017/EmbalsesCHG +server: + port : 9091 + batch: jdbc: testWhileIdle: true @@ -37,7 +40,6 @@ application: importJob: cron: 0 45 9 ? * 1,2,3,4,5 url: https://www.chguadalquivir.es/saih/EmbalNiv.aspx - excludeHeader: true lastExecutions: cron: 0 0 10 ? * 5 From b324e15b05af67171293917d99a98c4015762bf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Thu, 2 Feb 2023 00:22:31 +0100 Subject: [PATCH 03/11] Test coverage --- .../chguadalquivir-batch.log | 46 ++++++++++++ .../reader/DailyRegisterItemReader.java | 45 ++++++------ .../support/CHGuadalquivirHelper.java | 70 +++++++++---------- .../CHGuadalquivirApplicationTests.java | 3 +- .../chguadalquivir/TestComponents.java | 20 ++++++ 5 files changed, 121 insertions(+), 63 deletions(-) create mode 100644 LOG_PATH_IS_UNDEFINED/chguadalquivir-batch.log diff --git a/LOG_PATH_IS_UNDEFINED/chguadalquivir-batch.log b/LOG_PATH_IS_UNDEFINED/chguadalquivir-batch.log new file mode 100644 index 0000000..5592c68 --- /dev/null +++ b/LOG_PATH_IS_UNDEFINED/chguadalquivir-batch.log @@ -0,0 +1,46 @@ +13:51:42.582 [main] INFO n.b.c.c.ChGuadalquivirApplication - Starting ChGuadalquivirApplication using Java 1.8.0_202 on discovery with PID 47281 (/home/federico/git/chguadalquivir-batch/target/classes started by federico in /home/federico/git/chguadalquivir-batch) +13:51:42.585 [main] INFO n.b.c.c.ChGuadalquivirApplication - No active profile set, falling back to 1 default profile: "default" +13:52:26.159 [main] ERROR o.s.boot.SpringApplication - Application run failed +org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat server + at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) + at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) + at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) + at java.lang.Iterable.forEach(Iterable.java:75) + at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) + at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) + at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) + at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) + at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) + at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) + at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) + at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) + at net.bounceme.chronos.chguadalquivir.ChGuadalquivirApplication.main(ChGuadalquivirApplication.java:18) +Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat server + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:229) + at org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:43) + at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) + ... 14 common frames omitted +Caused by: java.lang.IllegalArgumentException: standardService.connector.startFailed + at org.apache.catalina.core.StandardService.addConnector(StandardService.java:238) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.addPreviouslyRemovedConnectors(TomcatWebServer.java:282) + at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:213) + ... 16 common frames omitted +Caused by: org.apache.catalina.LifecycleException: Protocol handler start failed + at org.apache.catalina.connector.Connector.startInternal(Connector.java:1077) + at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) + at org.apache.catalina.core.StandardService.addConnector(StandardService.java:234) + ... 18 common frames omitted +Caused by: java.net.BindException: La dirección ya se está usando + at sun.nio.ch.Net.bind0(Native Method) + at sun.nio.ch.Net.bind(Net.java:433) + at sun.nio.ch.Net.bind(Net.java:425) + at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223) + at org.apache.tomcat.util.net.NioEndpoint.initServerSocket(NioEndpoint.java:275) + at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:230) + at org.apache.tomcat.util.net.AbstractEndpoint.bindWithCleanup(AbstractEndpoint.java:1227) + at org.apache.tomcat.util.net.AbstractEndpoint.start(AbstractEndpoint.java:1313) + at org.apache.coyote.AbstractProtocol.start(AbstractProtocol.java:617) + at org.apache.catalina.connector.Connector.startInternal(Connector.java:1074) + ... 20 common frames omitted diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java index 02dc416..7c55b89 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/DailyRegisterItemReader.java @@ -17,6 +17,7 @@ import lombok.Getter; import lombok.Setter; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.bounceme.chronos.chguadalquivir.model.Embalse; import net.bounceme.chronos.chguadalquivir.model.Zona; @@ -32,7 +33,7 @@ public class DailyRegisterItemReader implements ItemReader, Initializin @Autowired private ObjectMapper mapper; - + @Autowired private CHGuadalquivirHelper helper; @@ -55,36 +56,34 @@ public void afterPropertiesSet() { /** * */ + @SneakyThrows private void initialize() { - try { - List zonas = helper.getZonesFromJsonResource(mapper, "zonas.json"); - records = new ArrayList<>(); - Document doc = helper.retrieveDocument(url); - Map postData = helper.initFormData(doc); - String selectName = helper.getNameFrom(doc, "form2", "DDBzona"); + List zonas = helper.getZonesFromJsonResource(mapper, "zonas.json"); + records = new ArrayList<>(); - for (Zona zona : zonas) { - postData.put(selectName, zona.getCodigo()); - log.debug("{}", postData.toString()); + Document doc = helper.retrieveDocument(url); + Map postData = helper.initFormData(doc); + String selectName = helper.getNameFrom(doc, "form2", "DDBzona"); - doc = Jsoup.connect(url).data(postData).post(); + for (Zona zona : zonas) { + postData.put(selectName, zona.getCodigo()); + log.debug("{}", postData.toString()); - Elements elements = doc.select("table#ContentPlaceHolder1_GridNivelesEmbalses > tbody > tr"); + doc = Jsoup.connect(url).data(postData).post(); - for (int i = 1; i < elements.size(); i++) { - ZonaElement ze = new ZonaElement(); - ze.setZona(zona); - ze.setElement(elements.get(i)); - - records.add(ze); - } - } + Elements elements = doc.select("table#ContentPlaceHolder1_GridNivelesEmbalses > tbody > tr"); - index = 0; - } catch (Exception e) { - log.error(e.getMessage()); + for (int i = 1; i < elements.size(); i++) { + ZonaElement ze = new ZonaElement(); + ze.setZona(zona); + ze.setElement(elements.get(i)); + + records.add(ze); + } } + + index = 0; } /** diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/support/CHGuadalquivirHelper.java b/src/main/java/net/bounceme/chronos/chguadalquivir/support/CHGuadalquivirHelper.java index bccf854..ef8386f 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/support/CHGuadalquivirHelper.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/support/CHGuadalquivirHelper.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import net.bounceme.chronos.chguadalquivir.model.Zona; @@ -32,9 +33,9 @@ @Scope("prototype") @Slf4j public class CHGuadalquivirHelper { - + private static final String VALUE = "value"; - + @Autowired private SimpleDateFormat dateFormat; @@ -64,26 +65,22 @@ public Document retrieveDocument(String url) throws IOException { return Jsoup.connect(url).get(); } + @SneakyThrows public Map initFormData(Document document) { - try { - Map data = new HashMap<>(); - - // Datos requeridos para hacer el POST - String eventValidation = document.select("input[name=__EVENTVALIDATION]").first().attr(VALUE); - String viewState = document.select("input[name=__VIEWSTATE]").first().attr(VALUE); - String viewStateGen = document.select("input[name=__VIEWSTATEGENERATOR]").first().attr(VALUE); - - data.put("__EVENTVALIDATION", eventValidation); - data.put("__VIEWSTATE", viewState); - data.put("__VIEWSTATEGENERATOR", viewStateGen); - - return data; - } catch (Exception e) { - log.error(e.getMessage()); - throw e; - } + Map data = new HashMap<>(); + + // Datos requeridos para hacer el POST + String eventValidation = document.select("input[name=__EVENTVALIDATION]").first().attr(VALUE); + String viewState = document.select("input[name=__VIEWSTATE]").first().attr(VALUE); + String viewStateGen = document.select("input[name=__VIEWSTATEGENERATOR]").first().attr(VALUE); + + data.put("__EVENTVALIDATION", eventValidation); + data.put("__VIEWSTATE", viewState); + data.put("__VIEWSTATEGENERATOR", viewStateGen); + + return data; } - + /** * @param document * @param idForm @@ -91,21 +88,18 @@ public Map initFormData(Document document) { * @return * @throws Exception */ + @SneakyThrows public String getNameFrom(Document document, String idForm, String idSelect) { - try { - Element formElement = document.getElementById(idForm); - if (!Objects.isNull(formElement)) { - Element select = formElement.getElementById(idSelect); - return select.attr("name"); - } - - return StringUtils.EMPTY; - } catch (Exception e) { - log.error(e.getMessage()); - throw e; + + Element formElement = document.getElementById(idForm); + if (!Objects.isNull(formElement)) { + Element select = formElement.getElementById(idSelect); + return select.attr("name"); } + + return StringUtils.EMPTY; } - + /** * @param d * @param numOfDays @@ -114,13 +108,13 @@ public String getNameFrom(Document document, String idForm, String idSelect) { public Date subtractDays(Date date, Integer numOfDays) { LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); - // minus numOfDays - localDateTime = localDateTime.minusDays(numOfDays); + // minus numOfDays + localDateTime = localDateTime.minusDays(numOfDays); - // convert LocalDateTime to date - return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + // convert LocalDateTime to date + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); } - + /** * @param date * @return @@ -128,7 +122,7 @@ public Date subtractDays(Date date, Integer numOfDays) { public String parseDate(Date date) { return dateFormat.format(date); } - + /** * @param input * @param scale diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java b/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java index 0384b74..27a1ef7 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java @@ -38,8 +38,7 @@ public class CHGuadalquivirApplicationTests { @BeforeEach public void setup(){ - status = Status.builder().version("1.8.0_202").platform("Linux").response("OK").build(); - + status = Status.builder().version("1.8.0_202").platform("Linux").response("OK").build(); } @AfterEach diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java index 88f3f11..84a7ac9 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestComponents.java @@ -7,11 +7,16 @@ import java.util.List; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import net.bounceme.chronos.chguadalquivir.model.Execution; +import net.bounceme.chronos.chguadalquivir.model.ExecutionStats; +import net.bounceme.chronos.chguadalquivir.repository.ExecutionStatsRepository; import net.bounceme.chronos.chguadalquivir.repository.impl.RepositoryCollectionCustomImpl; +import net.bounceme.chronos.chguadalquivir.services.ExecutionStatsService; import net.bounceme.chronos.chguadalquivir.support.StatsCalculations; @SpringBootTest @@ -22,6 +27,12 @@ public class TestComponents { @Autowired private RepositoryCollectionCustomImpl repositoryCollectionCustomImpl; + + @Autowired + private ExecutionStatsService executionStatsService; + + @MockBean + private ExecutionStatsRepository executionStatsRepository; @Test public void testStatCalculations() { @@ -36,6 +47,15 @@ public void testStatCalculations() { assertNotNull(variation); } + @Test + public void testExecutionStatsService() { + ExecutionStats executionStats = ExecutionStats.builder().average(1.0).deviation(1.0).variation(1.0).build(); + + Mockito.doReturn(executionStats).when(executionStatsRepository).save(Mockito.isA(ExecutionStats.class)); + + executionStatsService.save(executionStats); + } + @Test public void testRepositoryCollectionCustomImpl() { repositoryCollectionCustomImpl.setCollectionName("collection"); From 536349c1e0bc4db4f7ec6a8baa1bcb0363ab4e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Thu, 2 Feb 2023 00:25:03 +0100 Subject: [PATCH 04/11] remove unused import --- .../java/net/bounceme/chronos/chguadalquivir/TestListeners.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java index 80b3c6a..970d306 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestListeners.java @@ -4,7 +4,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.HashMap; -import java.util.Map; import org.junit.jupiter.api.Test; import org.springframework.batch.core.ExitStatus; From e982addcd7fc7964c5c38eeb8c9a5044240b73dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Thu, 2 Feb 2023 09:06:48 +0100 Subject: [PATCH 05/11] remove unused log --- .../chronos/chguadalquivir/reader/ExecutionsItemReader.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/ExecutionsItemReader.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/ExecutionsItemReader.java index ff755f0..f169013 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/ExecutionsItemReader.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/ExecutionsItemReader.java @@ -8,13 +8,11 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import lombok.extern.slf4j.Slf4j; import net.bounceme.chronos.chguadalquivir.model.Execution; import net.bounceme.chronos.chguadalquivir.repository.ExecutionsRepository; import net.bounceme.chronos.chguadalquivir.support.CHGuadalquivirHelper; @Component -@Slf4j public class ExecutionsItemReader implements ItemReader, InitializingBean { @Autowired From 64c6f8079f8c6a055d79fefae661a9536b09f852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Thu, 2 Feb 2023 09:20:13 +0100 Subject: [PATCH 06/11] Update config test --- src/test/resources/application.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index d1ea947..1afe5d6 100755 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -3,10 +3,10 @@ spring: datasource: batch: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:demo;DB_CLOSE_ON_EXIT=FALSE - username: sa - password: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost/batch_test?allowPublicKeyRetrieval=true&useSSL=false + username: federico + password: MedinaAzahara2468@ postgres: driver-class-name: org.h2.Driver url: jdbc:h2:mem:demo;DB_CLOSE_ON_EXIT=FALSE From a142b073eb2672a9407fd4a0b36eb2009c5d6f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Thu, 2 Feb 2023 17:14:11 +0100 Subject: [PATCH 07/11] Task executor by rest --- .../controller/JobController.java | 76 +++++++++++++++++++ .../chronos/chguadalquivir/model/Task.java | 27 +++++++ .../chronos/chguadalquivir/TestModels.java | 21 +++++ 3 files changed, 124 insertions(+) create mode 100644 src/main/java/net/bounceme/chronos/chguadalquivir/model/Task.java diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java index dcdeb20..2080af6 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java @@ -1,14 +1,44 @@ package net.bounceme.chronos.chguadalquivir.controller; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import javax.validation.Valid; + +import org.springframework.batch.core.ExitStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParametersBuilder; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import lombok.extern.slf4j.Slf4j; import net.bounceme.chronos.chguadalquivir.model.Status; +import net.bounceme.chronos.chguadalquivir.model.Task; @RestController @RequestMapping("/api") +@Slf4j public class JobController { + + @Autowired + private ApplicationContext ctx; + + @Autowired + private JobLauncher jobLauncher; /** * @return @@ -26,4 +56,50 @@ public Status status() { return Status.builder().version(System.getProperty("java.version")) .platform(System.getProperty("os.name")).response("OK").build(); } + + @PostMapping("/execute") + public ResponseEntity executeTask(@Valid @RequestBody Task task, BindingResult result) { + Map response = new HashMap<>(); + + try { + if (result.hasErrors()) { + List errors = result.getFieldErrors().stream() + .map(error -> String.format("El campo '%s' %s", error.getField(), error.getDefaultMessage())) + .collect(Collectors.toList()); + + response.put("errors", errors); + return new ResponseEntity>(response, HttpStatus.BAD_REQUEST); + } + + log.info("Ejecutar: {}", task.getName()); + run(task.getName()); + response.put("mensaje", "Tarea ejecutada correctamente"); + return new ResponseEntity>(response, HttpStatus.OK); + } catch (Exception e) { + response.put("error", e.getMessage()); + return new ResponseEntity>(response, HttpStatus.INTERNAL_SERVER_ERROR); + } + } + + /** + * @param name + * @throws Exception + */ + private void run(String name) throws Exception { + + JobParametersBuilder builder = new JobParametersBuilder(); + builder.addDate("date", new Date()); + + Job job = ctx.getBean(name, Job.class); + if (Objects.isNull(job)) { + throw new Exception("Tarea no encontrada"); + } + + JobExecution result = jobLauncher.run(job, builder.toJobParameters()); + + // Exit on failure + if (ExitStatus.FAILED.equals(result.getExitStatus())) { + throw new Exception("La tarea ha fallado"); + } + } } diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/model/Task.java b/src/main/java/net/bounceme/chronos/chguadalquivir/model/Task.java new file mode 100644 index 0000000..eec89c0 --- /dev/null +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/model/Task.java @@ -0,0 +1,27 @@ +package net.bounceme.chronos.chguadalquivir.model; + +import java.io.Serializable; + +import javax.validation.constraints.NotEmpty; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@AllArgsConstructor +@NoArgsConstructor +@Builder(toBuilder = true) +public class Task implements Serializable { + /** + * + */ + private static final long serialVersionUID = -6557119505034879817L; + + @NotEmpty(message = "no puede estar vacío") + @Getter + @Setter + private String name; + +} diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java b/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java index 4774ca6..61addc1 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/TestModels.java @@ -14,6 +14,7 @@ import net.bounceme.chronos.chguadalquivir.model.Execution; import net.bounceme.chronos.chguadalquivir.model.ExecutionStats; import net.bounceme.chronos.chguadalquivir.model.Status; +import net.bounceme.chronos.chguadalquivir.model.Task; import net.bounceme.chronos.chguadalquivir.model.Zona; import net.bounceme.chronos.chguadalquivir.model.ZonaElement; @@ -160,4 +161,24 @@ public void testStatus() { assertEquals(System.getProperty("os.name"), status.getPlatform()); assertEquals("OK", status.getResponse()); } + + @Test + public void testTask() { + Task task = Task.builder().name("task").build(); + assertNotNull(task); + + assertEquals("task", task.getName()); + + task = new Task("task"); + assertNotNull(task); + + assertEquals("task", task.getName()); + + task = new Task(); + task.setName("task"); + + assertNotNull(task); + + assertEquals("task", task.getName()); + } } From f69ccf1442c0b28599c1f53e68748706d8815ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Thu, 2 Feb 2023 22:52:24 +0100 Subject: [PATCH 08/11] Fixed job not found --- .../controller/JobController.java | 27 ++--- .../CHGuadalquivirApplicationTests.java | 114 ++++++++++++++---- 2 files changed, 106 insertions(+), 35 deletions(-) diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java index 2080af6..ed59d7b 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; import javax.validation.Valid; @@ -14,6 +13,7 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; @@ -86,20 +86,19 @@ public ResponseEntity executeTask(@Valid @RequestBody Task task, BindingResul * @throws Exception */ private void run(String name) throws Exception { - - JobParametersBuilder builder = new JobParametersBuilder(); - builder.addDate("date", new Date()); - - Job job = ctx.getBean(name, Job.class); - if (Objects.isNull(job)) { + try { + JobParametersBuilder builder = new JobParametersBuilder(); + builder.addDate("date", new Date()); + + Job job = ctx.getBean(name, Job.class); + JobExecution result = jobLauncher.run(job, builder.toJobParameters()); + + // Exit on failure + if (ExitStatus.FAILED.equals(result.getExitStatus())) { + throw new Exception("La tarea ha fallado"); + } + } catch (NoSuchBeanDefinitionException e) { throw new Exception("Tarea no encontrada"); } - - JobExecution result = jobLauncher.run(job, builder.toJobParameters()); - - // Exit on failure - if (ExitStatus.FAILED.equals(result.getExitStatus())) { - throw new Exception("La tarea ha fallado"); - } } } diff --git a/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java b/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java index 27a1ef7..64dbe3c 100644 --- a/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java +++ b/src/test/java/net/bounceme/chronos/chguadalquivir/CHGuadalquivirApplicationTests.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; @@ -10,11 +11,23 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +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.launch.JobLauncher; +import org.springframework.batch.test.MetaDataInstanceFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; @@ -23,6 +36,7 @@ import net.bounceme.chronos.chguadalquivir.controller.JobController; import net.bounceme.chronos.chguadalquivir.model.Status; +import net.bounceme.chronos.chguadalquivir.model.Task; @SpringBootTest @AutoConfigureMockMvc @@ -33,20 +47,30 @@ public class CHGuadalquivirApplicationTests { @Autowired private JobController controller; - + + @MockBean + private JobLauncher jobLauncher; + private Status status; - + + private JobExecution resultOK; + + private JobExecution resultFAIL; + @BeforeEach - public void setup(){ - status = Status.builder().version("1.8.0_202").platform("Linux").response("OK").build(); + public void setup() { + status = Status.builder().version("1.8.0_202").platform("Linux").response("OK").build(); + resultOK = MetaDataInstanceFactory.createJobExecution(); + resultOK.setExitStatus(ExitStatus.COMPLETED); + resultFAIL = MetaDataInstanceFactory.createJobExecution(); + resultFAIL.setExitStatus(ExitStatus.FAILED); } - + @AfterEach void tearDown() { - status = null; + status = null; } - @Test void contextLoads() { assertThat(controller).isNotNull(); @@ -57,21 +81,69 @@ public void testIndex() throws Exception { mockMvc.perform(get("/api/")).andDo(print()).andExpect(status().isOk()) .andExpect(content().string(containsString("Hello world"))); } - + @Test public void testStatus() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.get("/api/status"). - contentType(MediaType.APPLICATION_JSON). - content(asJsonString(status))). - andExpect(MockMvcResultMatchers.status().isOk()). - andDo(MockMvcResultHandlers.print()); + mockMvc.perform(MockMvcRequestBuilders.get("/api/status").contentType(MediaType.APPLICATION_JSON) + .content(asJsonString(status))).andExpect(MockMvcResultMatchers.status().isOk()) + .andDo(MockMvcResultHandlers.print()); + } + + @Test + public void testExecute() throws Exception { + Mockito.doReturn(resultOK).when(jobLauncher).run(Mockito.isA(Job.class), Mockito.isA(JobParameters.class)); + + Task task = new Task(); + task.setName("importJob"); + String json = asJsonString(task); + + RequestBuilder requestBuilder = createPostRequestBuilder(json); + + // Response OK + MvcResult result = mockMvc.perform(requestBuilder).andReturn(); + MockHttpServletResponse response = result.getResponse(); + assertEquals(HttpStatus.OK.value(), response.getStatus()); + + // Response 415 + task = new Task(); + json = asJsonString(task); + requestBuilder = createPostRequestBuilder(json); + result = mockMvc.perform(requestBuilder).andReturn(); + response = result.getResponse(); + assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus()); + + // Response 500 + Mockito.doReturn(resultFAIL).when(jobLauncher).run(Mockito.isA(Job.class), Mockito.isA(JobParameters.class)); + + task = new Task(); + task.setName("importJob"); + json = asJsonString(task); + requestBuilder = createPostRequestBuilder(json); + result = mockMvc.perform(requestBuilder).andReturn(); + response = result.getResponse(); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus()); + + // Task not found + task = new Task(); + task.setName("task"); + json = asJsonString(task); + requestBuilder = createPostRequestBuilder(json); + result = mockMvc.perform(requestBuilder).andReturn(); + response = result.getResponse(); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus()); + } + + private RequestBuilder createPostRequestBuilder(String json) { + RequestBuilder requestBuilder = MockMvcRequestBuilders.post("/api/execute").accept(MediaType.APPLICATION_JSON) + .content(json).contentType(MediaType.APPLICATION_JSON); + return requestBuilder; + } + + public static String asJsonString(final Object obj) { + try { + return new ObjectMapper().writeValueAsString(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } } - - public static String asJsonString(final Object obj){ - try{ - return new ObjectMapper().writeValueAsString(obj); - }catch (Exception e){ - throw new RuntimeException(e); - } - } } From dd7ca770a61ae2376491838d6e36d96bd66ed7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Fri, 3 Feb 2023 11:11:47 +0100 Subject: [PATCH 09/11] Fixed bug --- .../reader/mapping/StatExecutionsFieldMapper.java | 2 ++ .../chguadalquivir/reader/mapping/StatExecutionsLineMapper.java | 1 + .../reader/mapping/StatExecutionsLineTokenizer.java | 1 + 3 files changed, 4 insertions(+) diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsFieldMapper.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsFieldMapper.java index a6c4ad1..2797a46 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsFieldMapper.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsFieldMapper.java @@ -1,9 +1,11 @@ package net.bounceme.chronos.chguadalquivir.reader.mapping; import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper; +import org.springframework.stereotype.Component; import net.bounceme.chronos.chguadalquivir.model.Execution; +@Component public class StatExecutionsFieldMapper extends BeanWrapperFieldSetMapper { public StatExecutionsFieldMapper() { diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineMapper.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineMapper.java index e7c3634..9b4cfdb 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineMapper.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineMapper.java @@ -13,6 +13,7 @@ public class StatExecutionsLineMapper extends DefaultLineMapper imple @Autowired private StatExecutionsLineTokenizer lineTokenizer; + @Autowired private StatExecutionsFieldMapper fieldMapper; @Override diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineTokenizer.java b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineTokenizer.java index 1896634..4414015 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineTokenizer.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/reader/mapping/StatExecutionsLineTokenizer.java @@ -15,5 +15,6 @@ public void afterPropertiesSet() { private void initialize() { String[] names = new String[] { "id", "value", "executionTime" }; setNames(names); + setDelimiter(DELIMITER_COMMA); } } From f2ee1cb21e5fe497c1217b6decea9bc944df68bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Fri, 3 Feb 2023 11:23:14 +0100 Subject: [PATCH 10/11] Refactor response --- .../chronos/chguadalquivir/controller/JobController.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java index ed59d7b..44da021 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java @@ -52,9 +52,11 @@ public String index() { * @return */ @GetMapping("/status") - public Status status() { - return Status.builder().version(System.getProperty("java.version")) + public ResponseEntity status() { + Status status = Status.builder().version(System.getProperty("java.version")) .platform(System.getProperty("os.name")).response("OK").build(); + + return new ResponseEntity<>(status, HttpStatus.OK); } @PostMapping("/execute") From 067f17e0007ac5a526eac0699e15eeb1e002af28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Mart=C3=ADn=20Lara?= Date: Fri, 3 Feb 2023 11:25:19 +0100 Subject: [PATCH 11/11] Response refactor --- .../chronos/chguadalquivir/controller/JobController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java index 44da021..4fc276b 100644 --- a/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java +++ b/src/main/java/net/bounceme/chronos/chguadalquivir/controller/JobController.java @@ -60,7 +60,7 @@ public ResponseEntity status() { } @PostMapping("/execute") - public ResponseEntity executeTask(@Valid @RequestBody Task task, BindingResult result) { + public ResponseEntity> executeTask(@Valid @RequestBody Task task, BindingResult result) { Map response = new HashMap<>(); try { @@ -70,16 +70,16 @@ public ResponseEntity executeTask(@Valid @RequestBody Task task, BindingResul .collect(Collectors.toList()); response.put("errors", errors); - return new ResponseEntity>(response, HttpStatus.BAD_REQUEST); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); } log.info("Ejecutar: {}", task.getName()); run(task.getName()); response.put("mensaje", "Tarea ejecutada correctamente"); - return new ResponseEntity>(response, HttpStatus.OK); + return new ResponseEntity<>(response, HttpStatus.OK); } catch (Exception e) { response.put("error", e.getMessage()); - return new ResponseEntity>(response, HttpStatus.INTERNAL_SERVER_ERROR); + return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR); } }