prepare for js code injection

This commit is contained in:
HWienhold 2023-10-03 15:27:16 +02:00
parent ee01e122a8
commit 84470b4f4b
35 changed files with 633 additions and 60 deletions

View File

@ -0,0 +1,40 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.indu</groupId>
<artifactId>indu-flow-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>js-injection-service</artifactId>
<name>js-injection-service</name>
<description>JS Servic for User Injections</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.javadelight</groupId>
<artifactId>delight-nashorn-sandbox</artifactId>
<version>0.3.2</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,13 @@
package com.indu.jsinjectionservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JsInjectionApplication {
public static void main(String[] args) {
SpringApplication.run(JsInjectionApplication.class, args);
}
}

View File

@ -0,0 +1,32 @@
package com.indu.jsinjectionservice.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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 com.indu.jsinjectionservice.dto.JsExectutionRequest;
import com.indu.jsinjectionservice.service.JsExecutionService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/api/js")
@RequiredArgsConstructor
public class JsExecutionController {
private final JsExecutionService service;
@PostMapping
public ResponseEntity<?> execute(@Valid @RequestBody JsExectutionRequest request) {
try {
service.executeInSandbox(request);
return new ResponseEntity<Object>(HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<String>( e.getLocalizedMessage(),HttpStatus.I_AM_A_TEAPOT);
}
}
}

View File

@ -0,0 +1,18 @@
package com.indu.jsinjectionservice.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
@Data
@AllArgsConstructor
public class JsExectutionRequest {
@NonNull
private String script;
@NonNull
private String callerId;
@NonNull
private String endpoint;
}

View File

@ -0,0 +1,50 @@
package com.indu.jsinjectionservice.service;
import java.util.concurrent.Executors;
import javax.script.ScriptException;
import org.springframework.stereotype.Service;
import com.indu.jsinjectionservice.dto.JsExectutionRequest;
import delight.nashornsandbox.NashornSandbox;
import delight.nashornsandbox.exceptions.ScriptCPUAbuseException;
import delight.nashornsandbox.internal.NashornSandboxImpl;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class JsExecutionService {
private final JsGenerationService generator;
public void executeInSandbox(@Valid JsExectutionRequest request) throws ScriptCPUAbuseException, ScriptException {
NashornSandbox sandbox = initializeSandbox();
sandbox.eval( generator.getCallerIdGetter(request.getCallerId()));
sandbox.eval( generator.getEndpointGetter(request.getEndpoint()));
sandbox.eval( generator.getFetchCallerFunction());
}
private NashornSandbox initializeSandbox() {
// NashornSandbox sandbox = NashornSandboxes.create();
NashornSandbox sandbox = new NashornSandboxImpl() {
@Override
public Object eval(String js) throws ScriptCPUAbuseException, ScriptException {
log.info(String.format("Running command \"%s\"", js));
return super.eval(js);
}
};
sandbox.setMaxCPUTime(100);
sandbox.setMaxMemory(50 * 1024);
sandbox.allowNoBraces(false);
sandbox.setMaxPreparedStatements(30);
sandbox.setExecutor(Executors.newSingleThreadExecutor());
return sandbox;
}
}

View File

@ -0,0 +1,59 @@
package com.indu.jsinjectionservice.service;
import org.springframework.stereotype.Service;
@Service
public class JsGenerationService {
// TODO: maybe add update parent whatever ...
public String getEndpointGetter(String api) {
return getFunction("getEndpoint", ret(api));
}
public String getCallerIdGetter(String id) {
return getFunction("getCallerId", ret(id));
}
public String getFetchCallerFunction() {
// for now assume the other functions already added...
return "async " + getFunction("getCaller", getFetchBody());
}
private static String getFetchBody() {
StringBuilder body = new StringBuilder();
body.append("const response = await fetch( getEndpoint() );\n");
body.append("cost data = await response.json();\n");
return trycatch(body.toString() + ret("data"));
}
private static String getFunction (String name, String body, String... args) {
StringBuilder functionCode = new StringBuilder();
functionCode.append("function " + name);
functionCode.append("(" + String.join(", ", args ) +")");
functionCode.append("{" + body +"}");
return functionCode.toString();
}
private static String ret(String var) {
return "return " + var + ";";
}
private static String trycatch ( String tryBody, String catchBody, String finallyBody) {
StringBuilder builder = new StringBuilder();
builder.append(" try { " + tryBody + " } ");
builder.append((" catch (error) { " + catchBody) == null ? "console.error(error);" : catchBody+ " } ");
if ( finallyBody != null) {
builder.append("finally { " + finallyBody + " } ");
}
return builder.toString();
}
private static String trycatch ( String tryBody, String catchBody) {
return trycatch(tryBody, catchBody, null);
}
private static String trycatch ( String tryBody) {
return trycatch(tryBody, null);
}
}

View File

@ -24,6 +24,7 @@
<module>workflow-template-service</module>
<module>com.indu.exceptionservice</module>
<module>exception-service</module>
<module>js-injection-service</module>
</modules>
<properties>
<org.mapstruct.version>1.5.5.Final</org.mapstruct.version>

View File

@ -81,7 +81,6 @@ public class TaskController {
return service.setParentId(id, parentId);
}
@PostMapping(path="/byTemplate")
public TaskResponse createTaskFromTemplate(@RequestBody TaskTemplateDto template) {
return null;

View File

@ -0,0 +1,19 @@
package com.indu.taskservice.dto;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
@Data
@Builder
public class JsScriptDto {
@NonNull
private String script;
@NonNull
private String callerId;
@NonNull
@Builder.Default
private String endpoint="/api/tasks";
}

View File

@ -21,4 +21,5 @@ public class TaskResponse {
private String afterTaskId;
private String parentId;
private Map<String, String> data;
}

View File

@ -1,5 +1,6 @@
package com.indu.taskservice.model;
import java.util.List;
import java.util.Map;
import org.springframework.data.mongodb.core.index.Indexed;
@ -30,6 +31,7 @@ public class TaskTemplate {
@Builder.Default
private boolean checkPreviousBeforeActivate = true;
private Map<TaskState, List<String>> scriptActions;
private AbortAction abortAction;
private Map<String, String> requiredFieldOnFinish; // for now just requiredFields => pattern

View File

@ -0,0 +1,41 @@
package com.indu.taskservice.service;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import com.indu.taskservice.dto.JsScriptDto;
import com.indu.taskservice.model.Task;
import com.indu.taskservice.model.TaskState;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class JsExecutionService {
private final WebClient.Builder webClientBuilder;
public void handleScriptAction(Task task, TaskState state) {
if( !task.getTemplate().getScriptActions().containsKey(state) ) {
return;
}
task.getTemplate().getScriptActions().get(state).stream().forEach(s->executeScript(task,s));
}
private void executeScript(Task task, String s) {
webClientBuilder.build().post()
.uri("http://localhost:8080/api/js")
.bodyValue(JsScriptDto.builder()
.callerId(task.getId())
.script(s))
.retrieve()
.bodyToMono(Object.class)
.doOnNext(res->log.info(String.format("Successfully run scrip %s", res) ))
.doOnError(res->log.error(String.format("Scriptrun failed: %s", res.getMessage())))
.block(); // prevent parallel execution of diff scripts - they might all try to change the current task
}
}

View File

@ -24,6 +24,7 @@ public class TaskFlowService {
private final TaskService taskService;
private final TaskRepository repository;
private final JsExecutionService jsService;
public Task updateState(String id, @Valid TaskStateRequest taskRequest) {
Task task = taskService.getTaskById(id);
@ -40,21 +41,33 @@ public class TaskFlowService {
task.setReadyTime(new Date());
}
repository.save(task);
handleTransitionAction(task, taskRequest.getState());
return task;
}
public Task activateTask(String id, @Valid EditorData editor) {
Task task = taskService.getTaskById(id);
if(task.getTemplate().isCheckPreviousBeforeActivate() && !previousTasksDone(task)) {
log.error(String.format("Activation of task %s ot allowed. There seem to be previous open task. Aborting", id));
log.error(String.format("Activation of task %s not allowed. There seem to be previous open task. Aborting", id));
return task;
}
TaskState state = task.getTemplate().getActivationState();
// TODO: validate is WAITING (?)
task.setState(state);
task.setEditor(editor);
repository.save(task);
return task;
if(task.getState().equals(state)) {
log.error(String.format("State of %s already %s. sth. fishy. Aborting", id, state));
return task;
}
return updateState(id,
TaskStateRequest.builder().editor(editor).state(state).build()
);
}
// TBD: precedece? doesnt matter for the moment
public void handleTransitionAction(Task task, TaskState state) {
jsService.handleScriptAction(task, state);
if ( state.equals(TaskState.ABORT)) {
// AbortAction
}
}
private boolean previousTasksDone(Task task) {

View File

@ -0,0 +1,78 @@
package com.indu.tasktemplateservice.controller;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.indu.tasktemplateservice.dto.MultiScriptDto;
import com.indu.tasktemplateservice.dto.SingleScriptDto;
import com.indu.tasktemplateservice.dto.TaskTemplateDto;
import com.indu.tasktemplateservice.model.CustomAction;
import com.indu.tasktemplateservice.model.TaskState;
import com.indu.tasktemplateservice.service.TaskActionService;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping(path = "/api/template/tasks")
@RequiredArgsConstructor
public class StateChangeController {
private final TaskActionService service;
@GetMapping(path="/{id}/action/{action}")
public Object getStateTransitionActions(@PathVariable String id, @PathVariable CustomAction action) {
return service.getStateTransitionAction(id, action);
}
@PostMapping(path="/{id}/action/SCRIPT")
public MultiScriptDto setScriptAction(@PathVariable String id, @RequestBody MultiScriptDto scripts) {
return service.setScriptAction(id, scripts);
}
@PutMapping(path="/{id}/action/SCRIPT")
public MultiScriptDto setScriptAction(@PathVariable String id, @RequestBody SingleScriptDto script) {
return service.addScriptAction(id, script);
}
@PostMapping(path="/{id}/action/{action}")
public Object getStateTransitionActions(@PathVariable String id, @PathVariable CustomAction action, @RequestBody TaskTemplateDto foo) {
return service.getStateTransitionAction(id, action);
}
@DeleteMapping(path="/{id}/action/SCRIPT/{state}/{i}")
public MultiScriptDto removeScriptAction(@PathVariable String id, @PathVariable TaskState state, @PathVariable int i) {
service.removeScriptAction(id, state, i);
return service.getStateTransitionScriptAction(id);
}
@GetMapping(path="/{id}/action/SCRIPT/{state}/{i}")
public ResponseEntity<String> getSingleScriptAction(@PathVariable String id, @PathVariable TaskState state, @PathVariable int i) {
return service.getSingleScriptAction(id, state, i) == null
? new ResponseEntity(HttpStatus.NOT_FOUND)
: new ResponseEntity<String>(service.getSingleScriptAction(id, state, i), HttpStatus.OK);
}
@GetMapping(path="/{id}/action/SCRIPT/{state}")
public ResponseEntity<List<String>> getSingleScriptAction(@PathVariable String id, @PathVariable TaskState state) {
return service.getScriptActionForState(id, state) == null
? new ResponseEntity(HttpStatus.NOT_FOUND)
: new ResponseEntity<List<String>>(service.getScriptActionForState(id, state), HttpStatus.OK);
}
@DeleteMapping(path="/{id}/action/SCRIPT/{state}")
public MultiScriptDto removeScriptAction(@PathVariable String id, @PathVariable TaskState state) {
service.removeScriptAction(id, state);
return service.getStateTransitionScriptAction(id);
}
}

View File

@ -35,7 +35,12 @@ public class TaskTemplateController {
@GetMapping(path = "/{id}")
public TaskTemplateDto getTaskTemplateById(@PathVariable String id) {
return service.getTaskTemplateById(id);
return service.getTaskTemplateDtoById(id);
}
@PostMapping(path = "/{id}")
public TaskTemplateDto updateTaskTemplate(@PathVariable String id, @RequestBody TaskTemplateDto request) {
return service.updateTaskTemplateDto(id, request);
}
@GetMapping(params = "group_id")

View File

@ -0,0 +1,18 @@
package com.indu.tasktemplateservice.dto;
import java.util.HashMap;
import java.util.List;
import com.indu.tasktemplateservice.model.TaskState;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class MultiScriptDto extends HashMap<TaskState, List<String>> {
public MultiScriptDto(MultiScriptDto toCopy) {
for ( TaskState state : toCopy.keySet()) {
put(state, toCopy.get(state));
}
}
}

View File

@ -0,0 +1,7 @@
package com.indu.tasktemplateservice.dto;
import java.util.HashMap;
import com.indu.tasktemplateservice.model.TaskState;
public class SingleScriptDto extends HashMap<TaskState, String> {}

View File

@ -26,4 +26,6 @@ public class TaskTemplateDto {
private boolean checkPreviousDoneBeforeActivate;
// private AbortAction abortAction;
private Map<String, String> requiredFieldOnFinish;
private MultiScriptDto scriptActions;
}

View File

@ -8,6 +8,7 @@ import lombok.Getter;
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,4 +0,0 @@
package com.indu.tasktemplateservice.model;
public class Action {
}

View File

@ -0,0 +1,13 @@
package com.indu.tasktemplateservice.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum CustomAction {
CALL("call"),
SCRIPT("script");
private final String value;
}

View File

@ -6,6 +6,7 @@ import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.indu.tasktemplateservice.dto.MultiScriptDto;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -37,4 +38,7 @@ public class TaskTemplate {
// 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

@ -0,0 +1,117 @@
package com.indu.tasktemplateservice.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import com.indu.tasktemplateservice.dto.MultiScriptDto;
import com.indu.tasktemplateservice.dto.SingleScriptDto;
import com.indu.tasktemplateservice.dto.TaskTemplateDto;
import com.indu.tasktemplateservice.model.CustomAction;
import com.indu.tasktemplateservice.model.TaskState;
import com.indu.tasktemplateservice.model.TaskTemplate;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Service
@RequiredArgsConstructor
@Slf4j
public class TaskActionService {
private final TaskTemplateService templateService;
public MultiScriptDto getStateTransitionScriptAction(String id) {
TaskTemplate task = templateService.getTaskTemplateById(id);
if(task.getScriptActions() == null) {
return new MultiScriptDto();
}
return task.getScriptActions();
}
public Object getStateTransitionCallAction(String id) {
//TDB
TaskTemplate task = templateService.getTaskTemplateById(id);
return task;
}
public Object getStateTransitionAction(String id, CustomAction action) {
if ( CustomAction.SCRIPT.equals(action)) {
return getStateTransitionScriptAction(id);
}
if ( CustomAction.CALL.equals(action)) {
return getStateTransitionCallAction(id);
}
return null;
}
public MultiScriptDto setScriptAction(String id, MultiScriptDto scripts) {
TaskTemplateDto taskTemplateDto = templateService.getTaskTemplateDtoById(id);
taskTemplateDto.setScriptActions(scripts);
return templateService.updateTaskTemplate(id, taskTemplateDto).getScriptActions();
}
public MultiScriptDto addScriptAction(String id, SingleScriptDto script) {
TaskTemplateDto taskTemplateDto = templateService.getTaskTemplateDtoById(id);
for ( TaskState state : script.keySet() ) {
if(!taskTemplateDto.getScriptActions().containsKey(state)) {
taskTemplateDto.getScriptActions().put(state, new ArrayList<String>());
}
taskTemplateDto.getScriptActions().get(state).add(script.get(state));
}
return templateService.updateTaskTemplate(id, taskTemplateDto).getScriptActions();
}
public void removeScriptAction(String id, TaskState state, int i) {
TaskTemplateDto taskTemplateDto = templateService.getTaskTemplateDtoById(id);
if(!taskTemplateDto.getScriptActions().containsKey(state)) {
log.warn(String.format("Tried to delete script actions for state %s, but no actions found for this state. Aborting", state));
return;
}
if(taskTemplateDto.getScriptActions().get(state).size() <= i ) {
log.warn(String.format("Tried to delete script action no %s for state %s, but only %s actions found for this state. Aborting",
i+1, state, taskTemplateDto.getScriptActions().size()));
return;
}
taskTemplateDto.getScriptActions().get(state).remove(i);
if (taskTemplateDto.getScriptActions().get(state).size() == 0) {
taskTemplateDto.getScriptActions().remove(state);
}
templateService.updateTaskTemplate(id, taskTemplateDto);
}
public void removeScriptAction(String id, TaskState state) {
TaskTemplateDto taskTemplateDto = templateService.getTaskTemplateDtoById(id);
if(!taskTemplateDto.getScriptActions().containsKey(state)) {
log.warn(String.format("Tried to delete script actions for state %s, but no actions found for this state. Aborting", state));
return;
}
taskTemplateDto.getScriptActions().remove(state);
templateService.updateTaskTemplate(id, taskTemplateDto);
}
public String getSingleScriptAction(String id, TaskState state, int i) {
List<String> stateAction = getScriptActionForState(id, state);
if(stateAction == null) {
return null;
}
if(stateAction.size() <= i ) {
log.warn(String.format("Tried to get script action no %s for state %s, but only %s actions found for this state.",
i+1, state, stateAction.size()));
return null;
}
return stateAction.get(i);
}
public List<String> getScriptActionForState(String id, TaskState state) {
TaskTemplateDto taskTemplateDto = templateService.getTaskTemplateDtoById(id);
if(!taskTemplateDto.getScriptActions().containsKey(state)) {
log.warn(String.format("Tried to script actions for state %s, but no actions found.", state));
return null;
}
return taskTemplateDto.getScriptActions().get(state);
}
}

View File

@ -38,11 +38,13 @@ public class TaskTemplateService {
return repository.findAll().stream().map(mapper::taskTemplateToDto).toList();
}
public TaskTemplateDto getTaskTemplateById(String id) {
return mapper.taskTemplateToDto(
repository.findById(id)
.orElseThrow(() -> new ObjectWithUniqueFieldNotFoundException("BOID", id, getClass().getName()))
);
public TaskTemplateDto getTaskTemplateDtoById(String id) {
return mapper.taskTemplateToDto(getTaskTemplateById(id));
}
public TaskTemplate getTaskTemplateById(String id) {
return repository.findById(id)
.orElseThrow(() -> new ObjectWithUniqueFieldNotFoundException("BOID", id, getClass().getName()));
}
public TaskTemplateDto getTaskTemplateDtoByName(String name) {
@ -52,4 +54,15 @@ public class TaskTemplateService {
);
}
public TaskTemplate updateTaskTemplate(String id, TaskTemplateDto request) {
TaskTemplate template = getTaskTemplateById(id);
mapper.updateTaskTemplateFromRequest(request, template);
repository.save(template);
return template;
}
public TaskTemplateDto updateTaskTemplateDto(String id, TaskTemplateDto request) {
return mapper.taskTemplateToDto(updateTaskTemplate(id, request));
}
}

View File

@ -14,6 +14,6 @@ public class WorkflowTemplateDto {
private String id;
private String identiferPrefix;
private String name;
private List<String> tasks;
private List<String> workflows;
private List<String> taskTemplateIds;
private List<String> subWorkflowTemplateIds;
}

View File

@ -1,5 +1,7 @@
package com.indu.workflowservice.model;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -11,4 +13,6 @@ public class WorkflowTemplate {
private String id;
private String identiferPrefix;
private String name;
private List<String> taskTemplateIds;
private List<String> subWorkflowTemplateIds;
}

View File

@ -42,7 +42,7 @@ public class WorkflowService {
.taskIds(new ArrayList<String>())
.build();
log.info(String.format("Added asy fields. Workflow has form %s. Adding tasks", workflow));
addTasksToWorkflow(workflow, template.getTasks());
addTasksToWorkflow(workflow, template.getTaskTemplateIds());
addSubFlowsToWorkflow(workflow);
return workflow;
@ -87,6 +87,7 @@ public class WorkflowService {
public Workflow createNewWorkflowByTemplateId(String id) {
log.info("fetching template for wf creation....");
log.info(String.format("Feched template %s", templateRepo.getById(id)));
return createNewWorkflow(templateRepo.getById(id));
}
}

View File

@ -17,10 +17,12 @@ import com.indu.workflowtemplateservice.service.WorkflowTemplateService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@RequestMapping(path = "/api/template/workflow")
@RequiredArgsConstructor
@Slf4j
public class WorkflowTemplateController {
private final WorkflowTemplateService service;
@ -32,6 +34,7 @@ public class WorkflowTemplateController {
@GetMapping
public List<WorkflowTemplateResponse> getTemplates() {
log.info("Got getAll call");
return service.getAllWorkflowTemplates();
}

View File

@ -1,24 +0,0 @@
package com.indu.workflowtemplateservice.dto;
import java.util.ArrayList;
import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import com.indu.workflowtemplateservice.model.TaskTemplate;
import com.indu.workflowtemplateservice.model.WorkflowTemplate;
@Mapper
public interface WorkflowMapper {
WorkflowMapper instance = Mappers.getMapper(WorkflowMapper.class);
WorkflowTemplate requestToWorkflow(WorkflowTemplateRequest request);
WorkflowTemplateResponse workflowToResponse(WorkflowTemplate templateObject);
default List<TaskTemplate> map(List<String> value){
return new ArrayList<TaskTemplate>();
}
}

View File

@ -0,0 +1,49 @@
package com.indu.workflowtemplateservice.dto;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
import com.indu.workflowtemplateservice.model.WorkflowTemplate;
@Mapper
public interface WorkflowTemplateMapper {
WorkflowTemplateMapper instance = Mappers.getMapper(WorkflowTemplateMapper.class);
WorkflowTemplate map(WorkflowTemplateRequest request);
WorkflowTemplateResponse map(WorkflowTemplate templateObject);
WorkflowTemplateRequest mapToRequest(WorkflowTemplate template);
@AfterMapping
public default void addSubFlowFromTemplate1(WorkflowTemplate template, @MappingTarget WorkflowTemplateRequest request) {
if ( template.getSubWorkflow() == null) {
return;
}
request.setSubWorkflowId(
template.getSubWorkflow().stream().map(WorkflowTemplate::getId).toList()
);
}
@AfterMapping
public default void addSubFlowFromRequest(WorkflowTemplateRequest request, @MappingTarget WorkflowTemplate templateObject) {
if ( request.getSubWorkflowId() == null) {
return;
}
templateObject.setSubWorkflow(
request.getSubWorkflowId().stream().map(id -> WorkflowTemplate.builder().id(id).build()).toList()
);
}
@AfterMapping
public default void addSubFlowFromTemplate(WorkflowTemplate templateObject,@MappingTarget WorkflowTemplateResponse response ) {
if (templateObject.getSubWorkflow() == null) {
return;
}
response.setSubWorkflowTemplateIds(templateObject.getSubWorkflow().stream().map(WorkflowTemplate::getId).toList());
}
}

View File

@ -23,7 +23,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> tasksTemplateIds;
private List<String> workflowTemplateIds;
private List<String> subWorkflowId;
// private Map<String, String> requiredFieldOnFinish; // for now just requiredFields => pattern

View File

@ -22,5 +22,6 @@ public class WorkflowTemplateResponse {
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> tasks;
private List<String> tasksTemplateIds;
private List<String> subWorkflowTemplateIds;
}

View File

@ -3,6 +3,7 @@ package com.indu.workflowtemplateservice.model;
import java.util.List;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import com.fasterxml.jackson.annotation.JsonInclude;
@ -26,11 +27,11 @@ public class WorkflowTemplate {
@Indexed(unique = true)
private String name; // unique identifier for sequence
// private List<TaskTemplate> taskTemplates; //ids -> Expand????#
private List<String> taskTemplatesIds;
private List<String> tasksTemplateIds;
// @DBRef(lazy=true)
// private List<WorkflowTemplate> subWorkflow;
@DBRef(lazy=true)
private List<WorkflowTemplate> subWorkflow;
//
// private String parent_id;

View File

@ -13,7 +13,7 @@ import lombok.extern.slf4j.Slf4j;
@RequiredArgsConstructor
@Service
@Slf4j
public class WorkflowTemplateMapper {
public class WorkflowTemplateMapperManual {
private final TaskTemplateRepository taskTemplateRepo;
public WorkflowTemplateRequest mapToRequest(WorkflowTemplate template) {
@ -21,7 +21,7 @@ public class WorkflowTemplateMapper {
.name(template.getName())
.id(template.getId())
.identiferPrefix(template.getIdentiferPrefix())
.tasksTemplateIds(template.getTaskTemplatesIds())
.tasksTemplateIds(template.getTasksTemplateIds())
// .tasksTemplateIds(template.getTaskTemplates().stream().map(TaskTemplate::getId).toList())
.build();
}
@ -31,7 +31,7 @@ public class WorkflowTemplateMapper {
.name(template.getName())
.id(template.getId())
.identiferPrefix(template.getIdentiferPrefix())
.tasks(template.getTaskTemplatesIds())
.tasksTemplateIds(template.getTasksTemplateIds())
// .tasks(template.getTaskTemplatesIds().stream().map(taskTemplateRepo::getById).toList())
.build();
}
@ -43,7 +43,7 @@ public class WorkflowTemplateMapper {
.identiferPrefix(request.getIdentiferPrefix())
.name(request.getName())
.id(request.getId())
.taskTemplatesIds(request.getTasksTemplateIds())
.tasksTemplateIds(request.getTasksTemplateIds())
// .taskTemplates(request.getTasksTemplateIds().stream().map(taskTemplateRepo::getById).toList() )
.build();
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import org.springframework.stereotype.Service;
import com.indu.exceptionservice.exception.NameAlreadyTakenException;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateMapper;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateRequest;
import com.indu.workflowtemplateservice.dto.WorkflowTemplateResponse;
import com.indu.workflowtemplateservice.model.WorkflowTemplate;
@ -46,17 +47,12 @@ public class WorkflowTemplateService {
return mapper.map(upsertEmptyTemplate(request));
}
//TODO: needed?
public WorkflowTemplateRequest initializeTemplate(@Valid WorkflowTemplateRequest request) {
return mapper.mapToRequest(upsertEmptyTemplate(request));
}
public WorkflowTemplate findById(String id) {
return repository.findById(id).orElseThrow(() -> new NotFoundException("did not find required wf template"));
}
public List<WorkflowTemplateResponse> getAllWorkflowTemplates() {
return repository.findAll().stream().map(mapper::map).toList();
return repository.findAll().stream().map(WorkflowTemplateMapper.instance::map).toList();
}
public WorkflowTemplateResponse getWorkflowById(String id) {
@ -68,7 +64,7 @@ public class WorkflowTemplateService {
}
public WorkflowTemplate addTaskTemplateToWorkflowTemplate(WorkflowTemplate template, String templateTask) {
template.getTaskTemplatesIds().add(templateTask);
template.getTasksTemplateIds().add(templateTask);
return template;
}