All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fivefaces.structureclient.service.statemachine.impl.ScaffoldStateMachineServiceImpl Maven / Gradle / Ivy

There is a newer version: 1.0.62
Show newest version
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);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy