io.resys.wrench.assets.flow.spi.builders.CommandFlowModelBuilder Maven / Gradle / Ivy
package io.resys.wrench.assets.flow.spi.builders;
/*-
* #%L
* wrench-assets-flow
* %%
* Copyright (C) 2016 - 2019 Copyright 2016 ReSys OÜ
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.IOException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.IntNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.resys.wrench.assets.datatype.api.DataTypeRepository;
import io.resys.wrench.assets.flow.api.FlowAstFactory;
import io.resys.wrench.assets.flow.api.FlowAstFactory.Node;
import io.resys.wrench.assets.flow.api.FlowAstFactory.NodeBuilder;
import io.resys.wrench.assets.flow.api.FlowAstFactory.NodeFlow;
import io.resys.wrench.assets.flow.api.FlowAstFactory.NodeSwitch;
import io.resys.wrench.assets.flow.api.FlowAstFactory.NodeTask;
import io.resys.wrench.assets.flow.api.model.FlowAst.FlowCommandMessageType;
import io.resys.wrench.assets.flow.api.model.FlowAst.FlowCommandType;
import io.resys.wrench.assets.flow.api.model.FlowModel;
import io.resys.wrench.assets.flow.api.model.FlowModel.FlowTaskModel;
import io.resys.wrench.assets.flow.api.model.FlowModel.FlowTaskType;
import io.resys.wrench.assets.flow.api.model.FlowModel.FlowTaskValue;
import io.resys.wrench.assets.flow.api.model.ImmutableFlowModel;
import io.resys.wrench.assets.flow.api.model.ImmutableFlowTaskValue;
import io.resys.wrench.assets.flow.spi.FlowDefinitionException;
import io.resys.wrench.assets.flow.spi.exceptions.NodeFlowException;
import io.resys.wrench.assets.flow.spi.expressions.ExpressionFactory;
import io.resys.wrench.assets.flow.spi.model.ImmutableFlowTaskModel;
import io.resys.wrench.assets.flow.spi.model.NodeFlowBean;
import io.resys.wrench.assets.flow.spi.support.NodeFlowAdapter;
public class CommandFlowModelBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(CommandFlowModelBuilder.class);
private static final ImmutableFlowTaskModel EMPTY = new ImmutableFlowTaskModel("empty", null, FlowTaskType.END);
private final FlowAstFactory nodeRepository;
private final ObjectMapper objectMapper;
private final DataTypeRepository dataTypeRepository;
private final ExpressionFactory parser;
private final String input;
private final ImmutableFlowTaskModel endNode = new ImmutableFlowTaskModel("end", null, FlowTaskType.END);
private final Map taskModels = new HashMap<>();
private List tasksByOrder;
private Map tasksById;
private String flowId;
private Optional rename;
public CommandFlowModelBuilder(
FlowAstFactory nodeRepository,
ObjectMapper objectMapper,
DataTypeRepository dataTypeRepository,
ExpressionFactory parser,
String input, Optional rename) {
super();
this.dataTypeRepository = dataTypeRepository;
this.nodeRepository = nodeRepository;
this.parser = parser;
this.objectMapper = objectMapper;
this.input = input;
this.rename = rename;
}
private NodeFlow parseModel(ArrayNode src) throws IOException {
List messages = new ArrayList<>();
NodeBuilder nodeBuilder = nodeRepository.create(message -> {
if(message.getType() == FlowCommandMessageType.ERROR) {
messages.add(message.getValue());
}
});
src.forEach(command -> create((ObjectNode) command, nodeBuilder));
NodeFlow data = nodeBuilder.build();
if(!messages.isEmpty()) {
throw new NodeFlowException(messages.toString(), data.getValue());
}
return data;
}
public Map.Entry build() {
try {
final NodeFlow data;
final ArrayNode src;
final String input;
if(rename.isPresent()) {
ArrayNode original = (ArrayNode) objectMapper.readTree(this.input);
NodeFlow originalModel = parseModel(original);
Node idNode = originalModel.getId();
ObjectNode renameNode = objectMapper.createObjectNode();
renameNode.set("id", IntNode.valueOf(idNode.getStart()));
renameNode.set("type", TextNode.valueOf(FlowCommandType.SET.name()));
renameNode.set("value", TextNode.valueOf("id: " + rename.get()));
original.add(renameNode);
input = objectMapper.writeValueAsString(original);
src = (ArrayNode) objectMapper.readTree(input);
data = parseModel(src);
} else {
input = this.input;
src = (ArrayNode) objectMapper.readTree(input);
data = parseModel(src);
}
tasksById = data.getTasks().values().stream()
.collect(Collectors.toMap(n -> NodeFlowAdapter.getStringValue(n.getId()), n -> n));
tasksByOrder = new ArrayList<>(data.getTasks().values());
Collections.sort(tasksByOrder);
flowId = NodeFlowAdapter.getStringValue(data.getId());
NodeTask firstTask = data.getTasks().values().stream()
.filter(task -> task.getOrder() == 0)
.findFirst().orElse(null);
final ImmutableFlowTaskModel task = tasksById.isEmpty() ? EMPTY: createNode(firstTask);
final FlowModel model = ImmutableFlowModel.builder()
.id(flowId)
.rev(src.size())
.src(data.getValue())
.description(NodeFlowAdapter.getStringValue(data.getDescription()))
.task(task)
.tasks(getTasks(task))
.inputs(NodeFlowAdapter.getInputs(data, dataTypeRepository))
.build();
return new AbstractMap.SimpleEntry(input, model);
} catch(IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private Collection getTasks(FlowTaskModel node) {
Map result = new HashMap<>();
getServices(result, node);
return result.values();
}
private void getServices(Map visited, FlowTaskModel node) {
if(visited.containsKey(node.getId())) {
return;
}
visited.put(node.getId(), node);
node.getNext().forEach(n -> getServices(visited, n));
}
protected ImmutableFlowTaskModel createNode(NodeTask task) {
String taskId = NodeFlowAdapter.getStringValue(task.getId());
if(taskModels.containsKey(taskId)) {
return taskModels.get(taskId);
}
FlowTaskType type = NodeFlowAdapter.getTaskType(task);
FlowTaskValue taskValue = createFlowTaskValue(task, type);
final ImmutableFlowTaskModel result = new ImmutableFlowTaskModel(taskId, taskValue, type);
taskModels.put(taskId, result);
final ImmutableFlowTaskModel intermediate;
if(type != FlowTaskType.EMPTY) {
intermediate = new ImmutableFlowTaskModel(taskId + "-" + FlowTaskType.MERGE, null, FlowTaskType.MERGE);
result.addNext(intermediate);
} else {
intermediate = result;
}
boolean isEnd = endNode.getId().equals(NodeFlowAdapter.getStringValue(task.getThen()));
String then = NodeFlowAdapter.getStringValue(task.getThen());
// Add next
if(then != null && !isEnd) {
intermediate.addNext(createNode(tasksById.get(getThenTaskId(task, then, type))));
}
if(isEnd) {
intermediate.addNext(endNode);
}
// Only exclusive decisions have next in here
if(task.getSwitch().isEmpty()) {
return result;
}
// Exclusive decision gateway
ImmutableFlowTaskModel exclusive = new ImmutableFlowTaskModel(taskId + "-" + FlowTaskType.EXCLUSIVE, null, FlowTaskType.EXCLUSIVE);
intermediate.addNext(exclusive);
List decisions = new ArrayList<>(task.getSwitch().values());
Collections.sort(decisions, (o1, o2) -> Integer.compare(o1.getOrder(), o2.getOrder()));
for(NodeSwitch decision : decisions) {
String decisionId = decision.getKeyword();
try {
String when = NodeFlowAdapter.getStringValue(decision.getWhen());
String thenValue = NodeFlowAdapter.getStringValue(decision.getThen());
ImmutableFlowTaskModel next = new ImmutableFlowTaskModel(
decisionId,
parser.get(when),
FlowTaskType.DECISION);
exclusive.addNext(next);
if(thenValue != null && !endNode.getId().equals(thenValue)) {
next.addNext(createNode(tasksById.get(getThenTaskId(task, thenValue, type))));
}
} catch(Exception e) {
ImmutableFlowTaskModel errorModel = new ImmutableFlowTaskModel(decisionId, null, FlowTaskType.DECISION);
String message = "Failed to evaluate expression: \"" + taskId + "\" in flow: " + flowId + ", decision: " + errorModel.getId() + "!" + System.lineSeparator() + e.getMessage();
LOGGER.error(message, e);
throw new FlowDefinitionException(message, errorModel, e);
}
}
return result;
}
private String getThenTaskId(NodeTask task, String then, FlowTaskType type) {
if(!NodeFlowBean.VALUE_NEXT.equalsIgnoreCase(then)) {
return then;
}
NodeTask next = null;
for(NodeTask node : tasksByOrder) {
if(node.getStart() > task.getStart()) {
next = node;
}
}
if(next == null) {
String taskId = NodeFlowAdapter.getStringValue(task.getId());
ImmutableFlowTaskModel errorModel = new ImmutableFlowTaskModel(taskId, null, type);
String message = "There are no next task after: \"" + taskId + "\" in flow: " + flowId + ", decision: " + errorModel.getId() + "!";
throw new FlowDefinitionException(message, errorModel);
}
return NodeFlowAdapter.getStringValue(next.getId());
}
public FlowTaskValue createFlowTaskValue(NodeTask task, FlowTaskType type) {
if(type == FlowTaskType.SERVICE || type == FlowTaskType.DT || type == FlowTaskType.USER_TASK) {
boolean collection = NodeFlowAdapter.getBooleanValue(task.getRef().getCollection());
String ref = NodeFlowAdapter.getStringValue(task.getRef().getRef());
Map inputs = new HashMap<>();
for(Map.Entry entry : task.getRef().getInputs().entrySet()) {
inputs.put(entry.getKey(), NodeFlowAdapter.getStringValue(entry.getValue()));
}
return ImmutableFlowTaskValue.builder()
.isCollection(collection)
.ref(ref)
.putAllInputs(inputs)
.build();
} else if(type == FlowTaskType.EMPTY) {
return ImmutableFlowTaskValue.builder().isCollection(false).build();
}
throw new IllegalArgumentException("Can't create task value from type: " + type + "!");
}
private void create(ObjectNode command, NodeBuilder builder) {
int line = command.get("id").asInt();
FlowCommandType type = FlowCommandType.valueOf(command.get("type").asText());
if(type == FlowCommandType.DELETE) {
builder.delete(line, command.get("value").asInt());
} else if(type == FlowCommandType.ADD) {
builder.add(line, getText(command));
} else {
builder.set(line, getText(command));
}
}
private String getText(ObjectNode command) {
return command.hasNonNull("value") ? command.get("value").asText() : null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy