From 198bf9d15b3135d68c1431facf19ddd85b75b3b1 Mon Sep 17 00:00:00 2001 From: Corneil du Plessis Date: Thu, 14 Nov 2024 16:10:53 +0200 Subject: [PATCH] Update Task Thin Executions list function for parity with 2.11.x Updated task thin executions link handing and added extra test for parity with 2.11.x. Update minimum supported version of client. Added test for invalid old version. See #6062 --- .../rest/documentation/ApiDocumentation.java | 4 + .../TaskExecutionsDocumentation.java | 23 ++++ .../src/main/asciidoc/api-guide.adoc | 127 ++++++++++++++++++ .../dataflow/rest/client/TaskOperations.java | 8 ++ .../dataflow/rest/client/TaskTemplate.java | 29 +++- .../rest/client/TaskTemplateTests.java | 6 +- .../DataFlowControllerAutoConfiguration.java | 4 +- .../server/controller/RootController.java | 1 + .../TaskExecutionThinController.java | 12 +- .../repository/TaskDefinitionRepository.java | 1 + .../server/configuration/JobDependencies.java | 4 +- .../configuration/TestDependencies.java | 4 +- .../TaskExecutionControllerTests.java | 18 ++- .../resources/root-controller-result.json | 4 + 14 files changed, 226 insertions(+), 19 deletions(-) diff --git a/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java b/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java index d024765da6..5d07a4c7f8 100644 --- a/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java +++ b/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java @@ -124,6 +124,7 @@ public void index() throws Exception { linkWithRel("tasks/platforms").description("Provides platform accounts for launching tasks. The results can be filtered to show the platforms that support scheduling by adding a request parameter of 'schedulesEnabled=true"), linkWithRel("tasks/logs").description("Retrieve the task application log"), linkWithRel("tasks/thinexecutions").description("Returns thin Task executions"), + linkWithRel("tasks/thinexecutions/name").description("Returns all thin Task executions for a given Task name"), linkWithRel("schema/versions").description("List of Spring Boot related schemas"), linkWithRel("schema/targets").description("List of schema targets"), @@ -234,6 +235,9 @@ public void index() throws Exception { fieldWithPath("_links.tasks/thinexecutions.href").description("Link to the tasks/thinexecutions"), + fieldWithPath("_links.tasks/thinexecutions/name.href").description("Link to the tasks/thinexecutions/name"), + fieldWithPath("_links.tasks/thinexecutions/name.templated").type(JsonFieldType.BOOLEAN).optional().description("Link to the tasks/thinexecutions/name is templated"), + fieldWithPath("_links.tasks/schedules.href").description("Link to the tasks/executions/schedules"), fieldWithPath("_links.tasks/schedules/instances.href").description("Link to the tasks/schedules/instances"), fieldWithPath("_links.tasks/schedules/instances.templated").type(JsonFieldType.BOOLEAN).optional().description("Link tasks/schedules/instances is templated"), diff --git a/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/TaskExecutionsDocumentation.java b/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/TaskExecutionsDocumentation.java index 6d4690a841..91925ac91e 100644 --- a/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/TaskExecutionsDocumentation.java +++ b/spring-cloud-dataflow-classic-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/TaskExecutionsDocumentation.java @@ -314,6 +314,29 @@ public void listTaskExecutionsByName() throws Exception { subsectionWithPath("page").description("Pagination properties")))); } + @Test + public void listTaskThinExecutionsByName() throws Exception { + this.mockMvc.perform( + get("/tasks/thinexecutions") + .param("name", "taskB") + .param("page", "0") + .param("size", "10") + ) + .andExpect(status().isOk()).andDo(this.documentationHandler.document( + requestParameters( + parameterWithName("page") + .description("The zero-based page number (optional)"), + parameterWithName("size") + .description("The requested page size (optional)"), + parameterWithName("name") + .description("The name associated with the task execution")), + responseFields( + subsectionWithPath("_embedded.taskExecutionThinResourceList") + .description("Contains a collection of Task Executions/"), + subsectionWithPath("_links.self").description("Link to the task execution resource"), + subsectionWithPath("page").description("Pagination properties")))); + } + @Test public void stopTask() throws Exception { this.mockMvc.perform( diff --git a/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc b/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc index c96804b6da..4e9c99412c 100644 --- a/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc +++ b/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc @@ -1862,6 +1862,8 @@ The following topics provide more details: * <> * <> * <> +* <> +* <> * <> * <> * <> @@ -2066,7 +2068,132 @@ include::{snippets}/task-executions-documentation/list-task-executions-by-name/c include::{snippets}/task-executions-documentation/list-task-executions-by-name/http-response.adoc[] +[[api-guide-resources-task-thin-executions-list]] +==== List All Task Thin Executions +The task executions endpoint lets you list all task executions with only top-level data. +The following topics provide more details: + +* <> +* <> +* <> +* <> + +[[api-guide-resources-task-thin-executions-list-request-structure]] +===== Request Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions/http-request.adoc[] + +[[api-guide-resources-task-thin-executions-list-request-parameters]] +===== Request Parameters + +include::{snippets}/task-executions-documentation/list-task-thin-executions/query-parameters.adoc[] + +[[api-guide-resources-task-thin-executions-list-example-request]] +===== Example Request + +include::{snippets}/task-executions-documentation/list-task-thin-executions/curl-request.adoc[] + +[[api-guide-resources-task-thin-executions-list-response-structure]] +===== Response Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions/http-response.adoc[] + +[[api-guide-resources-task-thin-executions-list-by-name]] +==== List All Task Thin Executions With a Specified Task Name + +The task thin executions endpoint lets you list task executions with a specified task name. +The following topics provide more details: + +* <> +* <> +* <> +* <> + + + +[[api-guide-resources-task-thin-executions-list-by-name-request-structure]] +===== Request Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/http-request.adoc[] + + + +[[api-guide-resources-task-thin-executions-list-by-name-request-parameters]] +===== Request Parameters + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/query-parameters.adoc[] + + + +[[api-guide-resources-task-thin-executions-list-by-name-example-request]] +===== Example Request + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/curl-request.adoc[] + +[[api-guide-resources-task-thin-executions-list-by-name-response-structure]] +===== Response Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/http-response.adoc[] + +[[api-guide-resources-task-thin-executions-list-request-structure]] +===== Request Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions/http-request.adoc[] + + + +[[api-guide-resources-task-thin-executions-list-request-parameters]] +===== Request Parameters + +include::{snippets}/task-executions-documentation/list-task-thin-executions/request-parameters.adoc[] + + + +[[api-guide-resources-task-thin-executions-list-example-request]] +===== Example Request + +include::{snippets}/task-executions-documentation/list-task-thin-executions/curl-request.adoc[] + + + +[[api-guide-resources-task-thin-executions-list-response-structure]] +===== Response Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions/http-response.adoc[] + + + +[[api-guide-resources-task-thin-executions-list-by-name]] +==== List All Task Thin Executions With a Specified Task Name + +The task thin executions endpoint lets you list task executions with a specified task name. +The following topics provide more details: + +* <> +* <> +* <> +* <> + +[[api-guide-resources-task-thin-executions-list-by-name-request-structure]] +===== Request Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/http-request.adoc[] + +[[api-guide-resources-task-thin-executions-list-by-name-request-parameters]] +===== Request Parameters + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/request-parameters.adoc[] + +[[api-guide-resources-task-thin-executions-list-by-name-example-request]] +===== Example Request + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/curl-request.adoc[] + +[[api-guide-resources-task-thin-executions-list-by-name-response-structure]] +===== Response Structure + +include::{snippets}/task-executions-documentation/list-task-thin-executions-by-name/http-response.adoc[] [[api-guide-resources-task-executions-detail]] ==== Task Execution Detail diff --git a/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskOperations.java b/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskOperations.java index c38cc83135..b47bfba585 100644 --- a/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskOperations.java +++ b/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskOperations.java @@ -123,6 +123,14 @@ public interface TaskOperations { */ PagedModel executionListByTaskName(String taskName); + /** + * List task thin executions known to the system filtered by task name. + * + * @param taskName of the executions. + * @return the list of thin task executions known to the system. + */ + PagedModel thinExecutionListByTaskName(String taskName); + /** * Return the {@link TaskExecutionResource} for the id specified. * diff --git a/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskTemplate.java b/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskTemplate.java index 1d84bf20a6..d884e9cd68 100644 --- a/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskTemplate.java +++ b/spring-cloud-dataflow-rest-client/src/main/java/org/springframework/cloud/dataflow/rest/client/TaskTemplate.java @@ -39,7 +39,6 @@ import org.springframework.cloud.dataflow.schema.SchemaVersionTarget; import org.springframework.core.ParameterizedTypeReference; import org.springframework.hateoas.Link; -import org.springframework.hateoas.PagedModel; import org.springframework.hateoas.RepresentationModel; import org.springframework.http.HttpMethod; import org.springframework.util.Assert; @@ -71,11 +70,14 @@ public class TaskTemplate implements TaskOperations { private static final String VALIDATION_THIN_TASK_VERSION = "2.11.3"; private static final String VALIDATION_TASK_LAUNCH_VERSION = "2.11.0"; + private static final String VALIDATION_TASK_THIN_BY_NAME = "2.11.6"; private static final String EXECUTIONS_RELATION = "tasks/executions"; private static final String THIN_EXECUTIONS_RELATION = "tasks/thinexecutions"; + private static final String THIN_EXECUTIONS_BY_NAME_RELATION = "tasks/thinexecutions/name"; + private static final String EXECUTIONS_CURRENT_RELATION = "tasks/executions/current"; private static final String EXECUTION_RELATION = "tasks/executions/execution"; @@ -102,6 +104,8 @@ public class TaskTemplate implements TaskOperations { private final Link thinExecutionsLink; + private final Link thinExecutionsByNameLink; + private final Link executionLink; private final Link executionLaunchLink; @@ -150,13 +154,18 @@ public class TaskTemplate implements TaskOperations { this.validationLink = null; } - // TODO early 2.11.3-SNAPSHOT version didn't have this. Remove && resources.getLink(THIN_EXECUTIONS_RELATION).isPresent() when releasing 2.11.3 - if(VersionUtils.isDataFlowServerVersionGreaterThanOrEqualToRequiredVersion(version, VALIDATION_THIN_TASK_VERSION) && resources.getLink(THIN_EXECUTIONS_RELATION).isPresent()) { + if(VersionUtils.isDataFlowServerVersionGreaterThanOrEqualToRequiredVersion(version, VALIDATION_THIN_TASK_VERSION)) { Assert.isTrue(resources.getLink(THIN_EXECUTIONS_RELATION).isPresent(), () -> THIN_EXECUTIONS_RELATION + " relation is required"); this.thinExecutionsLink = resources.getLink(THIN_EXECUTIONS_RELATION).get(); } else { this.thinExecutionsLink = null; } + if(VersionUtils.isDataFlowServerVersionGreaterThanOrEqualToRequiredVersion(version, VALIDATION_TASK_THIN_BY_NAME)) { + Assert.isTrue(resources.getLink(THIN_EXECUTIONS_BY_NAME_RELATION).isPresent(), () -> THIN_EXECUTIONS_BY_NAME_RELATION + " relation is required"); + this.thinExecutionsByNameLink = resources.getLink(THIN_EXECUTIONS_BY_NAME_RELATION).get(); + } else { + this.thinExecutionsByNameLink = null; + } if (VersionUtils.isDataFlowServerVersionGreaterThanOrEqualToRequiredVersion(version, VALIDATION_TASK_LAUNCH_VERSION)) { Assert.isTrue(resources.getLink(EXECUTION_LAUNCH_RELATION).isPresent(), () -> EXECUTION_LAUNCH_RELATION + " relation is required"); @@ -283,7 +292,7 @@ public TaskExecutionResource.Page executionList() { } @Override - public PagedModel thinExecutionList() { + public TaskExecutionThinResource.Page thinExecutionList() { if(thinExecutionsLink != null) { return restTemplate.getForObject(thinExecutionsLink.getHref(), TaskExecutionThinResource.Page.class); } else { @@ -291,10 +300,18 @@ public PagedModel thinExecutionList() { } } + @Override + public TaskExecutionThinResource.Page thinExecutionListByTaskName(String taskName) { + if(thinExecutionsByNameLink != null) { + return restTemplate.getForObject(thinExecutionsByNameLink.expand(taskName).getHref(), TaskExecutionThinResource.Page.class); + } else { + return restTemplate.getForObject(executionByNameLink.expand(taskName).getHref(), TaskExecutionThinResource.Page.class); + } + } + @Override public TaskExecutionResource.Page executionListByTaskName(String taskName) { - return restTemplate.getForObject(executionByNameLink.expand(taskName).getHref(), - TaskExecutionResource.Page.class); + return restTemplate.getForObject(executionByNameLink.expand(taskName).getHref(), TaskExecutionResource.Page.class); } @Override diff --git a/spring-cloud-dataflow-rest-client/src/test/java/org/springframework/cloud/dataflow/rest/client/TaskTemplateTests.java b/spring-cloud-dataflow-rest-client/src/test/java/org/springframework/cloud/dataflow/rest/client/TaskTemplateTests.java index d159d0b00b..003cf55c9f 100644 --- a/spring-cloud-dataflow-rest-client/src/test/java/org/springframework/cloud/dataflow/rest/client/TaskTemplateTests.java +++ b/spring-cloud-dataflow-rest-client/src/test/java/org/springframework/cloud/dataflow/rest/client/TaskTemplateTests.java @@ -54,14 +54,12 @@ public void testOldDataFlow() { @Test public void testMinDataFlow() { - validateExecutionLinkPresent("1.7.0"); + validateExecutionLinkPresent("2.10.0"); } @Test public void testFutureDataFlow() { - validateExecutionLinkPresent("1.8.0"); - validateExecutionLinkPresent("1.9.0"); - validateExecutionLinkPresent("2.0.0"); + validateExecutionLinkPresent("2.11.6"); } diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataFlowControllerAutoConfiguration.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataFlowControllerAutoConfiguration.java index 122f69ca91..57284eadca 100644 --- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataFlowControllerAutoConfiguration.java +++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/config/DataFlowControllerAutoConfiguration.java @@ -300,8 +300,8 @@ public TaskExecutionController taskExecutionController( } @Bean - public TaskExecutionThinController taskExecutionThinController(AggregateTaskExplorer aggregateTaskExplorer, TaskJobService taskJobService) { - return new TaskExecutionThinController(aggregateTaskExplorer, taskJobService); + public TaskExecutionThinController taskExecutionThinController(AggregateTaskExplorer aggregateTaskExplorer, TaskDefinitionRepository taskDefinitionRepository, TaskJobService taskJobService) { + return new TaskExecutionThinController(aggregateTaskExplorer, taskDefinitionRepository, taskJobService); } @Bean diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/RootController.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/RootController.java index ff55fb6ec3..d45beb0a95 100644 --- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/RootController.java +++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/RootController.java @@ -159,6 +159,7 @@ public RootResource info() { root.add(linkTo(methodOn(TasksInfoController.class).getInfo(null, null, null)).withRel("tasks/info/executions")); root.add(linkTo(methodOn(TaskLogsController.class).getLog(null, null, null)).withRel("tasks/logs")); root.add(linkTo(methodOn(TaskExecutionThinController.class).listTasks(null, null)).withRel("tasks/thinexecutions")); + root.add(linkTo(methodOn(TaskExecutionThinController.class).retrieveTasksByName(null, null, null)).withRel("tasks/thinexecutions/name")); if (featuresProperties.isSchedulesEnabled()) { root.add(entityLinks.linkToCollectionResource(ScheduleInfoResource.class).withRel("tasks/schedules")); String scheduleTemplated = entityLinks.linkToCollectionResource(ScheduleInfoResource.class).getHref() diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionThinController.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionThinController.java index 210911b3c3..da44e34c76 100644 --- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionThinController.java +++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/controller/TaskExecutionThinController.java @@ -18,6 +18,8 @@ import org.springframework.cloud.dataflow.aggregate.task.AggregateTaskExplorer; import org.springframework.cloud.dataflow.rest.resource.TaskExecutionThinResource; import org.springframework.cloud.dataflow.schema.AggregateTaskExecution; +import org.springframework.cloud.dataflow.server.repository.NoSuchTaskDefinitionException; +import org.springframework.cloud.dataflow.server.repository.TaskDefinitionRepository; import org.springframework.cloud.dataflow.server.service.TaskJobService; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -46,13 +48,15 @@ public class TaskExecutionThinController { private final AggregateTaskExplorer explorer; + private final TaskDefinitionRepository taskDefinitionRepository; private final TaskExecutionThinResourceAssembler resourceAssembler; private final TaskJobService taskJobService; - public TaskExecutionThinController(AggregateTaskExplorer explorer, TaskJobService taskJobService) { + public TaskExecutionThinController(AggregateTaskExplorer explorer, TaskDefinitionRepository taskDefinitionRepository, TaskJobService taskJobService) { this.explorer = explorer; - this.taskJobService = taskJobService; + this.taskDefinitionRepository = taskDefinitionRepository; + this.taskJobService = taskJobService; this.resourceAssembler = new TaskExecutionThinResourceAssembler(); } @@ -68,6 +72,10 @@ public PagedModel listTasks(Pageable pageable, PagedR @ResponseStatus(HttpStatus.OK) public PagedModel retrieveTasksByName(@RequestParam("name") String taskName, Pageable pageable, PagedResourcesAssembler pagedAssembler) { + long tasks = this.taskDefinitionRepository.countByTaskName(taskName); + if(tasks == 0) { + throw new NoSuchTaskDefinitionException(taskName); + } Page page = this.explorer.findTaskExecutionsByName(taskName, pageable); taskJobService.populateComposeTaskRunnerStatus(page.getContent()); return pagedAssembler.toModel(page, resourceAssembler); diff --git a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/repository/TaskDefinitionRepository.java b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/repository/TaskDefinitionRepository.java index eb7464fae6..d67268bef5 100644 --- a/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/repository/TaskDefinitionRepository.java +++ b/spring-cloud-dataflow-server-core/src/main/java/org/springframework/cloud/dataflow/server/repository/TaskDefinitionRepository.java @@ -46,4 +46,5 @@ public interface TaskDefinitionRepository extends KeyValueRepository