Skip to content

Commit

Permalink
Add converters for Java Date/Time
Browse files Browse the repository at this point in the history
Added Tests to the PR --Glenn
  • Loading branch information
corneil authored and cppwfs committed Oct 30, 2023
1 parent 52aa41e commit 579e439
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.dataflow.server.converter;

import java.time.format.DateTimeFormatter;

/**
* Base class for date/time converters.
* To be discarded when moving to Boot 3.x and the converters from org.springframework.batch.core.converter used instead.
* @author Mahmoud Ben Hassine
* @author Corneil du Plessis
* @since 2.11.2
*/
@Deprecated
class AbstractDateTimeConverter {

protected DateTimeFormatter instantFormatter = DateTimeFormatter.ISO_INSTANT;

protected DateTimeFormatter localDateFormatter = DateTimeFormatter.ISO_LOCAL_DATE;

protected DateTimeFormatter localTimeFormatter = DateTimeFormatter.ISO_LOCAL_TIME;

protected DateTimeFormatter localDateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.dataflow.server.converter;

import java.util.Date;

import org.springframework.core.convert.converter.Converter;

/**
* {@link Converter} implementation from {@link Date} to {@link String}.
* To be discarded when moving to Boot 3.x and the converters from org.springframework.batch.core.converter used instead.
* This converter formats dates according to the
* {@link java.time.format.DateTimeFormatter#ISO_INSTANT} format.
* To be discarded when moving to Boot 3.x and the converters from org.springframework.batch.core.converter used instead.
* @author Mahmoud Ben Hassine
* @author Corneil du Plessis
* @since 2.11.2
*/
@Deprecated
public class DateToStringConverter extends AbstractDateTimeConverter implements Converter<Date, String> {

@Override
public String convert(Date source) {
return super.instantFormatter.format(source.toInstant());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.dataflow.server.converter;

import java.time.Instant;
import java.util.Date;

import org.springframework.core.convert.converter.Converter;

/**
* {@link Converter} implementation from {@link String} to {@link Date}.
*
* This converter expects strings in the
* {@link java.time.format.DateTimeFormatter#ISO_INSTANT} format.
* To be discarded when moving to Boot 3.x and the converters from org.springframework.batch.core.converter used instead.
* @author Mahmoud Ben Hassine
* @author Corneil du Plessis
* @since 2.11.2
*/
@Deprecated
public class StringToDateConverter extends AbstractDateTimeConverter implements Converter<String, Date> {

@Override
public Date convert(String source) {
return Date.from(super.instantFormatter.parse(source, Instant::from));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,15 @@
import org.springframework.cloud.dataflow.schema.SchemaVersionTarget;
import org.springframework.cloud.dataflow.schema.service.SchemaService;
import org.springframework.cloud.dataflow.server.batch.JobService;
import org.springframework.cloud.dataflow.server.converter.DateToStringConverter;
import org.springframework.cloud.dataflow.server.converter.StringToDateConverter;
import org.springframework.cloud.dataflow.server.service.JobServiceContainer;
import org.springframework.cloud.dataflow.server.service.impl.OffsetOutOfBoundsException;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.convert.Jsr310Converters;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
Expand Down Expand Up @@ -220,6 +223,10 @@ public JdbcAggregateJobQueryDao(DataSource dataSource, SchemaService schemaServi
this.schemaService = schemaService;
this.jobServiceContainer = jobServiceContainer;

conversionService.addConverter(new DateToStringConverter());
conversionService.addConverter(new StringToDateConverter());
Jsr310Converters.getConvertersToRegister().forEach(conversionService::addConverter);

allExecutionsPagingQueryProvider = getPagingQueryProvider(FIELDS_WITH_STEP_COUNT, FROM_CLAUSE_TASK_EXEC_BATCH, null);

executionsByDateRangeWithStepCountPagingQueryProvider = getPagingQueryProvider(FIELDS_WITH_STEP_COUNT, FROM_CLAUSE_TASK_EXEC_BATCH, DATE_RANGE_FILTER);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@

import java.util.Date;

import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -36,7 +37,6 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.dataflow.aggregate.task.AggregateExecutionSupport;
import org.springframework.cloud.dataflow.aggregate.task.TaskDefinitionReader;
import org.springframework.cloud.dataflow.schema.AppBootSchemaVersion;
import org.springframework.cloud.dataflow.schema.SchemaVersionTarget;
import org.springframework.cloud.dataflow.server.config.apps.CommonApplicationProperties;
import org.springframework.cloud.dataflow.server.configuration.JobDependencies;
Expand All @@ -47,6 +47,7 @@
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
Expand All @@ -57,7 +58,6 @@
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
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.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -204,23 +204,34 @@ public void testGetExecution() throws Exception {
.andExpect(jsonPath("$.jobExecution.stepExecutions", hasSize(1)));
}

@Test
public void testGetExecutionWithJobProperties() throws Exception {
MvcResult result = mockMvc.perform(get("/jobs/executions/10").accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.executionId", is(10)))
.andExpect(jsonPath("$.jobExecution.jobParameters.parameters", Matchers.hasKey(("javaUtilDate"))))
.andExpect(jsonPath("$.jobExecution.stepExecutions", hasSize(1))).andReturn();
assertThat(result.getResponse().getContentAsString()).contains("{\"identifying\":true,\"value\":\"2023-06-07T04:00:00.000+00:00\",\"type\":\"DATE\"}");
}

@Test
public void testGetAllExecutionsFailed() throws Exception {
createDirtyJob();
// expecting to ignore dirty job
mockMvc.perform(get("/jobs/executions/").accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.jobExecutionResourceList", hasSize(9)));
.andExpect(jsonPath("$._embedded.jobExecutionResourceList", hasSize(10)));
}

@Test
public void testGetAllExecutions() throws Exception {
mockMvc.perform(get("/jobs/executions/").accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.jobExecutionResourceList", hasSize(9)))
.andExpect(jsonPath("$._embedded.jobExecutionResourceList[*].executionId", containsInAnyOrder(9, 8, 7, 6, 5, 4, 3, 2, 1)));
.andExpect(jsonPath("$._embedded.jobExecutionResourceList", hasSize(10)))
.andExpect(jsonPath("$._embedded.jobExecutionResourceList[*].executionId", containsInAnyOrder(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)));
}

@Test
Expand Down Expand Up @@ -282,7 +293,7 @@ public void testFilteringByUnknownStatus() throws Exception {
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.jobExecutionResourceList", hasSize(4)));
.andExpect(jsonPath("$._embedded.jobExecutionResourceList", hasSize(5)));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ public void testJobExecutionThinControllerConstructorMissingRepository() {
@Test
public void testGetAllExecutionsJobExecutionOnly() throws Exception {
mockMvc.perform(get("/jobs/thinexecutions").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList[*].taskExecutionId", containsInAnyOrder(8, 7, 6, 5, 4, 3, 3, 2, 1)))
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList[*].taskExecutionId", containsInAnyOrder(9, 8, 7, 6, 5, 4, 3, 3, 2, 1)))
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList[0].stepExecutionCount", is(1)))
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList", hasSize(9)));
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList", hasSize(10)));
}

@Test
Expand All @@ -136,9 +136,9 @@ public void testGetExecutionsByDateRange() throws Exception {
new SimpleDateFormat(TimeUtils.DEFAULT_DATAFLOW_DATE_TIME_PARAMETER_FORMAT_PATTERN)
.format(toDate))
.accept(MediaType.APPLICATION_JSON)).andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList[*].taskExecutionId", containsInAnyOrder(8, 7, 6, 5, 4, 3, 3, 2, 1)))
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList[*].taskExecutionId", containsInAnyOrder(9, 8, 7, 6, 5, 4, 3, 3, 2, 1)))
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList[0].stepExecutionCount", is(1)))
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList", hasSize(9)));
.andExpect(jsonPath("$._embedded.jobExecutionThinResourceList", hasSize(10)));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018 the original author or authors.
* Copyright 2018-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,14 +16,21 @@

package org.springframework.cloud.dataflow.server.controller;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.StepExecution;

import org.springframework.batch.core.repository.JobRepository;
import org.springframework.cloud.dataflow.aggregate.task.AggregateExecutionSupport;
import org.springframework.cloud.dataflow.aggregate.task.TaskDefinitionReader;
Expand Down Expand Up @@ -59,6 +66,8 @@ class JobExecutionUtils

final static String JOB_NAME_ORIG = BASE_JOB_NAME + "_ORIG";

final static String JOB_NAME_ORIG_WITH_PARAM = BASE_JOB_NAME + "_ORIG_WITH_PARAM";

final static String JOB_NAME_STOPPED = BASE_JOB_NAME + "_FOO_STOPPED";

final static String JOB_NAME_STARTED = BASE_JOB_NAME + "_FOO_STARTED";
Expand Down Expand Up @@ -92,6 +101,20 @@ static MockMvc createBaseJobExecutionMockMvc(
JobExecutionUtils.createSampleJob(jobRepositoryContainer, taskBatchDaoContainer, taskExecutionDaoContainer, aggregateExecutionSupport, JOB_NAME_STOPPED, 1, BatchStatus.STOPPED, taskDefinitionReader);
JobExecutionUtils.createSampleJob(jobRepositoryContainer, taskBatchDaoContainer, taskExecutionDaoContainer, aggregateExecutionSupport, JOB_NAME_FAILED1, 1, BatchStatus.FAILED, taskDefinitionReader);
JobExecutionUtils.createSampleJob(jobRepositoryContainer, taskBatchDaoContainer, taskExecutionDaoContainer, aggregateExecutionSupport, JOB_NAME_FAILED2, 1, BatchStatus.FAILED, taskDefinitionReader);

Map<String, JobParameter> jobParameterMap = new HashMap<>();
String dateInString = "7-Jun-2023";
SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH);
Date date = null;
try {
date = formatter.parse(dateInString);
} catch (ParseException e) {
throw new RuntimeException(e);
}
jobParameterMap.put("javaUtilDate", new JobParameter(date));
JobExecutionUtils.createSampleJob(jobRepositoryContainer, taskBatchDaoContainer, taskExecutionDaoContainer, aggregateExecutionSupport, JOB_NAME_ORIG_WITH_PARAM, 1, BatchStatus.UNKNOWN, taskDefinitionReader, new JobParameters(jobParameterMap));


for (HttpMessageConverter<?> converter : adapter.getMessageConverters()) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
final MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
Expand Down Expand Up @@ -119,7 +142,31 @@ private static void createSampleJob(
jobName,
jobExecutionCount,
BatchStatus.UNKNOWN,
taskDefinitionReader
taskDefinitionReader,
new JobParameters()
);
}

private static void createSampleJob(
JobRepositoryContainer jobRepositoryContainer,
TaskBatchDaoContainer taskBatchDaoContainer,
TaskExecutionDaoContainer taskExecutionDaoContainer,
AggregateExecutionSupport aggregateExecutionSupport,
String jobName,
int jobExecutionCount,
BatchStatus status,
TaskDefinitionReader taskDefinitionReader
) {
createSampleJob(
jobRepositoryContainer,
taskBatchDaoContainer,
taskExecutionDaoContainer,
aggregateExecutionSupport,
jobName,
jobExecutionCount,
status,
taskDefinitionReader,
new JobParameters()
);
}

Expand All @@ -131,17 +178,18 @@ private static void createSampleJob(
String jobName,
int jobExecutionCount,
BatchStatus status,
TaskDefinitionReader taskDefinitionReader
TaskDefinitionReader taskDefinitionReader,
JobParameters jobParameters
) {
SchemaVersionTarget schemaVersionTarget = aggregateExecutionSupport.findSchemaVersionTarget(jobName, taskDefinitionReader);
JobRepository jobRepository = jobRepositoryContainer.get(schemaVersionTarget.getName());
JobInstance instance = jobRepository.createJobInstance(jobName, new JobParameters());
JobInstance instance = jobRepository.createJobInstance(jobName, jobParameters);
TaskExecutionDao taskExecutionDao = taskExecutionDaoContainer.get(schemaVersionTarget.getName());
TaskExecution taskExecution = taskExecutionDao.createTaskExecution(jobName, new Date(), new ArrayList<>(), null);
JobExecution jobExecution;
TaskBatchDao taskBatchDao = taskBatchDaoContainer.get(schemaVersionTarget.getName());
for (int i = 0; i < jobExecutionCount; i++) {
jobExecution = jobRepository.createJobExecution(instance, new JobParameters(), null);
jobExecution = jobRepository.createJobExecution(instance, jobParameters, null);
StepExecution stepExecution = new StepExecution("foo", jobExecution, 1L);
stepExecution.setId(null);
jobRepository.add(stepExecution);
Expand Down

0 comments on commit 579e439

Please sign in to comment.