Skip to content

Commit

Permalink
Add task thin executions by name and fix missing docs. (#5994)
Browse files Browse the repository at this point in the history
* Change TaskTemplate to use task/thinexecutions instead of task/executions.
 Add task thin executions by name.
 Update api-guide.adoc with links to generated documentation.
 Modify TaskCommands to use thin executions since it provides the data required.
 Removed duplicate entries from api docs.

 Fixes #5991
 Fixes #5973

* Fix root controller expected response with thinexecutions/name.

* Add rest docs generation with full profile to CI PR.

* Added a test for invalid name returning empty page on thinexecutions.

Fixes #5989

* Updated thin taskexecutions by name to return 404 when definition is not found.
Provide task/execution and thin executions by name test when task is not defined and where it is defined.
Removed excess `andDo(print())` from tests.

* Fix test with added definition for covering all tasks/executions and tasks/thinexecutions name search scenarios.

* Revert CI-PR.
  • Loading branch information
Corneil du Plessis authored Oct 24, 2024
1 parent b27f0c2 commit 78085ce
Show file tree
Hide file tree
Showing 19 changed files with 242 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,14 @@ void index() throws Exception {
linkWithRel("tasks/executions/external").description("Returns Task execution by external id"),
linkWithRel("tasks/executions/current").description("Provides the current count of running tasks"),
linkWithRel("tasks/thinexecutions").description("Returns thin Task executions"),
linkWithRel("tasks/thinexecutions/name").description("Returns thin Task executions for a given task name"),
linkWithRel("tasks/info/executions").description("Provides the task executions info"),
linkWithRel("tasks/schedules").description("Provides schedule information of tasks"),
linkWithRel("tasks/schedules/instances").description("Provides schedule information of a specific task "),
linkWithRel("tasks/executions/name").description("Returns all task executions for a given Task name"),
linkWithRel("tasks/executions/execution").description("Provides details for a specific task execution"),
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("streams/definitions").description("Exposes the Streams resource"),
linkWithRel("streams/definitions/definition").description("Handle a specific Stream definition"),
Expand Down Expand Up @@ -221,14 +221,15 @@ 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").description("Link to the tasks/thinexecutions/name is templated"),

fieldWithPath("_links.tasks/info/executions.href").description("Link to the tasks/info/executions"),
fieldWithPath("_links.tasks/info/executions.templated").type(JsonFieldType.BOOLEAN).optional().description("Link tasks/info is templated"),

fieldWithPath("_links.tasks/logs.href").description("Link to the tasks/logs"),
fieldWithPath("_links.tasks/logs.templated").type(JsonFieldType.BOOLEAN).optional().description("Link tasks/logs is templated"),

fieldWithPath("_links.tasks/thinexecutions.href").description("Link to the tasks/thinexecutions"),

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"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,9 @@ void listTaskThinExecutions() throws Exception {
}

@Test
void listTaskExecutionsByName() throws Exception {
void listTaskThinExecutionsByName() throws Exception {
this.mockMvc.perform(
get("/tasks/executions")
get("/tasks/thinexecutions")
.queryParam("name", "taskB")
.queryParam("page", "0")
.queryParam("size", "10")
Expand All @@ -328,11 +328,33 @@ void listTaskExecutionsByName() throws Exception {
parameterWithName("name")
.description("The name associated with the task execution")),
responseFields(
subsectionWithPath("_embedded.taskExecutionResourceList")
.description("Contains a collection of Task Executions/"),
subsectionWithPath("_embedded.taskExecutionThinResourceList")
.description("Contains a collection of thin Task Executions/"),
subsectionWithPath("_links.self").description("Link to the task execution resource"),
subsectionWithPath("page").description("Pagination properties"))));
}
@Test
void listTaskExecutionsByName() throws Exception {
this.mockMvc.perform(
get("/tasks/executions")
.queryParam("name", "taskB")
.queryParam("page", "0")
.queryParam("size", "10")
)
.andExpect(status().isOk()).andDo(this.documentationHandler.document(
queryParameters(
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.taskExecutionResourceList")
.description("Contains a collection of Task Executions/"),
subsectionWithPath("_links.self").description("Link to the task execution resource"),
subsectionWithPath("page").description("Pagination properties"))));
}

@Test
void stopTask() throws Exception {
Expand Down
79 changes: 79 additions & 0 deletions spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2067,6 +2067,85 @@ 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>>
* <<api-guide-resources-task-thin-executions-list-request-parameters>>
* <<api-guide-resources-task-thin-executions-list-example-request>>
* <<api-guide-resources-task-thin-executions-list-response-structure>>



[[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>>
* <<api-guide-resources-task-thin-executions-list-by-name-request-parameters>>
* <<api-guide-resources-task-thin-executions-list-by-name-example-request>>
* <<api-guide-resources-task-thin-executions-list-by-name-response-structure>>



[[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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ spring:
- DELETE /tasks/executions/* => hasRole('ROLE_DESTROY')
- GET /tasks/thinexecutions => hasRole('ROLE_VIEW')
- GET /tasks/thinexecutions/* => hasRole('ROLE_VIEW')
# Task Schedules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ public interface TaskOperations {
*/
PagedModel<TaskExecutionThinResource> thinExecutionList();

/**
* List task executions filtered by task name.
* @return the page of task executions for the given task name.
*/
PagedModel<TaskExecutionThinResource> thinExecutionListByTaskName(String taskName);

/**
* List task executions known to the system filtered by task name.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public class TaskTemplate implements TaskOperations {

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";
Expand All @@ -97,6 +99,8 @@ public class TaskTemplate implements TaskOperations {

private final Link thinExecutionsLink;

private final Link thinExecutionsByNameLink;

private final Link executionLink;

private final Link executionLaunchLink;
Expand Down Expand Up @@ -160,6 +164,12 @@ public class TaskTemplate implements TaskOperations {
} else {
this.thinExecutionsLink = null;
}
if(resources.getLink(THIN_EXECUTIONS_BY_NAME_RELATION).isPresent()) {
this.thinExecutionsByNameLink = resources.getLink(THIN_EXECUTIONS_BY_NAME_RELATION).get();
} else {
this.thinExecutionsByNameLink = null;
}

if(resources.getLink(EXECUTION_LAUNCH_RELATION).isPresent()) {
this.executionLaunchLink = resources.getLink(EXECUTION_LAUNCH_RELATION).get();
} else {
Expand Down Expand Up @@ -275,6 +285,15 @@ public PagedModel<TaskExecutionThinResource> thinExecutionList() {
}
}

@Override
public PagedModel<TaskExecutionThinResource> 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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,8 @@ public TaskExecutionController taskExecutionController(
}

@Bean
public TaskExecutionThinController taskExecutionThinController(DataflowTaskExplorer taskExplorer) {
return new TaskExecutionThinController(taskExplorer);
public TaskExecutionThinController taskExecutionThinController(DataflowTaskExplorer taskExplorer, TaskDefinitionRepository taskDefinitionRepository) {
return new TaskExecutionThinController(taskExplorer, taskDefinitionRepository);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,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)).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"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,11 @@ public PagedModel<TaskExecutionResource> retrieveTasksByName(
Pageable pageable,
PagedResourcesAssembler<TaskJobExecutionRel> assembler
) {
long tasks = this.taskDefinitionRepository.countByTaskName(taskName);
if(tasks == 0) {
throw new NoSuchTaskDefinitionException(taskName);
}
validatePageable(pageable);
this.taskDefinitionRepository.findById(taskName)
.orElseThrow(() -> new NoSuchTaskDefinitionException(taskName));
Page<TaskExecution> taskExecutions = this.explorer.findTaskExecutionsByName(taskName, pageable);
Page<TaskJobExecutionRel> result = getPageableRelationships(taskExecutions, pageable);
return assembler.toModel(result, this.taskAssembler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import org.springframework.cloud.dataflow.core.ThinTaskExecution;
import org.springframework.cloud.dataflow.rest.resource.TaskExecutionThinResource;
import org.springframework.cloud.dataflow.server.repository.NoSuchTaskDefinitionException;
import org.springframework.cloud.dataflow.server.repository.TaskDefinitionRepository;
import org.springframework.cloud.dataflow.server.task.DataflowTaskExplorer;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.data.domain.Page;
Expand All @@ -30,6 +32,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -46,10 +49,12 @@
public class TaskExecutionThinController {

private final DataflowTaskExplorer explorer;
private final TaskDefinitionRepository taskDefinitionRepository;
private final TaskExecutionThinResourceAssembler resourceAssembler;

public TaskExecutionThinController(DataflowTaskExplorer explorer) {
public TaskExecutionThinController(DataflowTaskExplorer explorer, TaskDefinitionRepository taskDefinitionRepository) {
this.explorer = explorer;
this.taskDefinitionRepository = taskDefinitionRepository;
this.resourceAssembler = new TaskExecutionThinResourceAssembler();
}

Expand All @@ -62,6 +67,23 @@ public PagedModel<TaskExecutionThinResource> listTasks(Pageable pageable, PagedR
return pagedAssembler.toModel(thinTaskExecutions, resourceAssembler);
}

@GetMapping(value = "", params = "name")
@ResponseStatus(HttpStatus.OK)
public PagedModel<TaskExecutionThinResource> retrieveTasksByName(
@RequestParam("name") String taskName,
Pageable pageable,
PagedResourcesAssembler<ThinTaskExecution> pagedAssembler
) {
long tasks = this.taskDefinitionRepository.countByTaskName(taskName);
if(tasks == 0) {
throw new NoSuchTaskDefinitionException(taskName);
}
Page<TaskExecution> page = this.explorer.findTaskExecutionsByName(taskName, pageable);
Page<ThinTaskExecution> thinTaskExecutions = new PageImpl<>(page.stream().map(ThinTaskExecution::new).toList(), pageable, page.getTotalElements());
explorer.populateCtrStatus(thinTaskExecutions.getContent());
return pagedAssembler.toModel(thinTaskExecutions, resourceAssembler);
}

static class TaskExecutionThinResourceAssembler extends RepresentationModelAssemblerSupport<ThinTaskExecution, TaskExecutionThinResource> {
public TaskExecutionThinResourceAssembler() {
super(TaskExecutionThinController.class, TaskExecutionThinResource.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ public interface TaskDefinitionRepository extends KeyValueRepository<TaskDefinit
*/
TaskDefinition findByTaskName(String name);

long countByTaskName(String taskName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -522,20 +522,19 @@ private List<String> getTaskArguments(long taskExecutionId) {

@Override
public void populateCtrStatus(Collection<ThinTaskExecution> thinTaskExecutions) {
Map<Long, ThinTaskExecution> taskExecutionMap = thinTaskExecutions.stream()
if(!thinTaskExecutions.isEmpty()) {
Map<Long, ThinTaskExecution> taskExecutionMap = thinTaskExecutions.stream()
.collect(Collectors.toMap(ThinTaskExecution::getExecutionId, Function.identity()));
String ids = taskExecutionMap.keySet()
.stream()
.map(Object::toString)
.collect(Collectors.joining(","));
String sql = FIND_CTR_STATUS.replace(":taskExecutionIds", ids);
jdbcTemplate.query(sql, rs -> {
Long id = rs.getLong("TASK_EXECUTION_ID");
String ctrStatus = rs.getString("CTR_STATUS");
logger.debug("populateCtrStatus:{}={}", id, ctrStatus);
ThinTaskExecution execution = taskExecutionMap.get(id);
Assert.notNull(execution, "Expected TaskExecution for " + id + " from " + ids);
execution.setCtrTaskStatus(ctrStatus);
});
String ids = taskExecutionMap.keySet().stream().map(Object::toString).collect(Collectors.joining(","));
String sql = FIND_CTR_STATUS.replace(":taskExecutionIds", ids);
jdbcTemplate.query(sql, rs -> {
Long id = rs.getLong("TASK_EXECUTION_ID");
String ctrStatus = rs.getString("CTR_STATUS");
logger.debug("populateCtrStatus:{}={}", id, ctrStatus);
ThinTaskExecution execution = taskExecutionMap.get(id);
Assert.notNull(execution, "Expected TaskExecution for " + id + " from " + ids);
execution.setCtrTaskStatus(ctrStatus);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ spring:
- GET /tasks/info/* => hasRole('ROLE_VIEW')

- GET /tasks/thinexecutions => hasRole('ROLE_VIEW')
- GET /tasks/thinexecutions/* => hasRole('ROLE_VIEW')

# Task Schedules

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,8 @@ public TaskExecutionController taskExecutionController(
}

@Bean
public TaskExecutionThinController taskExecutionThinController(DataflowTaskExplorer dataflowTaskExplorer) {
return new TaskExecutionThinController(dataflowTaskExplorer);
public TaskExecutionThinController taskExecutionThinController(DataflowTaskExplorer dataflowTaskExplorer, TaskDefinitionRepository taskDefinitionRepository) {
return new TaskExecutionThinController(dataflowTaskExplorer, taskDefinitionRepository);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -593,8 +593,8 @@ public TaskExecutionController taskExecutionController(
}

@Bean
public TaskExecutionThinController taskExecutionThinController(DataflowTaskExplorer dataflowTaskExplorer) {
return new TaskExecutionThinController(dataflowTaskExplorer);
public TaskExecutionThinController taskExecutionThinController(DataflowTaskExplorer dataflowTaskExplorer, TaskDefinitionRepository taskDefinitionRepository) {
return new TaskExecutionThinController(dataflowTaskExplorer, taskDefinitionRepository);
}
@Bean
public TasksInfoController taskExecutionsInfoController(TaskExecutionService taskExecutionService) {
Expand Down
Loading

0 comments on commit 78085ce

Please sign in to comment.