Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.xlrit.gears.runner.runnertarget.GraphQLRunnerTarget Maven / Gradle / Ivy
package com.xlrit.gears.runner.runnertarget;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
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.graphql.GraphQLClient;
import com.xlrit.gears.runner.graphql.Operation;
import com.xlrit.gears.runner.run.Config;
import com.xlrit.gears.runner.utils.GraphQLUtils;
import java.util.Optional;
public class GraphQLRunnerTarget implements RunnerTarget {
private final Memory memory = new Memory();
private final GraphQLClient client;
public GraphQLRunnerTarget(Config config) {
client = new GraphQLClient(config.endpoint, new ObjectMapper());
}
@Override
public void loadData(String srcDir, String pattern) {
JsonNode jsonResponse = client.invoke(Operation.loadData(srcDir, pattern));
System.out.printf("Load data: %s%n", jsonResponse);
}
private String processInstanceKey(String key) {
return "processinstance:" + key;
}
private String taskIdKey(String key) {
return "taskid:" + key;
}
public ProcessInstance getProcessInstance(String key) {
return memory.load(processInstanceKey(key));
}
public ArrayNode getInstanceTasks(String key) {
// get tasks by process id and extract tasks from response
String instanceId = getProcessInstance(key).processInstanceId();
Operation operation = Operation.getTasks(instanceId);
JsonNode jsonResponse = client.invoke(operation);
return ((ArrayNode)jsonResponse.get("processInstance").get("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 memory.load(taskIdKey(taskExpression.id()));
}
JsonNode jsonTask = getTaskFromList(taskExpression, getInstanceTasks(key));
String taskId = jsonTask.get("id").asText();
return new TaskId(taskId, taskExpression.id());
}
public String getDeploymentId(String key) {
return memory.load(processInstanceKey(key)).deploymentId();
}
@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 setDateTime(String value) {
Operation operation = Operation.overrideCurrentDateTime(value);
JsonNode json = client.invoke(operation);
assert(json.get("overrideCurrentDateTime").asBoolean());
}
@Override
public void scenarioStart(String scenario) {}
@Override
public void scenarioFinish() {
clearDateTime();
}
@Override
public void finish() {}
private void clearDateTime() {
JsonNode json = client.invoke(Operation.clearCurrentDateTime);
assert(json.get("clearCurrentDateTime").asBoolean());
}
@Override
public void startProcess(String key, JsonNode valuesOpt) {
JsonNode values = valuesOpt;
if (valuesOpt != null) {
JsonNode processDefinitionIdResult = client.invoke(Operation.processDefinitionByKey(key));
JsonNode jsonId = processDefinitionIdResult.path("processDefinitionByKey").path("id");
if (jsonId.isMissingNode()) {
throw new InterpreterException(String.format("Could not interpret processDefinitionId in result: %s",
processDefinitionIdResult));
}
String processDefinitionId = jsonId.asText();
values = resolveLabels(valuesOpt, processDefinitionId, null, false);
}
JsonNode result = client.invoke(Operation.startProcessByKey(key, values));
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();
if (kind.equals("InputErrors")) {
JsonNode errors = processInstanceJson.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);
}
// associate process result with the process key and also as the "current" process (without a specific key)
memory.store(processInstanceKey(key), new ProcessInstance(key, processInstanceJson));
memory.store(processInstanceKey(""), new ProcessInstance("", processInstanceJson));
}
@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) {
memory.store(taskIdKey(taskExpression.id()), taskId);
}
}
@Override
public void dataAssertion(String key, String grql) {
GrqlResolver grqlResolver = new GrqlResolver(key, this);
String jpql = grqlResolver.toJpql(grql);
// perform the operation
String deploymentId = key == null ? "" : getDeploymentId(key);
JsonNode json = client.invoke(Operation.dataAssertion(jpql, deploymentId));
JsonNode result = json.path("checkData");
if (result.isMissingNode()) {
throw new InterpreterException("Could not extract result from data assertion query");
}
if (!result.isBoolean()) {
throw new InterpreterException("Data assertion is not of type boolean: " + result);
}
if (!result.asBoolean()) {
StringBuilder msg = new StringBuilder();
msg.append("Data assertion failed:\n")
.append(grql);
if (!grqlResolver.resolved.isEmpty()) {
msg.append("With variable(s):");
for (var entry : grqlResolver.resolved.entrySet()) {
msg.append('\t')
.append(entry.getKey())
.append(": ")
.append(entry.getValue())
.append('\n');
}
}
throw new InterpreterException(msg.toString());
}
}
@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()));
// perform the operation
Operation operation = Operation.submitTask(taskId.id(), resolveLabels(values, taskId.id(), null, true));
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();
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);
}
}
@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 JsonNode resolveLabels(JsonNode values, String choicesId, String fieldId, boolean task) {
ObjectNode newValues = new ObjectMapper().createObjectNode();
for (var ite = values.fields(); ite.hasNext();) {
var current = ite.next();
String path = fieldId == null ? current.getKey() : String.format("%s.%s", fieldId, current.getKey());
if (current.getValue().isObject()) {
newValues.set(current.getKey(), convertObject((ObjectNode) current.getValue(), choicesId, path, task));
} else if (current.getValue().isArray()) {
newValues.set(current.getKey(), convertArray(current.getValue(), choicesId, path, task));
} else {
newValues.set(current.getKey(), current.getValue());
}
}
return newValues;
}
private ArrayNode convertArray(JsonNode array, String choicesId, String fieldId, boolean task) {
ArrayNode newArray = new ObjectMapper().createArrayNode();
int index = 0;
for (JsonNode cur : array) {
String path = String.format("%s[%d]", fieldId, index++);
newArray.add(resolveLabels(cur, choicesId, path, task));
}
return newArray;
}
private JsonNode convertObject(ObjectNode objectValue, String choicesId, String fieldId, boolean task) {
if (objectValue.size() > 1) {
return resolveLabels(objectValue, choicesId, fieldId, task);
}
var kvp = objectValue.fields().next();
return switch (kvp.getKey()) {
case "label" -> new TextNode(labelToValue(kvp.getValue().asText(), choicesId, fieldId, task));
case "value" -> new TextNode(kvp.getValue().asText());
default -> resolveLabels(objectValue, choicesId, fieldId, task);
};
}
private String labelToValue(String label, String choicesId, String fieldId, boolean task) {
ArrayNode choices = (ArrayNode)(client.invoke(Operation.choicesByLabel(choicesId, fieldId, label, task)).get("choices"));
if (choices.isEmpty()) {
throw new InterpreterException(String.format("Label %s not found for input %s", label, fieldId));
}
return choices.get(0).get("value").asText();
}
}