diff --git a/README.md b/README.md index 62e0765..700a9d4 100644 --- a/README.md +++ b/README.md @@ -449,43 +449,36 @@ automator.servers: Rebuilt the image via ```` mvn clean install -mvn springboot:build-image ```` +# Push the docker image The docker image is build using the Dockerfile present on the root level. Push the image to -``` -ghcr.io/camunda-community-hub/process-execution-automator: -``` - -## Detail - -Run command -```` -mvn clean install -```` -Now, create a docker image ```` -docker build -t pierre-yves-monnet/processautomator:1.7.1 . +docker build -t pierre-yves-monnet/process-execution-automator:1.8.0 . +docker build pycamunda/camunda-community-hub/process-execution-automator:1.8.0 ```` Push the image to the Camunda hub (you must be login first to the docker registry) ```` -docker tag pierre-yves-monnet/processautomator:1.7.1 ghcr.io/camunda-community-hub/process-execution-automator:1.7.1 -docker push ghcr.io/camunda-community-hub/process-execution-automator:1.7.1 - +docker tag pierre-yves-monnet/process-execution-automator:1.8.0 ghcr.io/camunda-community-hub/process-execution-automator:1.8.0 +docker push ghcr.io/camunda-community-hub/process-execution-automator:1.8.0 ```` +docker tag pierre-yves-monnet/process-execution-automator:1.8.0 pycamunda/camunda-hub:process-execution-automator-1.8.0 +docker push pycamunda/camunda-hub:process-execution-automator-1.8.0 + Tag as the latest: ```` -docker tag pierre-yves-monnet/processautomator:1.7.1 ghcr.io/camunda-community-hub/process-execution-automator:latest +docker tag pierre-yves-monnet/process-execution-automator:1.8.0 ghcr.io/camunda-community-hub/process-execution-automator:latest docker push ghcr.io/camunda-community-hub/process-execution-automator:latest ```` Check on -https://github.com/camunda-community-hub/process-execution-automator/pkgs/container/process-execution-automator +https://github.com/camunda-community-hub/zeebe-cherry-runtime/pkgs/container/process-execution-automator + diff --git a/doc/unittestscenario/README.md b/doc/unittestscenario/README.md index 4f486ef..44a02c5 100644 --- a/doc/unittestscenario/README.md +++ b/doc/unittestscenario/README.md @@ -16,26 +16,18 @@ There is multiple use case: ### Verification (path and performance) -![Process](../explanationProcess.png) +![ScoreAcceptance.png](resources/ScoreAcceptance.png) in a CD/CI, you want to verify that a process follows the same behavior in the same performance time. Running every day (or hours) or asking via an API call to replay a scenario is useful to -verify there is no difference. If the customer is 4555, do we still move the process instance to -Review Level 1"? The second verification is the performance. The scenario can record an expected -duration target (for example, 4 seconds to execute the Get Context service task. Does the execution -still at this time? +verify there is no difference. If the score is 200, do we still move the process instance to +Send Acceptation? -### Coverage report - -Execute multiple scenarios to be sure that all the process is covered correctly. An "Execution -round" is a set of scenarios executed at the same time. At the end of the execution, a coverage test -can be performed. A CD/CI verification may be to check the scenario execution, the target time, and -the coverage. ### Advance process instances to a step for development -During the development, you verify the task "Notify applicant". To test it in the situation, you -must have a process instance in the process and pass four user tasks. Each test takes time: when you +During the development, you debug the task "Send rejection". To test it in the situation, you +must have a process instance in the process and pass all user tasks. Each test takes time: when you deploy a new process or want a new process instance, you need to execute again the different user task. Using Automator with the correct scenario solves the issue. Deploy a new process, but instead of starting from the beginning of a new process instance, start it via Automator. The scenario will @@ -50,27 +42,55 @@ In the unit scenario, you should place some Event (for example, the end event): This verification implies to give an Operate access. -The scenario will contains: - -The name, the process ID +The scenario contain: -A list of flow to execute under the attribut `executions` +* The name, the process ID, +* A list of flow to execute under attribut `executions` +* A list of verification under attribut `verifications` -* a STARTEVENT, to start one process instance -* the list of all SERVICETASK ## Scenario definition -## Generate from a real execution -Automator can generate a scenario from a real execution. The user creates a process instance and -executes it. It executes user tasks until the end of the process instance or at a certain point. Via -the UI (or the API), the user gives the process instance. Automator queries Camunda Engine to -collect the history of the process and, for each user task, which variable was provided. A new -scenario is created from this example. +Check the scenario: -Note: this function is yet available +[ScoreAcceptanceScn.json](resources/ScoreAcceptanceScn.json) ## execute -In progress +1. First, upload the scenario file in a config map + +``` +kubectl create configmap scoreacceptancescn --from-file=doc/unittestscenario/resources/scoreacceptancescn.json -n camunda +``` + +2. Deploy the scenario on the cluster, via the Modeler + +3. Create the pod process-execution-automator + +``` +kubectl create -f doc/unittestscenario/resources/UnittestAutomator.yaml -n camunda +``` +This configuration will upload the scenario + + +4. Port forward + +``` +kubectl port-forward svc/process-execution-automator 8381:8381 -n camunda +``` + +6. Check the scenario is uploaded + +``` +curl -X GET "http://localhost:8381/api/content/list" -H "Content-Type: application/json" +``` + + +7. upload the scenario +``` +curl -X POST -F "file=@/path/to/your/file.txt" http://localhost:8080/api/files/upload + +curl -X GET "http://localhost:8381/api/unittest/get?id=1732767184446" -H "Content-Type: application/json" +``` + diff --git a/doc/unittestscenario/SendUnitTestCommand.rest b/doc/unittestscenario/SendUnitTestCommand.rest index a41ae51..92c0bcb 100644 --- a/doc/unittestscenario/SendUnitTestCommand.rest +++ b/doc/unittestscenario/SendUnitTestCommand.rest @@ -1,5 +1,5 @@ ### POST Request -POST http://localhost:8381/api/unittest/run?name=ScoreAcceptanceScn&server=Camunda8Ruby&wait=false +POST http://localhost:8381/api/unittest/run?name=ScoreAcceptanceScn&server=Camunda8Ruby&wait=true Content-Type: application/json { @@ -19,3 +19,22 @@ Content-Type: application/json { } + +### Content Manager +GET http://localhost:8381/api/content/list +Content-Type: application/json + +{ +} + +### Upload file +POST http://localhost:8381/api/content/add +Content-Type: multipart/form-data + +--boundary +Content-Disposition: form-data; name="File"; filename="file1.txt" +Content-Type: text/plain + +< ./resources/ScoreAcceptanceScn.json + +--boundary-- diff --git a/src/main/java/org/camunda/automator/AutomatorAPI.java b/src/main/java/org/camunda/automator/AutomatorAPI.java index 7683984..baba334 100644 --- a/src/main/java/org/camunda/automator/AutomatorAPI.java +++ b/src/main/java/org/camunda/automator/AutomatorAPI.java @@ -20,8 +20,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import java.io.File; import java.io.InputStream; +import java.nio.file.Path; @Component public class AutomatorAPI { @@ -48,7 +48,7 @@ public Scenario createScenario() { * @return the scenario * @throws AutomatorException if scenario can't be read */ - public Scenario loadFromFile(File scenarioFile) throws AutomatorException { + public Scenario loadFromFile(Path scenarioFile) throws AutomatorException { return Scenario.createFromFile(scenarioFile); } diff --git a/src/main/java/org/camunda/automator/AutomatorCLI.java b/src/main/java/org/camunda/automator/AutomatorCLI.java index 257785a..93665bc 100644 --- a/src/main/java/org/camunda/automator/AutomatorCLI.java +++ b/src/main/java/org/camunda/automator/AutomatorCLI.java @@ -17,8 +17,13 @@ import org.springframework.stereotype.Component; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; @SpringBootApplication @@ -98,14 +103,20 @@ private static BpmnEngineList decodeConfiguration(String propertiesFileName) thr throw new Exception("Not yet implemented"); } - private static List detectRecursiveScenario(File folderRecursive) { - List listFiles = new ArrayList<>(); - for (File file : folderRecursive.listFiles()) { - if (file.isDirectory()) { - listFiles.addAll(detectRecursiveScenario(file)); - } else if (file.getName().endsWith(".json")) { - listFiles.add(file); - } + private static List detectRecursiveScenario(Path folderRecursive) { + List listFiles = new ArrayList<>(); + try (Stream files = Files.list(folderRecursive)) { + // Iterate over all files in the directory + files.forEach(file -> { + if (Files.isRegularFile(file)) { + listFiles.add(file); + } + if (Files.isDirectory(file)) { + listFiles.addAll(detectRecursiveScenario(file)); + } + }); + } catch (IOException e) { + logger.error("During detection scenario file: {}", e.getMessage()); } return listFiles; } @@ -122,8 +133,8 @@ private static void logOutLn(String message) { public void run(String[] args) { if (!isRunningCLI) return; - File scenarioFile = null; - File folderRecursive = null; + Path scenarioFile = null; + Path folderRecursive = null; RunParameters runParameters = new RunParameters(); runParameters.setExecution(true) @@ -184,13 +195,13 @@ public void run(String[] args) { if (args.length < i + 1) throw new AutomatorException("Bad usage : run "); action = ACTION.RUN; - scenarioFile = new File(args[i + 1]); + scenarioFile = Paths.get(args[i + 1]); i++; } else if ("recursive".equals(args[i])) { if (args.length < i + 1) throw new AutomatorException("Bad usage : recursive "); action = ACTION.RECURSIVE; - folderRecursive = new File(args[i + 1]); + folderRecursive = Paths.get(args[i + 1]); i++; } else { printUsage(); @@ -230,8 +241,8 @@ public void run(String[] args) { logger.info(scenarioExecutionResult.getSynthesis(runParameters.isFullDetailsSynthesis())); } case RECURSIVE -> { - List listScenario = detectRecursiveScenario(folderRecursive); - for (File scenarioFileIndex : listScenario) { + List listScenario = detectRecursiveScenario(folderRecursive); + for (Path scenarioFileIndex : listScenario) { Scenario scenario = automatorAPI.loadFromFile(scenarioFileIndex); BpmnEngine bpmnEngineScenario = automatorAPI.getBpmnEngine(serverDefinition, true); RunResult scenarioExecutionResult = automatorAPI.executeScenario( diff --git a/src/main/java/org/camunda/automator/AutomatorRest.java b/src/main/java/org/camunda/automator/AutomatorRest.java index a1b62a2..ce9c260 100644 --- a/src/main/java/org/camunda/automator/AutomatorRest.java +++ b/src/main/java/org/camunda/automator/AutomatorRest.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -116,11 +116,11 @@ private void startTest(String scenarioName, String serverName, String unitTestId // now proceed the scenario try { Scenario scenario = null; - File scenarioFile = contentManager.getFromName(scenarioName); + Path scenarioFile = contentManager.getFromName(scenarioName); try { scenario = automatorAPI.loadFromFile(scenarioFile); } catch (Exception e) { - logger.error("Error during accessing InputStream from File [{}]: {}", scenarioFile.getAbsolutePath(), + logger.error("Error during accessing InputStream from File [{}]: {}", scenarioFile.toAbsolutePath().toString(), e.getMessage()); } if (scenario == null) { @@ -140,13 +140,13 @@ private void startTest(String scenarioName, String serverName, String unitTestId return; } - bpmnEngine.turnHighFlowMode(true); + bpmnEngine.turnHighFlowMode(false); logger.info("Scenario [{}] file[{}] use BpmnEngine {}", scenario.getName(), scenario.getName(), bpmnEngine.getSignature()); RunResult scenarioExecutionResult = automatorAPI.executeScenario(bpmnEngine, runParameters, scenario); logger.info("AutomatorRest: end scenario [{}] in {} ms", scenario.getName(), scenarioExecutionResult.getTimeExecution()); - bpmnEngine.turnHighFlowMode(false); + resultMap.put(JSON_STATUS, "EXECUTED"); resultMap.putAll(resultToJson(scenarioExecutionResult)); diff --git a/src/main/java/org/camunda/automator/bpmnengine/BpmnEngine.java b/src/main/java/org/camunda/automator/bpmnengine/BpmnEngine.java index 1ccc67c..f2be874 100644 --- a/src/main/java/org/camunda/automator/bpmnengine/BpmnEngine.java +++ b/src/main/java/org/camunda/automator/bpmnengine/BpmnEngine.java @@ -1,6 +1,7 @@ package org.camunda.automator.bpmnengine; -import io.camunda.operate.search.DateFilter; +// import io.camunda.operate.search.DateFilter; + import io.camunda.zeebe.client.api.worker.JobWorker; import org.camunda.automator.configuration.BpmnEngineList; import org.camunda.automator.definition.ScenarioDeployment; @@ -186,10 +187,10 @@ List searchProcessInstanceByVariable(String processId, /* CountInformation */ /* */ /* ******************************************************************** */ - long countNumberOfProcessInstancesCreated(String processId, DateFilter startDate, DateFilter endDate) + long countNumberOfProcessInstancesCreated(String processId, Date startDate, Date endDate) throws AutomatorException; - long countNumberOfProcessInstancesEnded(String processId, DateFilter startDate, DateFilter endDate) + long countNumberOfProcessInstancesEnded(String processId, Date startDate, Date endDate) throws AutomatorException; long countNumberOfTasks(String processId, String taskId) throws AutomatorException; diff --git a/src/main/java/org/camunda/automator/bpmnengine/BpmnEngineConfigurationInstance.java b/src/main/java/org/camunda/automator/bpmnengine/BpmnEngineConfigurationInstance.java index 89af914..6cfb3c7 100644 --- a/src/main/java/org/camunda/automator/bpmnengine/BpmnEngineConfigurationInstance.java +++ b/src/main/java/org/camunda/automator/bpmnengine/BpmnEngineConfigurationInstance.java @@ -31,12 +31,14 @@ public static BpmnEngineList getCamunda7(String serverUrl) { return bpmEngineConfiguration; } - public static BpmnEngineList getCamunda8(String zeebeGatewayAddress) { + public static BpmnEngineList getCamunda8(String zeebeGatewayAddress, String zeebeGrpcAddress, String zeebeRestAddress) { BpmnEngineList bpmEngineConfiguration = new BpmnEngineList(); BpmnEngineList.BpmnServerDefinition serverDefinition = new BpmnEngineList.BpmnServerDefinition(); serverDefinition.serverType = BpmnEngineList.CamundaEngine.CAMUNDA_8; serverDefinition.zeebeGatewayAddress = zeebeGatewayAddress; + serverDefinition.zeebeGrpcAddress = zeebeGrpcAddress; + serverDefinition.zeebeRestAddress = zeebeRestAddress; bpmEngineConfiguration.addExplicitServer(serverDefinition); diff --git a/src/main/java/org/camunda/automator/bpmnengine/camunda7/BpmnEngineCamunda7.java b/src/main/java/org/camunda/automator/bpmnengine/camunda7/BpmnEngineCamunda7.java index 33e20d7..7f4cb16 100644 --- a/src/main/java/org/camunda/automator/bpmnengine/camunda7/BpmnEngineCamunda7.java +++ b/src/main/java/org/camunda/automator/bpmnengine/camunda7/BpmnEngineCamunda7.java @@ -1,6 +1,6 @@ package org.camunda.automator.bpmnengine.camunda7; -import io.camunda.operate.search.DateFilter; + import org.camunda.automator.bpmnengine.BpmnEngine; import org.camunda.automator.configuration.BpmnEngineList; import org.camunda.automator.definition.ScenarioDeployment; @@ -422,7 +422,7 @@ public Map getVariables(String processInstanceId) throws Automat /* ******************************************************************** */ @Override - public long countNumberOfProcessInstancesCreated(String processName, DateFilter startDate, DateFilter endDate) + public long countNumberOfProcessInstancesCreated(String processName, Date startDate, Date endDate) throws AutomatorException { try { @@ -445,7 +445,7 @@ public long countNumberOfProcessInstancesCreated(String processName, DateFilter Date datePI = stringToDate(t.getBusinessKey()); if (datePI == null) return false; - return datePI.after(startDate.getDate()); + return datePI.after(startDate); }).count(); } while (processInstanceDtos.size() >= SEARCH_MAX_SIZE && maxLoop < 1000); @@ -458,7 +458,7 @@ public long countNumberOfProcessInstancesCreated(String processName, DateFilter } @Override - public long countNumberOfProcessInstancesEnded(String processName, DateFilter startDate, DateFilter endDate) + public long countNumberOfProcessInstancesEnded(String processName, Date startDate, Date endDate) throws AutomatorException { throw new AutomatorException("Not yet implemented"); } diff --git a/src/main/java/org/camunda/automator/bpmnengine/camunda8/BpmnEngineCamunda8.java b/src/main/java/org/camunda/automator/bpmnengine/camunda8/BpmnEngineCamunda8.java index c79dc65..4084d77 100644 --- a/src/main/java/org/camunda/automator/bpmnengine/camunda8/BpmnEngineCamunda8.java +++ b/src/main/java/org/camunda/automator/bpmnengine/camunda8/BpmnEngineCamunda8.java @@ -10,7 +10,6 @@ import io.camunda.operate.CamundaOperateClientBuilder; import io.camunda.operate.exception.OperateException; import io.camunda.operate.model.*; -import io.camunda.operate.search.DateFilter; import io.camunda.operate.search.*; import io.camunda.tasklist.CamundaTaskListClient; import io.camunda.tasklist.CamundaTaskListClientBuilder; @@ -51,7 +50,7 @@ public class BpmnEngineCamunda8 implements BpmnEngine { private final BenchmarkStartPiExceptionHandlingStrategy exceptionHandlingStrategy; boolean hightFlowMode = false; /** - * It is not possible to search user task for a specfic processInstance. So, to realize this, a marker is created in each process instance. Retrieving the user task, + * It is not possible to search user task for a specific processInstance. So, to realize this, a marker is created in each process instance. Retrieving the user task, * the process instance can be found and correction can be done */ Map cacheProcessInstanceMarker = new HashMap<>(); @@ -93,6 +92,8 @@ public static BpmnEngineCamunda8 getFromServerDefinition(BpmnEngineList.BpmnServ * @param tasklistUrl Url to access TaskList */ public static BpmnEngineCamunda8 getFromCamunda8(String zeebeSelfGatewayAddress, + String zeebeGrpcAddress, + String zeebeRestAddress, Boolean zeebePlainText, String operateUrl, String operateUserName, @@ -104,6 +105,8 @@ public static BpmnEngineCamunda8 getFromCamunda8(String zeebeSelfGatewayAddress, bpmnEngineCamunda8.serverDefinition.serverType = BpmnEngineList.CamundaEngine.CAMUNDA_8; bpmnEngineCamunda8.serverDefinition = new BpmnEngineList.BpmnServerDefinition(); bpmnEngineCamunda8.serverDefinition.zeebeGatewayAddress = zeebeSelfGatewayAddress; + bpmnEngineCamunda8.serverDefinition.zeebeGrpcAddress = zeebeGrpcAddress; + bpmnEngineCamunda8.serverDefinition.zeebeRestAddress = zeebeRestAddress; bpmnEngineCamunda8.serverDefinition.zeebePlainText = zeebePlainText; @@ -303,24 +306,31 @@ public List searchUserTasksByProcessInstance(String processInstanceId, S taskSearch.setPagination(new Pagination().setPageSize(maxResult)); TaskList tasksList = taskClient.getTasks(taskSearch); + boolean getAllTasks = tasksList.size() < maxResult; List listTasksResult = new ArrayList<>(); do { - listTasksResult.addAll(tasksList.getItems().stream().filter(t -> { - List listVariables = t.getVariables(); - Optional markerTask = listVariables.stream() - .filter(v -> v.getName().equals(THIS_IS_A_COMPLETE_IMPOSSIBLE_VARIABLE_NAME)) - .findFirst(); - - if (markerTask.isEmpty()) - return false; - Long processInstanceIdTask = cacheProcessInstanceMarker.get(markerTask.get().getValue()); - return (processInstanceIdLong.equals(processInstanceIdTask)); - }).map(Task::getId) // Task to ID - .toList()); + if (!hightFlowMode) { + // We check that the task is the one expected + listTasksResult.addAll(tasksList.getItems().stream().filter(t -> { + List listVariables = t.getVariables(); + Optional markerTask = listVariables.stream() + .filter(v -> v.getName().equals(THIS_IS_A_COMPLETE_IMPOSSIBLE_VARIABLE_NAME)) + .findFirst(); + if (markerTask.isEmpty()) + return false; + Long processInstanceIdTask = cacheProcessInstanceMarker.get(markerTask.get().getValue()); + return (processInstanceIdLong.equals(processInstanceIdTask)); + }).map(Task::getId) // Task to ID + .toList()); + } else { + listTasksResult.addAll(tasksList.getItems().stream() + .map(Task::getId) // Task to ID + .toList()); + } - if (tasksList.size() > 0) + if (tasksList.size() > 0 && !getAllTasks) tasksList = taskClient.after(tasksList); - } while (tasksList.size() > 0); + } while (tasksList.size() > 0 && !getAllTasks); return listTasksResult; @@ -409,6 +419,8 @@ public void executeUserTask(String userTaskId, String userId, Map getVariables(String processInstanceId) throws Automat /* CountInformation */ /* */ /* ******************************************************************** */ - public long countNumberOfProcessInstancesCreated(String processId, DateFilter startDate, DateFilter endDate) + public long countNumberOfProcessInstancesCreated(String processId, Date startDate, Date endDate) throws AutomatorException { if (operateClient == null) { throw new AutomatorException("No Operate connection was provided"); @@ -644,7 +656,7 @@ public long countNumberOfProcessInstancesCreated(String processId, DateFilter st searchQuery.setSize(SEARCH_MAX_SIZE); searchResult = operateClient.searchProcessInstanceResults(searchQuery); - cumul += searchResult.getItems().stream().filter(t -> t.getStartDate().after(startDate.getDate())).count(); + cumul += searchResult.getItems().stream().filter(t -> t.getStartDate().after(startDate)).count(); } while (searchResult.getItems().size() >= SEARCH_MAX_SIZE && maxLoop < 1000); return cumul; @@ -653,7 +665,7 @@ public long countNumberOfProcessInstancesCreated(String processId, DateFilter st } } - public long countNumberOfProcessInstancesEnded(String processId, DateFilter startDate, DateFilter endDate) + public long countNumberOfProcessInstancesEnded(String processId, Date startDate, Date endDate) throws AutomatorException { if (operateClient == null) { throw new AutomatorException("No Operate connection was provided"); @@ -679,7 +691,7 @@ public long countNumberOfProcessInstancesEnded(String processId, DateFilter star SearchQuery searchQuery = queryBuilder.build(); searchQuery.setSize(SEARCH_MAX_SIZE); searchResult = operateClient.searchProcessInstanceResults(searchQuery); - cumul += searchResult.getItems().stream().filter(t -> t.getStartDate().after(startDate.getDate())).count(); + cumul += searchResult.getItems().stream().filter(t -> t.getStartDate().after(startDate)).count(); } while (searchResult.getItems().size() >= SEARCH_MAX_SIZE && maxLoop < 1000); return cumul; @@ -855,6 +867,8 @@ else if (BpmnEngineList.CamundaEngine.CAMUNDA_8.equals(this.typeCamundaEngine)) .build(); clientBuilder = ZeebeClient.newClientBuilder() .gatewayAddress(serverDefinition.zeebeGatewayAddress) + .grpcAddress(new URI(serverDefinition.zeebeGrpcAddress)) + .restAddress(new URI(serverDefinition.zeebeRestAddress)) .defaultTenantId(serverDefinition.zeebeTenantId == null ? "" : serverDefinition.zeebeTenantId) .credentialsProvider(credentialsProvider); if (Boolean.TRUE.equals(serverDefinition.zeebePlainText)) @@ -867,11 +881,24 @@ else if (BpmnEngineList.CamundaEngine.CAMUNDA_8.equals(this.typeCamundaEngine)) "BadCredential[" + serverDefinition.name + "] Analysis:" + analysis + " : " + e.getMessage()); } } else { - analysis.append("NoAuthentication;"); - // connect to local deployment; assumes that authentication is disabled - clientBuilder = ZeebeClient.newClientBuilder() - .gatewayAddress(serverDefinition.zeebeGatewayAddress) - .usePlaintext(); + try { + analysis.append("NoAuthentication;"); + // connect to local deployment; assumes that authentication is disabled + clientBuilder = ZeebeClient.newClientBuilder() + .gatewayAddress(serverDefinition.zeebeGatewayAddress); + if (serverDefinition.zeebeGrpcAddress != null) { + clientBuilder = clientBuilder.grpcAddress(new URI(serverDefinition.zeebeGrpcAddress)); + } + if (serverDefinition.zeebeRestAddress != null) { + clientBuilder = clientBuilder.restAddress(new URI(serverDefinition.zeebeRestAddress)); + } + clientBuilder = clientBuilder.usePlaintext(); + } catch (Exception e) { + zeebeClient = null; + logger.error("Can't connect to Server[{}] Analysis:{} : {}", serverDefinition.name, analysis, e); + throw new AutomatorException( + "badURL[" + serverDefinition.name + "] Analysis:" + analysis + " : " + e.getMessage()); + } } } else throw new AutomatorException("Invalid configuration"); @@ -1136,8 +1163,10 @@ private void connectTaskList(StringBuilder analysis) throws AutomatorException { // ---------------- connection try { - + taskListBuilder.zeebeClient(zeebeClient); + taskListBuilder.useZeebeUserTasks(); taskClient = taskListBuilder.build(); + analysis.append("successfully, "); } catch (Exception e) { diff --git a/src/main/java/org/camunda/automator/bpmnengine/dummy/BpmnEngineDummy.java b/src/main/java/org/camunda/automator/bpmnengine/dummy/BpmnEngineDummy.java index 8be203f..5ed76ea 100644 --- a/src/main/java/org/camunda/automator/bpmnengine/dummy/BpmnEngineDummy.java +++ b/src/main/java/org/camunda/automator/bpmnengine/dummy/BpmnEngineDummy.java @@ -1,6 +1,5 @@ package org.camunda.automator.bpmnengine.dummy; -import io.camunda.operate.search.DateFilter; import org.camunda.automator.bpmnengine.BpmnEngine; import org.camunda.automator.configuration.BpmnEngineList; import org.camunda.automator.definition.ScenarioDeployment; @@ -12,6 +11,7 @@ import java.io.File; import java.time.Duration; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Map; @@ -123,13 +123,13 @@ public Map getVariables(String processInstanceId) throws Automat /* ******************************************************************** */ @Override - public long countNumberOfProcessInstancesCreated(String processName, DateFilter startDate, DateFilter endDate) + public long countNumberOfProcessInstancesCreated(String processName, Date startDate, Date endDate) throws AutomatorException { throw new AutomatorException("Not yet implemented"); } @Override - public long countNumberOfProcessInstancesEnded(String processName, DateFilter startDate, DateFilter endDate) + public long countNumberOfProcessInstancesEnded(String processName, Date startDate, Date endDate) throws AutomatorException { throw new AutomatorException("Not yet implemented"); } diff --git a/src/main/java/org/camunda/automator/configuration/BpmnEngineList.java b/src/main/java/org/camunda/automator/configuration/BpmnEngineList.java index 5222766..efa90bf 100644 --- a/src/main/java/org/camunda/automator/configuration/BpmnEngineList.java +++ b/src/main/java/org/camunda/automator/configuration/BpmnEngineList.java @@ -40,6 +40,8 @@ public class BpmnEngineList { public static final String CONF_OPERATE_AUDIENCE = "operateAudientce"; public static final String CONF_ZEEBE_GATEWAY_ADDRESS = "zeebeGatewayAddress"; + public static final String CONF_ZEEBE_GRPC_ADDRESS = "zeebeGrpcAddress"; + public static final String CONF_ZEEBE_REST_ADDRESS = "zeebeRestAddress"; public static final String CONF_URL = "url"; public static final String CONF_TYPE = "type"; public static final String CONF_TYPE_V_CAMUNDA_8 = "camunda8"; @@ -204,6 +206,12 @@ private List getFromServersList() throws AutomatorExceptio bpmnServerDefinition.serverType = CamundaEngine.CAMUNDA_8; bpmnServerDefinition.zeebeGatewayAddress = getString(CONF_ZEEBE_GATEWAY_ADDRESS, serverMap, null, contextLog, true); + bpmnServerDefinition.zeebeGrpcAddress = getString(CONF_ZEEBE_GRPC_ADDRESS, serverMap, null, contextLog, + false); + + bpmnServerDefinition.zeebeRestAddress = getString(CONF_ZEEBE_REST_ADDRESS, serverMap, null, contextLog, + false); + bpmnServerDefinition.zeebeClientId = getString(CONF_ZEEBE_CLIENT_ID, serverMap, null, contextLog, false); bpmnServerDefinition.zeebeClientSecret = getString(CONF_ZEEBE_SECRET, serverMap, null, contextLog, false); bpmnServerDefinition.zeebeAudience = getString(CONF_ZEEBE_AUDIENCE, serverMap, ZEEBE_DEFAULT_AUDIENCE, @@ -358,6 +366,9 @@ private List getFromServerConfiguration() { camunda8.serverType = CamundaEngine.CAMUNDA_8; camunda8.name = configurationServersEngine.zeebeName; camunda8.zeebeGatewayAddress = configurationServersEngine.zeebeGatewayAddress; + camunda8.zeebeGrpcAddress = configurationServersEngine.zeebeGrpcAddress; + camunda8.zeebeRestAddress = configurationServersEngine.zeebeRestAddress; + camunda8.workerExecutionThreads = parseInt("Camunda8." + CONF_WORKER_EXECUTION_THREADS, configurationServersEngine.zeebeWorkerExecutionThreads, DEFAULT_VALUE_EXECUTION_THREADS, ""); camunda8.workerMaxJobsActive = parseInt("Camunda8." + CONF_WORKER_MAX_JOBS_ACTIVE, @@ -511,6 +522,8 @@ public static class BpmnServerDefinition { * My Zeebe Address */ public String zeebeGatewayAddress; + public String zeebeGrpcAddress; + public String zeebeRestAddress; public Boolean zeebePlainText; /** @@ -580,7 +593,7 @@ public String getSynthesis() { synthesis += " url[" + camunda7ServerUrl + "] userName[" + camunda7UserName + "]"; } if (serverType.equals(CamundaEngine.CAMUNDA_8)) { - synthesis += " address[" + zeebeGatewayAddress + "] workerThread[" + workerExecutionThreads + "] MaxJobActive[" + synthesis += " GrpcAddress[" + zeebeGatewayAddress + "] RestAddress[" + zeebeRestAddress + "] workerThread[" + workerExecutionThreads + "] MaxJobActive[" + workerMaxJobsActive + "]"; } if (serverType.equals(CamundaEngine.CAMUNDA_8_SAAS)) { diff --git a/src/main/java/org/camunda/automator/configuration/ConfigurationServersEngine.java b/src/main/java/org/camunda/automator/configuration/ConfigurationServersEngine.java index d4877ae..8fb092d 100644 --- a/src/main/java/org/camunda/automator/configuration/ConfigurationServersEngine.java +++ b/src/main/java/org/camunda/automator/configuration/ConfigurationServersEngine.java @@ -34,6 +34,10 @@ public class ConfigurationServersEngine { public String zeebeName; @Value("${automator.servers.camunda8.zeebeGatewayAddress:''}") public String zeebeGatewayAddress; + @Value("${automator.servers.camunda8.zeebeGrpcAddress:''}") + public String zeebeGrpcAddress; + @Value("${automator.servers.camunda8.zeebeRestAddress:''}") + public String zeebeRestAddress; @Value("${automator.servers.camunda8.operateUrl:''}") public String zeebeOperateUrl; @Value("${automator.servers.camunda8.operateUserName:''}") diff --git a/src/main/java/org/camunda/automator/content/ContentManager.java b/src/main/java/org/camunda/automator/content/ContentManager.java index dafa0fd..4b2f866 100644 --- a/src/main/java/org/camunda/automator/content/ContentManager.java +++ b/src/main/java/org/camunda/automator/content/ContentManager.java @@ -1,10 +1,15 @@ package org.camunda.automator.content; +import org.camunda.automator.configuration.ConfigurationStartup; +import org.camunda.automator.definition.Scenario; +import org.camunda.automator.engine.AutomatorException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; @@ -13,71 +18,114 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @Component @PropertySource("classpath:application.yaml") @Configuration - public class ContentManager { + private static final Logger logger = LoggerFactory.getLogger(ContentManager.class.getName()); @Value("${automator.content.repositoryPath}") public String repositoryPath; @Value("${automator.content.uploadPath}") public String uploadPath; + @Autowired + ConfigurationStartup configurationStartup; + RepositoryManager repositoryManager = new RepositoryManager(); + @Value("${automator.content.scenario:}") + private Resource scenarioResource; - public File getFromName(String scenarioName) { - return new File(repositoryPath + File.separator + scenarioName + ".json"); + public Path getFromName(String scenarioName) { + return repositoryManager.getFromName(scenarioName); } - public void saveFromMultiPartFile(MultipartFile file, String fileName) { - // save the file on a temporary disk - OutputStream outputStream = null; - File fileScenario = null; + + @PostConstruct + public void init() { try { - fileScenario = new File(repositoryPath + File.separator + fileName); - // Open an OutputStream to the temporary file - outputStream = new FileOutputStream(fileScenario); - // Transfer data from InputStream to OutputStream - byte[] buffer = new byte[1024 * 100]; // 100Ko - int bytesRead; - int count = 0; - InputStream inputStream = file.getInputStream(); - while ((bytesRead = inputStream.read(buffer)) != -1) { - count += bytesRead; - outputStream.write(buffer, 0, bytesRead); - } - outputStream.flush(); - outputStream.close(); - outputStream = null; + repositoryManager.initializeRepository(repositoryPath); + loadUploadPath(); + LoadContentResource(); } catch (Exception e) { - logger.error("Can't load File [" + fileName + "] : " + e.getMessage()); - } finally { - if (outputStream != null) - try { - outputStream.close(); - } catch (Exception e) { - // do nothing - } + logger.error("ContentManager: error during initialization {}", e.getMessage()); } } - @PostConstruct - public void init() { - Path sourceDirectory = Paths.get(uploadPath); - Path targetDirectory = Paths.get(repositoryPath); - logger.info("ContentManager initiaalisation Copied: [{}] to [{}]", sourceDirectory, targetDirectory); - int nbFilesCopied = 0; - try { - // Create target directory if it doesn't exist - if (!Files.exists(targetDirectory)) { - Files.createDirectories(targetDirectory); + /* ******************************************************************** */ + /* */ + /* Repository management */ + /* */ + /* ******************************************************************** */ + + public List getContent() { + return repositoryManager.getContentRepository(); + } + + public List getContentScenario() { + List listScenario = new ArrayList<>(); + List listContent = repositoryManager.getContentRepository(); + for (Path path : listContent) { + // The content can have multiple files, not only scenario + if (! path.getFileName().toString().endsWith(".json")) + continue; + try { + Scenario scenario = Scenario.createFromFile(path); + listScenario.add(scenario); + } catch (AutomatorException e) { + logger.error("ContentManager/getContentScenario: path [{}] failed: {}", path.getFileName(), e.getMessage()); } + } + return listScenario; + } + + + public Path addFile(Path file) throws IOException { + return repositoryManager.addFile(file); + } + + public Path addResource(Resource resource) throws IOException { + return repositoryManager.addResource(resource); + } + + public Path addFromMultipart(MultipartFile file, String fileName) throws IOException { + // save the file on a temporary disk + return repositoryManager.addFromInputStream(file.getInputStream(), fileName); + } + /* ******************************************************************** */ + /* */ + /* Load management */ + /* */ + /* ******************************************************************** */ + + /** + * Load from the content resource. This is typicaly provided on a Pod + */ + private void LoadContentResource() { + if (scenarioResource == null) { + logger.info("ContentManager/LoadContentResource: No scenario resource"); + return; + } + try { + logger.info("ContentManager/LoadContentResource: Detect [Resource] name[{}]", scenarioResource.getFilename()); + Path scenario = repositoryManager.addResource(scenarioResource); + } catch (IOException e) { + logger.error("ContentManager/LoadContentResource: Error occurred: {} ", e.getMessage()); + } + } + + /** + * Upload from a path. This is typicaly provided on a local machine + */ + private void loadUploadPath() { + try { + Path sourceDirectory = Paths.get(uploadPath); + logger.info("ContentManager/Upload: from [{}]", sourceDirectory); + int nbFilesCopied = 0; // Copy all files from source to target List listFiles = Files.walk(sourceDirectory) .filter(Files::isRegularFile).toList(); @@ -85,20 +133,16 @@ public void init() { for (Path sourcePath : listFiles)// Filter only regular files { try { - Path targetPath = targetDirectory.resolve(sourceDirectory.relativize(sourcePath)); - Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); - logger.info("Copied: [{}] tp [{}]", sourcePath, targetPath); + repositoryManager.addFile(sourcePath); nbFilesCopied++; } catch (IOException e) { - logger.error("Error copying file: [{}] -> [{}] : {}", sourcePath, targetDirectory, e.getMessage()); + logger.error("ContentManager/Upload: Error copying[{}] -> [{}] : {}", sourcePath, repositoryManager.getRepositoryPath(), e.getMessage()); } } - + logger.info("ContentManager/Upload: upload {} files", nbFilesCopied); } catch (IOException e) { - logger.error("Error occurred: {} ", e.getMessage()); + logger.error("ContentManager/Upload: Error occurred: {} ", e.getMessage()); } - logger.info("End of ContentManager {} files copied", nbFilesCopied); } - } diff --git a/src/main/java/org/camunda/automator/content/ContentRestController.java b/src/main/java/org/camunda/automator/content/ContentRestController.java index 41db040..3f75eba 100644 --- a/src/main/java/org/camunda/automator/content/ContentRestController.java +++ b/src/main/java/org/camunda/automator/content/ContentRestController.java @@ -1,15 +1,20 @@ package org.camunda.automator.content; +import org.camunda.automator.definition.Scenario; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.server.ResponseStatusException; -import java.util.HashMap; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -21,20 +26,35 @@ public class ContentRestController { @Autowired ContentManager contentManager; + /** + * curl -X POST "http://localhost:8381/api/content/add" -H "Content-Type: multipart/form-data" -F "File=@C:/dev/intellij/community/process-execution-automator/doc/unittestscenario/resources/ScoreAcceptanceScn.json" + **/ @PostMapping(value = "/api/content/add", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE}, produces = MediaType.APPLICATION_JSON_VALUE) - public Map upload(@RequestPart("File") List uploadedfiles) { - Map status = new HashMap<>(); + public List> upload(@RequestPart("File") List uploadedfiles) { + List> result = new ArrayList<>(); for (MultipartFile file : uploadedfiles) { - String resultFile = "Load [" + file.getName() + "]"; - - // is this worker is running? - String jarFileName = file.getOriginalFilename(); - contentManager.saveFromMultiPartFile(file, jarFileName); + try { + Path fileSaved = contentManager.addFromMultipart(file, file.getOriginalFilename()); + result.add(Map.of("filename", fileSaved.getFileName(), "status", "UPLOADED")); + } catch (Exception e) { + result.add(Map.of("filename", file.getOriginalFilename(), "status", "ERROR", "error", e.getMessage())); + } } - return new HashMap<>(); + return result; } + @GetMapping("/api/content/list") + List> getContentScenario() { + try { + return contentManager.getContentScenario().stream() + .map(Scenario::getDescription) + .toList(); + } catch (Exception e) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Error during Content : " + e.getMessage()); + } + } + } \ No newline at end of file diff --git a/src/main/java/org/camunda/automator/content/RepositoryManager.java b/src/main/java/org/camunda/automator/content/RepositoryManager.java new file mode 100644 index 0000000..3b424e9 --- /dev/null +++ b/src/main/java/org/camunda/automator/content/RepositoryManager.java @@ -0,0 +1,127 @@ +package org.camunda.automator.content; + +import org.camunda.automator.engine.AutomatorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public class RepositoryManager { + private final Logger logger = LoggerFactory.getLogger(RepositoryManager.class); + + private Path repositoryPath; + + public void initializeRepository(String repositoryProposition) throws AutomatorException { + if (repositoryProposition != null && !repositoryProposition.isEmpty()) { + Path path = Paths.get(repositoryProposition); + if (Files.exists(path) && Files.isDirectory(path)) { + repositoryPath = path; + } + } else { + // Not exist: create a subfolder + Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); + + // Create a new folder in the temporary directory + try { + repositoryPath = Files.createDirectory(tempDir.resolve("repository")); + } catch (Exception e) { + logger.error("Can't create folder [{}]", tempDir.toAbsolutePath() + "/repository"); + throw new AutomatorException("Can't create folder[" + tempDir.toAbsolutePath() + "/repository]"); + } + } + logger.info("RepositoryManager: directory under [{}] ", repositoryPath.toAbsolutePath()); + + } + + public Path addResource(Resource resource) throws IOException { + Path targetPath = repositoryPath.resolve(resource.getFilename()); + Files.copy(resource.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING); + logger.info("CopiedReource: [{}] tp [{}]", resource.getFilename(), targetPath); + return targetPath; + } + + public Path addFile(Path sourcePath) throws IOException { + Path sourceFileName = sourcePath.getFileName(); + // Get the directory from targetPath + Path targetDir = repositoryPath.getParent(); + // Combine the directory of targetPath with the filename of sourcePath + Path targetPath = targetDir.resolve(sourceFileName); + Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); + logger.info("CopiedFile: [{}] tp [{}]", sourcePath, targetPath); + return targetPath; + } + + public Path addFromInputStream(InputStream inputStream, String fileName) throws IOException { + OutputStream outputStream = null; + File fileContent = null; + try { + fileContent = new File(repositoryPath + File.separator + fileName); + // Open an OutputStream to the temporary file + outputStream = new FileOutputStream(fileContent); + // Transfer data from InputStream to OutputStream + byte[] buffer = new byte[1024 * 100]; // 100Ko + int bytesRead; + int count = 0; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + count += bytesRead; + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + outputStream.close(); + outputStream = null; + return fileContent.toPath(); + } catch (Exception e) { + logger.error("RepositoryManager/addFromInputStream: Can't upload File [" + fileName + "] : " + e.getMessage()); + throw e; + } finally { + if (outputStream != null) + try { + outputStream.close(); + } catch (Exception e) { + // do nothing + } + } + } + + public Path getRepositoryPath() { + return repositoryPath; + } + + public Path getFromName(String scenarioName) { + Path scenarioPath = Paths.get(repositoryPath + File.separator + scenarioName + ".json"); + if (Files.exists(scenarioPath)) { + return scenarioPath; + } + return null; + } + + + /** + * Return the content of the repository path + * + * @return list of files + */ + public List getContentRepository() { + try (Stream files = Files.walk(repositoryPath)) { + return files.filter(Files::isRegularFile) // You can filter by file type if needed + .toList(); + } catch (IOException e) { + logger.error("Error reading content [{}]", repositoryPath.toString()); + return Collections.emptyList(); + } + } + + public File prepareFileToUpload(String fileName) { + Path scenarioPath = Paths.get(repositoryPath + File.separator + fileName); + return scenarioPath.toFile(); + } +} diff --git a/src/main/java/org/camunda/automator/definition/Scenario.java b/src/main/java/org/camunda/automator/definition/Scenario.java index ff8bce3..461abee 100644 --- a/src/main/java/org/camunda/automator/definition/Scenario.java +++ b/src/main/java/org/camunda/automator/definition/Scenario.java @@ -13,8 +13,10 @@ import org.slf4j.LoggerFactory; import java.io.*; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * the Scenario Head group a scenario definition @@ -50,30 +52,34 @@ public class Scenario { */ private String scenarioFile = null; - public static Scenario createFromJson(String jsonContent) { + public static Scenario createFromJson(String jsonContent) throws AutomatorException { GsonBuilder builder = new GsonBuilder(); builder.setPrettyPrinting(); - - Gson gson = builder.create(); - Scenario scenario = gson.fromJson(jsonContent, Scenario.class); - if (scenario == null) { - logger.error("Scenario: Can't build scenario from content [{}]", jsonContent); - return null; + try { + Gson gson = builder.create(); + Scenario scenario = gson.fromJson(jsonContent, Scenario.class); + if (scenario == null) { + logger.error("Scenario: Can't build scenario from content [{}]", jsonContent); + return null; + } + scenario.afterUnSerialize(); + return scenario; + } catch (Exception e) { + logger.error("Scenario: can't unparse Json content [{}]", jsonContent); + throw new AutomatorException("Scenario: can't unparse GSon file:" + e.getMessage()); } - scenario.afterUnSerialize(); - return scenario; } - public static Scenario createFromFile(File scenarioFile) throws AutomatorException { + public static Scenario createFromFile(Path scenarioFile) throws AutomatorException { try { - Scenario scenario = createFromInputStream(new FileInputStream(scenarioFile), scenarioFile.getAbsolutePath()); - scenario.scenarioFile = scenarioFile.getAbsolutePath(); + Scenario scenario = createFromInputStream(new FileInputStream(scenarioFile.toFile()), scenarioFile.toAbsolutePath().toString()); + scenario.scenarioFile = scenarioFile.toAbsolutePath().toString(); scenario.initialize(); return scenario; } catch (FileNotFoundException e) { - throw new AutomatorException("Can't access file [" + scenarioFile.getAbsolutePath() + "] " + e.getMessage()); + throw new AutomatorException("Can't access file [" + scenarioFile.getFileName() + "] " + e.getMessage()); } catch (AutomatorException e) { throw e; } @@ -191,6 +197,15 @@ private void afterUnSerialize() { } } + + public Map getDescription() { + return Map.of("name", name==null?"":name,// + "server", serverName==null? "": serverName, // + "serverType", serverType==null?"": serverType, // + "processId", processId==null?"":processId, // + "typeScenario", typeScenario==null? "": typeScenario.toString()); + } + public enum TYPESCENARIO {FLOW, UNIT} } diff --git a/src/main/java/org/camunda/automator/definition/ScenarioVerification.java b/src/main/java/org/camunda/automator/definition/ScenarioVerification.java index 9b8a074..d6dbda5 100644 --- a/src/main/java/org/camunda/automator/definition/ScenarioVerification.java +++ b/src/main/java/org/camunda/automator/definition/ScenarioVerification.java @@ -7,6 +7,11 @@ public class ScenarioVerification { private final ScenarioExecution scenarioExecution; + /** + * List of duration to check + * Maybe null due the Gson deserializer if there is no definition + */ + private final List performances = new ArrayList<>(); /** * List of activities to check * Maybe null due the Gson deserializer if there is no definition @@ -17,13 +22,6 @@ public class ScenarioVerification { * Maybe null due the Gson deserializer if there is no definition */ private List variables = new ArrayList<>(); - - /** - * List of duration to check - * Maybe null due the Gson deserializer if there is no definition - */ - private final List performances = new ArrayList<>(); - /** * Variable to search the process instance, if only the verification is running * Maybe null due the Gson deserializer if there is no definition diff --git a/src/main/java/org/camunda/automator/engine/RunResult.java b/src/main/java/org/camunda/automator/engine/RunResult.java index 1e44c8d..a112d40 100644 --- a/src/main/java/org/camunda/automator/engine/RunResult.java +++ b/src/main/java/org/camunda/automator/engine/RunResult.java @@ -38,18 +38,16 @@ public class RunResult { * Keep a photo of process instance created/failed per processid */ private final Map recordCreationPIMap = new HashMap<>(); + private final List listRunResults = new ArrayList<>(); Logger logger = LoggerFactory.getLogger(RunResult.class); private int numberOfSteps = 0; private int numberOfErrorSteps = 0; - /** * Time to execute it */ private long timeExecution; - private Date startDate; private Date endDate; - private final List listRunResults = new ArrayList<>(); public RunResult(RunScenario runScenario) { this.runScenario = runScenario; diff --git a/src/main/java/org/camunda/automator/engine/flow/RunObjectives.java b/src/main/java/org/camunda/automator/engine/flow/RunObjectives.java index 6dbf3a3..7017575 100644 --- a/src/main/java/org/camunda/automator/engine/flow/RunObjectives.java +++ b/src/main/java/org/camunda/automator/engine/flow/RunObjectives.java @@ -6,7 +6,6 @@ /* ******************************************************************** */ package org.camunda.automator.engine.flow; -import io.camunda.operate.search.DateFilter; import org.camunda.automator.bpmnengine.BpmnEngine; import org.camunda.automator.definition.ScenarioFlowControl; import org.camunda.automator.engine.AutomatorException; @@ -22,8 +21,8 @@ public class RunObjectives { private final Map> flowRateMnObjective = new HashMap<>(); private final List listObjectives; Logger logger = LoggerFactory.getLogger(RunObjectives.class); - private DateFilter startDateFilter; - private DateFilter endDateFilter; + private Date startDateFilter; + private Date endDateFilter; private long lastHeartBeat; public RunObjectives(List listObjectives, @@ -39,12 +38,12 @@ public RunObjectives(List listObjectives, } public void setStartDate(Date startTestDate) { - this.startDateFilter = new DateFilter(startTestDate); + this.startDateFilter = startTestDate; this.lastHeartBeat = System.currentTimeMillis(); } public void setEndDate(Date endTestDate) { - this.endDateFilter = new DateFilter(endTestDate); + this.endDateFilter = endTestDate; } /** diff --git a/src/main/java/org/camunda/automator/engine/flow/RunScenarioFlowServiceTask.java b/src/main/java/org/camunda/automator/engine/flow/RunScenarioFlowServiceTask.java index 85e5396..bb799a8 100644 --- a/src/main/java/org/camunda/automator/engine/flow/RunScenarioFlowServiceTask.java +++ b/src/main/java/org/camunda/automator/engine/flow/RunScenarioFlowServiceTask.java @@ -23,7 +23,6 @@ import org.camunda.bpm.client.task.ExternalTaskService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.TaskScheduler; import java.time.Duration; @@ -41,7 +40,7 @@ public class RunScenarioFlowServiceTask extends RunScenarioFlowBasic { private BpmnEngine.RegisteredTask registeredTask; private boolean stopping; - private BenchmarkCompleteJobExceptionHandlingStrategy exceptionHandlingStrategy=null; + private final BenchmarkCompleteJobExceptionHandlingStrategy exceptionHandlingStrategy = null; public RunScenarioFlowServiceTask(TaskScheduler scheduler, ScenarioStep scenarioStep, diff --git a/src/main/java/org/camunda/automator/engine/flow/RunScenarioWarmingUp.java b/src/main/java/org/camunda/automator/engine/flow/RunScenarioWarmingUp.java index 5d23b79..de4c0c5 100644 --- a/src/main/java/org/camunda/automator/engine/flow/RunScenarioWarmingUp.java +++ b/src/main/java/org/camunda/automator/engine/flow/RunScenarioWarmingUp.java @@ -6,7 +6,6 @@ /* ******************************************************************** */ package org.camunda.automator.engine.flow; -import io.camunda.operate.search.DateFilter; import org.camunda.automator.definition.ScenarioStep; import org.camunda.automator.definition.ScenarioWarmingUp; import org.camunda.automator.engine.AutomatorException; @@ -311,7 +310,7 @@ private CheckFunctionResult endCheckFunction(String function, RunResult runResul long value = runScenario.getBpmnEngine() .countNumberOfProcessInstancesEnded(runScenario.getScenario().getProcessId(), - new DateFilter(runResult.getStartDate()), new DateFilter(new Date())); + runResult.getStartDate(), new Date()); return new CheckFunctionResult(value >= threshold, "End[" + endId + "] value [" + value + "] / threshold[" + threshold + "]"); diff --git a/src/main/java/org/camunda/automator/services/AutomatorStartup.java b/src/main/java/org/camunda/automator/services/AutomatorStartup.java index bf189b7..c5eb633 100644 --- a/src/main/java/org/camunda/automator/services/AutomatorStartup.java +++ b/src/main/java/org/camunda/automator/services/AutomatorStartup.java @@ -5,6 +5,7 @@ import org.camunda.automator.bpmnengine.BpmnEngine; import org.camunda.automator.configuration.BpmnEngineList; import org.camunda.automator.configuration.ConfigurationStartup; +import org.camunda.automator.content.ContentManager; import org.camunda.automator.definition.Scenario; import org.camunda.automator.engine.AutomatorException; import org.camunda.automator.engine.RunParameters; @@ -16,7 +17,10 @@ import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -40,6 +44,8 @@ public class AutomatorStartup { @Autowired ServiceAccess serviceAccess; + @Autowired + private ContentManager contentManager; @PostConstruct public void init() { @@ -69,57 +75,6 @@ private void runFixedWarmup() { } } - /** - * Load all scenario. List of File or Resource - * - * @return list of scenario - */ - private List registerScenario() { - List scenarioList = new ArrayList<>(); - // File - if (configurationStartup.getScenarioFileAtStartup().isEmpty()) { - logger.info("No scenario [File] from variable {} given", configurationStartup.getScenarioFileAtStartupName()); - } else { - logger.info("Detect {} scenario [File] from variable [{}] ScenarioPath[{}]", - configurationStartup.getScenarioFileAtStartup().size(), configurationStartup.getScenarioFileAtStartupName(), - configurationStartup.scenarioPath); - - for (String scenarioFileName : configurationStartup.getScenarioFileAtStartup()) { - logger.info("Register scenario [File] [{}]", scenarioFileName); - - File scenarioFile = new File(configurationStartup.scenarioPath + "/" + scenarioFileName); - if (!scenarioFile.exists()) { - scenarioFile = new File(scenarioFileName); - } - if (!scenarioFile.exists()) { - logger.error("ScenarioFile: Can't find File [{}/{}] or [{}]", configurationStartup.scenarioPath, - scenarioFileName, scenarioFileName); - continue; - } - scenarioList.add(scenarioFile); - } - } - // Resource - if (configurationStartup.getScenarioResourceAtStartup().isEmpty()) { - logger.info("No scenario [Resource] from variable {} given", - configurationStartup.getScenarioResourceAtStartupName()); - } else { - List scenarioResource = configurationStartup.getScenarioResourceAtStartup().stream() - .filter(t -> t != null) - .collect(Collectors.toList()); - - logger.info("Detect {} scenario [Resource] from variable [{}]", - scenarioResource.size(), - configurationStartup.getScenarioResourceAtStartupName()); - - for (Resource resource : scenarioResource) { - logger.info("Load scenario [Resource] from [{}]", resource.getDescription()); - scenarioList.add(resource); - } - } - - return scenarioList; - } /** * AutomatorSetupRunnable - run in parallel @@ -179,27 +134,19 @@ public void run() { runFixedWarmup(); // Load scenario - List scenarioList = registerScenario(); + List scenarioList = loadStartupScenario(); // now proceed all scenario - for (Object scenarioObject : scenarioList) { + for (Path scenarioPath : scenarioList) { Scenario scenario = null; - if (scenarioObject instanceof File scenarioFile) - try { - scenario = automatorAPI.loadFromFile(scenarioFile); - } catch (Exception e) { - logger.error("Error during accessing InputStream from File [{}]: {}", scenarioFile.getAbsolutePath(), - e.getMessage()); - } - else if (scenarioObject instanceof Resource scenarioResource) { - try { - scenario = automatorAPI.loadFromInputStream(scenarioResource.getInputStream(), - scenarioResource.getDescription()); - } catch (Exception e) { - logger.error("Error during accessing InputStream from resource [{}]: {}", scenarioResource.getDescription(), - e.getMessage()); - } + try { + scenario = automatorAPI.loadFromFile(scenarioPath); + } catch (Exception e) { + logger.error("Error during accessing InputStream from File [{}]: {}", scenarioPath.getFileName(), + e.getMessage()); } + + if (scenario == null) continue; logger.info("Start scenario [{}] on (1)ScenarioServerName[{}] (2)ConfigurationServerName[{}]", @@ -272,5 +219,62 @@ else if (scenarioObject instanceof Resource scenarioResource) { } } } + + private List loadStartupScenario() { + List scenarioList = new ArrayList<>(); + // File + if (configurationStartup.getScenarioFileAtStartup().isEmpty()) { + logger.info("AutomatorStartup/StartupScenario: no scenario [File] from {} given", configurationStartup.getScenarioFileAtStartupName()); + } else { + logger.info("Detect {} scenario [File] from variable [{}] ScenarioPath[{}]", + configurationStartup.getScenarioFileAtStartup().size(), configurationStartup.getScenarioFileAtStartupName(), + configurationStartup.scenarioPath); + + for (String scenarioFileName : configurationStartup.getScenarioFileAtStartup()) { + logger.info("AutomatorStartup/StartupScenario: Register scenario [File] [{}]", scenarioFileName); + + Path scenarioFile = Paths.get(configurationStartup.scenarioPath + "/" + scenarioFileName); + if (!Files.exists(scenarioFile)) { + scenarioFile = Paths.get(scenarioFileName); + } + if (Files.exists(scenarioFile)) { + try { + contentManager.addFile(scenarioFile); + }catch (IOException e) { + logger.error("AutomatorStartup/StartupScenario: File [{}] Can't add in the repository: {}", scenarioFile.toAbsolutePath().toString(), e.getMessage()); + } + } else { + logger.error("AutomatorStartup/StartupScenario:: Can't find File [{}/{}] or [{}]", configurationStartup.scenarioPath, + scenarioFileName, scenarioFileName); + continue; + } + } + + } + + // Resource + if (configurationStartup.getScenarioResourceAtStartup().isEmpty()) { + logger.info("No scenario [Resource] from variable {} given", + configurationStartup.getScenarioResourceAtStartupName()); + } else { + List scenarioResource = configurationStartup.getScenarioResourceAtStartup().stream() + .filter(t -> t != null) + .collect(Collectors.toList()); + + logger.info("Detect {} scenario [Resource] from variable [{}]", + scenarioResource.size(), + configurationStartup.getScenarioResourceAtStartupName()); + for (Resource resource : scenarioResource) { + try { + scenarioList.add(contentManager.addResource(resource)); + } catch (IOException e) { + logger.error("Error loading resource [{}]", resource.getFilename()); + } + } + } + + return scenarioList; + } + } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 61fe1d6..729dc17 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -4,7 +4,9 @@ automator: content: repositoryPath: "c:/temp/processautomator" - uploadPath: "C:/dev/intellij/community/process-execution-automator/doc/unittestscenario/resources" + uploadPath: "C:/temp/upload" + scenario: + startup: # give the server to run all tests at startup. The name must be registered in the list of server after serverName: Camunda8Ruby @@ -16,6 +18,8 @@ automator: # one scenario resource - to be accessible in a Docker container via a configMap scenarioResourceAtStartup: + + # DEBUG, INFO, MONITORING, MAIN, NOTHING logLevel: MONITORING # string composed with DEPLOYPROCESS, WARMINGUP, CREATION, SERVICETASK, USERTASK @@ -53,6 +57,7 @@ automator: description: "Simple authentication" name: "Camunda8Ruby" zeebeGatewayAddress: "127.0.0.1:26500" + zeebeRestAddress: "http://localhost:9600" operateUserName: "demo" operateUserPassword: "demo" operateUrl: "http://localhost:8081" @@ -67,6 +72,7 @@ automator: name: "Camunda8Lazuli" description: "A Zeebe+Identity server" zeebeGatewayAddress: "127.0.0.1:26500" + zeebeRestAddress: "http://localhost:9600" zeebeClientId: "zeebe" zeebeClientSecret: "LHwdAq56bZ" zeebeAudience: "zeebe" @@ -93,6 +99,7 @@ automator: - type: "camunda8" name: "Camunda8ZeebeOnly" zeebeGatewayAddress: "127.0.0.1:26500" + zeebeRestAddress: "http://localhost:9600" zeebePlainText: true workerExecutionThreads: 200 # -1 means : align the jobsActive to the workerExecutionThreads diff --git a/src/test/java/automatorapi/TestSimpleUserTask.java b/src/test/java/automatorapi/TestSimpleUserTask.java index 1ae5a72..5787ff6 100644 --- a/src/test/java/automatorapi/TestSimpleUserTask.java +++ b/src/test/java/automatorapi/TestSimpleUserTask.java @@ -14,6 +14,8 @@ import org.springframework.context.annotation.Bean; import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; public class TestSimpleUserTask { @@ -58,7 +60,7 @@ public void SimpleUserTaskScenario() { assert(true); return; } - File userTaskFile = new File("./test/resources/simpleusertask/AutomatorSimpleUserTask.json"); + Path userTaskFile = Paths.get("./test/resources/simpleusertask/AutomatorSimpleUserTask.json"); Scenario scenario = automatorApi.loadFromFile(userTaskFile); assert(scenario!=null); } catch (Exception e) {