com.fivefaces.structureclient.service.statemachine.impl.ScaffoldStateMachineServiceImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of common-structure-client Show documentation
Show all versions of common-structure-client Show documentation
structure Client for Five Faces
package com.fivefaces.structureclient.service.statemachine.impl;
import com.fivefaces.structure.schema.FieldType;
import com.fivefaces.structure.schema.StructureFieldSchema;
import com.fivefaces.structure.schema.StructureSchema;
import com.fivefaces.structure.service.StructureSchemaLoader;
import com.fivefaces.structureclient.service.statemachine.ScaffoldStateMachineResult;
import com.fivefaces.structureclient.service.statemachine.ScaffoldStateMachineService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.CaseUtils;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class ScaffoldStateMachineServiceImpl implements ScaffoldStateMachineService {
private final StructureSchemaLoader structureSchemaLoader;
@Override
public ScaffoldStateMachineResult scaffold(String fileContents) throws Exception {
String arn = "arn:aws:states:ap-southeast-2:705130463218";
String apiEndPoint = "l3zjr899d9.execute-api.ap-southeast-2.amazonaws.com";
ScaffoldStateMachineResult result = scaffoldStateMachine(fileContents, arn, apiEndPoint);
if (!result.getWarnings().isEmpty()) {
throw new IllegalArgumentException(result.getWarnings().toString());
}
return result;
}
@Override
public ScaffoldStateMachineResult unscaffold(String fileContents) {
ScaffoldStateMachineResult result = reverseStateMachine(fileContents);
if (!result.getWarnings().isEmpty()) {
throw new IllegalArgumentException(result.getWarnings().toString());
}
return result;
}
private ScaffoldStateMachineResult reverseStateMachine(final String definition) {
ScaffoldStateMachineResult result = new ScaffoldStateMachineResult();
result.getStateMachines().put("reverse", definition);
String[] lines = definition.split("\n");
String stateMachine;
String name = "";
String comment;
for (String line : lines) {
if (line.trim().startsWith("name")) {
name = line.substring(line.indexOf("_") + 1).
replaceAll("\"", "").
replaceAll("([A-Z])", " $1");
name = name.substring(0, 1).toUpperCase() + name.substring(1);
}
}
// sometimes the step definition starts with the < 0) {
stateMachine = definition.substring(definition.indexOf("< {
JSONObject stateJson = states.getJSONObject(state);
final String stepType = stateJson.getString("Type");
switch (stepType) {
case "Task":
// Check Validate
if (state.startsWith("Validate")) {
reverseValidate(stateMachineTextDefinition, state);
} else {
if (stateJson.has("Resource")) {
JSONObject parameters = stateJson.getJSONObject("Parameters");
if (parameters.has("StateMachineArn")) {
reverseSubTask(parameters, stateMachineTextDefinition, state, stateJson);
} else {
reverseQuery(parameters, stateMachineTextDefinition, state);
}
}
}
result.getSteps().put(state, stateMachineTextDefinition.toString());
stateMachineTextDefinition.setLength(0);
break;
case "Pass":
reversePass(stateMachineTextDefinition, state);
result.getSteps().put(state, stateMachineTextDefinition.toString());
stateMachineTextDefinition.setLength(0);
break;
case "Choice":
reverseChoice(stateMachineTextDefinition, state, stateJson);
result.getSteps().put(state, stateMachineTextDefinition + "\n");
stateMachineTextDefinition.setLength(0);
break;
}
});
return result;
}
private void reverseValidate(StringBuilder stateMachineTextDefinition, String state) {
stateMachineTextDefinition.append("Validate\n\t-name ").append(state.replaceAll("Validate ", "")).append("\n");
}
private void reverseSubTask(JSONObject parameters, StringBuilder stateMachineTextDefinition, String state, JSONObject stateJson) {
String stateMachineArn = parameters.getString("StateMachineArn");
String entity;
if (stateMachineArn.contains("${var.environment}_")) {
entity = stateMachineArn.substring(stateMachineArn.indexOf("}_") + 2);
entity = entity.substring(stateMachineArn.indexOf(":create") + 7);
} else if (stateMachineArn.contains("REGION:ACCOUNT_ID")) {
entity = "";
} else {
entity = stateMachineArn.substring(stateMachineArn.indexOf(":create") + 7);
}
String stepName = stateMachineArn.substring(stateMachineArn.indexOf("stateMachine:") + 13);
if (stepName.startsWith("${var.environment}_")) {
stepName = stepName.substring(19);
}
if (stepName.equals("STATE_MACHINE_NAME")) {
stepName = "";
} else {
stepName = stepName.replaceAll("([A-Z])", " $1");
stepName = stepName.substring(0, 1).toUpperCase() + stepName.substring(1);
}
stateMachineTextDefinition.append("Subtask\n\t-name ").append(state).append("\n\t-entity ").append(entity).append("\n\t-task ").append(stepName);
if (stateJson.has("Next")) {
stateMachineTextDefinition.append("\n\t-next ").append(stateJson.getString("Next")).append(" ");
}
stateMachineTextDefinition.append("\n");
}
private void reverseQuery(JSONObject parameters, StringBuilder stateMachineTextDefinition, String state) {
JSONObject requestBody = parameters.getJSONObject("RequestBody");
String dbType = "";
if (requestBody.has("type")) {
dbType = requestBody.getString("type");
}
String fail = " ";
if (requestBody.has("failIfNotFound") && requestBody.getBoolean("failIfNotFound")) {
fail = "\n\t-fail failIfNotFound ";
} else if (requestBody.has("failIfFound") && requestBody.getBoolean("failIfFound")) {
fail = "\n\t-fail failIfFound ";
}
String criteria = " ";
if (requestBody.has("criteria")) {
criteria = requestBody.getJSONArray("criteria").toString();
criteria = criteria.substring(1, criteria.length() - 1);
}
stateMachineTextDefinition.append("Query\n\t-name ").append(state).append("\n\t-type ").
append(dbType).append(fail).
append("\n\t-criteria ").append(criteria).append("\n");
}
private void reversePass(StringBuilder stateMachineTextDefinition, String state) {
stateMachineTextDefinition.append("Pass\n\t-name ").append(state).append("\n");
}
private void reverseChoice(StringBuilder stateMachineTextDefinition, String state, JSONObject stateJson) {
stateMachineTextDefinition.append("Choice\n\t-name ").append(state).append(" ");
JSONArray choices = stateJson.getJSONArray("Choices");
choices.forEach(a -> {
JSONObject jsonObject = (JSONObject) a;
String operator = "";
if (jsonObject.has("Not")) {
operator = "not";
jsonObject = jsonObject.getJSONObject("Not");
}
if (jsonObject.has("StringEquals")) {
operator = operator + " equals " + jsonObject.get("StringEquals"); // TODO more here
} else if (jsonObject.has("IsPresent")) {
operator = operator + " isPresent " + jsonObject.get("IsPresent"); // TODO more here
}
String variable = " ";
if (jsonObject.has("Variable")) {
variable = jsonObject.getString("Variable");
if (variable.startsWith("$.query")) {
variable = variable.substring(variable.indexOf("$.query.") + 8);
} else if (variable.startsWith("$.input")) {
// todo might need to make this fancier later
}
}
jsonObject = (JSONObject) a;
stateMachineTextDefinition.append("\n\t-choice ").append(variable).append(operator).append(" ").append(jsonObject.get("Next")).append(" ");
});
stateMachineTextDefinition.append("\n\t-default ").append(stateJson.get("Default")).append(" ");
}
private ScaffoldStateMachineResult scaffoldStateMachine(final String definition, final String arn, final String apiEndPoint) throws Exception {
ScaffoldStateMachineResult result = new ScaffoldStateMachineResult();
String[] originalLines = definition.split("\n");
String stateMachine = "";
int counter = 0;
List linesList = new LinkedList<>();
for (String readline : originalLines) {
if (readline.startsWith("\t") || readline.startsWith(" ")) {
// is a continuation so add to the last line
String l = linesList.remove(linesList.size() - 1);
l += readline;
linesList.add(l);
} else {
linesList.add(readline);
}
}
String[] linesCopy = new String[linesList.size()];
linesCopy = linesList.toArray(linesCopy);
final String[] lines = linesCopy;
for (String line : lines) {
final String lineLowerCase = line.toLowerCase(Locale.ROOT);
if (lineLowerCase.startsWith("#")) {
System.out.println("comment");
// nothing to do - commented out
} else if (lineLowerCase.startsWith("create")) {
final Map createParameters = getParameters(line, "name", "comment");
String comment = "A description of my state machine";
if (createParameters.containsKey("comment")) {
comment = createParameters.get("comment");
}
String name = CaseUtils.toCamelCase(createParameters.get("name"), false, ' ');
result.setName(name);
final String firstActionName = getStepName(lines[counter + 1]);
final String allActionStepPlaceholders = getAllActionStepPlaceholders(lines);
stateMachine = String.format("{\n" +
" \"Comment\": \"%s\",\n" +
" \"StartAt\": \"%s\",\n" +
" \"States\": {\n" +
" %s\n" +
" }\n" +
"}", comment, firstActionName, allActionStepPlaceholders);
} else if (lineLowerCase.startsWith("validate")) {
final Map validateParameters = getParameters(line, "name", "next", "entity", "roles", "properties", "exclude");
final String validateEntity = validateParameters.get("entity");
if (validateEntity != null && !validateEntity.toLowerCase(Locale.ROOT).equals("none")) {
// check that roles are supplied when entity is supplied
if (!validateParameters.containsKey("roles")) {
result.getWarnings().add(String.format("%s step is missing roles", validateParameters.get("name")));
}
String requiredRoleNames = validateParameters.get("roles");
if (requiredRoleNames == null) {
requiredRoleNames = "";
}
List exclusions = new ArrayList<>();
validateParameters.keySet().stream().filter(s -> s.startsWith("exclude")).forEach(s -> {
String[] parameters = validateParameters.get(s).split(" ");
exclusions.addAll(Arrays.asList(parameters));
});
StructureSchema schema = structureSchemaLoader.getTargetSchemas().get(validateEntity);
String name = CaseUtils.toCamelCase(validateParameters.get("name"), false, ' ');
final String validateStateMachine = getValidateUpdateMethod(name,
"\"" + requiredRoleNames.trim().replaceAll(" ", "\",\"") + "\"", schema, exclusions);
result.getStateMachines().put(validateParameters.get("name"), validateStateMachine);
}
String next = "\"End\": true";
if (validateParameters.containsKey("next")) {
next = String.format("\"Next\": \"%s\"", validateParameters.get("next"));
if (!getSteps(lines).contains(validateParameters.get("next"))) {
result.getWarnings().add(String.format("Next value of %s is not valid for step %s", validateParameters.get("next"), validateParameters.get("name")));
}
} else {
if (counter + 1 < lines.length) {
next = String.format("\"Next\": \"%s\"", getStepName(lines[counter + 1]));
}
}
final String validateStep = String.format(" \"Type\": \"Task\",\n" +
" \"Resource\": \"arn:aws:states:::states:startExecution.sync:2\",\n" +
" \"ResultPath\": \"$.input.validate%s\",\n" +
" \"Parameters\": {\n" +
" \"StateMachineArn\": \"%s:stateMachine:${var.environment}_validate%s\",\n" +
" \"Input\": {\n" +
" \"NeedCallback \": true,\n" +
" \"query.$\": \"$.query\",\n" +
" \"AWS_STEP_FUNCTIONS_STARTED_BY_EXECUTION_ID.$\": \"$$.Execution.Id\"\n" +
" }\n" +
" },\n" +
" %s",
CaseUtils.toCamelCase(validateParameters.get("name"), true, ' '),
arn,
CaseUtils.toCamelCase(validateParameters.get("name"), true, ' '),
next);
final String replaceString = "***" + validateParameters.get("name") + "***";
stateMachine = stateMachine.replace(replaceString, validateStep);
} else if (lineLowerCase.startsWith("choice")) {
final Map choiceParameters = getParameters(line, "name", "choice", "default");
final String defaultStep = choiceParameters.get("default");
if (defaultStep == null) {
result.getWarnings().add(String.format("Choice %s has a missing default", choiceParameters.get("name")));
} else {
if (!getSteps(lines).contains(defaultStep.trim())) {
result.getWarnings().add(String.format("Choice default action %s for choice %s is invalid", defaultStep, choiceParameters.get("name")));
}
}
final StringBuilder choices = new StringBuilder();
choiceParameters.keySet().stream().filter(s -> s.startsWith("choice")).forEach(s -> {
String choiceParam = choiceParameters.get(s);
if (!choiceParam.isEmpty()) {
String[] parameters = choiceParam.split(" ");
String comparision = "NOT CORRECT";
String field = parameters[0];
String action = "StringEquals";
if (Arrays.asList(parameters).contains("equals")) {
action = "StringEquals";
comparision = parameters[Arrays.asList(parameters).indexOf("equals") + 1];
}
if (Arrays.asList(parameters).contains("is")) {
action = "BooleanEquals";
comparision = parameters[Arrays.asList(parameters).indexOf("is") + 1];
}
if (Arrays.asList(parameters).contains(">")) {
action = "NumericGreaterThan";
comparision = parameters[Arrays.asList(parameters).indexOf(">") + 1];
}
if (Arrays.asList(parameters).contains(">")) {
action = "NumericGreaterThan";
comparision = parameters[Arrays.asList(parameters).indexOf(">") + 1];
}
if (Arrays.asList(parameters).contains(">=")) {
action = "NumericGreaterThanEquals";
comparision = parameters[Arrays.asList(parameters).indexOf(">=") + 1];
}
if (Arrays.asList(parameters).contains("<")) {
action = "NumericLessThan";
comparision = parameters[Arrays.asList(parameters).indexOf("<") + 1];
}
if (Arrays.asList(parameters).contains(">=")) {
action = "NumericLessThanEquals";
comparision = parameters[Arrays.asList(parameters).indexOf("<=") + 1];
}
final StringBuilder next = new StringBuilder();
final StringBuilder choiceParametersSB = new StringBuilder();
for (int i = 0; i < parameters.length - 5; i++) {
choiceParametersSB.append(parameters[i]);
}
if (choiceParametersSB.toString().contains("[")) {
boolean foundOpenBracket = false;
int c = 0;
for (String parameter : parameters) {
if (parameter.contains("[")) {
foundOpenBracket = true;
}
if (foundOpenBracket) {
if (c > 2) {
next.append(parameter).append(" ");
}
c++;
}
}
String fieldName = choiceParametersSB.substring(0, choiceParametersSB.indexOf("["));
fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
String fieldValue = choiceParametersSB.substring(choiceParametersSB.indexOf("[") + 1, choiceParametersSB.length() - 1);
field = "$.input." + fieldName + ".ResponseBody[0]." + fieldValue;
if (action.startsWith("Boolean") || action.startsWith("Numeric")) {
choices.append(String.format(" {\n" +
" \"Variable\": \"%s\",\n" +
" \"%s\": %s,\n" +
" \"Next\": \"%s\"\n" +
" },\n", field, action, comparision, next.toString().trim()));
} else {
choices.append(String.format(" {\n" +
" \"Variable\": \"%s\",\n" +
" \"%s\": \"%s\",\n" +
" \"Next\": \"%s\"\n" +
" },\n", field, action, comparision, next.toString().trim()));
}
} else {
int c = 0;
for (String parameter : parameters) {
if (c > 2) {
next.append(parameter).append(" ");
}
c++;
}
if (action.startsWith("Boolean") || action.startsWith("Numeric")) {
choices.append(String.format(" {\n" +
" \"Variable\": \"$.query.%s\",\n" +
" \"%s\": %s,\n" +
" \"Next\": \"%s\"\n" +
" },\n", field, action, comparision, next.toString().trim()));
} else {
choices.append(String.format(" {\n" +
" \"Variable\": \"$.query.%s\",\n" +
" \"%s\": \"%s\",\n" +
" \"Next\": \"%s\"\n" +
" },\n", field, action, comparision, next.toString().trim()));
}
}
try {
if (!getSteps(lines).contains(next.toString().trim())) {
result.getWarnings().add(String.format("Choice next action %s for choice %s is invalid", next, choiceParameters.get("name")));
}
} catch (Exception exception) {
exception.printStackTrace();
}
}
});
final String choiceStep = String.format(" \"Type\": \"Choice\",\n" +
" \"Choices\": [\n" +
"%s\n" +
" ],\n" +
" \"Default\": \"%s\"", choices.substring(0, choices.toString().length() - 2), defaultStep);
final String replaceString = "***" + choiceParameters.get("name") + "***";
stateMachine = stateMachine.replace(replaceString, choiceStep);
} else if (lineLowerCase.startsWith("pass")) {
final Map passParameters = getParameters(line, "name");
String next = " \"End\": true";
if (passParameters.containsKey("next")) {
next = String.format(" \"Next\": \"%s\"", passParameters.get("next"));
if (!getSteps(lines).contains(passParameters.get("next"))) {
result.getWarnings().add(String.format("Next value of %s is not valid for step %s",
passParameters.get("next"), passParameters.get("name")));
}
} else {
if (counter + 1 < lines.length) {
next = String.format(" \"Next\": \"%s\"", getStepName(lines[counter + 1]));
}
}
final String passStep = String.format(" \"Type\": \"Pass\",\n%s", next);
final String replaceString = "***" + passParameters.get("name") + "***";
stateMachine = stateMachine.replace(replaceString, passStep);
} else if (lineLowerCase.startsWith("subtask")) {
final Map subtaskParameters = getParameters(line, "name", "next", "task", "entity", "property", "exclude", "embedded");
String query = "";
if (subtaskParameters.containsKey("entity")) {
StructureSchema schema = structureSchemaLoader.getTargetSchemas().get(subtaskParameters.get("entity"));
Map fieldNames = new HashMap<>(schema.getFields());
final StringBuilder querys = new StringBuilder();
subtaskParameters.keySet().stream().filter(s -> s.startsWith("exclude")).forEach(s -> {
String[] parameters = subtaskParameters.get(s).split(" ");
if (parameters.length == 1) {
final String field = parameters[0];
fieldNames.remove(field);
if (!schema.getFields().containsKey(field)) {
result.getWarnings().add(String.format("%s is not a valid exclusion for step %s", field, subtaskParameters.get("name")));
}
} else {
Arrays.stream(parameters).forEach(field -> {
fieldNames.remove(field);
result.getWarnings().add(String.format("%s is not a valid exclusion for step %s", field, subtaskParameters.get("name")));
});
}
});
subtaskParameters.keySet().stream().filter(s -> s.startsWith("property")).forEach(s -> {
String[] parameters = subtaskParameters.get(s).split(" ");
final String field = parameters[0];
// check if a straight parameter
if (parameters.length == 1) {
querys.append(" \"").append(field).append(".$\": \"$.query.").append(field).append("\",\n");
fieldNames.remove(field);
if (!schema.getFields().containsKey(field)) {
result.getWarnings().add(String.format("%s is not a valid property for step %s", field, subtaskParameters.get("name")));
}
} else {
final StringBuilder choiceParametersSB = new StringBuilder();
for (int i = 1; i < parameters.length; i++) {
choiceParametersSB.append(parameters[i]);
}
final StringBuilder val = new StringBuilder();
if (choiceParametersSB.toString().contains("[")) {
String fieldName = choiceParametersSB.substring(0, choiceParametersSB.indexOf("["));
fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1);
String fieldValue = choiceParametersSB.substring(choiceParametersSB.indexOf("[") + 1, choiceParametersSB.length() - 1);
val.append("$.input.").append(fieldName).append(".ResponseBody[0].").append(fieldValue);
} else {
int c = 0;
for (String parameter : parameters) {
if (c > 0) {
val.append(parameter).append(" ");
}
c++;
}
if (val.toString().trim().equalsIgnoreCase("empty")) {
val.setLength(0);
}
}
StructureFieldSchema f = fieldNames.get(field);
if (f == null) {
result.getWarnings().add(String.format("%s is not a valid property for step %s", field, subtaskParameters.get("name")));
} else {
String dynamic = "";
if (val.toString().startsWith("$")) {
dynamic = ".$";
}
if (f.getType().equals(FieldType.BOOLEAN)) {
if (val.toString().trim().equals("true") || val.toString().trim().equals("false")) {
querys.append(" \"").append(field).append(dynamic).append("\": ").append(val.toString().trim()).append(",\n");
} else {
result.getWarnings().add(String.format("Boolean value must be case sensitive true or false for step %s", subtaskParameters.get("name")));
}
} else {
querys.append(" \"").append(field).append(dynamic).append("\": \"").append(val.toString().trim()).append("\",\n");
}
fieldNames.remove(field);
}
if (!schema.getFields().containsKey(field)) {
result.getWarnings().add(String.format("%s is not a valid property for step %s", field, subtaskParameters.get("name")));
}
}
});
if (!subtaskParameters.get("task").toLowerCase(Locale.ROOT).startsWith("delete")) {
if (!fieldNames.isEmpty()) {
result.getWarnings().add("Subtask " + subtaskParameters.get("name") + " MISSING Field/s " + fieldNames.keySet().toString().replaceAll(",", ""));
}
}
boolean hasAssociation = false;
for (StructureFieldSchema field : schema.getFields().values()) {
if (field.isAssociation()) {
hasAssociation = true;
break;
}
}
query = querys + " \"username.$\": \"$.query.authorization.username\",";
query = query + "\n \"authorization.$\": \"$.query.authorization\"";
if (subtaskParameters.get("task").toLowerCase(Locale.ROOT).startsWith("update")) {
query = query + ",\n \"id.$\": \"$.query.id\",";
if (hasAssociation) {
if (subtaskParameters.containsKey("embedded")) {
String embedded = subtaskParameters.get("embedded");
if (!embedded.equalsIgnoreCase("exclude")) {
query = query + "\n " + embedded + ",";
}
} else {
result.getWarnings().add(String.format("Missing embedded for step %s", subtaskParameters.get("name")));
}
}
query = query + "\n \"version.$\": \"$.query.version\"";
}
if (subtaskParameters.get("task").toLowerCase(Locale.ROOT).startsWith("delete")) {
query = query + ",\n \"id.$\": \"$.query.id\"";
}
}
String next = "\"End\": true";
if (subtaskParameters.containsKey("next")) {
next = String.format("\"Next\": \"%s\"", subtaskParameters.get("next"));
if (!getSteps(lines).contains(subtaskParameters.get("next"))) {
result.getWarnings().add(String.format("Next value of %s is not valid for step %s", subtaskParameters.get("next"), subtaskParameters.get("name")));
}
} else {
if (counter + 1 < lines.length) {
next = String.format("\"Next\": \"%s\"", getStepName(lines[counter + 1]));
}
}
String destination = CaseUtils.toCamelCase(subtaskParameters.get("task"), false, ' ');
if (subtaskParameters.get("task").startsWith("update")
|| subtaskParameters.get("task").startsWith("create")
|| subtaskParameters.get("task").startsWith("delete")) {
destination = subtaskParameters.get("task");
}
final String subTaskStep = String.format(" \"Type\": \"Task\",\n" +
" \"Resource\": \"arn:aws:states:::states:startExecution.sync:2\",\n" +
" \"ResultPath\": \"$.input.%s\",\n" +
" \"Parameters\": {\n" +
" \"StateMachineArn\": \"%s:stateMachine:${var.environment}_%s\",\n" +
" \"Input\": {\n" +
" \"NeedCallback \": true,\n" +
" \"query\": {\n" +
"%s\n" +
" },\n" +
" \"AWS_STEP_FUNCTIONS_STARTED_BY_EXECUTION_ID.$\": \"$$.Execution.Id\"\n" +
" }\n" +
" },\n" +
" %s",
CaseUtils.toCamelCase(subtaskParameters.get("name"), false, ' '),
arn,
destination,
query,
next);
final String replaceString = "***" + subtaskParameters.get("name") + "***";
stateMachine = stateMachine.replace(replaceString, subTaskStep);
} else if (lineLowerCase.startsWith("query")) {
final Map queryParameters = getParameters(line, "name", "next", "type", "fail", "criteria");
String criteria = queryParameters.get("criteria");
if (criteria.trim().equalsIgnoreCase("empty")) {
criteria = "";
}
String type = queryParameters.get("type");
String fail = "";
if (queryParameters.containsKey("fail")) {
fail = String.format(" \"%s\": true,", queryParameters.get("fail"));
}
String next = "\"End\": true";
if (queryParameters.containsKey("next")) {
next = String.format("\"Next\": \"%s\"", queryParameters.get("next"));
if (!getSteps(lines).contains(queryParameters.get("next"))) {
result.getWarnings().add(String.format("Next value of %s is not valid for step %s", queryParameters.get("next"), queryParameters.get("name")));
}
} else {
if (counter + 1 < lines.length) {
next = String.format("\"Next\": \"%s\"", getStepName(lines[counter + 1]));
}
}
final String queryTaskStep = String.format(" \"Type\": \"Task\",\n" +
" \"Resource\": \"arn:aws:states:::apigateway:invoke\",\n" +
" \"ResultPath\": \"$.input.%s\",\n" +
" \"Parameters\": {\n" +
" \"ApiEndpoint\": \"%s\",\n" +
" \"Path\": \"/structure/query\",\n" +
" \"Method\": \"POST\",\n" +
" \"Headers\": {\n" +
" \"Content-Type\": [\n" +
" \"application/json\"\n" +
" ]\n" +
" },\n" +
" \"RequestBody\": {\n" +
" \"username.$\": \"$.query.authorization.username\",\n" +
" \"type\": \"%s\",\n" +
" %s\n" +
" \"criteria\": [\n" +
" %s\n" +
" ]\n" +
" },\n" +
" \"AuthType\": \"IAM_ROLE\"\n" +
" },\n" +
" %s",
CaseUtils.toCamelCase(queryParameters.get("name"), false, ' '),
apiEndPoint,
type.toLowerCase(),
fail,
criteria,
next);
final String replaceString = "***" + queryParameters.get("name") + "***";
stateMachine = stateMachine.replace(replaceString, queryTaskStep);
}
counter++;
}
result.getStateMachines().put(CaseUtils.toCamelCase("Name of the state machine", false, ' '), stateMachine);
return result;
}
private String getAllActionStepPlaceholders(String[] lines) throws Exception {
final StringBuilder result = new StringBuilder();
int counter = 0;
for (String line : lines) {
final String lineLowerCase = line.toLowerCase(Locale.ROOT);
if (!lineLowerCase.startsWith("create") && !lineLowerCase.startsWith("#") && !lineLowerCase.startsWith("\t") && !lineLowerCase.replaceAll("\t", "").startsWith("-next")) {
final String stepName = getStepName(line);
result.append("\t\"").append(stepName).append("\": {\n");
result.append("\t***").append(stepName).append("***\n");
if (counter == lines.length - 1) {
result.append("\t}");
} else {
result.append("\t},\n");
}
}
counter++;
}
return result.toString();
}
private List getSteps(String[] lines) throws Exception {
List result = new ArrayList<>();
for (String line : lines) {
final String lineLowerCase = line.toLowerCase(Locale.ROOT);
if (!lineLowerCase.startsWith("create") && !lineLowerCase.startsWith("#")) {
final String stepName = getStepName(line);
result.add(stepName);
}
}
return result;
}
private String getStepName(String line) throws Exception {
String[] parameters = line.split("-");
for (String parameter : parameters) {
final String parameterLowerCase = parameter.toLowerCase(Locale.ROOT);
if (parameterLowerCase.startsWith("name ")) {
return parameter.substring("name ".length()).trim();
}
}
throw new Exception(String.format("Can't find step name for line %s", line));
}
private Map getParameters(String line, String... parametersList) {
final Map result = new HashMap<>();
String[] parameters = line.split("-");
for (String parameter : parameters) {
for (String searchParameter : parametersList) {
final String parameterLowerCase = parameter.toLowerCase(Locale.ROOT);
if (parameterLowerCase.startsWith(searchParameter)) {
if (searchParameter.equals("choice")
|| searchParameter.equals("property")
|| searchParameter.equals("exclude")) {
result.put(searchParameter + UUID.randomUUID(), parameter.substring(searchParameter.length() + 1).trim());
} else {
result.put(searchParameter, parameter.substring(searchParameter.length() + 1).trim());
}
}
}
}
return result;
}
private String getProperties(final List exclusions,
StructureSchema schema) {
final StringBuilder result = new StringBuilder();
schema.getFields().forEach((key, value) -> {
if (exclusions.contains(key)) {
return;
}
if (value.getType().equals(FieldType.STRING)) {
result.append("\t\t\t\t\"").append(key).append("\": {\n");
result.append("\t\t\t\t\t\"description\": \"").append(key).append("\",\n");
result.append("\t\t\t\t\t\"type\": \"").append(value.getType()).append("\"");
if (StringUtils.isNotBlank(value.getMin())) {
result.append(",\"minLength\":").append(value.getMin());
}
if (StringUtils.isNotBlank(value.getMax())) {
result.append(",\"maxLength\":").append(value.getMax());
}
result.append("\n\t\t\t\t},\n");
} else if (value.getType().equals(FieldType.INTEGER)) {
result.append("\t\t\t\t\"").append(key).append("\": {\n");
result.append("\t\t\t\t\t\"description\": \"").append(key).append("\",\n");
result.append("\t\t\t\t\t\"type\": \"").append(value.getType()).append("\"");
result.append("\n\t\t\t\t},\n");
} else if (value.getType().equals(FieldType.BOOLEAN)) {
result.append("\t\t\t\t\"").append(key).append("\": {\n");
result.append("\t\t\t\t\t\"description\": \"").append(key).append("\",\n");
result.append("\t\t\t\t\t\"type\": \"").append(value.getType()).append("\"");
result.append("\n\t\t\t\t},\n");
} else if (value.getType().equals(FieldType.ARRAY)) {
result.append("\t\t\t\t\"").append(key).append("\": {\n");
result.append("\t\t\t\t\t\"description\": \"").append(key).append("\",\n");
result.append("\t\t\t\t\t\"type\": \"").append(value.getType()).append("\"");
result.append("\n\t\t\t\t},\n");
} else if (value.getType().equals(FieldType.DATETIME)) {
result.append("\t\t\t\t\"").append(key).append("\": {\n");
result.append("\t\t\t\t\t\"description\": \"").append(key).append("\",\n");
result.append("\t\t\t\t\t\"type\": \"string\",\"format\": \"date-time\"");
result.append("\n\t\t\t\t},\n");
}
});
return result.substring(0, result.lastIndexOf(",") - 1);
}
private String getRequiredProperties(final List exclusions,
StructureSchema schema,
final boolean update) {
final StringBuilder result = new StringBuilder();
schema.getFields().forEach((key, value) -> {
if (value.isRequired() && !exclusions.contains(key)) {
result.append("\"").append(key).append("\",");
}
});
if (update) {
result.append("\"id\",");
}
return result.substring(0, result.length() - 1);
}
private String getValidateUpdateMethod(final String name, final String roles,
StructureSchema schema,
final List exclusions) {
final String properties = getProperties(exclusions, schema);
final String required = getRequiredProperties(exclusions, schema, true);
final String input = "{\n" +
" \"Comment\": \"A description of my state machine\",\n" +
" \"StartAt\": \"Check User Role\",\n" +
" \"States\": {\n" +
" \"Check User Role\": {\n" +
" \"Type\": \"Task\", \"ResultPath\": \"$.input.checkAuthorization\",\n" +
" \"Resource\": \"arn:aws:states:::apigateway:invoke\",\n" +
" \"Parameters\": {\n" +
" \"ApiEndpoint\": \"${var.structureEndpoint}\",\n" +
" \"Path\": \"/structure/hasAnyRole\",\n" +
" \"Method\": \"POST\",\n" +
" \"Headers\": {\n" +
" \"Content-Type\": [\n" +
" \"application/json\"\n" +
" ]\n" +
" },\n" +
" \"RequestBody\": {\n" +
" \"requiredRoles\": [\n" +
" %s\n" +
" ],\n" +
" \"userRoles.$\": \"$.query.authorization.roles\"\n" +
" },\n" +
" \"AuthType\": \"IAM_ROLE\"\n" +
" },\n" +
" \"Next\": \"Validate JSON\"\n" +
" },\n" +
" \"Validate JSON\": {\n" +
" \"Type\": \"Task\", \"ResultPath\": \"$.input.validateJson\",\n" +
" \"Resource\": \"arn:aws:states:::apigateway:invoke\",\n" +
" \"Parameters\": {\n" +
" \"ApiEndpoint\": \"${var.structureEndpoint}\",\n" +
" \"Method\": \"POST\",\n" +
" \"Path\": \"/structure/validate\",\n" +
" \"Headers\": {\n" +
" \"Content-Type\": [\n" +
" \"application/json\"\n" +
" ]\n" +
" },\n" +
" \"RequestBody\": {\n" +
" \"workflow\": \"%s\",\n" +
" \"query.$\": \"$.query\",\n" +
" \"schema\": {\n" +
" \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n" +
" \"title\": \"%s\",\n" +
" \"description\": \"%s\",\n" +
" \"type\": \"object\",\n" +
" \"properties\": {\n" +
"%s\n" +
" }\n" +
" },\n" +
" \"required\": [%s]\n" +
" }\n" +
" },\n" +
" \"AuthType\": \"IAM_ROLE\"\n" +
" },\n" +
" \"End\": true\n" +
" }\n" +
" }\n" +
"}";
return String.format(input, roles, name, name, name, properties, required);
}
}