com.xlrit.gears.runner.runnertarget.GraphQLRunnerTarget Maven / Gradle / Ivy
The newest version!
package com.xlrit.gears.runner.runnertarget;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.xlrit.gears.runner.driver.RunConfig;
import com.xlrit.gears.runner.graphql.Operation;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GraphQLRunnerTarget extends AbstractRunnerTarget {
private static final Logger LOG = LoggerFactory.getLogger(GraphQLRunnerTarget.class);
@Getter
private String startTime = "";
private final Map processInstances = new HashMap<>();
private final Map namedTasks = new HashMap<>();
public GraphQLRunnerTarget(RunConfig config, RunnerContext context) {
super(config, context);
}
public ArrayNode getInstanceTasks(String key) {
// get tasks by process id and extract tasks from response
String instanceId = processInstances.get(key).processInstanceId();
Operation operation = Operation.getTasks(instanceId);
JsonNode jsonResponse = client.invoke(operation);
if (!(jsonResponse.at("/processInstance/tasks") instanceof ArrayNode tasks)) {
//throw new InterpreterException("Expected response to contain array at /processInstance/tasks: " + jsonResponse);
return objectMapper.createArrayNode();
}
return tasks;
}
public JsonNode getTaskFromList(TaskExpression taskExpression, ArrayNode jsonTasks) {
switch(taskExpression.type()) {
case First -> {
if (jsonTasks == null || jsonTasks.isEmpty()) throw new InterpreterException(String.format(
"Could not obtain the first task from task list: %s", jsonTasks));
return jsonTasks.get(0);
}
case Only -> {
if (jsonTasks == null || jsonTasks.size() != 1) throw new InterpreterException(String.format(
"There are %d tasks available. Thus the 'only' requirement is not met: %s",
jsonTasks == null ? 0 : jsonTasks.size(), jsonTasks));
return jsonTasks.get(0);
}
}
throw new InterpreterException("Invalid task expression: " + taskExpression);
}
public TaskId getTaskId(String key, TaskExpression taskExpression) {
if (taskExpression.type() == TaskType.Identifier) {
return namedTasks.get(taskExpression.id());
}
JsonNode jsonTask = getTaskFromList(taskExpression, getInstanceTasks(key));
String taskId = jsonTask.get("id").asText();
return new TaskId(taskId, taskExpression.id());
}
@Override
public void login(String username) {
// login at the graph ql server
Operation operation = Operation.login(username, username);
JsonNode json = client.invoke(operation);
// get user token
if (json.get("login") == null) {
throw new InterpreterException(String.format("Could not obtain user token from %s", json));
}
String token = json.get("login").asText();
client.setToken(token);
}
@Override
public void startProcess(String key, JsonNode valuesOpt) {
JsonNode values = valuesOpt;
List files = new ArrayList<>();
if (valuesOpt != null) {
values = resolveStartForm(key, valuesOpt);
values = extractFiles(values, files);
}
Operation operation = files.isEmpty() ? Operation.startProcessByKey(key, values) : Operation.startProcessByKey(key, values, files);
JsonNode result = client.invoke(operation);
JsonNode processInstanceJson = result.path("startProcessByKey");
if (processInstanceJson.isMissingNode()) {
throw new InterpreterException(String.format("Could not interpret ProcessInstance in result: %s", result));
}
String kind = processInstanceJson.get("__typename").asText();
processErrors(processInstanceJson, kind);
startTime = processInstanceJson.get("startTime").asText();
deploymentId = processInstanceJson.get("processDefinition").get("deploymentId").asText();
processInstances.put(key, new ProcessInstance(key, processInstanceJson.get("id").asText()));
}
@Override
public void claimTask(String key, TaskExpression taskExpression) {
TaskId taskId = getTaskId(key, taskExpression);
// claim the task
JsonNode jsonClaimResponse = client.invoke(Operation.claimTask(taskId.id()));
// verify result
JsonNode success = jsonClaimResponse.path("claimTask");
if (success.isMissingNode()) {
throw new InterpreterException("Could not determine claim task response");
}
if (!success.asBoolean()) {
throw new InterpreterException(String.format("Claiming task '%s' has failed", taskId.id()));
}
// store task id
if (taskExpression.id() != null) {
namedTasks.put(taskExpression.id(), taskId);
}
}
@Override
public void transferTask(String key, TaskExpression taskExpression, String currentAssignee) {
TaskId taskId = getTaskId(key, taskExpression);
// transfer the task
JsonNode jsonClaimResponse = client.invoke(Operation.transferTask(taskId.id(), currentAssignee));
// verify result
JsonNode success = jsonClaimResponse.path("transferTask");
if (success.isMissingNode()) {
throw new InterpreterException("Could not determine transfer task response");
}
if (!success.asBoolean()) {
throw new InterpreterException(String.format("Transfer of task '%s' has failed", taskId.id()));
}
// store task id
if (taskExpression.id() != null) {
namedTasks.put(taskExpression.id(), taskId);
}
}
@Override
public void submitTask(String processKey, TaskExpression taskExpression, JsonNode values) {
// obtain task id
TaskId taskId = getTaskId(processKey, taskExpression);
// get the individual task (including the form) and log it
JsonNode task = client.invoke(Operation.getTask(taskId.id()));
JsonNode form = task.at("/task/form");
if (context.config.renderForms) {
LOG.debug("Task {}: {}", taskId.id(), task);
}
// perform the operation
JsonNode resolvedValues = choiceResolver
.form(form)
.formId(taskId.id())
.isTask(true)
.resolve(values);
List files = new ArrayList<>();
resolvedValues = extractFiles(resolvedValues, files);
final Operation operation;
if (files.isEmpty()) operation = Operation.submitTask(taskId.id(), resolvedValues);
else operation = Operation.submitTask(taskId.id(), resolvedValues, files);
JsonNode json = client.invoke(operation);
// verify the result
JsonNode result = json.path("submitTask");
if (result.isMissingNode()) throw new InterpreterException("Submitting data to a task has failed");
String kind = result.asText();
processErrors(result, kind);
}
@Override
public void basedOn(String value) {}
// TODO does this actually verify the process has ended?
public void processCompleted(String processKey) {
// see if any tasks remain
JsonNode jsonTasks = getInstanceTasks(processKey);
if (jsonTasks != null && !jsonTasks.isEmpty()) {
StringBuilder tasks = new StringBuilder();
for (JsonNode task : jsonTasks) {
tasks.append(task);
}
throw new InterpreterException(String.format("%d task(s) remaining upon process complete:%n%s",
jsonTasks.size(), tasks));
}
}
private void processErrors(JsonNode result, String kind) {
if (kind.equals("InputErrors")) {
JsonNode errors = result.get("errors");
if (!errors.isArray()) throw new InterpreterException("Could not obtain errors from result: " + result);
ArrayNode errorsArray = (ArrayNode)errors;
StringBuilder msgs = new StringBuilder();
for (JsonNode error : errorsArray) {
msgs.append(String.format("Entering value '%s' for %s of type %s caused error %d: %s%n",
error.get("value").asText(), error.get("label").asText(), error.get("type").asText(),
error.get("status").asInt(), error.get("message").asText()));
}
throw new InterpreterException(errors.size() + " user error(s) occurred: \n" + msgs);
}
}
private JsonNode extractFiles(JsonNode values, List files) {
if (values.isObject()) {
ObjectNode object = (ObjectNode) values;
ObjectNode newObject = objectMapper.createObjectNode();
for (var ite = object.fields(); ite.hasNext();) {
var entry = ite.next();
newObject.set(entry.getKey(), extractFiles(entry.getValue(), files));
}
return newObject;
} else if (values.isArray()) {
ArrayNode array = (ArrayNode) values;
ArrayNode newArray = objectMapper.createArrayNode();
for (var ite = array.elements(); ite.hasNext();) {
newArray.add(extractFiles(ite.next(), files));
}
return newArray;
} else if (values.isTextual()) {
String text = values.asText();
if (text.startsWith("filename:")) {
String fileName = text.substring(9);
File file = new File(fileName);
if (!file.exists()) {
throw new IllegalArgumentException(String.format("File %s doesn't exist", file.getAbsolutePath()));
}
files.add(file);
int index = files.size() - 1;
return TextNode.valueOf("name:file" + index);
}
}
return values;
}
}