added subflows, simplified code

This commit is contained in:
HWienhold 2023-10-03 21:48:42 +02:00
parent 028a5d54bc
commit 65e6c72008
20 changed files with 129 additions and 153 deletions

View File

@ -26,13 +26,9 @@ public class JsExecutionService {
}
private void executeScript(Task task, String s) {
log.info("start script. task is following");
// log.info(task.toString());
log.info(s);
JsScriptDto body = JsScriptDto.builder()
.callerId(task.getId())
.script(s).build();
log.info("body" + body.toString());
webClientBuilder.build().post()
.uri("http://localhost:8080/api/js")
.bodyValue(body)

View File

@ -16,8 +16,8 @@ public class TaskStateTransitionValidator {
static {
allowedTransitions.put(TaskState.WAITING, Arrays.asList(TaskState.READY, TaskState.DONE, TaskState.ABORT));
allowedTransitions.put(TaskState.READY, Arrays.asList(TaskState.IN_PROGRESS, TaskState.ABORT));
allowedTransitions.put(TaskState.IN_PROGRESS, Arrays.asList(TaskState.READY, TaskState.DONE, TaskState.ABORT));
allowedTransitions.put(TaskState.READY, Arrays.asList(TaskState.IN_PROGRESS, TaskState.ABORT, TaskState.WAITING));
allowedTransitions.put(TaskState.IN_PROGRESS, Arrays.asList(TaskState.READY, TaskState.DONE, TaskState.ABORT, TaskState.WAITING));
allowedTransitions.put(TaskState.DONE, Arrays.asList(TaskState.ABORT));
allowedTransitions.put(TaskState.ABORT, Arrays.asList());
}

View File

@ -3,6 +3,7 @@ package com.indu.tasktemplateservice.dto;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.indu.tasktemplateservice.model.AbortAction;
import com.indu.tasktemplateservice.model.TaskState;
import jakarta.validation.constraints.NotNull;
@ -24,8 +25,10 @@ public class TaskTemplateDto {
private TaskState activationState;
private boolean checkPreviousDoneBeforeActivate;
// private AbortAction abortAction;
private AbortAction abortAction;
private Map<String, String> requiredFieldOnFinish;
private MultiScriptDto scriptActions;
private MultiScriptDto scriptActions;
// merge data in parent
}

View File

@ -33,12 +33,10 @@ public class TaskTemplate {
@Builder.Default
private boolean checkPreviousDoneBeforeActivate = true;
private AbortAction abortAction;
@Builder.Default
private AbortAction abortAction = AbortAction.STEP_BACK;
private Map<String, String> requiredFieldOnFinish; // for now just requiredFields => pattern
// TODO -> i think it might be better to let workflow handle post action? maybe just notify -> TBD
// private Map<TaskState, List< Action> > postAction;
// private Map<TaskState, List< Pair<CustomAction, List<String> > > > actions;
private MultiScriptDto scriptActions;
}

View File

@ -2,6 +2,7 @@ package com.indu.workflowservice.model;
import java.util.List;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -23,8 +24,8 @@ public class Workflow {
private String type; //corresponds to name in workflow-template
private List<String> taskIds;
// TDB - id or object
private List<String> workflowIds;
@DBRef(lazy = true)
private List<Workflow> subFlows;
private String parentId;

View File

@ -35,16 +35,16 @@ public class FlowService {
repository.save(workflow);
if(newState.equals(WorkflowStates.IN_PROGRESS)) {
activationProcess(workflow);
activateChilds(workflow);
} else if(newState.equals(WorkflowStates.DONE)) {
//Notify parent
}
return workflow;
}
private void activationProcess(Workflow workflow) {
if((workflow.getWorkflowIds() != null) && !workflow.getWorkflowIds().isEmpty()) {
workflow.getWorkflowIds().forEach(this::activateWorkflow);
private void activateChilds(Workflow workflow) {
if((workflow.getSubFlows() != null) && !workflow.getSubFlows().isEmpty()) {
workflow.getSubFlows().forEach(this::activateWorkflow);
}
if((workflow.getTaskIds() != null) && !workflow.getTaskIds().isEmpty()) {
@ -69,8 +69,8 @@ public class FlowService {
}
public boolean canBeFinished(Workflow workflow) {
return ((workflow.getWorkflowIds() == null)
|| workflow.getWorkflowIds().stream().allMatch(id->getState(id).equals(WorkflowStates.DONE)))
return ((workflow.getSubFlows() == null)
|| workflow.getSubFlows().stream().allMatch(id->getState(id).equals(WorkflowStates.DONE)))
&& ((workflow.getTaskIds() == null)
|| workflow.getTaskIds().stream().allMatch(id -> taskService.getState(id).equals(TaskState.DONE)));
}

View File

@ -15,7 +15,7 @@ public class WorkflowMapService {
.workflowId(workflow.getWorkflowId())
.type(workflow.getType())
.taskIds(workflow.getTaskIds())
.workflowIds(workflow.getWorkflowIds())
.workflowIds(workflow.getSubFlows().stream().map(Workflow::getId).toList())
.state(workflow.getState())
.parentId(workflow.getParentId())
.build();

View File

@ -30,8 +30,7 @@ public class WorkflowService {
log.info(String.format("Created wf: %s. Saving....", workflow));
repository.save(workflow);
// workflow saved, now there should be a id to add to tasks
workflow.getTaskIds().stream().forEach(id->taskService.updateParent(id, workflow.getId()));
addParetIdToChilds(workflow);
return workflow;
}
@ -41,34 +40,42 @@ public class WorkflowService {
.workflowId(sequence.generateCustomId(template))
.taskIds(new ArrayList<String>())
.build();
log.info(String.format("Added asy fields. Workflow has form %s. Adding tasks", workflow));
log.info(String.format("Added easy fields. Workflow has form %s. Adding tasks", workflow));
addTasksToWorkflow(workflow, template.getTaskTemplateIds());
addSubFlowsToWorkflow(workflow);
addSubFlowsToWorkflow(workflow, template.getSubWorkflowTemplateIds());
return workflow;
}
private void addSubFlowsToWorkflow(Workflow workflow) {
if( ( workflow.getWorkflowIds() == null) || workflow.getWorkflowIds().isEmpty()) {
private void addSubFlowsToWorkflow(Workflow workflow, List<String> subFlowTemplates) {
if( ( subFlowTemplates == null) || subFlowTemplates.isEmpty()) {
return;
}
workflow.setWorkflowIds(
workflow.getWorkflowIds().stream().map(this::createNewWorkflowByTemplateId).map(w -> connectFlows(w, workflow)).toList()
);
}
workflow.setSubFlows(
subFlowTemplates.stream().map(this::createNewWorkflowByTemplateId).toList()
);
}
private String connectFlows(Workflow subFlow, Workflow workflow) {
private void addParetIdToChilds(Workflow workflow) {
if (workflow.getTaskIds()!= null ) {
workflow.getTaskIds().stream().forEach(id->taskService.updateParent(id, workflow.getId()));
}
if (workflow.getSubFlows() != null) {
workflow.getSubFlows().stream().forEach(w -> connectFlow(w, workflow));
}
}
private void connectFlow(Workflow subFlow, Workflow workflow) {
subFlow.setParentId(workflow.getId());
repository.save(subFlow);
return subFlow.getId();
}
public void addTasksToWorkflow(Workflow workflow, List<String> taskTemplateIds) {
Iterator<String> it = taskTemplateIds.iterator();
if(!it.hasNext()) {
if ( (taskTemplateIds == null) || taskTemplateIds.isEmpty()) {
return;
}
Iterator<String> it = taskTemplateIds.iterator();
TaskDto followUpTask = addTaskToWorkflow(workflow, it.next());
log.info("Adding references ... ");
while ( it.hasNext()) {

View File

@ -4,14 +4,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.indu.workflowtemplateservice.model.AbortAction;
import com.indu.workflowtemplateservice.model.TaskState;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
public class TaskTemplateDto {
private String id;
@NotNull
@ -20,8 +21,9 @@ public class TaskTemplateDto {
private TaskState activationState;
private boolean checkPreviousDoneBeforeActivate;
// private AbortAction abortAction;
private AbortAction abortAction;
private Map<String, String> requiredFieldOnFinish;
private HashMap<TaskState, List<String>> scriptActions;
}

View File

@ -14,7 +14,6 @@ public interface WorkflowTemplateMapper {
WorkflowTemplate map(WorkflowTemplateRequest request);
WorkflowTemplateResponse map(WorkflowTemplate templateObject);
// WorkflowTemplateRequest mapToRequest(WorkflowTemplate template);
@AfterMapping
@ -22,23 +21,23 @@ public interface WorkflowTemplateMapper {
if ( template.getSubWorkflow() == null) {
return;
}
request.setSubWorkflowId(
request.setSubWorkflowIds(
template.getSubWorkflow().stream().map(WorkflowTemplate::getId).toList()
);
}
@AfterMapping
public default void addSubFlowFromRequest(WorkflowTemplateRequest request, @MappingTarget WorkflowTemplate templateObject) {
if ( request.getSubWorkflowId() == null) {
if ( request.getSubWorkflowIds() == null) {
return;
}
templateObject.setSubWorkflow(
request.getSubWorkflowId().stream().map(id -> WorkflowTemplate.builder().id(id).build()).toList()
request.getSubWorkflowIds().stream().map(WorkflowTemplate::new).toList()
);
}
@AfterMapping
public default void addSubFlowFromTemplate(WorkflowTemplate templateObject,@MappingTarget WorkflowTemplateResponse response ) {
public default void addSubFlowFromTemplate(WorkflowTemplate templateObject, @MappingTarget WorkflowTemplateResponse response ) {
if (templateObject.getSubWorkflow() == null) {
return;
}

View File

@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -14,7 +13,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@Builder
//@Builder
public class WorkflowTemplateRequest {
private String id;
private String identiferPrefix;
@ -23,7 +22,7 @@ public class WorkflowTemplateRequest {
// TODO: allow to be added direclty, for now tasks must be added bf and added here via id (cf. WorkflowTemplateResponse as well)
private List<String> taskTemplateIds;
private List<String> subWorkflowId;
private List<String> subWorkflowIds;
// private Map<String, String> requiredFieldOnFinish; // for now just requiredFields => pattern

View File

@ -6,7 +6,6 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -14,7 +13,7 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@Builder
//@Builder
// TODO: this might be better request bc this way we could implement wf template with list of task templates
// and app would add task templates if not already exist.... RN ...request o.t.o.h. has hast of task_ids, i.e.
// taskstemplates need to be created bf workflow template can...
@ -24,7 +23,6 @@ public class WorkflowTemplateResponse {
@NotNull
private String name; // unique identifier for sequence
// Mostly bTaskTemplate not used - for now fetch by hand whenever needed
// private List<TaskTemplate> tasks;
private List<String> taskTemplateIds;
private List<String> subWorkflowTemplateIds;
}

View File

@ -0,0 +1,15 @@
package com.indu.workflowtemplateservice.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum AbortAction {
ABORT_WORKFLOW("abort_workflow"),
STEP_BACK("step_back"),
// RESTART i.e. clone for now
RESTART_WORKFLOW("restart_workflow");
private final String value;
}

View File

@ -1,5 +1,9 @@
package com.indu.workflowtemplateservice.model;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.Builder;
import lombok.Data;
@ -11,8 +15,9 @@ public class TaskTemplate {
private TaskState initialState;
private TaskState activationState;
private AbortAction abortAction;
private boolean checkPreviousBeforeDone;
// TODO: thos two need to exclude each other
private boolean abortWorkflowOnAbort;
private boolean restartFlowOnAbort;
private Map<String, String> requiredFieldOnFinish;
private HashMap<TaskState, List<String>> scriptActions;
}

View File

@ -10,16 +10,15 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
@Document(value="workflow-template")
//@Builder
public class WorkflowTemplate {
private String id;
@NotNull
@ -29,11 +28,12 @@ public class WorkflowTemplate {
// private List<TaskTemplate> taskTemplates; //ids -> Expand????#
private List<String> taskTemplateIds;
@DBRef(lazy=true)
private List<WorkflowTemplate> subWorkflow;
//
// private String parent_id;
public WorkflowTemplate(String id) {
this.id = id;
}
}

View File

@ -3,25 +3,22 @@ package com.indu.workflowtemplateservice.repository;
import org.springframework.stereotype.Repository;
import org.springframework.web.reactive.function.client.WebClient;
import com.indu.workflowtemplateservice.dto.TaskTemplateDto;
import com.indu.workflowtemplateservice.model.TaskTemplate;
import com.indu.workflowtemplateservice.service.TaskTemplateMapService;
import lombok.RequiredArgsConstructor;
@Repository
@RequiredArgsConstructor
public class TaskTemplateRepository {
// TODO: Needed?
private final WebClient.Builder webClientBuilder;
public TaskTemplate getById(String id) {
TaskTemplateDto taskTemplate = webClientBuilder.build().get()
.uri("http://localhost:8080/api/template/tasks", uri->uri.pathSegment(id).build()
).retrieve()
.bodyToMono(TaskTemplateDto.class)
.block();
return TaskTemplateMapService.map(taskTemplate);
}
// public TaskTemplate getById(String id) {
// TaskTemplateDto taskTemplate = webClientBuilder.build().get()
// .uri("http://localhost:8080/api/template/tasks", uri->uri.pathSegment(id).build()
// ).retrieve()
// .bodyToMono(TaskTemplateDto.class)
// .block();
//
// return TaskTemplateMapService.map(taskTemplate);
// }
}

View File

@ -0,0 +1,11 @@
package com.indu.workflowtemplateservice.service;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class TMP {
public static void foobar(String msg) {
log.info(msg);
}
}

View File

@ -2,32 +2,33 @@ package com.indu.workflowtemplateservice.service;
import org.springframework.stereotype.Service;
import com.indu.workflowtemplateservice.dto.TaskTemplateDto;
import com.indu.workflowtemplateservice.model.TaskTemplate;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class TaskTemplateMapService {
public static TaskTemplate map(TaskTemplateDto dto) {
return TaskTemplate.builder()
.id(dto.getId())
// .abortWorkflowOnAbort(dto.isAbortWorkflowOnAbort())
.activationState(dto.getActivationState())
.initialState(dto.getInitialState())
.name(dto.getName())
.build();
}
public static TaskTemplateDto map(TaskTemplate taskTemplate) {
return TaskTemplateDto.builder()
.id(taskTemplate.getId())
// .abortWorkflowOnAbort(taskTemplate.isAbortWorkflowOnAbort())
.activationState(taskTemplate.getActivationState())
.initialState(taskTemplate.getInitialState())
.name(taskTemplate.getName())
.build();
}
// public static TaskTemplate map(TaskTemplateDto dto) {
// return TaskTemplate.builder()
// .id(dto.getId())
// .abortAction(dto.getAbortAction())
// .activationState(dto.getActivationState())
// .initialState(dto.getInitialState())
// .requiredFieldOnFinish(dto.getRequiredFieldOnFinish())
// .scriptActions(dto.getScriptActions())
// .name(dto.getName())
// .build();
// }
//
// public static TaskTemplateDto map(TaskTemplate taskTemplate) {
// return TaskTemplateDto.builder()
// .id(taskTemplate.getId())
// .abortAction(taskTemplate.getAbortAction())
// .activationState(taskTemplate.getActivationState())
// .initialState(taskTemplate.getInitialState())
// .requiredFieldOnFinish(taskTemplate.getRequiredFieldOnFinish())
// .scriptActions(taskTemplate.getScriptActions())
// .name(taskTemplate.getName())
// .build();
// }
}

View File

@ -1,51 +0,0 @@
package com.indu.workflowtemplateservice.service;
import org.springframework.stereotype.Service;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateRequest;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateResponse;
import com.indu.workflowtemplateservice.model.WorkflowTemplate;
import com.indu.workflowtemplateservice.repository.TaskTemplateRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
@Service
@Slf4j
public class WorkflowTemplateMapperManual {
private final TaskTemplateRepository taskTemplateRepo;
public WorkflowTemplateRequest mapToRequest(WorkflowTemplate template) {
return WorkflowTemplateRequest.builder()
.name(template.getName())
.id(template.getId())
.identiferPrefix(template.getIdentiferPrefix())
.taskTemplateIds(template.getTaskTemplateIds())
// .tasksTemplateIds(template.getTaskTemplates().stream().map(TaskTemplate::getId).toList())
.build();
}
public WorkflowTemplateResponse map(WorkflowTemplate template) {
return WorkflowTemplateResponse.builder()
.name(template.getName())
.id(template.getId())
.identiferPrefix(template.getIdentiferPrefix())
.taskTemplateIds(template.getTaskTemplateIds())
// .tasks(template.getTaskTemplatesIds().stream().map(taskTemplateRepo::getById).toList())
.build();
}
public WorkflowTemplate map(WorkflowTemplateRequest request) {
return WorkflowTemplate.builder()
.identiferPrefix(request.getIdentiferPrefix())
.name(request.getName())
.id(request.getId())
.taskTemplateIds(request.getTaskTemplateIds())
// .taskTemplates(request.getTasksTemplateIds().stream().map(taskTemplateRepo::getById).toList() )
.build();
}
}

View File

@ -9,36 +9,31 @@ import com.indu.workflowtemplateservice.dto.WorkflowTemplateMapper;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateRequest;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateResponse;
import com.indu.workflowtemplateservice.model.WorkflowTemplate;
import com.indu.workflowtemplateservice.repository.TaskTemplateRepository;
import com.indu.workflowtemplateservice.repository.WorkflowTemplateRepository;
import jakarta.validation.Valid;
import jakarta.ws.rs.NotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class WorkflowTemplateService {
// TODO check if response needed or request enough, either rm old or use response
private final WorkflowTemplateRepository repository;
private final WorkflowTemplateMapper mapper;
public WorkflowTemplate upsertEmptyTemplate(@Valid WorkflowTemplateRequest request) {
log.info("Upsert " + request.getName());
//TODO: Needed? might be easier to just let mongo
if (repository.findByName(request.getName()).isPresent()) {
throw new NameAlreadyTakenException(request.getName());
}
WorkflowTemplate template = mapper.map(request);
WorkflowTemplate template = WorkflowTemplateMapper.instance.map(request);
repository.save(template);
return template;
}
public WorkflowTemplateResponse upsertAndRespond(@Valid WorkflowTemplateRequest request ) {
return mapper.map(upsertEmptyTemplate(request));
return WorkflowTemplateMapper.instance.map(upsertEmptyTemplate(request));
}
public WorkflowTemplate findById(String id) {
@ -50,11 +45,11 @@ public class WorkflowTemplateService {
}
public WorkflowTemplateResponse getWorkflowById(String id) {
return mapper.map(findById(id));
return WorkflowTemplateMapper.instance.map(findById(id));
}
public WorkflowTemplateResponse getWorkflowByName(String name) {
return mapper.map(repository.findByName(name).orElseThrow(() -> new NotFoundException("did not find required wf template")));
return WorkflowTemplateMapper.instance.map(repository.findByName(name).orElseThrow(() -> new NotFoundException("did not find required wf template")));
}
public WorkflowTemplate addTaskTemplateToWorkflowTemplate(WorkflowTemplate template, String templateTask) {
@ -65,7 +60,7 @@ public class WorkflowTemplateService {
public WorkflowTemplateResponse addTasksToWorkflow(String id, List<String> taskId) {
WorkflowTemplate workflow = findById(id);
taskId.stream().forEach(t -> addTaskTemplateToWorkflowTemplate(workflow, t));
return mapper.map(workflow);
return WorkflowTemplateMapper.instance.map(workflow);
}
}