시나리오 수정
This commit is contained in:
@@ -264,7 +264,7 @@ const DiagramArea = forwardRef(({onSave, onFlowChange, isExecuting}, ref) => {
|
||||
position,
|
||||
data: {
|
||||
...apiData,
|
||||
id: `api-${Date.now()}` // Ensure unique API ID
|
||||
id: apiData.id,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.eactive.testmaster.client.dto;
|
||||
|
||||
|
||||
public class ReactFlowEdge {
|
||||
private String id;
|
||||
private String source;
|
||||
private String target;
|
||||
private String sourceHandle;
|
||||
private String targetHandle;
|
||||
private String type = "smoothstep";
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(String target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public String getSourceHandle() {
|
||||
return sourceHandle;
|
||||
}
|
||||
|
||||
public void setSourceHandle(String sourceHandle) {
|
||||
this.sourceHandle = sourceHandle;
|
||||
}
|
||||
|
||||
public String getTargetHandle() {
|
||||
return targetHandle;
|
||||
}
|
||||
|
||||
public void setTargetHandle(String targetHandle) {
|
||||
this.targetHandle = targetHandle;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.eactive.testmaster.client.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ReactFlowGraph {
|
||||
private List<ReactFlowNode> nodes = new ArrayList<>();
|
||||
private List<ReactFlowEdge> edges = new ArrayList<>();
|
||||
|
||||
public List<ReactFlowNode> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setNodes(List<ReactFlowNode> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public List<ReactFlowEdge> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public void setEdges(List<ReactFlowEdge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.eactive.testmaster.client.dto;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ReactFlowNode {
|
||||
private String id;
|
||||
private String type;
|
||||
private Position position;
|
||||
private NodeData data;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(Position position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public NodeData getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(NodeData data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public static class Position {
|
||||
private double x;
|
||||
private double y;
|
||||
|
||||
public Position() {
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NodeData {
|
||||
private String id;
|
||||
private String name;
|
||||
private String method;
|
||||
private String path;
|
||||
private String condition;
|
||||
private Long duration;
|
||||
private Map<String, Object> additionalData;
|
||||
|
||||
public NodeData() {
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public void setCondition(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public Long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(Long duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAdditionalData() {
|
||||
return additionalData;
|
||||
}
|
||||
|
||||
public void setAdditionalData(Map<String, Object> additionalData) {
|
||||
this.additionalData = additionalData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
import org.thymeleaf.util.StringUtils;
|
||||
|
||||
@Service
|
||||
//@Scope("session")
|
||||
@@ -49,7 +50,7 @@ public class LoadTestService extends TextWebSocketHandler {
|
||||
|
||||
private final RingBuffer<ApiRequestEvent> ringBuffer;
|
||||
|
||||
public LoadTestService(SimpMessagingTemplate template, LoadTestMonitorMap loadTestMonitorMap,ApiClientFactory apiClientFactory, ApiScenarioMapper apiScenarioMapper, ApiRequestMapper apiRequestMapper) {
|
||||
public LoadTestService(SimpMessagingTemplate template, LoadTestMonitorMap loadTestMonitorMap, ApiClientFactory apiClientFactory, ApiScenarioMapper apiScenarioMapper, ApiRequestMapper apiRequestMapper) {
|
||||
this.template = template;
|
||||
this.loadTestMonitorMap = loadTestMonitorMap;
|
||||
this.apiScenarioMapper = apiScenarioMapper;
|
||||
@@ -130,7 +131,15 @@ public class LoadTestService extends TextWebSocketHandler {
|
||||
taskScheduler.setPoolSize(numberOfUsers + 1); // Add extra space for cancellation tasks
|
||||
taskScheduler.initialize();
|
||||
ApiScenario apiScenario = apiScenarioMapper.map(loadTestDTO.getScenarioId());
|
||||
ScenarioNode scenario = LoadTestGraphBuilder.getTopNode(apiScenario.getScenario());
|
||||
|
||||
ScenarioNode scenario = null;
|
||||
if (apiScenario != null) {
|
||||
if (!StringUtils.isEmpty(apiScenario.getScenario()) && apiScenario.getScenario().contains("nodes")) {
|
||||
scenario = ReactFlowGraphBuilder.convertToScenarioNode(apiScenario.getScenario());
|
||||
} else {
|
||||
scenario = LoadTestGraphBuilder.getTopNode(apiScenario.getScenario());
|
||||
}
|
||||
}
|
||||
traverseDFS(scenario);
|
||||
|
||||
List<ScheduledFuture<?>> scheduledFutures = new ArrayList<>();
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
package com.eactive.testmaster.loadtest.service;
|
||||
|
||||
import com.eactive.testmaster.client.dto.ReactFlowGraph;
|
||||
import com.eactive.testmaster.client.dto.internal.ScenarioNode;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class ReactFlowGraphBuilder {
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
static {
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
public static ReactFlowGraph loadData(String data) throws JsonProcessingException {
|
||||
return objectMapper.readValue(data, new TypeReference<ReactFlowGraph>() {
|
||||
});
|
||||
}
|
||||
|
||||
public static ScenarioNode convertToScenarioNode(String data) {
|
||||
try {
|
||||
return convertToScenarioNode(loadData(data));
|
||||
} catch (JsonProcessingException e) {
|
||||
return new ScenarioNode();
|
||||
}
|
||||
}
|
||||
|
||||
public static ScenarioNode convertToScenarioNode(ReactFlowGraph flowGraph) {
|
||||
Map<String, ScenarioNode> nodeMap = new HashMap<>();
|
||||
|
||||
// First pass: Create all nodes
|
||||
flowGraph.getNodes().forEach(flowNode -> {
|
||||
ScenarioNode scenarioNode = new ScenarioNode();
|
||||
scenarioNode.setId(flowNode.getId());
|
||||
scenarioNode.setName(flowNode.getData().getName());
|
||||
scenarioNode.setClassName(getScenarioNodeClass(flowNode.getType()));
|
||||
|
||||
// Convert node-specific data
|
||||
Map<String, String> data = new HashMap<>();
|
||||
switch (flowNode.getType()) {
|
||||
case "apiNode":
|
||||
data.put("id", flowNode.getData().getId());
|
||||
data.put("method", flowNode.getData().getMethod());
|
||||
data.put("path", flowNode.getData().getPath());
|
||||
break;
|
||||
case "conditionNode":
|
||||
data.put("condition", flowNode.getData().getCondition());
|
||||
break;
|
||||
case "delayNode":
|
||||
data.put("duration", String.valueOf(flowNode.getData().getDuration()));
|
||||
break;
|
||||
}
|
||||
scenarioNode.setData(data);
|
||||
nodeMap.put(flowNode.getId(), scenarioNode);
|
||||
});
|
||||
|
||||
// Second pass: Connect nodes
|
||||
flowGraph.getEdges().forEach(edge -> {
|
||||
ScenarioNode sourceNode = nodeMap.get(edge.getSource());
|
||||
if (sourceNode != null) {
|
||||
sourceNode.addNode(nodeMap.get(edge.getTarget()));
|
||||
}
|
||||
});
|
||||
|
||||
// Find and return the start node
|
||||
return nodeMap.values().stream()
|
||||
.filter(node -> "start".equals(node.getClassName()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static String getScenarioNodeClass(String nodeType) {
|
||||
switch (nodeType) {
|
||||
case "startNode":
|
||||
return "start";
|
||||
case "apiNode":
|
||||
return "api";
|
||||
case "conditionNode":
|
||||
return "condition";
|
||||
case "delayNode":
|
||||
return "delay";
|
||||
default:
|
||||
return "default";
|
||||
}
|
||||
}
|
||||
|
||||
public static String testData = "{\n"
|
||||
+ " \"nodes\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"id\": \"start\",\n"
|
||||
+ " \"type\": \"startNode\",\n"
|
||||
+ " \"position\": {\n"
|
||||
+ " \"x\": 100,\n"
|
||||
+ " \"y\": 50\n"
|
||||
+ " },\n"
|
||||
+ " \"data\": {\n"
|
||||
+ " \"id\": \"start\"\n"
|
||||
+ " },\n"
|
||||
+ " \"measured\": {\n"
|
||||
+ " \"width\": 100,\n"
|
||||
+ " \"height\": 56\n"
|
||||
+ " },\n"
|
||||
+ " \"className\": null\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"id\": \"node-1738370266933-9tienpcqk\",\n"
|
||||
+ " \"type\": \"apiNode\",\n"
|
||||
+ " \"position\": {\n"
|
||||
+ " \"x\": 360,\n"
|
||||
+ " \"y\": 195\n"
|
||||
+ " },\n"
|
||||
+ " \"data\": {\n"
|
||||
+ " \"id\": \"c240c9da-17a4-4dc7-bc93-a29b54aa324e\",\n"
|
||||
+ " \"name\": \"api1\",\n"
|
||||
+ " \"path\": \"api1?test={{test}}\",\n"
|
||||
+ " \"method\": \"POST\",\n"
|
||||
+ " \"type\": \"HTTP\",\n"
|
||||
+ " \"server\": 53,\n"
|
||||
+ " \"headers\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"enabled\": true,\n"
|
||||
+ " \"key\": \"hhaa\",\n"
|
||||
+ " \"value\": \"{{test}}\"\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"queryParams\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"enabled\": true,\n"
|
||||
+ " \"key\": \"test\",\n"
|
||||
+ " \"value\": \"{{test}}\"\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"variables\": [],\n"
|
||||
+ " \"requestBody\": \"{{test}}\",\n"
|
||||
+ " \"preRequestScript\": \"console.log(11);\",\n"
|
||||
+ " \"postRequestScript\": \"console.log(22);\",\n"
|
||||
+ " \"layout\": null,\n"
|
||||
+ " \"collectionId\": \"d4088731-685e-41ea-b557-61d7ac3d4ab3\"\n"
|
||||
+ " },\n"
|
||||
+ " \"measured\": {\n"
|
||||
+ " \"width\": 200,\n"
|
||||
+ " \"height\": 106\n"
|
||||
+ " }\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"id\": \"node-1738370270698-hj4f76knw\",\n"
|
||||
+ " \"type\": \"apiNode\",\n"
|
||||
+ " \"position\": {\n"
|
||||
+ " \"x\": 720,\n"
|
||||
+ " \"y\": 315\n"
|
||||
+ " },\n"
|
||||
+ " \"data\": {\n"
|
||||
+ " \"id\": \"c240c9da-17a4-4dc7-bc93-a29b54aa324e\",\n"
|
||||
+ " \"name\": \"api1\",\n"
|
||||
+ " \"path\": \"api1?test={{test}}\",\n"
|
||||
+ " \"method\": \"POST\",\n"
|
||||
+ " \"type\": \"HTTP\",\n"
|
||||
+ " \"server\": 53,\n"
|
||||
+ " \"headers\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"enabled\": true,\n"
|
||||
+ " \"key\": \"hhaa\",\n"
|
||||
+ " \"value\": \"{{test}}\"\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"queryParams\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"enabled\": true,\n"
|
||||
+ " \"key\": \"test\",\n"
|
||||
+ " \"value\": \"{{test}}\"\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"variables\": [],\n"
|
||||
+ " \"requestBody\": \"{{test}}\",\n"
|
||||
+ " \"preRequestScript\": \"console.log(11);\",\n"
|
||||
+ " \"postRequestScript\": \"console.log(22);\",\n"
|
||||
+ " \"layout\": null,\n"
|
||||
+ " \"collectionId\": \"d4088731-685e-41ea-b557-61d7ac3d4ab3\"\n"
|
||||
+ " },\n"
|
||||
+ " \"measured\": {\n"
|
||||
+ " \"width\": 200,\n"
|
||||
+ " \"height\": 106\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"edges\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"smoothstep\",\n"
|
||||
+ " \"source\": \"start\",\n"
|
||||
+ " \"sourceHandle\": null,\n"
|
||||
+ " \"target\": \"node-1738370266933-9tienpcqk\",\n"
|
||||
+ " \"targetHandle\": null,\n"
|
||||
+ " \"id\": \"edge-1738370269180-dqspjlvej\"\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"smoothstep\",\n"
|
||||
+ " \"source\": \"node-1738370266933-9tienpcqk\",\n"
|
||||
+ " \"sourceHandle\": null,\n"
|
||||
+ " \"target\": \"node-1738370270698-hj4f76knw\",\n"
|
||||
+ " \"targetHandle\": null,\n"
|
||||
+ " \"id\": \"edge-1738370272761-db6f2xim6\"\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"viewport\": {\n"
|
||||
+ " \"x\": 0,\n"
|
||||
+ " \"y\": 0,\n"
|
||||
+ " \"zoom\": 1\n"
|
||||
+ " }\n"
|
||||
+ "}";
|
||||
|
||||
public static void main(String[] args) throws JsonProcessingException {
|
||||
ScenarioNode node = ReactFlowGraphBuilder.convertToScenarioNode(testData);
|
||||
|
||||
System.out.println(node);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user