Merge branch 'v1.8' of github.com:metersphere/metersphere into v1.8
This commit is contained in:
commit
e56e2e19b5
|
@ -17,7 +17,7 @@
|
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<shiro.version>1.6.0</shiro.version>
|
||||
<java.version>1.8</java.version>
|
||||
<jmeter.version>5.2.1</jmeter.version>
|
||||
<jmeter.version>5.4.1</jmeter.version>
|
||||
<nacos.version>1.1.3</nacos.version>
|
||||
<dubbo.version>2.7.8</dubbo.version>
|
||||
<graalvm.version>20.1.0</graalvm.version>
|
||||
|
@ -276,18 +276,11 @@
|
|||
<artifactId>spring-boot-starter-data-ldap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger2 解析 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-parser</artifactId>
|
||||
<version>1.0.51</version>
|
||||
</dependency>
|
||||
|
||||
<!-- swagger3 解析 最新版本会有swagger-core版本冲突 -->
|
||||
<!-- swagger 解析 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.parser.v3</groupId>
|
||||
<artifactId>swagger-parser</artifactId>
|
||||
<version>2.0.18</version>
|
||||
<version>2.0.22</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 执行 js 代码依赖 -->
|
||||
|
@ -674,4 +667,4 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
@ -74,11 +74,21 @@ public class ApiAutomationController {
|
|||
apiAutomationService.deleteBatch(ids);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteBatchByCondition")
|
||||
public void deleteBatchByCondition(@RequestBody ApiScenarioBatchRequest request) {
|
||||
apiAutomationService.deleteBatchByCondition(request);
|
||||
}
|
||||
|
||||
@PostMapping("/removeToGc")
|
||||
public void removeToGc(@RequestBody List<String> ids) {
|
||||
apiAutomationService.removeToGc(ids);
|
||||
}
|
||||
|
||||
@PostMapping("/removeToGcByBatch")
|
||||
public void removeToGcByBatch(@RequestBody ApiScenarioBatchRequest request) {
|
||||
apiAutomationService.removeToGcByBatch(request);
|
||||
}
|
||||
|
||||
@PostMapping("/reduction")
|
||||
public void reduction(@RequestBody List<String> ids) {
|
||||
apiAutomationService.reduction(ids);
|
||||
|
|
|
@ -7,12 +7,12 @@ import io.metersphere.api.dto.definition.*;
|
|||
import io.metersphere.api.service.ApiTestCaseService;
|
||||
import io.metersphere.base.domain.ApiTestCase;
|
||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.ApiRunMode;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
|
||||
import io.metersphere.track.service.TestPlanApiCaseService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
@ -29,7 +29,8 @@ public class ApiTestCaseController {
|
|||
|
||||
@Resource
|
||||
private ApiTestCaseService apiTestCaseService;
|
||||
|
||||
@Resource
|
||||
private TestPlanApiCaseService testPlanApiCaseService;
|
||||
@PostMapping("/list")
|
||||
public List<ApiTestCaseResult> list(@RequestBody ApiTestCaseRequest request) {
|
||||
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
|
@ -48,6 +49,12 @@ public class ApiTestCaseController {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
@GetMapping("/getStateByTestPlan/{id}")
|
||||
public String getStateByTestPlan(@PathVariable String id ) {
|
||||
String status=testPlanApiCaseService.getState(id);
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
@PostMapping("/list/{goPage}/{pageSize}")
|
||||
public Pager<List<ApiTestCaseDTO>> listSimple(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiTestCaseRequest request) {
|
||||
|
@ -131,7 +138,8 @@ public class ApiTestCaseController {
|
|||
return apiTestCaseService.run(request);
|
||||
}
|
||||
@GetMapping(value = "/jenkins/exec/result/{id}")
|
||||
public String getExecResult(@PathVariable String id) {
|
||||
return apiTestCaseService.getExecResult(id);
|
||||
public String getExecResult(@PathVariable String id) {
|
||||
return apiTestCaseService.getExecResult(id);
|
||||
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ public class EsbDataStruct {
|
|||
private boolean required;
|
||||
private String description;
|
||||
private List<EsbDataStruct> children;
|
||||
private String status = "";
|
||||
|
||||
public void init(){
|
||||
this.uuid = UUID.randomUUID().toString();
|
||||
|
@ -127,6 +128,9 @@ public class EsbDataStruct {
|
|||
|
||||
if (element != null) {
|
||||
if (this.children == null || this.children.isEmpty()) {
|
||||
if(this.value == null ){
|
||||
this.value = "";
|
||||
}
|
||||
element.addText(this.value);
|
||||
} else {
|
||||
for (EsbDataStruct child : children) {
|
||||
|
|
|
@ -75,6 +75,8 @@ import org.apache.jorphan.collections.HashTree;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
||||
|
@ -132,12 +134,68 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
return (HashTree) field.get(scriptWrapper);
|
||||
}
|
||||
|
||||
public boolean isProtocolDefaultPort(HTTPSamplerProxy source) {
|
||||
String portAsString = source.getPropertyAsString("HTTPSampler.port");
|
||||
if (portAsString != null && !portAsString.isEmpty()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public String url(String protocol, String host, String port, String file) {
|
||||
protocol = protocol.toLowerCase();
|
||||
if (StringUtils.isNotEmpty(file) && !file.startsWith("/")) {
|
||||
file += "/";
|
||||
}
|
||||
return protocol + "://" + host + ":" + port + file;
|
||||
}
|
||||
|
||||
public String getUrl(HTTPSamplerProxy source) throws MalformedURLException {
|
||||
String path = source.getPath();
|
||||
if (!path.startsWith("http://") && !path.startsWith("https://")) {
|
||||
String domain = source.getDomain();
|
||||
String protocol = source.getProtocol();
|
||||
String method = source.getMethod();
|
||||
StringBuilder pathAndQuery = new StringBuilder(100);
|
||||
if ("file".equalsIgnoreCase(protocol)) {
|
||||
domain = null;
|
||||
} else if (!path.startsWith("/")) {
|
||||
pathAndQuery.append('/');
|
||||
}
|
||||
|
||||
pathAndQuery.append(path);
|
||||
if ("GET".equals(method) || "DELETE".equals(method) || "OPTIONS".equals(method)) {
|
||||
String queryString = source.getQueryString(source.getContentEncoding());
|
||||
if (queryString.length() > 0) {
|
||||
if (path.contains("?")) {
|
||||
pathAndQuery.append("&");
|
||||
} else {
|
||||
pathAndQuery.append("?");
|
||||
}
|
||||
|
||||
pathAndQuery.append(queryString);
|
||||
}
|
||||
}
|
||||
String portAsString = source.getPropertyAsString("HTTPSampler.port");
|
||||
return this.isProtocolDefaultPort(source) ? new URL(protocol, domain, pathAndQuery.toString()).toExternalForm() : this.url(protocol, domain, portAsString, pathAndQuery.toString());
|
||||
} else {
|
||||
return new URL(path).toExternalForm();
|
||||
}
|
||||
}
|
||||
|
||||
private void convertHttpSampler(MsHTTPSamplerProxy samplerProxy, Object key) {
|
||||
try {
|
||||
HTTPSamplerProxy source = (HTTPSamplerProxy) key;
|
||||
BeanUtils.copyBean(samplerProxy, source);
|
||||
samplerProxy.setRest(new ArrayList<KeyValue>() {{
|
||||
this.add(new KeyValue());
|
||||
}});
|
||||
samplerProxy.setArguments(new ArrayList<KeyValue>() {{
|
||||
this.add(new KeyValue());
|
||||
}});
|
||||
if (source != null && source.getHTTPFiles().length > 0) {
|
||||
samplerProxy.getBody().setBinary(new ArrayList<>());
|
||||
samplerProxy.getBody().initBinary();
|
||||
samplerProxy.getBody().setType(Body.FORM_DATA);
|
||||
List<KeyValue> keyValues = new LinkedList<>();
|
||||
for (HTTPFileArg arg : source.getHTTPFiles()) {
|
||||
|
@ -156,13 +214,15 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
samplerProxy.getBody().setKvs(keyValues);
|
||||
}
|
||||
samplerProxy.setProtocol(RequestType.HTTP);
|
||||
samplerProxy.setPort(source.getPort() + "");
|
||||
samplerProxy.setPort(source.getPropertyAsString("HTTPSampler.port"));
|
||||
samplerProxy.setDomain(source.getDomain());
|
||||
if (source.getArguments() != null) {
|
||||
if (source.getPostBodyRaw()) {
|
||||
samplerProxy.getBody().setType(Body.RAW);
|
||||
source.getArguments().getArgumentsAsMap().forEach((k, v) -> {
|
||||
samplerProxy.getBody().setRaw(v);
|
||||
});
|
||||
samplerProxy.getBody().initKvs();
|
||||
} else {
|
||||
List<KeyValue> keyValues = new LinkedList<>();
|
||||
source.getArguments().getArgumentsAsMap().forEach((k, v) -> {
|
||||
|
@ -173,11 +233,12 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
samplerProxy.setArguments(keyValues);
|
||||
}
|
||||
}
|
||||
samplerProxy.getBody().initBinary();
|
||||
}
|
||||
samplerProxy.setPath("");
|
||||
// samplerProxy.setPath(source.getPath());
|
||||
samplerProxy.setMethod(source.getMethod());
|
||||
if (source.getUrl() != null) {
|
||||
samplerProxy.setUrl(source.getUrl().toString());
|
||||
if (this.getUrl(source) != null) {
|
||||
samplerProxy.setUrl(this.getUrl(source));
|
||||
}
|
||||
samplerProxy.setId(UUID.randomUUID().toString());
|
||||
samplerProxy.setType("HTTPSamplerProxy");
|
||||
|
|
|
@ -58,14 +58,16 @@ public class MsDefinitionParser extends MsAbstractParser<ApiDefinitionImport> {
|
|||
private ApiDefinitionImport parseMsFormat(String testStr, ApiTestImportRequest importRequest) {
|
||||
ApiDefinitionImport apiDefinitionImport = JSON.parseObject(testStr, ApiDefinitionImport.class);
|
||||
Map<String, List<ApiTestCaseWithBLOBs>> caseMap = new HashMap<>();
|
||||
apiDefinitionImport.getCases().forEach(item -> {
|
||||
List<ApiTestCaseWithBLOBs> caseList = caseMap.get(item.getApiDefinitionId());
|
||||
if (caseList == null) {
|
||||
caseList = new ArrayList<>();
|
||||
caseMap.put(item.getApiDefinitionId(), caseList);
|
||||
}
|
||||
caseList.add(item);
|
||||
});
|
||||
if (apiDefinitionImport.getCases() != null) {
|
||||
apiDefinitionImport.getCases().forEach(item -> {
|
||||
List<ApiTestCaseWithBLOBs> caseList = caseMap.get(item.getApiDefinitionId());
|
||||
if (caseList == null) {
|
||||
caseList = new ArrayList<>();
|
||||
caseMap.put(item.getApiDefinitionId(), caseList);
|
||||
}
|
||||
caseList.add(item);
|
||||
});
|
||||
}
|
||||
apiDefinitionImport.getData().forEach(apiDefinition -> {
|
||||
parseApiDefinition(apiDefinition, importRequest, caseMap);
|
||||
});
|
||||
|
|
|
@ -70,8 +70,9 @@ public class Swagger2Parser extends SwaggerAbstractParser {
|
|||
parseParameters(operation, request);
|
||||
addBodyHeader(request);
|
||||
if (StringUtils.isNotBlank(basePath)) {
|
||||
apiDefinition.setPath(basePath + apiDefinition.getPath());
|
||||
request.setPath(basePath + request.getPath());
|
||||
String pathStr = basePath + apiDefinition.getPath().replaceAll("//","/");
|
||||
apiDefinition.setPath(pathStr);
|
||||
request.setPath(pathStr);
|
||||
}
|
||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses())));
|
||||
|
|
|
@ -114,23 +114,26 @@ public class MsScenario extends MsTestElement {
|
|||
// 设置共享cookie
|
||||
config.setEnableCookieShare(enableCookieShare);
|
||||
Map<String, EnvironmentConfig> envConfig = new HashMap<>(16);
|
||||
// 兼容历史数据
|
||||
if (environmentMap == null || environmentMap.isEmpty()) {
|
||||
environmentMap = new HashMap<>(16);
|
||||
if (StringUtils.isNotBlank(environmentId)) {
|
||||
environmentMap.put(SessionUtils.getCurrentProjectId(), environmentId);
|
||||
}
|
||||
}
|
||||
if (environmentMap != null && !environmentMap.isEmpty()) {
|
||||
environmentMap.keySet().forEach(projectId -> {
|
||||
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
|
||||
ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentMap.get(projectId));
|
||||
if (environment != null && environment.getConfig() != null) {
|
||||
EnvironmentConfig env = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
envConfig.put(projectId, env);
|
||||
if (config.getConfig() == null) {
|
||||
// 兼容历史数据
|
||||
if (this.environmentMap == null || this.environmentMap.isEmpty()) {
|
||||
this.environmentMap = new HashMap<>(16);
|
||||
if (StringUtils.isNotBlank(environmentId)) {
|
||||
// 兼容1.8之前 没有environmentMap但有environmentId的数据
|
||||
this.environmentMap.put("historyProjectID", environmentId);
|
||||
}
|
||||
});
|
||||
config.setConfig(envConfig);
|
||||
}
|
||||
if (this.environmentMap != null && !this.environmentMap.isEmpty()) {
|
||||
this.environmentMap.keySet().forEach(projectId -> {
|
||||
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
|
||||
ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.environmentMap.get(projectId));
|
||||
if (environment != null && environment.getConfig() != null) {
|
||||
EnvironmentConfig env = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
envConfig.put(projectId, env);
|
||||
}
|
||||
});
|
||||
config.setConfig(envConfig);
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(this.getVariables())) {
|
||||
config.setVariables(this.variables);
|
||||
|
|
|
@ -213,7 +213,7 @@ public abstract class MsTestElement {
|
|||
csvDataSet.setName(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName());
|
||||
csvDataSet.setProperty("fileEncoding", StringUtils.isEmpty(item.getEncoding()) ? "UTF-8" : item.getEncoding());
|
||||
if (CollectionUtils.isNotEmpty(item.getFiles())) {
|
||||
if (!config.isOperating() && new File(BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()).exists()) {
|
||||
if (!config.isOperating() && !new File(BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()).exists()) {
|
||||
MSException.throwException(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName() + ":[ CSV文件不存在 ]");
|
||||
}
|
||||
csvDataSet.setProperty("filename", BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName());
|
||||
|
|
|
@ -49,7 +49,7 @@ public class MsIfController extends MsTestElement {
|
|||
ifController.setName(this.getName());
|
||||
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
|
||||
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
|
||||
ifController.setCondition("true");
|
||||
ifController.setCondition(this.getCondition());
|
||||
ifController.setEvaluateAll(false);
|
||||
ifController.setUseExpression(true);
|
||||
return ifController;
|
||||
|
|
|
@ -91,6 +91,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
@JSONField(ordinal = 36)
|
||||
private MsAuthManager authManager;
|
||||
|
||||
@JSONField(ordinal = 37)
|
||||
private boolean urlOrPath;
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
// 非导出操作,且不是启用状态则跳过执行
|
||||
|
@ -126,6 +129,11 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
config.setConfig(getEnvironmentConfig(useEnvironment));
|
||||
}
|
||||
|
||||
// 1.8 之前历史数据
|
||||
if(StringUtils.isEmpty(this.getProjectId()) && config.getConfig()!= null && !config.getConfig().isEmpty()){
|
||||
this.setProjectId("historyProjectID");
|
||||
}
|
||||
|
||||
// 添加环境中的公共变量
|
||||
Arguments arguments = this.addArguments(config);
|
||||
if (arguments != null) {
|
||||
|
@ -140,23 +148,26 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
url = this.getUrl();
|
||||
isUrl = true;
|
||||
}
|
||||
URL urlObject = new URL(url);
|
||||
if (isUrl) {
|
||||
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
url.replaceAll(this.getPort(), "10990");
|
||||
}
|
||||
URL urlObject = new URL(url);
|
||||
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8"));
|
||||
if (urlObject.getPort() > 0) {
|
||||
if (urlObject.getPort() > 0 && urlObject.getPort() != 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
sampler.setPort(urlObject.getPort());
|
||||
} else {
|
||||
sampler.setProperty("HTTPSampler.port", this.getPort());
|
||||
}
|
||||
sampler.setProtocol(urlObject.getProtocol());
|
||||
sampler.setPath(urlObject.getPath());
|
||||
} else {
|
||||
sampler.setDomain(config.getConfig().get(this.getProjectId()).getHttpConfig().getDomain());
|
||||
sampler.setPort(config.getConfig().get(this.getProjectId()).getHttpConfig().getPort());
|
||||
sampler.setProtocol(config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol());
|
||||
sampler.setPath(this.getPath());
|
||||
}
|
||||
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
|
||||
if (StringUtils.isNotBlank(this.getPath()) && !isUrl) {
|
||||
envPath += this.getPath();
|
||||
sampler.setPath(envPath);
|
||||
}
|
||||
String envPath = sampler.getPath();
|
||||
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
|
||||
envPath = getRestParameters(URLDecoder.decode(envPath, "UTF-8"));
|
||||
sampler.setPath(envPath);
|
||||
|
@ -177,9 +188,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||
url = "http://" + url;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
url.replaceAll(this.getPort(), "10990");
|
||||
}
|
||||
URL urlObject = new URL(url);
|
||||
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8"));
|
||||
sampler.setPort(urlObject.getPort());
|
||||
if (urlObject.getPort() > 0 && urlObject.getPort() != 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
|
||||
sampler.setPort(urlObject.getPort());
|
||||
} else {
|
||||
sampler.setProperty("HTTPSampler.port", this.getPort());
|
||||
}
|
||||
sampler.setProtocol(urlObject.getProtocol());
|
||||
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
|
||||
sampler.setPath(envPath);
|
||||
|
@ -327,10 +345,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
|
||||
public boolean isURL(String str) {
|
||||
//转换为小写
|
||||
try {
|
||||
new URL(str);
|
||||
return true;
|
||||
String regex = "^((https|http|ftp|rtsp|mms)?://)"
|
||||
+ "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?"
|
||||
+ "(([0-9]{1,3}\\.){3}[0-9]{1,3}" + "|" + "([0-9a-z_!~*'()-]+\\.)*"
|
||||
+ "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\."
|
||||
+ "[a-z]{2,6})"
|
||||
+ "(:[0-9]{1,5})?"
|
||||
+ "((/?)|"
|
||||
+ "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
|
||||
return str.matches(regex) || (str.matches("^(http|https|ftp)://.*$") && str.matches(".*://\\$\\{.*$"));
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
@ -339,5 +363,5 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
private boolean isRest() {
|
||||
return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.api.dto.definition.request.sampler;
|
|||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import io.metersphere.api.dto.automation.EsbDataStruct;
|
||||
import io.metersphere.api.dto.definition.request.MsTestElement;
|
||||
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
|
||||
|
@ -70,6 +71,12 @@ public class MsTCPSampler extends MsTestElement {
|
|||
@JSONField(ordinal = 39)
|
||||
private String projectId;
|
||||
|
||||
/**
|
||||
* 新加两个参数,场景保存/修改时需要的参数。不会传递JMeter,只是用于最后的保留。
|
||||
*/
|
||||
private List<EsbDataStruct> esbDataStruct;
|
||||
private List<EsbDataStruct> backEsbDataStruct;
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
// 非导出操作,且不是启用状态则跳过执行
|
||||
|
|
|
@ -66,14 +66,11 @@ public class Body {
|
|||
sampler.setDoMultipart(true);
|
||||
}
|
||||
} else {
|
||||
if (!this.isJson()) {
|
||||
sampler.setPostBodyRaw(true);
|
||||
} else {
|
||||
if (StringUtils.isNotEmpty(this.format) && "JSON-SCHEMA".equals(this.format) && this.getJsonSchema() != null) {
|
||||
this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema()));
|
||||
}
|
||||
if (StringUtils.isNotEmpty(this.format) && "JSON-SCHEMA".equals(this.format) && this.getJsonSchema() != null) {
|
||||
this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema()));
|
||||
}
|
||||
KeyValue keyValue = new KeyValue("", "JSON-SCHEMA", this.getRaw(), true, true);
|
||||
sampler.setPostBodyRaw(true);
|
||||
keyValue.setEnable(true);
|
||||
keyValue.setEncode(false);
|
||||
body.add(keyValue);
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
package io.metersphere.api.jmeter;
|
||||
|
||||
import io.metersphere.api.dto.definition.ApiTestCaseInfo;
|
||||
import io.metersphere.api.dto.scenario.request.RequestType;
|
||||
import io.metersphere.api.service.*;
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||
import io.metersphere.base.domain.ApiScenarioReport;
|
||||
import io.metersphere.base.domain.ApiTestReport;
|
||||
import io.metersphere.base.domain.TestPlanReport;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
|
@ -16,7 +14,6 @@ import io.metersphere.notice.sender.NoticeModel;
|
|||
import io.metersphere.notice.service.NoticeSendService;
|
||||
import io.metersphere.service.SystemParameterService;
|
||||
import io.metersphere.track.service.TestPlanReportService;
|
||||
import io.metersphere.track.service.TestPlanService;
|
||||
import io.metersphere.track.service.TestPlanTestCaseService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -201,11 +198,11 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
apiDefinitionService.addResult(testResult);
|
||||
|
||||
//测试计划定时任务-接口执行逻辑的话,需要同步测试计划的报告数据
|
||||
if (StringUtils.equals(this.runMode, ApiRunMode.SCHEDULE_API_PLAN.name())) {
|
||||
if (StringUtils.equalsAny(this.runMode, ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) {
|
||||
apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult, ApiRunMode.SCHEDULE_API_PLAN.name());
|
||||
List<String> testPlanReportIdList = new ArrayList<>();
|
||||
testPlanReportIdList.add(debugReportId);
|
||||
for(String testPlanReportId : testPlanReportIdList) { // 更新每个测试计划的状态
|
||||
for (String testPlanReportId : testPlanReportIdList) { // 更新每个测试计划的状态
|
||||
testPlanReportService.checkTestPlanStatus(testPlanReportId);
|
||||
}
|
||||
testPlanReportService.updateReport(testPlanReportIdList, ApiRunMode.SCHEDULE_API_PLAN.name(), ReportTriggerMode.SCHEDULE.name());
|
||||
|
@ -250,10 +247,16 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
|
||||
}
|
||||
}
|
||||
sendTask(report, reportUrl, testResult);
|
||||
if (StringUtils.equals(ReportTriggerMode.API.name(), report.getTriggerMode())||StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), report.getTriggerMode())) {
|
||||
sendTask(report, reportUrl, testResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void sendTask(ApiTestReport report, String reportUrl, TestResult testResult) {
|
||||
if (report == null) {
|
||||
return;
|
||||
}
|
||||
SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class);
|
||||
NoticeSendService noticeSendService = CommonBeanFactory.getBean(NoticeSendService.class);
|
||||
assert systemParameterService != null;
|
||||
|
|
|
@ -90,6 +90,8 @@ public class ApiAutomationService {
|
|||
@Resource
|
||||
@Lazy
|
||||
private TestPlanScenarioCaseService testPlanScenarioCaseService;
|
||||
@Resource
|
||||
private EsbApiParamService esbApiParamService;
|
||||
|
||||
public List<ApiScenarioDTO> list(ApiScenarioRequest request) {
|
||||
request = this.initRequest(request, true, true);
|
||||
|
@ -184,6 +186,9 @@ public class ApiAutomationService {
|
|||
scenario.setCreateTime(System.currentTimeMillis());
|
||||
scenario.setNum(getNextNum(request.getProjectId()));
|
||||
|
||||
//检查场景的请求步骤。如果含有ESB请求步骤的话,要做参数计算处理。
|
||||
esbApiParamService.checkScenarioRequests(request);
|
||||
|
||||
apiScenarioMapper.insert(scenario);
|
||||
|
||||
List<String> bodyUploadIds = request.getBodyUploadIds();
|
||||
|
@ -205,6 +210,9 @@ public class ApiAutomationService {
|
|||
List<String> bodyUploadIds = request.getBodyUploadIds();
|
||||
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
|
||||
|
||||
//检查场景的请求步骤。如果含有ESB请求步骤的话,要做参数计算处理。
|
||||
esbApiParamService.checkScenarioRequests(request);
|
||||
|
||||
final ApiScenarioWithBLOBs scenario = buildSaveScenario(request);
|
||||
apiScenarioMapper.updateByPrimaryKeySelective(scenario);
|
||||
extScheduleMapper.updateNameByResourceID(request.getId(), request.getName());// 修改场景name,同步到修改首页定时任务
|
||||
|
@ -234,6 +242,10 @@ public class ApiAutomationService {
|
|||
} else {
|
||||
scenario.setUserId(request.getUserId());
|
||||
}
|
||||
if (StringUtils.isEmpty(request.getApiScenarioModuleId()) || StringUtils.isEmpty(request.getModulePath())) {
|
||||
scenario.setApiScenarioModuleId("root");
|
||||
scenario.setModulePath("/默认模块");
|
||||
}
|
||||
return scenario;
|
||||
}
|
||||
|
||||
|
@ -269,7 +281,7 @@ public class ApiAutomationService {
|
|||
}
|
||||
|
||||
private void deleteApiScenarioReport(List<String> scenarioIds) {
|
||||
if(scenarioIds == null || scenarioIds.isEmpty()){
|
||||
if (scenarioIds == null || scenarioIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ApiScenarioReportExample scenarioReportExample = new ApiScenarioReportExample();
|
||||
|
@ -371,6 +383,9 @@ public class ApiAutomationService {
|
|||
|
||||
public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID) {
|
||||
APIScenarioReportResult report = new APIScenarioReportResult();
|
||||
if (triggerMode.equals(ApiRunMode.SCENARIO.name()) || triggerMode.equals(ApiRunMode.DEFINITION.name())) {
|
||||
triggerMode = ReportTriggerMode.MANUAL.name();
|
||||
}
|
||||
report.setId(id);
|
||||
report.setTestId(id);
|
||||
if (StringUtils.isNotEmpty(scenarioName)) {
|
||||
|
@ -998,4 +1013,18 @@ public class ApiAutomationService {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void removeToGcByBatch(ApiScenarioBatchRequest request) {
|
||||
ServiceUtils.getSelectAllIds(request, request.getCondition(),
|
||||
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
|
||||
|
||||
this.removeToGc(request.getIds());
|
||||
}
|
||||
|
||||
public void deleteBatchByCondition(ApiScenarioBatchRequest request) {
|
||||
ServiceUtils.getSelectAllIds(request, request.getCondition(),
|
||||
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
|
||||
|
||||
this.deleteBatch(request.getIds());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,7 @@ package io.metersphere.api.service;
|
|||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
|
||||
import io.metersphere.api.jmeter.TestResult;
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResultExample;
|
||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
||||
import io.metersphere.base.domain.TestPlanApiCase;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
|
||||
import io.metersphere.base.mapper.ApiTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
|
||||
|
@ -43,7 +40,7 @@ public class ApiDefinitionExecResultService {
|
|||
@Resource
|
||||
private ApiTestCaseMapper apiTestCaseMapper;
|
||||
@Resource
|
||||
private TestCaseReviewApiCaseService testCaseReviewApiCaseService;
|
||||
private TestCaseReviewApiCaseService testCaseReviewApiCaseService;
|
||||
|
||||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
|
@ -73,6 +70,13 @@ public class ApiDefinitionExecResultService {
|
|||
testCaseReviewApiCaseService.setExecResult(item.getName(), status);
|
||||
|
||||
}
|
||||
|
||||
// 清空上次执行结果的内容,只保留当前最新一条内容
|
||||
ApiDefinitionExecResult prevResult = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(item.getName(), type);
|
||||
if (prevResult != null) {
|
||||
prevResult.setContent(null);
|
||||
definitionExecResultMapper.updateByPrimaryKeyWithBLOBs(prevResult);
|
||||
}
|
||||
// 更新用例最后执行结果
|
||||
ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = new ApiTestCaseWithBLOBs();
|
||||
apiTestCaseWithBLOBs.setId(saveResult.getResourceId());
|
||||
|
@ -94,7 +98,7 @@ public class ApiDefinitionExecResultService {
|
|||
*/
|
||||
public void saveApiResultByScheduleTask(TestResult result, String type) {
|
||||
String saveResultType = type;
|
||||
if(StringUtils.equalsAny(ApiRunMode.SCHEDULE_API_PLAN.name(),saveResultType)){
|
||||
if (StringUtils.equalsAny(ApiRunMode.SCHEDULE_API_PLAN.name(), saveResultType)) {
|
||||
saveResultType = ApiRunMode.API_PLAN.name();
|
||||
}
|
||||
|
||||
|
@ -127,6 +131,12 @@ public class ApiDefinitionExecResultService {
|
|||
}
|
||||
|
||||
saveResult.setUserId(userID);
|
||||
// 前一条数据内容清空
|
||||
ApiDefinitionExecResult prevResult = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(item.getName(), finalSaveResultType);
|
||||
if (prevResult != null) {
|
||||
prevResult.setContent(null);
|
||||
apiDefinitionExecResultMapper.updateByPrimaryKeyWithBLOBs(prevResult);
|
||||
}
|
||||
apiDefinitionExecResultMapper.insert(saveResult);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -262,7 +262,10 @@ public class ApiDefinitionService {
|
|||
test.setEnvironmentId(request.getEnvironmentId());
|
||||
test.setUserId(request.getUserId());
|
||||
test.setTags(request.getTags());
|
||||
|
||||
if (StringUtils.isEmpty(request.getModulePath()) || StringUtils.isEmpty(request.getModuleId())) {
|
||||
test.setModulePath("/默认模块");
|
||||
test.setModuleId("root");
|
||||
}
|
||||
apiDefinitionMapper.updateByPrimaryKeySelective(test);
|
||||
return test;
|
||||
}
|
||||
|
@ -279,7 +282,6 @@ public class ApiDefinitionService {
|
|||
test.setProtocol(request.getProtocol());
|
||||
test.setMethod(request.getMethod());
|
||||
test.setPath(request.getPath());
|
||||
test.setModuleId(request.getModuleId());
|
||||
test.setProjectId(request.getProjectId());
|
||||
request.getRequest().setId(request.getId());
|
||||
test.setRequest(JSONObject.toJSONString(request.getRequest()));
|
||||
|
@ -287,6 +289,11 @@ public class ApiDefinitionService {
|
|||
test.setUpdateTime(System.currentTimeMillis());
|
||||
test.setStatus(APITestStatus.Underway.name());
|
||||
test.setModulePath(request.getModulePath());
|
||||
test.setModuleId(request.getModuleId());
|
||||
if (StringUtils.isEmpty(request.getModulePath()) || StringUtils.isEmpty(request.getModuleId())) {
|
||||
test.setModulePath("/默认模块");
|
||||
test.setModuleId("root");
|
||||
}
|
||||
test.setResponse(JSONObject.toJSONString(request.getResponse()));
|
||||
test.setEnvironmentId(request.getEnvironmentId());
|
||||
test.setNum(getNextNum(request.getProjectId()));
|
||||
|
@ -344,13 +351,13 @@ public class ApiDefinitionService {
|
|||
private void _importCreate(List<ApiDefinition> sameRequest, ApiDefinitionMapper batchMapper, ApiDefinitionWithBLOBs apiDefinition,
|
||||
ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List<ApiTestCaseWithBLOBs> cases) {
|
||||
if (CollectionUtils.isEmpty(sameRequest)) {
|
||||
if(StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(),RequestType.HTTP)){
|
||||
if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestType.HTTP)) {
|
||||
String request = setImportHashTree(apiDefinition);
|
||||
batchMapper.insert(apiDefinition);
|
||||
apiDefinition.setRequest(request);
|
||||
importApiCase(apiDefinition, apiTestCaseMapper, apiTestImportRequest, true);
|
||||
}else{
|
||||
if(StringUtils.equalsAnyIgnoreCase(apiDefinition.getProtocol(),RequestType.TCP)){
|
||||
} else {
|
||||
if (StringUtils.equalsAnyIgnoreCase(apiDefinition.getProtocol(), RequestType.TCP)) {
|
||||
String request = setImportTCPHashTree(apiDefinition);
|
||||
}
|
||||
batchMapper.insert(apiDefinition);
|
||||
|
@ -358,7 +365,7 @@ public class ApiDefinitionService {
|
|||
|
||||
} else {
|
||||
String originId = apiDefinition.getId();
|
||||
if(StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(),RequestType.HTTP)){
|
||||
if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), RequestType.HTTP)) {
|
||||
//如果存在则修改
|
||||
apiDefinition.setId(sameRequest.get(0).getId());
|
||||
String request = setImportHashTree(apiDefinition);
|
||||
|
@ -373,9 +380,9 @@ public class ApiDefinitionService {
|
|||
}
|
||||
});
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
apiDefinition.setId(sameRequest.get(0).getId());
|
||||
if(StringUtils.equalsAnyIgnoreCase(apiDefinition.getProtocol(),RequestType.TCP)){
|
||||
if (StringUtils.equalsAnyIgnoreCase(apiDefinition.getProtocol(), RequestType.TCP)) {
|
||||
String request = setImportTCPHashTree(apiDefinition);
|
||||
}
|
||||
apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition);
|
||||
|
@ -392,6 +399,7 @@ public class ApiDefinitionService {
|
|||
apiDefinition.setRequest(JSONObject.toJSONString(msHTTPSamplerProxy));
|
||||
return request;
|
||||
}
|
||||
|
||||
private String setImportTCPHashTree(ApiDefinitionWithBLOBs apiDefinition) {
|
||||
String request = apiDefinition.getRequest();
|
||||
MsTCPSampler tcpSampler = JSONObject.parseObject(request, MsTCPSampler.class);
|
||||
|
@ -411,7 +419,7 @@ public class ApiDefinitionService {
|
|||
if (CollectionUtils.isNotEmpty(cases)) {
|
||||
int batchCount = 0;
|
||||
cases.forEach(item -> {
|
||||
if(!existCaseName.contains(item.getName())) {
|
||||
if (!existCaseName.contains(item.getName())) {
|
||||
item.setId(UUID.randomUUID().toString());
|
||||
item.setCreateTime(System.currentTimeMillis());
|
||||
item.setUpdateTime(System.currentTimeMillis());
|
||||
|
@ -509,7 +517,7 @@ public class ApiDefinitionService {
|
|||
}
|
||||
|
||||
public void addResult(TestResult res) {
|
||||
if (!res.getScenarios().isEmpty() && !res.getScenarios().get(0).getRequestResults().isEmpty()) {
|
||||
if (res != null && CollectionUtils.isNotEmpty(res.getScenarios()) && res.getScenarios().get(0) != null && CollectionUtils.isNotEmpty(res.getScenarios().get(0).getRequestResults())) {
|
||||
RequestResult result = res.getScenarios().get(0).getRequestResults().get(0);
|
||||
if (result.getName().indexOf("<->") != -1) {
|
||||
result.setName(result.getName().substring(0, result.getName().indexOf("<->")));
|
||||
|
@ -596,21 +604,25 @@ public class ApiDefinitionService {
|
|||
}
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
ApiDefinitionWithBLOBs item = data.get(i);
|
||||
if (StringUtils.isEmpty(item.getModuleId()) || StringUtils.isEmpty(item.getModulePath())) {
|
||||
item.setModuleId("root");
|
||||
item.setModulePath("/默认模块");
|
||||
}
|
||||
if (item.getName().length() > 255) {
|
||||
item.setName(item.getName().substring(0, 255));
|
||||
}
|
||||
item.setNum(num++);
|
||||
//如果EsbData需要存储,则需要进行接口是否更新的判断
|
||||
if(apiImport.getEsbApiParamsMap()!= null){
|
||||
if (apiImport.getEsbApiParamsMap() != null) {
|
||||
String apiId = item.getId();
|
||||
EsbApiParamsWithBLOBs model = apiImport.getEsbApiParamsMap().get(apiId);
|
||||
importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases());
|
||||
if(model!=null){
|
||||
if (model != null) {
|
||||
apiImport.getEsbApiParamsMap().remove(apiId);
|
||||
model.setResourceId(item.getId());
|
||||
apiImport.getEsbApiParamsMap().put(item.getId(),model);
|
||||
apiImport.getEsbApiParamsMap().put(item.getId(), model);
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases());
|
||||
}
|
||||
if (i % 300 == 0) {
|
||||
|
@ -618,15 +630,15 @@ public class ApiDefinitionService {
|
|||
}
|
||||
}
|
||||
//判断EsbData是否需要存储
|
||||
if(apiImport.getEsbApiParamsMap()!= null && apiImport.getEsbApiParamsMap().size() > 0){
|
||||
if (apiImport.getEsbApiParamsMap() != null && apiImport.getEsbApiParamsMap().size() > 0) {
|
||||
EsbApiParamsMapper esbApiParamsMapper = sqlSession.getMapper(EsbApiParamsMapper.class);
|
||||
for (EsbApiParamsWithBLOBs model : apiImport.getEsbApiParamsMap().values()) {
|
||||
EsbApiParamsExample example = new EsbApiParamsExample();
|
||||
example.createCriteria().andResourceIdEqualTo(model.getResourceId());
|
||||
List<EsbApiParamsWithBLOBs> exiteModelList = esbApiParamsMapper.selectByExampleWithBLOBs(example);
|
||||
if(exiteModelList.isEmpty()){
|
||||
if (exiteModelList.isEmpty()) {
|
||||
esbApiParamsMapper.insert(model);
|
||||
}else{
|
||||
} else {
|
||||
model.setId(exiteModelList.get(0).getId());
|
||||
esbApiParamsMapper.updateByPrimaryKeyWithBLOBs(model);
|
||||
}
|
||||
|
|
|
@ -86,216 +86,240 @@ public class ApiDocumentService {
|
|||
apiInfoDTO.setStatus(apiModel.getStatus());
|
||||
|
||||
if (apiModel.getRequest() != null) {
|
||||
JSONObject requestJsonObj = JSONObject.parseObject(apiModel.getRequest());
|
||||
//head赋值conversionModelToDTO
|
||||
if (requestJsonObj.containsKey("headers")) {
|
||||
JSONArray requestHeadDataArr = new JSONArray();
|
||||
//head赋值
|
||||
JSONArray headArr = requestJsonObj.getJSONArray("headers");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
requestHeadDataArr.add(headObj);
|
||||
JSONObject requestObj = this.genJSONObject(apiModel.getRequest());
|
||||
if(requestObj!=null){
|
||||
if (requestObj.containsKey("headers")) {
|
||||
JSONArray requestHeadDataArr = new JSONArray();
|
||||
//head赋值
|
||||
JSONArray headArr = requestObj.getJSONArray("headers");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
requestHeadDataArr.add(headObj);
|
||||
}
|
||||
}
|
||||
apiInfoDTO.setRequestHead(requestHeadDataArr.toJSONString());
|
||||
}
|
||||
apiInfoDTO.setRequestHead(requestHeadDataArr.toJSONString());
|
||||
}
|
||||
//url参数赋值
|
||||
JSONArray urlParamArr = new JSONArray();
|
||||
if (requestJsonObj.containsKey("arguments")) {
|
||||
//urlParam -- query赋值
|
||||
JSONArray headArr = requestJsonObj.getJSONArray("arguments");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
urlParamArr.add(headObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (requestJsonObj.containsKey("rest")) {
|
||||
//urlParam -- rest赋值
|
||||
JSONArray headArr = requestJsonObj.getJSONArray("rest");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
urlParamArr.add(headObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
apiInfoDTO.setUrlParams(urlParamArr.toJSONString());
|
||||
//请求体参数类型
|
||||
if (requestJsonObj.containsKey("body")) {
|
||||
JSONObject bodyObj = requestJsonObj.getJSONObject("body");
|
||||
if (bodyObj.containsKey("type")) {
|
||||
String type = bodyObj.getString("type");
|
||||
if (StringUtils.equals(type, "WWW_FORM")) {
|
||||
apiInfoDTO.setRequestBodyParamType("x-www-from-urlencoded");
|
||||
} else if (StringUtils.equals(type, "Form Data")) {
|
||||
apiInfoDTO.setRequestBodyParamType("form-data");
|
||||
} else {
|
||||
apiInfoDTO.setRequestBodyParamType(type);
|
||||
}
|
||||
//url参数赋值
|
||||
JSONArray urlParamArr = new JSONArray();
|
||||
if (requestObj.containsKey("arguments")) {
|
||||
try{
|
||||
JSONArray headArr = requestObj.getJSONArray("arguments");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
|
||||
if (StringUtils.equals(type, "JSON")) {
|
||||
//判断是否是JsonSchema
|
||||
boolean isJsonSchema = false;
|
||||
if (bodyObj.containsKey("format")) {
|
||||
String foramtValue = String.valueOf(bodyObj.get("format"));
|
||||
if (StringUtils.equals("JSON-SCHEMA", foramtValue)) {
|
||||
isJsonSchema = true;
|
||||
}
|
||||
}
|
||||
if (isJsonSchema) {
|
||||
apiInfoDTO.setRequestBodyParamType("JSON-SCHEMA");
|
||||
apiInfoDTO.setJsonSchemaBody(bodyObj);
|
||||
} else {
|
||||
if (bodyObj.containsKey("raw")) {
|
||||
String raw = bodyObj.getString("raw");
|
||||
apiInfoDTO.setRequestBodyStrutureData(raw);
|
||||
//转化jsonObje 或者 jsonArray
|
||||
this.setPreviewData(previewJsonArray, raw);
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.equalsAny(type, "XML", "Raw")) {
|
||||
if (bodyObj.containsKey("raw")) {
|
||||
String raw = bodyObj.getString("raw");
|
||||
apiInfoDTO.setRequestBodyStrutureData(raw);
|
||||
JSONObject previewObj = JSONObject.parseObject(raw);
|
||||
this.setPreviewData(previewJsonArray, raw);
|
||||
}
|
||||
} else if (StringUtils.equalsAny(type, "Form Data", "WWW_FORM")) {
|
||||
if (bodyObj.containsKey("kvs")) {
|
||||
JSONArray bodyParamArr = new JSONArray();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("kvs");
|
||||
Map<String, String> previewObjMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("name") && kv.containsKey("value")) {
|
||||
bodyParamArr.add(kv);
|
||||
previewObjMap.put(String.valueOf(kv.get("name")), String.valueOf(kv.get("value")));
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
urlParamArr.add(headObj);
|
||||
}
|
||||
}
|
||||
this.setPreviewData(previewJsonArray, JSONObject.toJSONString(previewObjMap));
|
||||
apiInfoDTO.setRequestBodyFormData(bodyParamArr.toJSONString());
|
||||
}
|
||||
} else if (StringUtils.equals(type, "BINARY")) {
|
||||
if (bodyObj.containsKey("binary")) {
|
||||
List<Map<String, String>> bodyParamList = new ArrayList<>();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("binary");
|
||||
}catch (Exception e){
|
||||
}
|
||||
}
|
||||
if (requestObj.containsKey("rest")) {
|
||||
try{
|
||||
//urlParam -- rest赋值
|
||||
JSONArray headArr = requestObj.getJSONArray("rest");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
urlParamArr.add(headObj);
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
}
|
||||
}
|
||||
apiInfoDTO.setUrlParams(urlParamArr.toJSONString());
|
||||
//请求体参数类型
|
||||
if (requestObj.containsKey("body")) {
|
||||
try{
|
||||
JSONObject bodyObj = requestObj.getJSONObject("body");
|
||||
if (bodyObj.containsKey("type")) {
|
||||
String type = bodyObj.getString("type");
|
||||
if (StringUtils.equals(type, "WWW_FORM")) {
|
||||
apiInfoDTO.setRequestBodyParamType("x-www-from-urlencoded");
|
||||
} else if (StringUtils.equals(type, "Form Data")) {
|
||||
apiInfoDTO.setRequestBodyParamType("form-data");
|
||||
} else {
|
||||
apiInfoDTO.setRequestBodyParamType(type);
|
||||
}
|
||||
|
||||
Map<String, String> previewObjMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("description") && kv.containsKey("files")) {
|
||||
Map<String, String> bodyMap = new HashMap<>();
|
||||
String name = kv.getString("description");
|
||||
JSONArray fileArr = kv.getJSONArray("files");
|
||||
String value = "";
|
||||
for (int j = 0; j < fileArr.size(); j++) {
|
||||
JSONObject fileObj = fileArr.getJSONObject(j);
|
||||
if (fileObj.containsKey("name")) {
|
||||
value += fileObj.getString("name") + " ;";
|
||||
if (StringUtils.equals(type, "JSON")) {
|
||||
//判断是否是JsonSchema
|
||||
boolean isJsonSchema = false;
|
||||
if (bodyObj.containsKey("format")) {
|
||||
String foramtValue = String.valueOf(bodyObj.get("format"));
|
||||
if (StringUtils.equals("JSON-SCHEMA", foramtValue)) {
|
||||
isJsonSchema = true;
|
||||
}
|
||||
}
|
||||
if (isJsonSchema) {
|
||||
apiInfoDTO.setRequestBodyParamType("JSON-SCHEMA");
|
||||
apiInfoDTO.setJsonSchemaBody(bodyObj);
|
||||
} else {
|
||||
if (bodyObj.containsKey("raw")) {
|
||||
String raw = bodyObj.getString("raw");
|
||||
apiInfoDTO.setRequestBodyStrutureData(raw);
|
||||
//转化jsonObje 或者 jsonArray
|
||||
this.setPreviewData(previewJsonArray, raw);
|
||||
}
|
||||
}
|
||||
} else if (StringUtils.equalsAny(type, "XML", "Raw")) {
|
||||
if (bodyObj.containsKey("raw")) {
|
||||
String raw = bodyObj.getString("raw");
|
||||
apiInfoDTO.setRequestBodyStrutureData(raw);
|
||||
this.setPreviewData(previewJsonArray, raw);
|
||||
}
|
||||
} else if (StringUtils.equalsAny(type, "Form Data", "WWW_FORM")) {
|
||||
if (bodyObj.containsKey("kvs")) {
|
||||
JSONArray bodyParamArr = new JSONArray();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("kvs");
|
||||
Map<String, String> previewObjMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("name") && kv.containsKey("value")) {
|
||||
bodyParamArr.add(kv);
|
||||
previewObjMap.put(String.valueOf(kv.get("name")), String.valueOf(kv.get("value")));
|
||||
}
|
||||
}
|
||||
bodyMap.put("name", name);
|
||||
bodyMap.put("value", value);
|
||||
bodyMap.put("contentType", "File");
|
||||
bodyParamList.add(bodyMap);
|
||||
this.setPreviewData(previewJsonArray, JSONObject.toJSONString(previewObjMap));
|
||||
apiInfoDTO.setRequestBodyFormData(bodyParamArr.toJSONString());
|
||||
}
|
||||
} else if (StringUtils.equals(type, "BINARY")) {
|
||||
if (bodyObj.containsKey("binary")) {
|
||||
List<Map<String, String>> bodyParamList = new ArrayList<>();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("binary");
|
||||
|
||||
previewObjMap.put(String.valueOf(name), String.valueOf(value));
|
||||
Map<String, String> previewObjMap = new LinkedHashMap<>();
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("description") && kv.containsKey("files")) {
|
||||
Map<String, String> bodyMap = new HashMap<>();
|
||||
String name = kv.getString("description");
|
||||
JSONArray fileArr = kv.getJSONArray("files");
|
||||
String value = "";
|
||||
for (int j = 0; j < fileArr.size(); j++) {
|
||||
JSONObject fileObj = fileArr.getJSONObject(j);
|
||||
if (fileObj.containsKey("name")) {
|
||||
value += fileObj.getString("name") + " ;";
|
||||
}
|
||||
}
|
||||
bodyMap.put("name", name);
|
||||
bodyMap.put("value", value);
|
||||
bodyMap.put("contentType", "File");
|
||||
bodyParamList.add(bodyMap);
|
||||
|
||||
previewObjMap.put(String.valueOf(name), String.valueOf(value));
|
||||
|
||||
}
|
||||
}
|
||||
this.setPreviewData(previewJsonArray, JSONObject.toJSONString(previewObjMap));
|
||||
apiInfoDTO.setRequestBodyFormData(JSONArray.toJSONString(bodyParamList));
|
||||
}
|
||||
}
|
||||
this.setPreviewData(previewJsonArray, JSONObject.toJSONString(previewObjMap));
|
||||
apiInfoDTO.setRequestBodyFormData(JSONArray.toJSONString(bodyParamList));
|
||||
}
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//赋值响应头
|
||||
if (apiModel.getResponse() != null) {
|
||||
JSONObject responseJsonObj = JSONObject.parseObject(apiModel.getResponse());
|
||||
JSONObject responseJsonObj = this.genJSONObject(apiModel.getResponse());
|
||||
if (responseJsonObj!=null && responseJsonObj.containsKey("headers")) {
|
||||
JSONArray responseHeadDataArr = new JSONArray();
|
||||
JSONArray headArr = responseJsonObj.getJSONArray("headers");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
responseHeadDataArr.add(headObj);
|
||||
try{
|
||||
JSONArray responseHeadDataArr = new JSONArray();
|
||||
JSONArray headArr = responseJsonObj.getJSONArray("headers");
|
||||
for (int index = 0; index < headArr.size(); index++) {
|
||||
JSONObject headObj = headArr.getJSONObject(index);
|
||||
if (headObj.containsKey("name") && headObj.containsKey("value")) {
|
||||
responseHeadDataArr.add(headObj);
|
||||
}
|
||||
}
|
||||
apiInfoDTO.setResponseHead(responseHeadDataArr.toJSONString());
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
apiInfoDTO.setResponseHead(responseHeadDataArr.toJSONString());
|
||||
}
|
||||
// 赋值响应体
|
||||
if (responseJsonObj!=null && responseJsonObj.containsKey("body")) {
|
||||
JSONObject bodyObj = responseJsonObj.getJSONObject("body");
|
||||
if (bodyObj.containsKey("type")) {
|
||||
String type = bodyObj.getString("type");
|
||||
if (StringUtils.equals(type, "WWW_FORM")) {
|
||||
apiInfoDTO.setResponseBodyParamType("x-www-from-urlencoded");
|
||||
} else if (StringUtils.equals(type, "Form Data")) {
|
||||
apiInfoDTO.setResponseBodyParamType("form-data");
|
||||
} else {
|
||||
apiInfoDTO.setResponseBodyParamType(type);
|
||||
}
|
||||
if (StringUtils.equalsAny(type, "JSON", "XML", "Raw")) {
|
||||
if (bodyObj.containsKey("raw")) {
|
||||
String raw = bodyObj.getString("raw");
|
||||
apiInfoDTO.setResponseBodyStrutureData(raw);
|
||||
try {
|
||||
JSONObject bodyObj = responseJsonObj.getJSONObject("body");
|
||||
if (bodyObj.containsKey("type")) {
|
||||
String type = bodyObj.getString("type");
|
||||
if (StringUtils.equals(type, "WWW_FORM")) {
|
||||
apiInfoDTO.setResponseBodyParamType("x-www-from-urlencoded");
|
||||
} else if (StringUtils.equals(type, "Form Data")) {
|
||||
apiInfoDTO.setResponseBodyParamType("form-data");
|
||||
} else {
|
||||
apiInfoDTO.setResponseBodyParamType(type);
|
||||
}
|
||||
} else if (StringUtils.equalsAny(type, "Form Data", "WWW_FORM")) {
|
||||
if (bodyObj.containsKey("kvs")) {
|
||||
JSONArray bodyParamArr = new JSONArray();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("kvs");
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("name")) {
|
||||
bodyParamArr.add(kv);
|
||||
}
|
||||
if (StringUtils.equalsAny(type, "JSON", "XML", "Raw")) {
|
||||
if (bodyObj.containsKey("raw")) {
|
||||
String raw = bodyObj.getString("raw");
|
||||
apiInfoDTO.setResponseBodyStrutureData(raw);
|
||||
}
|
||||
apiInfoDTO.setResponseBodyFormData(bodyParamArr.toJSONString());
|
||||
}
|
||||
} else if (StringUtils.equals(type, "BINARY")) {
|
||||
if (bodyObj.containsKey("binary")) {
|
||||
List<Map<String, String>> bodyParamList = new ArrayList<>();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("kvs");
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("description") && kv.containsKey("files")) {
|
||||
Map<String, String> bodyMap = new HashMap<>();
|
||||
|
||||
String name = kv.getString("description");
|
||||
JSONArray fileArr = kv.getJSONArray("files");
|
||||
String value = "";
|
||||
for (int j = 0; j < fileArr.size(); j++) {
|
||||
JSONObject fileObj = fileArr.getJSONObject(j);
|
||||
if (fileObj.containsKey("name")) {
|
||||
value += fileObj.getString("name") + " ;";
|
||||
}
|
||||
} else if (StringUtils.equalsAny(type, "Form Data", "WWW_FORM")) {
|
||||
if (bodyObj.containsKey("kvs")) {
|
||||
JSONArray bodyParamArr = new JSONArray();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("kvs");
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("name")) {
|
||||
bodyParamArr.add(kv);
|
||||
}
|
||||
bodyMap.put("name", name);
|
||||
bodyMap.put("value", value);
|
||||
bodyParamList.add(bodyMap);
|
||||
}
|
||||
apiInfoDTO.setResponseBodyFormData(bodyParamArr.toJSONString());
|
||||
}
|
||||
} else if (StringUtils.equals(type, "BINARY")) {
|
||||
if (bodyObj.containsKey("binary")) {
|
||||
List<Map<String, String>> bodyParamList = new ArrayList<>();
|
||||
JSONArray kvsArr = bodyObj.getJSONArray("kvs");
|
||||
for (int i = 0; i < kvsArr.size(); i++) {
|
||||
JSONObject kv = kvsArr.getJSONObject(i);
|
||||
if (kv.containsKey("description") && kv.containsKey("files")) {
|
||||
Map<String, String> bodyMap = new HashMap<>();
|
||||
|
||||
String name = kv.getString("description");
|
||||
JSONArray fileArr = kv.getJSONArray("files");
|
||||
String value = "";
|
||||
for (int j = 0; j < fileArr.size(); j++) {
|
||||
JSONObject fileObj = fileArr.getJSONObject(j);
|
||||
if (fileObj.containsKey("name")) {
|
||||
value += fileObj.getString("name") + " ;";
|
||||
}
|
||||
}
|
||||
bodyMap.put("name", name);
|
||||
bodyMap.put("value", value);
|
||||
bodyParamList.add(bodyMap);
|
||||
}
|
||||
}
|
||||
apiInfoDTO.setResponseBodyFormData(JSONArray.toJSONString(bodyParamList));
|
||||
}
|
||||
apiInfoDTO.setResponseBodyFormData(JSONArray.toJSONString(bodyParamList));
|
||||
}
|
||||
}
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// 赋值响应码
|
||||
if (responseJsonObj!=null && responseJsonObj.containsKey("statusCode")) {
|
||||
JSONArray responseStatusDataArr = new JSONArray();
|
||||
JSONArray statusArr = responseJsonObj.getJSONArray("statusCode");
|
||||
for (int index = 0; index < statusArr.size(); index++) {
|
||||
JSONObject statusObj = statusArr.getJSONObject(index);
|
||||
if (statusObj.containsKey("name") && statusObj.containsKey("value")) {
|
||||
responseStatusDataArr.add(statusObj);
|
||||
try {
|
||||
JSONArray responseStatusDataArr = new JSONArray();
|
||||
JSONArray statusArr = responseJsonObj.getJSONArray("statusCode");
|
||||
for (int index = 0; index < statusArr.size(); index++) {
|
||||
JSONObject statusObj = statusArr.getJSONObject(index);
|
||||
if (statusObj.containsKey("name") && statusObj.containsKey("value")) {
|
||||
responseStatusDataArr.add(statusObj);
|
||||
}
|
||||
}
|
||||
apiInfoDTO.setResponseCode(responseStatusDataArr.toJSONString());
|
||||
}catch (Exception e){
|
||||
|
||||
}
|
||||
apiInfoDTO.setResponseCode(responseStatusDataArr.toJSONString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -304,6 +328,15 @@ public class ApiDocumentService {
|
|||
return apiInfoDTO;
|
||||
}
|
||||
|
||||
private JSONObject genJSONObject(String request) {
|
||||
JSONObject returnObj = null;
|
||||
try{
|
||||
returnObj = JSONObject.parseObject(request);
|
||||
}catch (Exception e){
|
||||
}
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
private void setPreviewData(JSONArray previewArray, String data) {
|
||||
try {
|
||||
JSONObject previewObj = JSONObject.parseObject(data);
|
||||
|
|
|
@ -253,6 +253,13 @@ public class ApiScenarioReportService {
|
|||
String status = "Success";
|
||||
report.setStatus(status);
|
||||
scenarioReportMapper.updateByPrimaryKeySelective(report);
|
||||
// 把上一条调试的数据内容清空
|
||||
ApiScenarioReport prevResult = extApiScenarioReportMapper.selectPreviousReportByScenarioId(report.getScenarioId(), reportId);
|
||||
if (prevResult != null) {
|
||||
ApiScenarioReportDetailExample example = new ApiScenarioReportDetailExample();
|
||||
example.createCriteria().andReportIdEqualTo(prevResult.getId());
|
||||
apiScenarioReportDetailMapper.deleteByExample(example);
|
||||
}
|
||||
});
|
||||
sqlSession.flushStatements();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import io.metersphere.api.jmeter.JMeterService;
|
|||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.*;
|
||||
import io.metersphere.commons.constants.ApiRunMode;
|
||||
import io.metersphere.commons.constants.TestPlanStatus;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.*;
|
||||
|
@ -551,7 +552,14 @@ public class ApiTestCaseService {
|
|||
}
|
||||
|
||||
public String run(RunCaseRequest request) {
|
||||
ApiTestCaseWithBLOBs testCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId());
|
||||
ApiTestCaseWithBLOBs testCaseWithBLOBs=new ApiTestCaseWithBLOBs();
|
||||
if(StringUtils.equals(request.getRunMode(), ApiRunMode.JENKINS_API_PLAN.name())){
|
||||
testCaseWithBLOBs= apiTestCaseMapper.selectByPrimaryKey(request.getReportId());
|
||||
request.setCaseId(request.getReportId());
|
||||
}else{
|
||||
testCaseWithBLOBs= apiTestCaseMapper.selectByPrimaryKey(request.getCaseId());
|
||||
|
||||
}
|
||||
// 多态JSON普通转换会丢失内容,需要通过 ObjectMapper 获取
|
||||
if (testCaseWithBLOBs != null && StringUtils.isNotEmpty(testCaseWithBLOBs.getRequest())) {
|
||||
try {
|
||||
|
|
|
@ -3,11 +3,13 @@ package io.metersphere.api.service;
|
|||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.automation.EsbDataStruct;
|
||||
import io.metersphere.api.dto.automation.SaveApiScenarioRequest;
|
||||
import io.metersphere.api.dto.automation.parse.EsbDataParser;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionResult;
|
||||
import io.metersphere.api.dto.definition.ApiTestCaseResult;
|
||||
import io.metersphere.api.dto.definition.SaveApiDefinitionRequest;
|
||||
import io.metersphere.api.dto.definition.SaveApiTestCaseRequest;
|
||||
import io.metersphere.api.dto.definition.request.MsTestElement;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
||||
|
@ -313,6 +315,35 @@ public class EsbApiParamService {
|
|||
return keyValueList;
|
||||
}
|
||||
|
||||
private List<KeyValue> genKeyValueListByDataStruct(MsTCPSampler tcpSampler, List<EsbDataStruct> dataStructRequestList) {
|
||||
List<KeyValue> keyValueList = new ArrayList<>();
|
||||
String sendRequest = tcpSampler.getRequest();
|
||||
String paramRegexStr = "\\$\\{([^}]*)\\}";
|
||||
try {
|
||||
if (StringUtils.isNotEmpty(sendRequest)) {
|
||||
List<String> paramList = new ArrayList<>();
|
||||
Pattern regex = Pattern.compile(paramRegexStr);
|
||||
Matcher matcher = regex.matcher(sendRequest);
|
||||
while (matcher.find()) {
|
||||
paramList.add(matcher.group(1));
|
||||
}
|
||||
for (String param : paramList) {
|
||||
String value = this.genValueFromEsbDataStructByParam(dataStructRequestList, param);
|
||||
if (StringUtils.isNotEmpty(value)) {
|
||||
KeyValue kv = new KeyValue();
|
||||
kv.setName(param);
|
||||
kv.setValue(value);
|
||||
kv.setRequired(true);
|
||||
keyValueList.add(kv);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return keyValueList;
|
||||
}
|
||||
|
||||
//通过报文模版中的变量参数,解析报文数据结构,生成对应的xml数据
|
||||
private String genValueFromEsbDataStructByParam(List<EsbDataStruct> dataStructRequestList, String param) {
|
||||
String returnValue = "";
|
||||
|
@ -341,10 +372,32 @@ public class EsbApiParamService {
|
|||
return request;
|
||||
}
|
||||
|
||||
public void handleEsbRequest(MsTCPSampler tcpSampler) {
|
||||
try {
|
||||
//修改reqeust.parameters, 将树结构类型数据转化为表格类型数据,供执行时参数的提取
|
||||
if (tcpSampler.getEsbDataStruct() != null ) {
|
||||
List<KeyValue> keyValueList = this.genKeyValueListByDataStruct(tcpSampler, tcpSampler.getEsbDataStruct());
|
||||
tcpSampler.setParameters(keyValueList);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteByResourceIdIn(List<String> apiIds) {
|
||||
EsbApiParamsExample example = new EsbApiParamsExample();
|
||||
example.createCriteria().andResourceIdIn(apiIds);
|
||||
esbApiParamsMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
public void checkScenarioRequests(SaveApiScenarioRequest request) {
|
||||
if(request.getScenarioDefinition() != null ){
|
||||
List<MsTestElement> hashTreeList = request.getScenarioDefinition().getHashTree();
|
||||
for (MsTestElement testElement :hashTreeList) {
|
||||
if(testElement instanceof MsTCPSampler){
|
||||
this.handleEsbRequest((MsTCPSampler)testElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,12 +76,17 @@ public class HistoricalDataUpgradeService {
|
|||
return scenario;
|
||||
}
|
||||
|
||||
private MsScenario createScenario(Scenario oldScenario) {
|
||||
private MsScenario createScenario(Scenario oldScenario, String projectId) {
|
||||
MsScenario scenario = new MsScenario();
|
||||
scenario.setOldVariables(oldScenario.getVariables());
|
||||
scenario.setName(oldScenario.getName());
|
||||
scenario.setEnableCookieShare(oldScenario.isEnableCookieShare());
|
||||
scenario.setEnvironmentId(oldScenario.getEnvironmentId());
|
||||
if (StringUtils.isNotEmpty(oldScenario.getEnvironmentId())) {
|
||||
HashMap<String, String> envMap = new HashMap<>();
|
||||
envMap.put(projectId, oldScenario.getEnvironmentId());
|
||||
scenario.setEnvironmentMap(envMap);
|
||||
}
|
||||
scenario.setReferenced("Upgrade");
|
||||
scenario.setId(oldScenario.getId());
|
||||
scenario.setResourceId(UUID.randomUUID().toString());
|
||||
|
@ -397,6 +402,7 @@ public class HistoricalDataUpgradeService {
|
|||
MsScenario scenarioTest = createScenarioByTest(test);
|
||||
LinkedList<MsTestElement> listSteps = new LinkedList<>();
|
||||
List<Scenario> scenarios = JSON.parseArray(test.getScenarioDefinition(), Scenario.class);
|
||||
String envId = null;
|
||||
if (CollectionUtils.isNotEmpty(scenarios)) {
|
||||
// 批量处理
|
||||
for (Scenario scenario : scenarios) {
|
||||
|
@ -405,7 +411,7 @@ public class HistoricalDataUpgradeService {
|
|||
}
|
||||
scenario.setId(test.getId() + "=" + scenario.getId());
|
||||
scenario.setName(test.getName() + "_" + scenario.getName());
|
||||
MsScenario scenario1 = createScenario(scenario);
|
||||
MsScenario scenario1 = createScenario(scenario, saveHistoricalDataUpgrade.getProjectId());
|
||||
String scenarioDefinition = JSON.toJSONString(scenario1);
|
||||
num++;
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario.getId(), scenario.getName(), scenario.getRequests().size(), scenarioDefinition, mapper, num);
|
||||
|
@ -417,10 +423,20 @@ public class HistoricalDataUpgradeService {
|
|||
step.setResourceId(UUID.randomUUID().toString());
|
||||
step.setReferenced("REF");
|
||||
listSteps.add(step);
|
||||
if (StringUtils.isNotEmpty(scenario.getEnvironmentId())) {
|
||||
envId = scenario.getEnvironmentId();
|
||||
}
|
||||
}
|
||||
}
|
||||
num++;
|
||||
scenarioTest.setHashTree(listSteps);
|
||||
if (StringUtils.isNotEmpty(envId)) {
|
||||
HashMap<String, String> envMap = new HashMap<>();
|
||||
envMap.put(saveHistoricalDataUpgrade.getProjectId(), envId);
|
||||
scenarioTest.setEnvironmentMap(envMap);
|
||||
scenarioTest.setEnvironmentId(envId);
|
||||
}
|
||||
|
||||
String scenarioDefinition = JSON.toJSONString(scenarioTest);
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenarioTest.getId(), scenarioTest.getName(), listSteps.size(), scenarioDefinition, mapper, num);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TestCaseWithBLOBs extends TestCase implements Serializable {
|
||||
private String remark;
|
||||
|
||||
private String steps;
|
||||
private String steps; //与TestCaseExcelData里的属性名不一致,BeanUtils.copyBean()复制不了值,需要手动赋值
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -3,6 +3,7 @@ package io.metersphere.base.mapper.ext;
|
|||
import io.metersphere.api.dto.QueryAPIReportRequest;
|
||||
import io.metersphere.api.dto.automation.APIScenarioReportResult;
|
||||
import io.metersphere.api.dto.datacount.ApiDataCountResult;
|
||||
import io.metersphere.base.domain.ApiDefinitionExecResult;
|
||||
import io.metersphere.base.domain.ApiScenarioReport;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
@ -23,4 +24,7 @@ public interface ExtApiScenarioReportMapper {
|
|||
List<ApiDataCountResult> countByProjectIdGroupByExecuteResult(String projectId);
|
||||
|
||||
List<ApiScenarioReport> selectLastReportByIds(@Param("scenarioIdList") List<String> ids);
|
||||
|
||||
ApiScenarioReport selectPreviousReportByScenarioId(@Param("scenarioId") String scenarioId, @Param("nowId") String nowId);
|
||||
|
||||
}
|
|
@ -216,4 +216,9 @@
|
|||
) orderData ON orderData.id = report.id;
|
||||
</select>
|
||||
|
||||
<select id="selectPreviousReportByScenarioId" resultType="io.metersphere.base.domain.ApiScenarioReport">
|
||||
select * from api_scenario_report
|
||||
WHERE execute_type in ("Completed","Debug") and scenario_id=#{scenarioId} and id != #{nowId} ORDER BY create_time desc LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -91,6 +91,9 @@
|
|||
<if test="reportRequest.projectId != null">
|
||||
AND project.id = #{reportRequest.projectId,jdbcType=VARCHAR}
|
||||
</if>
|
||||
<if test="reportRequest.testId != null">
|
||||
AND ltr.test_id = #{reportRequest.testId,jdbcType=VARCHAR}
|
||||
</if>
|
||||
<if test="reportRequest.filters != null and reportRequest.filters.size() > 0">
|
||||
<foreach collection="reportRequest.filters.entrySet()" index="key" item="values">
|
||||
<if test="values != null and values.size() > 0">
|
||||
|
|
|
@ -274,7 +274,7 @@
|
|||
#{value}
|
||||
</foreach>
|
||||
</when>
|
||||
<when test="key=='status'">
|
||||
<when test="key=='reviewStatus'">
|
||||
and test_case.review_status in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.commons.user;
|
||||
|
||||
import io.metersphere.commons.utils.CodingUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.dto.UserDTO;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
@ -28,7 +29,7 @@ public class SessionUser extends UserDTO implements Serializable {
|
|||
SessionUser sessionUser = new SessionUser();
|
||||
BeanUtils.copyProperties(user, sessionUser);
|
||||
|
||||
List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.random(6), "" + System.currentTimeMillis());
|
||||
List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.randomAlphabetic(6), SessionUtils.getSessionId(), "" + System.currentTimeMillis());
|
||||
sessionUser.csrfToken = CodingUtil.aesEncrypt(StringUtils.join(infos, "|"), secret, iv);
|
||||
return sessionUser;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.apache.shiro.subject.support.DefaultSubjectContext;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static io.metersphere.commons.constants.SessionConstants.ATTR_USER;
|
||||
|
||||
|
@ -32,6 +31,10 @@ public class SessionUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static String getSessionId() {
|
||||
return (String) SecurityUtils.getSubject().getSession().getId();
|
||||
}
|
||||
|
||||
private static Session getSessionByUsername(String username) {
|
||||
DefaultSessionManager sessionManager = CommonBeanFactory.getBean(DefaultSessionManager.class);
|
||||
Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
|
||||
|
|
|
@ -10,7 +10,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||
public class KafkaProperties {
|
||||
public static final String KAFKA_PREFIX = "kafka";
|
||||
|
||||
private String acks = "all";
|
||||
private String acks = "0"; // 不要设置all
|
||||
private String topic;
|
||||
private String fields;
|
||||
private String timestamp;
|
||||
|
|
|
@ -9,5 +9,7 @@ public class ExcelResponse<T> {
|
|||
|
||||
private Boolean success;
|
||||
private List<ExcelErrData<T>> errList;
|
||||
private Boolean isUpdated; //是否有更新过用例
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import lombok.Setter;
|
|||
@Setter
|
||||
public class TestCaseExcelData {
|
||||
|
||||
@ExcelIgnore
|
||||
private Integer num;
|
||||
@ExcelIgnore
|
||||
private String name;
|
||||
@ExcelIgnore
|
||||
|
|
|
@ -13,6 +13,10 @@ import javax.validation.constraints.Pattern;
|
|||
@ColumnWidth(15)
|
||||
public class TestCaseExcelDataCn extends TestCaseExcelData {
|
||||
|
||||
@ExcelProperty("ID")
|
||||
@NotRequired
|
||||
private Integer num;
|
||||
|
||||
@NotBlank(message = "{cannot_be_null}")
|
||||
@Length(max = 255)
|
||||
@ExcelProperty("用例名称")
|
||||
|
|
|
@ -13,6 +13,10 @@ import javax.validation.constraints.Pattern;
|
|||
@ColumnWidth(15)
|
||||
public class TestCaseExcelDataTw extends TestCaseExcelData {
|
||||
|
||||
@ExcelProperty("ID")
|
||||
@NotRequired
|
||||
private Integer num;
|
||||
|
||||
@NotBlank(message = "{cannot_be_null}")
|
||||
@Length(max = 255)
|
||||
@ExcelProperty("用例名稱")
|
||||
|
|
|
@ -14,6 +14,10 @@ import javax.validation.constraints.Pattern;
|
|||
@ColumnWidth(15)
|
||||
public class TestCaseExcelDataUs extends TestCaseExcelData {
|
||||
|
||||
@ExcelProperty("ID")
|
||||
@NotRequired
|
||||
private Integer num;
|
||||
|
||||
@NotBlank(message = "{cannot_be_null}")
|
||||
@Length(max = 255)
|
||||
@ExcelProperty("Name")
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package io.metersphere.excel.listener;
|
||||
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.excel.domain.ExcelErrData;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.excel.utils.ExcelValidateHelper;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -22,10 +26,18 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
|
||||
private String projectId;
|
||||
|
||||
protected List<TestCaseExcelData> updateList = new ArrayList<>(); //存储待更新用例的集合
|
||||
|
||||
protected boolean isUpdated = false; //判断是否更新过用例,将会传给前端
|
||||
|
||||
Set<String> testCaseNames;
|
||||
|
||||
Set<String> userIds;
|
||||
|
||||
public boolean isUpdated() {
|
||||
return isUpdated;
|
||||
}
|
||||
|
||||
public TestCaseDataListener(Class clazz, String projectId, Set<String> testCaseNames, Set<String> userIds) {
|
||||
this.clazz = clazz;
|
||||
this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService");
|
||||
|
@ -39,12 +51,15 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
String nodePath = data.getNodePath();
|
||||
StringBuilder stringBuilder = new StringBuilder(errMsg);
|
||||
|
||||
//校验”所属模块"
|
||||
if (nodePath != null) {
|
||||
String[] nodes = nodePath.split("/");
|
||||
//校验模块深度
|
||||
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||
stringBuilder.append(Translator.get("test_case_node_level_tip") +
|
||||
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
|
||||
}
|
||||
//模块名不能为空
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
||||
stringBuilder.append(Translator.get("module_not_null") + "; ");
|
||||
|
@ -57,10 +72,39 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
// stringBuilder.append(Translator.get("functional_method_tip") + "; ");
|
||||
// }
|
||||
|
||||
|
||||
//校验维护人
|
||||
if (!userIds.contains(data.getMaintainer())) {
|
||||
stringBuilder.append(Translator.get("user_not_exists") + ":" + data.getMaintainer() + "; ");
|
||||
}
|
||||
|
||||
/*
|
||||
校验Excel中是否有ID
|
||||
有的话校验ID是否已在当前项目中存在,存在则更新用例,
|
||||
不存在则继续校验看是否重复,不重复则新建用例。
|
||||
*/
|
||||
if (null != data.getNum()) { //当前读取的数据有ID
|
||||
|
||||
if (null != testCaseService.checkIdExist(data.getNum(), projectId)) { //该ID在当前项目中存在
|
||||
//如果前面所经过的校验都没报错
|
||||
if (StringUtils.isEmpty(stringBuilder)) {
|
||||
updateList.add(data); //将当前数据存入更新列表
|
||||
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
} else {
|
||||
/*
|
||||
该ID在当前数据库中不存在,应当继续校验用例是否重复,
|
||||
在下面的校验过程中,num的值会被用于判断是否重复,所以应当先设置为null
|
||||
*/
|
||||
data.setNum(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
校验用例
|
||||
*/
|
||||
if (testCaseNames.contains(data.getName())) {
|
||||
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
|
||||
BeanUtils.copyBean(testCase, data);
|
||||
|
@ -96,18 +140,27 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
@Override
|
||||
public void saveData() {
|
||||
|
||||
//无错误数据才插入数据
|
||||
if (!errList.isEmpty()) {
|
||||
//excel中用例都有错误时就返回,只要有用例可用于更新或者插入就不返回
|
||||
if (!errList.isEmpty() && list.size() == 0 && updateList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Collections.reverse(list);
|
||||
if (!(list.size() == 0)){
|
||||
Collections.reverse(list); //因为saveImportData里面是先分配最大的ID,这个ID应该先发给list中最后的数据,所以要reverse
|
||||
List<TestCaseWithBLOBs> result = list.stream()
|
||||
.map(item -> this.convert2TestCase(item))
|
||||
.collect(Collectors.toList());
|
||||
testCaseService.saveImportData(result, projectId);
|
||||
}
|
||||
|
||||
List<TestCaseWithBLOBs> result = list.stream()
|
||||
.map(item -> this.convert2TestCase(item))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
testCaseService.saveImportData(result, projectId);
|
||||
if (!(updateList.size() == 0)) {
|
||||
List<TestCaseWithBLOBs> result2 = updateList.stream()
|
||||
.map(item -> this.convert2TestCaseForUpdate(item))
|
||||
.collect(Collectors.toList());
|
||||
testCaseService.updateImportDataCarryId(result2, projectId);
|
||||
this.isUpdated = true;
|
||||
updateList.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -131,6 +184,32 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
testCase.setNodePath(nodePath);
|
||||
|
||||
|
||||
String steps = getSteps(data);
|
||||
testCase.setSteps(steps);
|
||||
|
||||
return testCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Excel中的数据对象转换为用于更新操作的用例数据对象,
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
private TestCaseWithBLOBs convert2TestCaseForUpdate(TestCaseExcelData data) {
|
||||
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
|
||||
BeanUtils.copyBean(testCase, data);
|
||||
testCase.setProjectId(this.projectId);
|
||||
testCase.setUpdateTime(System.currentTimeMillis());
|
||||
|
||||
String nodePath = data.getNodePath();
|
||||
if (!nodePath.startsWith("/")) {
|
||||
nodePath = "/" + nodePath;
|
||||
}
|
||||
if (nodePath.endsWith("/")) {
|
||||
nodePath = nodePath.substring(0, nodePath.length() - 1);
|
||||
}
|
||||
testCase.setNodePath(nodePath);
|
||||
|
||||
String steps = getSteps(data);
|
||||
testCase.setSteps(steps);
|
||||
|
||||
|
@ -189,4 +268,38 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
return jsonArray.toJSONString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(TestCaseExcelData testCaseExcelData, AnalysisContext analysisContext) {
|
||||
String errMsg;
|
||||
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
|
||||
String updateMsg = "update_testcase";
|
||||
try {
|
||||
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
|
||||
errMsg = ExcelValidateHelper.validateEntity(testCaseExcelData);
|
||||
//自定义校验规则
|
||||
errMsg = validate(testCaseExcelData, errMsg);
|
||||
} catch (NoSuchFieldException e) {
|
||||
errMsg = Translator.get("parse_data_error");
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(errMsg)) {
|
||||
|
||||
//如果errMsg只有"update testcase",说明用例待更新
|
||||
if (!errMsg.equals(updateMsg)){
|
||||
ExcelErrData excelErrData = new ExcelErrData(testCaseExcelData, rowIndex,
|
||||
Translator.get("number") + " " + rowIndex + " " + Translator.get("row") + Translator.get("error")
|
||||
+ ":" + errMsg);
|
||||
|
||||
errList.add(excelErrData);
|
||||
}
|
||||
} else {
|
||||
list.add(testCaseExcelData);
|
||||
}
|
||||
|
||||
if (list.size() > BATCH_COUNT) {
|
||||
saveData();
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Map;
|
|||
public class ReportRequest {
|
||||
private String name;
|
||||
private String workspaceId;
|
||||
private String testId;
|
||||
private String userId;
|
||||
private List<OrderRequest> orders;
|
||||
private Map<String, List<String>> filters;
|
||||
|
|
|
@ -127,12 +127,18 @@ public class DockerTestEngine extends AbstractEngine {
|
|||
Integer port = node.getPort();
|
||||
|
||||
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
|
||||
ResultHolder result = restTemplateWithTimeOut.getForObject(uri, ResultHolder.class);
|
||||
if (result == null) {
|
||||
MSException.throwException(Translator.get("container_delete_fail"));
|
||||
}
|
||||
if (!result.isSuccess()) {
|
||||
MSException.throwException(result.getMessage());
|
||||
try {
|
||||
ResultHolder result = restTemplateWithTimeOut.getForObject(uri, ResultHolder.class);
|
||||
if (result == null) {
|
||||
MSException.throwException(Translator.get("container_delete_fail"));
|
||||
}
|
||||
if (!result.isSuccess()) {
|
||||
MSException.throwException(result.getMessage());
|
||||
}
|
||||
} catch (MSException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
MSException.throwException("Please check node-controller status.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -71,11 +71,14 @@ public class CsrfFilter extends AnonymousFilter {
|
|||
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
|
||||
|
||||
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
|
||||
if (signatureArray.length != 3) {
|
||||
if (signatureArray.length != 4) {
|
||||
throw new RuntimeException("invalid token");
|
||||
}
|
||||
if (!StringUtils.equals(SessionUtils.getUserId(), signatureArray[0])) {
|
||||
throw new RuntimeException("Please check csrf token.");
|
||||
}
|
||||
if (!StringUtils.equals(SessionUtils.getSessionId(), signatureArray[2])) {
|
||||
throw new RuntimeException("Please check csrf token.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ import io.metersphere.base.mapper.TestResourceMapper;
|
|||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.controller.ResultHolder;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
import io.metersphere.dto.TestResourcePoolDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
@ -71,12 +71,18 @@ public class NodeResourcePoolService {
|
|||
|
||||
private boolean validateNode(NodeDTO node) {
|
||||
try {
|
||||
ResponseEntity<String> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class);
|
||||
return HttpStatus.OK.equals(entity.getStatusCode());
|
||||
ResponseEntity<ResultHolder> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), ResultHolder.class);
|
||||
ResultHolder body = entity.getBody();
|
||||
if (body == null) {
|
||||
return false;
|
||||
}
|
||||
if (body.getData() != null && StringUtils.equalsIgnoreCase("OK", body.getData().toString())) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateTestResource(TestResource testResource) {
|
||||
|
|
|
@ -14,13 +14,11 @@ import io.metersphere.excel.domain.ExcelResponse;
|
|||
import io.metersphere.service.CheckPermissionService;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.request.testcase.EditTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseMinderEditRequest;
|
||||
import io.metersphere.track.request.testplan.FileOperationRequest;
|
||||
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
|
@ -126,8 +124,8 @@ public class TestCaseController {
|
|||
|
||||
@PostMapping(value = "/edit", consumes = {"multipart/form-data"})
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void editTestCase(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file") List<MultipartFile> files) {
|
||||
testCaseService.edit(request, files);
|
||||
public String editTestCase(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file") List<MultipartFile> files) {
|
||||
return testCaseService.edit(request, files);
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{testCaseId}")
|
||||
|
|
|
@ -92,7 +92,7 @@ public class TestPlanController {
|
|||
@PostMapping("/edit")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void editTestPlan(@RequestBody TestPlanDTO testPlanDTO) {
|
||||
testPlanService.editTestPlan(testPlanDTO);
|
||||
testPlanService.editTestPlan(testPlanDTO, true);
|
||||
}
|
||||
|
||||
@PostMapping("/edit/status/{planId}")
|
||||
|
|
|
@ -2,22 +2,20 @@ package io.metersphere.track.controller;
|
|||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.TestCaseReport;
|
||||
import io.metersphere.base.domain.TestPlanReport;
|
||||
import io.metersphere.commons.constants.ReportTriggerMode;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTOWithMetric;
|
||||
import io.metersphere.track.dto.TestPlanReportDTO;
|
||||
import io.metersphere.track.request.report.QueryTestPlanReportRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.request.report.TestPlanReportSaveRequest;
|
||||
import io.metersphere.track.service.TestPlanReportService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author song.tianyang
|
||||
|
@ -62,14 +60,18 @@ public class TestPlanReportController {
|
|||
|
||||
@GetMapping("/apiExecuteFinish/{planId}/{userId}")
|
||||
public void apiExecuteFinish(@PathVariable String planId,@PathVariable String userId) {
|
||||
TestPlanReport report = testPlanReportService.genTestPlanReport(planId,userId,ReportTriggerMode.API.name());
|
||||
String reportId = UUID.randomUUID().toString();
|
||||
TestPlanReportSaveRequest saveRequest = new TestPlanReportSaveRequest(reportId,planId,userId,ReportTriggerMode.API.name());
|
||||
TestPlanReport report = testPlanReportService.genTestPlanReport(saveRequest);
|
||||
testPlanReportService.countReportByTestPlanReportId(report.getId(),null, ReportTriggerMode.API.name());
|
||||
}
|
||||
|
||||
@GetMapping("/saveTestPlanReport/{planId}/{triggerMode}")
|
||||
public String saveTestPlanReport(@PathVariable String planId,@PathVariable String triggerMode) {
|
||||
String userId = SessionUtils.getUser().getId();
|
||||
TestPlanReport report = testPlanReportService.genTestPlanReport(planId,userId,triggerMode);
|
||||
String reportId = UUID.randomUUID().toString();
|
||||
TestPlanReportSaveRequest saveRequest = new TestPlanReportSaveRequest(reportId,planId,userId,triggerMode);
|
||||
TestPlanReport report = testPlanReportService.genTestPlanReport(saveRequest);
|
||||
testPlanReportService.countReportByTestPlanReportId(report.getId(),null, triggerMode);
|
||||
return "success";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package io.metersphere.track.request.report;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author song.tianyang
|
||||
* @Date 2021/1/8 4:36 下午
|
||||
* @Description
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class TestPlanReportSaveRequest {
|
||||
private String reportID;
|
||||
private String planId;
|
||||
private String userId;
|
||||
private String triggerMode;
|
||||
|
||||
private boolean countResources;
|
||||
private boolean apiCaseIsExecuting;
|
||||
private boolean scenarioIsExecuting;
|
||||
private boolean performanceIsExecuting;
|
||||
|
||||
private String apiCaseIdListJSON;
|
||||
private String scenarioIdListJSON;
|
||||
private String performanceIdListJSON;
|
||||
|
||||
public TestPlanReportSaveRequest(String reportID, String planId, String userId, String triggerMode) {
|
||||
this.reportID = reportID;
|
||||
this.planId = planId;
|
||||
this.userId = userId;
|
||||
this.triggerMode = triggerMode;
|
||||
|
||||
this.countResources = true;
|
||||
}
|
||||
|
||||
public TestPlanReportSaveRequest(String reportID, String planId, String userId, String triggerMode, boolean apiCaseIsExecuting, boolean scenarioIsExecuting, boolean performanceIsExecuting, String apiCaseIdListJSON, String scenarioIdListJSON, String performanceIdListJSON) {
|
||||
this.reportID = reportID;
|
||||
this.planId = planId;
|
||||
this.userId = userId;
|
||||
this.triggerMode = triggerMode;
|
||||
|
||||
this.countResources = false;
|
||||
|
||||
this.apiCaseIsExecuting = apiCaseIsExecuting;
|
||||
this.scenarioIsExecuting = scenarioIsExecuting;
|
||||
this.performanceIsExecuting = performanceIsExecuting;
|
||||
|
||||
this.apiCaseIdListJSON = apiCaseIdListJSON;
|
||||
this.scenarioIdListJSON = scenarioIdListJSON;
|
||||
this.performanceIdListJSON = performanceIdListJSON;
|
||||
}
|
||||
}
|
|
@ -331,20 +331,6 @@ public class TestCaseReviewService {
|
|||
}
|
||||
|
||||
public void testReviewRelevance(ReviewRelevanceRequest request) {
|
||||
String reviewId = request.getReviewId();
|
||||
List<String> userIds = getTestCaseReviewerIds(reviewId);
|
||||
|
||||
String creator = "";
|
||||
TestCaseReview review = testCaseReviewMapper.selectByPrimaryKey(reviewId);
|
||||
if (review != null) {
|
||||
creator = review.getCreator();
|
||||
}
|
||||
|
||||
String currentId = SessionUtils.getUser().getId();
|
||||
if (!userIds.contains(currentId) && !StringUtils.equals(creator, currentId)) {
|
||||
MSException.throwException("没有权限,不能关联用例!");
|
||||
}
|
||||
|
||||
List<String> testCaseIds = request.getTestCaseIds();
|
||||
|
||||
if (testCaseIds.isEmpty()) {
|
||||
|
|
|
@ -19,7 +19,6 @@ import io.metersphere.excel.domain.ExcelErrData;
|
|||
import io.metersphere.excel.domain.ExcelResponse;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.excel.domain.TestCaseExcelDataFactory;
|
||||
import io.metersphere.excel.listener.EasyExcelListener;
|
||||
import io.metersphere.excel.listener.TestCaseDataListener;
|
||||
import io.metersphere.excel.utils.EasyExcelExporter;
|
||||
import io.metersphere.i18n.Translator;
|
||||
|
@ -126,11 +125,26 @@ public class TestCaseService {
|
|||
|
||||
// 全部字段值相同才判断为用例存在
|
||||
if (testCase != null) {
|
||||
|
||||
/*
|
||||
例如对于“/模块5”,用户的输入可能为“模块5”或者“/模块5/”或者“模块5/”。
|
||||
不这样处理的话,下面进行判断时就会用用户输入的错误格式进行判断,而模块名为“/模块5”、
|
||||
“模块5”、“/模块5/”、“模块5/”时,它们应该被认为是同一个模块。
|
||||
数据库存储的node_path都是“/模块5”这种格式的
|
||||
*/
|
||||
String nodePath = testCase.getNodePath();
|
||||
if (!nodePath.startsWith("/")) {
|
||||
nodePath = "/" + nodePath;
|
||||
}
|
||||
if (nodePath.endsWith("/")) {
|
||||
nodePath = nodePath.substring(0, nodePath.length() - 1);
|
||||
}
|
||||
|
||||
TestCaseExample example = new TestCaseExample();
|
||||
TestCaseExample.Criteria criteria = example.createCriteria();
|
||||
criteria.andNameEqualTo(testCase.getName())
|
||||
.andProjectIdEqualTo(testCase.getProjectId())
|
||||
.andNodePathEqualTo(testCase.getNodePath())
|
||||
.andNodePathEqualTo(nodePath)
|
||||
.andTypeEqualTo(testCase.getType())
|
||||
.andMaintainerEqualTo(testCase.getMaintainer())
|
||||
.andPriorityEqualTo(testCase.getPriority());
|
||||
|
@ -165,7 +179,7 @@ public class TestCaseService {
|
|||
String steps = tc.getSteps();
|
||||
String remark = tc.getRemark();
|
||||
if (StringUtils.equals(steps, testCase.getSteps()) && StringUtils.equals(remark, caseRemark)) {
|
||||
MSException.throwException(Translator.get("test_case_already_exists"));
|
||||
//MSException.throwException(Translator.get("test_case_already_exists"));
|
||||
isExt = true;
|
||||
}
|
||||
}
|
||||
|
@ -177,6 +191,26 @@ public class TestCaseService {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id和pojectId查询id是否在数据库中存在。
|
||||
* 在数据库中单id的话是可重复的,id与projectId的组合是唯一的
|
||||
*/
|
||||
public Integer checkIdExist(Integer id, String projectId){
|
||||
TestCaseExample example = new TestCaseExample();
|
||||
TestCaseExample.Criteria criteria = example.createCriteria();
|
||||
if (null != id) {
|
||||
criteria.andNumEqualTo(id);
|
||||
criteria.andProjectIdEqualTo(projectId);
|
||||
long count = testCaseMapper.countByExample(example); //查询是否有包含此ID的数据
|
||||
if(count == 0){ //如果ID不存在
|
||||
return null;
|
||||
}else { //有对应ID的数据
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int deleteTestCase(String testCaseId) {
|
||||
TestPlanTestCaseExample example = new TestPlanTestCaseExample();
|
||||
example.createCriteria().andCaseIdEqualTo(testCaseId);
|
||||
|
@ -286,6 +320,7 @@ public class TestCaseService {
|
|||
public ExcelResponse testCaseImport(MultipartFile multipartFile, String projectId, String userId) {
|
||||
|
||||
ExcelResponse excelResponse = new ExcelResponse();
|
||||
boolean isUpdated = false; //判断是否更新了用例
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
|
||||
queryTestCaseRequest.setProjectId(projectId);
|
||||
|
@ -338,10 +373,15 @@ public class TestCaseService {
|
|||
Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet());
|
||||
|
||||
try {
|
||||
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
|
||||
Class clazz = new TestCaseExcelDataFactory().getExcelDataByLocal();
|
||||
EasyExcelListener easyExcelListener = new TestCaseDataListener(clazz, projectId, testCaseNames, userIds);
|
||||
|
||||
TestCaseDataListener easyExcelListener = new TestCaseDataListener(clazz, projectId, testCaseNames, userIds);
|
||||
//读取excel数据
|
||||
EasyExcelFactory.read(multipartFile.getInputStream(), clazz, easyExcelListener).sheet().doRead();
|
||||
|
||||
errList = easyExcelListener.getErrList();
|
||||
isUpdated = easyExcelListener.isUpdated();
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
|
@ -352,6 +392,7 @@ public class TestCaseService {
|
|||
if (!errList.isEmpty()) {
|
||||
excelResponse.setSuccess(false);
|
||||
excelResponse.setErrList(errList);
|
||||
excelResponse.setIsUpdated(isUpdated);
|
||||
} else {
|
||||
excelResponse.setSuccess(true);
|
||||
}
|
||||
|
@ -374,7 +415,7 @@ public class TestCaseService {
|
|||
testcase.setSort(sort.getAndIncrement());
|
||||
testcase.setNum(num.decrementAndGet());
|
||||
testcase.setReviewStatus(TestCaseReviewStatus.Prepare.name());
|
||||
mapper.insert(testcase);
|
||||
mapper.insert(testcase);
|
||||
});
|
||||
}
|
||||
sqlSession.flushStatements();
|
||||
|
@ -400,6 +441,43 @@ public class TestCaseService {
|
|||
sqlSession.flushStatements();
|
||||
}
|
||||
|
||||
/**
|
||||
* 把Excel中带ID的数据更新到数据库
|
||||
* @param testCases
|
||||
* @param projectId
|
||||
*/
|
||||
public void updateImportDataCarryId(List<TestCaseWithBLOBs> testCases, String projectId) {
|
||||
Map<String, String> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
|
||||
|
||||
/*
|
||||
获取用例的“网页上所显示id”与“数据库ID”映射。
|
||||
*/
|
||||
List<Integer> nums = testCases.stream()
|
||||
.map(TestCase::getNum)
|
||||
.collect(Collectors.toList());
|
||||
TestCaseExample example = new TestCaseExample();
|
||||
example.createCriteria().andNumIn(nums)
|
||||
.andProjectIdEqualTo(projectId);
|
||||
List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
|
||||
Map<Integer, String> numIdMap = testCasesList.stream()
|
||||
.collect(Collectors.toMap(TestCase::getNum, TestCase::getId));
|
||||
|
||||
|
||||
if (!testCases.isEmpty()) {
|
||||
AtomicInteger sort = new AtomicInteger();
|
||||
testCases.forEach(testcase -> {
|
||||
testcase.setUpdateTime(System.currentTimeMillis());
|
||||
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
|
||||
testcase.setSort(sort.getAndIncrement());
|
||||
testcase.setId(numIdMap.get(testcase.getNum()));
|
||||
mapper.updateByPrimaryKeySelective(testcase);
|
||||
});
|
||||
}
|
||||
sqlSession.flushStatements();
|
||||
}
|
||||
|
||||
|
||||
public void testCaseTemplateExport(HttpServletResponse response) {
|
||||
try {
|
||||
|
@ -510,6 +588,7 @@ public class TestCaseService {
|
|||
StringBuilder result = new StringBuilder("");
|
||||
TestCaseList.forEach(t -> {
|
||||
TestCaseExcelData data = new TestCaseExcelData();
|
||||
data.setNum(t.getNum());
|
||||
data.setName(t.getName());
|
||||
data.setNodePath(t.getNodePath());
|
||||
data.setPriority(t.getPriority());
|
||||
|
@ -533,12 +612,15 @@ public class TestCaseService {
|
|||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < jsonArray.size(); j++) {
|
||||
int num = j + 1;
|
||||
step.append(num + "." + jsonArray.getJSONObject(j).getString("desc") + "\r\n");
|
||||
result.append(num + "." + jsonArray.getJSONObject(j).getString("result") + "\r\n");
|
||||
if (CollectionUtils.isNotEmpty(jsonArray)) {
|
||||
for (int j = 0; j < jsonArray.size(); j++) {
|
||||
int num = j + 1;
|
||||
step.append(num + "." + jsonArray.getJSONObject(j).getString("desc") + "\r\n");
|
||||
result.append(num + "." + jsonArray.getJSONObject(j).getString("result") + "\r\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
data.setStepDesc(step.toString());
|
||||
data.setStepResult(result.toString());
|
||||
step.setLength(0);
|
||||
|
|
|
@ -1,21 +1,13 @@
|
|||
package io.metersphere.track.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
|
||||
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
|
||||
import io.metersphere.api.dto.definition.RunDefinitionRequest;
|
||||
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
|
||||
import io.metersphere.api.dto.definition.request.MsTestElement;
|
||||
import io.metersphere.api.dto.definition.request.MsTestPlan;
|
||||
import io.metersphere.api.dto.definition.request.MsThreadGroup;
|
||||
import io.metersphere.api.service.ApiDefinitionExecResultService;
|
||||
import io.metersphere.api.service.ApiDefinitionService;
|
||||
import io.metersphere.api.service.ApiTestCaseService;
|
||||
import io.metersphere.base.domain.ApiTestCaseExample;
|
||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
||||
import io.metersphere.base.domain.TestPlanApiCase;
|
||||
import io.metersphere.base.domain.TestPlanApiCaseExample;
|
||||
import io.metersphere.base.mapper.TestPlanApiCaseMapper;
|
||||
|
@ -25,15 +17,16 @@ import io.metersphere.commons.utils.Pager;
|
|||
import io.metersphere.commons.utils.ServiceUtils;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
@ -150,4 +143,11 @@ public class TestPlanApiCaseService {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
public String getState(String id) {
|
||||
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
|
||||
example.createCriteria().andApiCaseIdEqualTo(id);
|
||||
return testPlanApiCaseMapper.selectByExample(example).get(0).getStatus();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@ package io.metersphere.track.service;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionRequest;
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionResult;
|
||||
import io.metersphere.api.jmeter.TestResult;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper;
|
||||
|
@ -21,15 +18,14 @@ import io.metersphere.track.Factory.ReportComponentFactory;
|
|||
import io.metersphere.track.domain.ReportComponent;
|
||||
import io.metersphere.track.dto.*;
|
||||
import io.metersphere.track.request.report.QueryTestPlanReportRequest;
|
||||
import io.metersphere.track.request.report.TestPlanReportSaveRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.request.testplan.LoadCaseRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.python.bouncycastle.pqc.math.linearalgebra.IntUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -80,36 +76,25 @@ public class TestPlanReportService {
|
|||
}
|
||||
|
||||
/**
|
||||
* 生成测试计划
|
||||
* @param planId
|
||||
* @param userId
|
||||
* @param triggerMode
|
||||
* @param reportId 报告ID(外部传入)
|
||||
* @param planId 测试计划ID
|
||||
* @param userId 用户ID
|
||||
* @param triggerMode 执行方式
|
||||
* @param countResources 是否统计资源-false的话, 下面三个不同资源是否运行则由参数决定。 true的话则由统计后的结果决定
|
||||
* @param apiCaseIsExecuting 接口案例是否执行中
|
||||
* @param scenarioIsExecuting 场景案例是否执行中
|
||||
* @param performanceIsExecuting 性能案例是否执行中
|
||||
* @return
|
||||
*/
|
||||
public TestPlanReport genTestPlanReport(String planId, String userId,String triggerMode) {
|
||||
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId);
|
||||
public TestPlanReport genTestPlanReport(TestPlanReportSaveRequest saveRequest) {
|
||||
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(saveRequest.getPlanId());
|
||||
testPlan.setExecutionTimes(1);
|
||||
testPlan.setExecutionTimes(testPlan.getExecutionTimes() + 1);
|
||||
testPlanMapper.updateByPrimaryKey(testPlan);
|
||||
TestPlanApiCaseExample apiExample = new TestPlanApiCaseExample();
|
||||
apiExample.createCriteria().andTestPlanIdEqualTo(planId);
|
||||
List<String> apiCaseIdList = testPlanApiCaseMapper.selectByExample(apiExample)
|
||||
.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
|
||||
|
||||
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
|
||||
example.createCriteria().andTestPlanIdEqualTo(planId);
|
||||
List<String> scenarioIdList = testPlanScenarioCaseMapper.selectByExample(example)
|
||||
.stream().map(TestPlanApiScenario::getApiScenarioId).collect(Collectors.toList());
|
||||
|
||||
LoadCaseRequest loadCaseRequest = new LoadCaseRequest();
|
||||
loadCaseRequest.setTestPlanId(planId);
|
||||
loadCaseRequest.setProjectId(testPlan.getProjectId());
|
||||
List<String> performanceIdList = testPlanLoadCaseService.list(loadCaseRequest)
|
||||
.stream().map(TestPlanLoadCaseDTO::getLoadCaseId).collect(Collectors.toList());
|
||||
|
||||
String testPlanReportID = UUID.randomUUID().toString();
|
||||
String testPlanReportID = saveRequest.getReportID();
|
||||
TestPlanReport testPlanReport = new TestPlanReport();
|
||||
testPlanReport.setTestPlanId(planId);
|
||||
testPlanReport.setTestPlanId(saveRequest.getPlanId());
|
||||
testPlanReport.setId(testPlanReportID);
|
||||
testPlanReport.setCreateTime(System.currentTimeMillis());
|
||||
testPlanReport.setUpdateTime(System.currentTimeMillis());
|
||||
|
@ -117,41 +102,68 @@ public class TestPlanReportService {
|
|||
testPlanReport.setName(testPlan.getName() + "-" + DateUtils.getTimeString(new Date()));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
testPlanReport.setTriggerMode(triggerMode);
|
||||
testPlanReport.setCreator(userId);
|
||||
testPlanReport.setTriggerMode(saveRequest.getTriggerMode());
|
||||
testPlanReport.setCreator(saveRequest.getUserId());
|
||||
testPlanReport.setStartTime(System.currentTimeMillis());
|
||||
testPlanReport.setEndTime(System.currentTimeMillis());
|
||||
if (apiCaseIdList.isEmpty()) {
|
||||
testPlanReport.setIsApiCaseExecuting(false);
|
||||
} else {
|
||||
testPlanReport.setIsApiCaseExecuting(true);
|
||||
}
|
||||
if (scenarioIdList.isEmpty()) {
|
||||
testPlanReport.setIsScenarioExecuting(false);
|
||||
} else {
|
||||
testPlanReport.setIsScenarioExecuting(true);
|
||||
}
|
||||
if (performanceIdList.isEmpty()) {
|
||||
testPlanReport.setIsPerformanceExecuting(false);
|
||||
} else {
|
||||
testPlanReport.setIsPerformanceExecuting(true);
|
||||
}
|
||||
testPlanReport.setPrincipal(testPlan.getPrincipal());
|
||||
|
||||
if(testPlanReport.getIsScenarioExecuting() || testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting()){
|
||||
testPlanReport.setStatus(APITestStatus.Starting.name());
|
||||
}else {
|
||||
testPlanReport.setStatus(APITestStatus.Completed.name());
|
||||
}
|
||||
testPlanReportMapper.insert(testPlanReport);
|
||||
|
||||
TestPlanReportDataWithBLOBs testPlanReportData = new TestPlanReportDataWithBLOBs();
|
||||
testPlanReportData.setId(UUID.randomUUID().toString());
|
||||
testPlanReportData.setTestPlanReportId(testPlanReportID);
|
||||
|
||||
testPlanReportData.setApiCaseInfo(JSONArray.toJSONString(apiCaseIdList));
|
||||
testPlanReportData.setScenarioInfo(JSONArray.toJSONString(scenarioIdList));
|
||||
testPlanReportData.setPerformanceInfo(JSONArray.toJSONString(performanceIdList));
|
||||
if (saveRequest.isCountResources()) {
|
||||
TestPlanApiCaseExample apiExample = new TestPlanApiCaseExample();
|
||||
apiExample.createCriteria().andTestPlanIdEqualTo(saveRequest.getPlanId());
|
||||
List<String> apiCaseIdList = testPlanApiCaseMapper.selectByExample(apiExample)
|
||||
.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
|
||||
if (apiCaseIdList.isEmpty()) {
|
||||
testPlanReport.setIsApiCaseExecuting(false);
|
||||
} else {
|
||||
testPlanReport.setIsApiCaseExecuting(true);
|
||||
}
|
||||
|
||||
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
|
||||
example.createCriteria().andTestPlanIdEqualTo(saveRequest.getPlanId());
|
||||
List<String> scenarioIdList = testPlanScenarioCaseMapper.selectByExample(example)
|
||||
.stream().map(TestPlanApiScenario::getApiScenarioId).collect(Collectors.toList());
|
||||
if (scenarioIdList.isEmpty()) {
|
||||
testPlanReport.setIsScenarioExecuting(false);
|
||||
} else {
|
||||
testPlanReport.setIsScenarioExecuting(true);
|
||||
}
|
||||
|
||||
LoadCaseRequest loadCaseRequest = new LoadCaseRequest();
|
||||
loadCaseRequest.setTestPlanId(saveRequest.getPlanId());
|
||||
loadCaseRequest.setProjectId(testPlan.getProjectId());
|
||||
List<String> performanceIdList = testPlanLoadCaseService.list(loadCaseRequest)
|
||||
.stream().map(TestPlanLoadCaseDTO::getLoadCaseId).collect(Collectors.toList());
|
||||
if (performanceIdList.isEmpty()) {
|
||||
testPlanReport.setIsPerformanceExecuting(false);
|
||||
} else {
|
||||
testPlanReport.setIsPerformanceExecuting(true);
|
||||
}
|
||||
|
||||
testPlanReportData.setApiCaseInfo(JSONArray.toJSONString(apiCaseIdList));
|
||||
testPlanReportData.setScenarioInfo(JSONArray.toJSONString(scenarioIdList));
|
||||
testPlanReportData.setPerformanceInfo(JSONArray.toJSONString(performanceIdList));
|
||||
} else {
|
||||
testPlanReport.setIsApiCaseExecuting(saveRequest.isApiCaseIsExecuting());
|
||||
testPlanReport.setIsScenarioExecuting(saveRequest.isScenarioIsExecuting());
|
||||
testPlanReport.setIsPerformanceExecuting(saveRequest.isPerformanceIsExecuting());
|
||||
|
||||
testPlanReportData.setApiCaseInfo(saveRequest.getApiCaseIdListJSON());
|
||||
testPlanReportData.setScenarioInfo(saveRequest.getScenarioIdListJSON());
|
||||
testPlanReportData.setPerformanceInfo(saveRequest.getPerformanceIdListJSON());
|
||||
}
|
||||
|
||||
testPlanReport.setPrincipal(testPlan.getPrincipal());
|
||||
if (testPlanReport.getIsScenarioExecuting() || testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting()) {
|
||||
testPlanReport.setStatus(APITestStatus.Starting.name());
|
||||
} else {
|
||||
testPlanReport.setStatus(APITestStatus.Completed.name());
|
||||
}
|
||||
|
||||
testPlanReportMapper.insert(testPlanReport);
|
||||
testPlanReportDataMapper.insert(testPlanReportData);
|
||||
|
||||
//更新TestPlan状态,改为进行中
|
||||
|
@ -203,9 +215,9 @@ public class TestPlanReportService {
|
|||
return returnDTO;
|
||||
}
|
||||
|
||||
public synchronized void updateReport(List<String> testPlanReportIdList, String runMode,String triggerMode) {
|
||||
public synchronized void updateReport(List<String> testPlanReportIdList, String runMode, String triggerMode) {
|
||||
for (String planReportId : testPlanReportIdList) {
|
||||
this.countReportByTestPlanReportId(planReportId,runMode,triggerMode);
|
||||
this.countReportByTestPlanReportId(planReportId, runMode, triggerMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,12 +231,11 @@ public class TestPlanReportService {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param planReportId 测试计划报告ID
|
||||
* @param resourceRunMode 资源的运行模式,triggerMode非Scedule可以为null
|
||||
* @param triggerMode 触发方式 ReportTriggerMode.enum
|
||||
* @param planReportId 测试计划报告ID
|
||||
* @param resourceRunMode 资源的运行模式,triggerMode非Scedule可以为null
|
||||
* @param triggerMode 触发方式 ReportTriggerMode.enum
|
||||
*/
|
||||
public void countReportByTestPlanReportId(String planReportId,String resourceRunMode,String triggerMode) {
|
||||
public void countReportByTestPlanReportId(String planReportId, String resourceRunMode, String triggerMode) {
|
||||
TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(planReportId);
|
||||
|
||||
QueryTestPlanRequest queryTestPlanRequest = new QueryTestPlanRequest();
|
||||
|
@ -233,26 +244,26 @@ public class TestPlanReportService {
|
|||
String issuesInfo = null;
|
||||
|
||||
//因为接口案例的定时任务是单个案例开线程运行, 所以要检查是否都执行完成。全部执行完成时才会进行统一整理
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode)
|
||||
&&StringUtils.equalsAny(resourceRunMode, ApiRunMode.SCHEDULE_API_PLAN.name())) {
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), triggerMode)
|
||||
&& StringUtils.equalsAny(resourceRunMode, ApiRunMode.SCHEDULE_API_PLAN.name())) {
|
||||
List<String> statusList = extTestPlanApiCaseMapper.getStatusByTestPlanId(testPlan.getId());
|
||||
for (String status : statusList) {
|
||||
if (status == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}else if(StringUtils.equals(ReportTriggerMode.TEST_PLAN_SCHEDULE.name(),triggerMode)){
|
||||
} else if (StringUtils.equals(ReportTriggerMode.TEST_PLAN_SCHEDULE.name(), triggerMode)) {
|
||||
}
|
||||
|
||||
testPlanReport.setEndTime(System.currentTimeMillis());
|
||||
testPlanReport.setUpdateTime(System.currentTimeMillis());
|
||||
|
||||
//手动触发的需要保存手工执行的信息
|
||||
int [] componentIndexArr = null;
|
||||
if(StringUtils.equals(ReportTriggerMode.MANUAL.name(),triggerMode)){
|
||||
componentIndexArr = new int[]{1,2,3,4,5};
|
||||
}else {
|
||||
componentIndexArr = new int[]{1,3,4};
|
||||
int[] componentIndexArr = null;
|
||||
if (StringUtils.equals(ReportTriggerMode.MANUAL.name(), triggerMode)) {
|
||||
componentIndexArr = new int[]{1, 2, 3, 4, 5};
|
||||
} else {
|
||||
componentIndexArr = new int[]{1, 3, 4};
|
||||
}
|
||||
testPlanReport.setComponents(JSONArray.toJSONString(componentIndexArr));
|
||||
|
||||
|
@ -263,23 +274,23 @@ public class TestPlanReportService {
|
|||
testPlanService.buildScenarioCaseReport(testPlanReport.getTestPlanId(), components);
|
||||
testPlanService.buildLoadCaseReport(testPlanReport.getTestPlanId(), components);
|
||||
|
||||
if(StringUtils.equals(ReportTriggerMode.MANUAL.name(),triggerMode)){
|
||||
if (StringUtils.equals(ReportTriggerMode.MANUAL.name(), triggerMode)) {
|
||||
List<Issues> issues = testPlanService.buildFunctionalCaseReport(testPlanReport.getTestPlanId(), components);
|
||||
issuesInfo = JSONArray.toJSONString(issues);
|
||||
}
|
||||
|
||||
|
||||
//只针对定时任务做处理
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode)
|
||||
&&StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_API_PLAN.name())) {
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), triggerMode)
|
||||
&& StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_API_PLAN.name())) {
|
||||
testPlanReport.setIsApiCaseExecuting(false);
|
||||
} else if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode)
|
||||
&&StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) {
|
||||
} else if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), triggerMode)
|
||||
&& StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) {
|
||||
testPlanReport.setIsScenarioExecuting(false);
|
||||
} else if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode)
|
||||
&&StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name())) {
|
||||
} else if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), triggerMode)
|
||||
&& StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name())) {
|
||||
testPlanReport.setIsPerformanceExecuting(false);
|
||||
}else {
|
||||
} else {
|
||||
testPlanReport.setIsPerformanceExecuting(false);
|
||||
testPlanReport.setIsScenarioExecuting(false);
|
||||
testPlanReport.setIsApiCaseExecuting(false);
|
||||
|
@ -289,12 +300,12 @@ public class TestPlanReportService {
|
|||
component.afterBuild(testCaseReportMetricDTO);
|
||||
});
|
||||
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode)
|
||||
&&StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name())) {
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), triggerMode)
|
||||
&& StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name())) {
|
||||
//如果是性能测试作为触发,由于延迟原因可能会出现报告已经结束但是状态还是进行中的状态
|
||||
List<TestCaseReportStatusResultDTO> loadResult = testCaseReportMetricDTO.getExecuteResult().getLoadResult();
|
||||
for (TestCaseReportStatusResultDTO dto: loadResult) {
|
||||
if(StringUtils.equals(dto.getStatus(),TestPlanTestCaseStatus.Underway.name())){
|
||||
for (TestCaseReportStatusResultDTO dto : loadResult) {
|
||||
if (StringUtils.equals(dto.getStatus(), TestPlanTestCaseStatus.Underway.name())) {
|
||||
dto.setStatus(TestPlanTestCaseStatus.Pass.name());
|
||||
}
|
||||
}
|
||||
|
@ -311,49 +322,50 @@ public class TestPlanReportService {
|
|||
testPlanReportData.setExecuteResult(JSONObject.toJSONString(testCaseReportMetricDTO.getExecuteResult()));
|
||||
testPlanReportData.setFailurTestCases(JSONObject.toJSONString(testCaseReportMetricDTO.getFailureTestCases()));
|
||||
testPlanReportData.setModuleExecuteResult(JSONArray.toJSONString(testCaseReportMetricDTO.getModuleExecuteResult()));
|
||||
if(issuesInfo!=null){
|
||||
if (issuesInfo != null) {
|
||||
testPlanReportData.setIssuesInfo(issuesInfo);
|
||||
}
|
||||
testPlanReportDataMapper.updateByPrimaryKeyWithBLOBs(testPlanReportData);
|
||||
}
|
||||
|
||||
String testPlanStatus = this.getTestPlanReportStatus(testPlanReport,testPlanReportData);
|
||||
String testPlanStatus = this.getTestPlanReportStatus(testPlanReport, testPlanReportData);
|
||||
testPlanReport.setStatus(testPlanStatus);
|
||||
this.update(testPlanReport);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算测试计划的状态
|
||||
*
|
||||
* @param testPlanReport
|
||||
* @return
|
||||
*/
|
||||
private String getTestPlanReportStatus(TestPlanReport testPlanReport, TestPlanReportDataWithBLOBs testPlanReportData) {
|
||||
String status = TestPlanReportStatus.COMPLETED.name();
|
||||
if(testPlanReport!=null){
|
||||
if(testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting() || testPlanReport.getIsScenarioExecuting()){
|
||||
if (testPlanReport != null) {
|
||||
if (testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting() || testPlanReport.getIsScenarioExecuting()) {
|
||||
status = TestPlanReportStatus.RUNNING.name();
|
||||
}else {
|
||||
if(testPlanReportData == null){
|
||||
} else {
|
||||
if (testPlanReportData == null) {
|
||||
String failCaseString = testPlanReportData.getFailurTestCases();
|
||||
status = TestPlanReportStatus.SUCCESS.name();
|
||||
try {
|
||||
JSONObject failurCaseObject = JSONObject.parseObject(failCaseString);
|
||||
if(failurCaseObject.containsKey("apiTestCases")&&failurCaseObject.getJSONArray("apiTestCases").size()>=0){
|
||||
if (failurCaseObject.containsKey("apiTestCases") && failurCaseObject.getJSONArray("apiTestCases").size() >= 0) {
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
return status;
|
||||
}
|
||||
if(failurCaseObject.containsKey("loadTestCases")&&failurCaseObject.getJSONArray("loadTestCases").size()>=0){
|
||||
if (failurCaseObject.containsKey("loadTestCases") && failurCaseObject.getJSONArray("loadTestCases").size() >= 0) {
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
return status;
|
||||
}
|
||||
if(failurCaseObject.containsKey("scenarioTestCases")&&failurCaseObject.getJSONArray("scenarioTestCases").size()>=0){
|
||||
if (failurCaseObject.containsKey("scenarioTestCases") && failurCaseObject.getJSONArray("scenarioTestCases").size() >= 0) {
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
return status;
|
||||
}
|
||||
}catch (Exception e){
|
||||
} catch (Exception e) {
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
status = TestPlanReportStatus.COMPLETED.name();
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +383,7 @@ public class TestPlanReportService {
|
|||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
}
|
||||
|
||||
if(StringUtils.equalsAny(report.getTriggerMode(),ReportTriggerMode.SCHEDULE.name())){
|
||||
if (StringUtils.equalsAny(report.getTriggerMode(), ReportTriggerMode.SCHEDULE.name())) {
|
||||
//发送通知
|
||||
sendMessage(report);
|
||||
}
|
||||
|
@ -379,7 +391,7 @@ public class TestPlanReportService {
|
|||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
}
|
||||
testPlanReportMapper.updateByPrimaryKey(report);
|
||||
}
|
||||
|
@ -417,7 +429,7 @@ public class TestPlanReportService {
|
|||
String successfulMailTemplate = "";
|
||||
String errfoMailTemplate = "";
|
||||
|
||||
if(StringUtils.equals(testPlanReport.getTriggerMode(),ReportTriggerMode.SCHEDULE.name())){
|
||||
if (StringUtils.equals(testPlanReport.getTriggerMode(), ReportTriggerMode.SCHEDULE.name())) {
|
||||
successfulMailTemplate = "TestPlanSuccessfulNotification";
|
||||
errfoMailTemplate = "TestPlanFailedNotification";
|
||||
}
|
||||
|
@ -446,7 +458,7 @@ public class TestPlanReportService {
|
|||
* @param testPlanReport
|
||||
* @param performaneReportIDList
|
||||
*/
|
||||
public void updatePerformanceInfo(TestPlanReport testPlanReport, List<String> performaneReportIDList,String triggerMode) {
|
||||
public void updatePerformanceInfo(TestPlanReport testPlanReport, List<String> performaneReportIDList, String triggerMode) {
|
||||
TestPlanReportDataExample example = new TestPlanReportDataExample();
|
||||
example.createCriteria().andTestPlanReportIdEqualTo(testPlanReport.getId());
|
||||
List<TestPlanReportDataWithBLOBs> reportDataList = testPlanReportDataMapper.selectByExampleWithBLOBs(example);
|
||||
|
@ -462,36 +474,36 @@ public class TestPlanReportService {
|
|||
List<String> updatePerformaneReportIDList = new ArrayList<>(performaneReportIDList);
|
||||
executorService.submit(() -> {
|
||||
//错误数据检查集合。 如果错误数据出现超过20次,则取消该条数据的检查
|
||||
Map<String,Integer> errorDataCheckMap = new HashMap<>();
|
||||
while (performaneReportIDList.size()>0) {
|
||||
Map<String, Integer> errorDataCheckMap = new HashMap<>();
|
||||
while (performaneReportIDList.size() > 0) {
|
||||
List<String> selectList = new ArrayList<>(performaneReportIDList);
|
||||
for (String loadTestReportId:selectList) {
|
||||
for (String loadTestReportId : selectList) {
|
||||
LoadTestReportWithBLOBs loadTestReportFromDatabase = loadTestReportMapper.selectByPrimaryKey(loadTestReportId);
|
||||
if(loadTestReportFromDatabase == null){
|
||||
if (loadTestReportFromDatabase == null) {
|
||||
//检查错误数据
|
||||
if(errorDataCheckMap.containsKey(loadTestReportId)){
|
||||
if(errorDataCheckMap.get(loadTestReportId)>10){
|
||||
if (errorDataCheckMap.containsKey(loadTestReportId)) {
|
||||
if (errorDataCheckMap.get(loadTestReportId) > 10) {
|
||||
performaneReportIDList.remove(loadTestReportId);
|
||||
}else {
|
||||
errorDataCheckMap.put(loadTestReportId,errorDataCheckMap.get(loadTestReportId)+1);
|
||||
} else {
|
||||
errorDataCheckMap.put(loadTestReportId, errorDataCheckMap.get(loadTestReportId) + 1);
|
||||
}
|
||||
}else {
|
||||
errorDataCheckMap.put(loadTestReportId,1);
|
||||
} else {
|
||||
errorDataCheckMap.put(loadTestReportId, 1);
|
||||
}
|
||||
}else if (StringUtils.equalsAny(loadTestReportFromDatabase.getStatus(),
|
||||
} else if (StringUtils.equalsAny(loadTestReportFromDatabase.getStatus(),
|
||||
PerformanceTestStatus.Completed.name(), PerformanceTestStatus.Error.name())) {
|
||||
performaneReportIDList.remove(loadTestReportId);
|
||||
}
|
||||
}
|
||||
if(performaneReportIDList.isEmpty()){
|
||||
for (String string: updatePerformaneReportIDList) {
|
||||
if (performaneReportIDList.isEmpty()) {
|
||||
for (String string : updatePerformaneReportIDList) {
|
||||
TestPlanLoadCaseEventDTO eventDTO = new TestPlanLoadCaseEventDTO();
|
||||
eventDTO.setReportId(string);
|
||||
eventDTO.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
|
||||
eventDTO.setStatus(PerformanceTestStatus.Completed.name());
|
||||
this.updatePerformanceTestStatus(eventDTO);
|
||||
}
|
||||
}else {
|
||||
} else {
|
||||
try {
|
||||
//查询定时任务是否关闭
|
||||
Thread.sleep(1000 * 10);// 检查 loadtest 的状态
|
||||
|
@ -505,7 +517,7 @@ public class TestPlanReportService {
|
|||
|
||||
public void updatePerformanceTestStatus(TestPlanLoadCaseEventDTO eventDTO) {
|
||||
List<String> testPlanReportId = extTestPlanMapper.findIdByPerformanceReportId(eventDTO.getReportId());
|
||||
this.updateReport(testPlanReportId, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name(),eventDTO.getTriggerMode());
|
||||
this.updateReport(testPlanReportId, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name(), eventDTO.getTriggerMode());
|
||||
}
|
||||
|
||||
public void delete(List<String> testPlanReportIdList) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import io.metersphere.service.SystemParameterService;
|
|||
import io.metersphere.track.Factory.ReportComponentFactory;
|
||||
import io.metersphere.track.domain.ReportComponent;
|
||||
import io.metersphere.track.dto.*;
|
||||
import io.metersphere.track.request.report.TestPlanReportSaveRequest;
|
||||
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.request.testplan.AddTestPlanRequest;
|
||||
|
@ -128,6 +129,8 @@ public class TestPlanService {
|
|||
private ApiScenarioMapper apiScenarioMapper;
|
||||
@Resource
|
||||
private TestCaseTestMapper testCaseTestMapper;
|
||||
@Resource
|
||||
private ApiScenarioReportMapper apiScenarioReportMapper;
|
||||
|
||||
public synchronized String addTestPlan(AddTestPlanRequest testPlan) {
|
||||
if (getTestPlanByName(testPlan.getName()).size() > 0) {
|
||||
|
@ -173,7 +176,7 @@ public class TestPlanService {
|
|||
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan());
|
||||
}
|
||||
|
||||
public int editTestPlan(TestPlanDTO testPlan) {
|
||||
public int editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) {
|
||||
checkTestPlanExist(testPlan);
|
||||
TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库
|
||||
testPlan.setUpdateTime(System.currentTimeMillis());
|
||||
|
@ -211,7 +214,7 @@ public class TestPlanService {
|
|||
extScheduleMapper.updateNameByResourceID(testPlan.getId(), testPlan.getName());// 同步更新该测试的定时任务的name
|
||||
i = testPlanMapper.updateByPrimaryKeyWithBLOBs(testPlan); // 更新
|
||||
}
|
||||
if (!StringUtils.isBlank(testPlan.getStatus())) {
|
||||
if (!StringUtils.isBlank(testPlan.getStatus()) && isSendMessage) {
|
||||
BeanUtils.copyBean(testPlans, getTestPlan(testPlan.getId()));
|
||||
String context = getTestPlanContext(testPlans, NoticeConstants.Event.UPDATE);
|
||||
User user = userMapper.selectByPrimaryKey(testPlans.getCreator());
|
||||
|
@ -397,7 +400,7 @@ public class TestPlanService {
|
|||
testPlanDTO.setId(testPlanId);
|
||||
if(statusList.size() == 0) { // 原先status不是prepare, 但删除所有关联用例的情况
|
||||
testPlanDTO.setStatus(TestPlanStatus.Prepare.name());
|
||||
editTestPlan(testPlanDTO);
|
||||
editTestPlan(testPlanDTO, false);
|
||||
return;
|
||||
}
|
||||
int passNum = 0, prepareNum = 0, failNum = 0;
|
||||
|
@ -406,7 +409,7 @@ public class TestPlanService {
|
|||
|| StringUtils.equals(res, "success")
|
||||
|| StringUtils.equals(res, ScenarioStatus.Success.name())) {
|
||||
passNum++;
|
||||
} else if (res == null) {
|
||||
} else if (res == null || StringUtils.equals(TestPlanStatus.Prepare.name(), res)) {
|
||||
prepareNum++;
|
||||
} else {
|
||||
failNum++;
|
||||
|
@ -414,13 +417,13 @@ public class TestPlanService {
|
|||
}
|
||||
if(passNum == statusList.size()) { // 全部通过
|
||||
testPlanDTO.setStatus(TestPlanStatus.Completed.name());
|
||||
this.editTestPlan(testPlanDTO);
|
||||
this.editTestPlan(testPlanDTO, false);
|
||||
} else if(prepareNum == 0 && passNum + failNum == statusList.size()) { // 已结束
|
||||
testPlanDTO.setStatus(TestPlanStatus.Finished.name());
|
||||
editTestPlan(testPlanDTO);
|
||||
editTestPlan(testPlanDTO, false);
|
||||
} else if(prepareNum != 0) { // 进行中
|
||||
testPlanDTO.setStatus(TestPlanStatus.Underway.name());
|
||||
editTestPlan(testPlanDTO);
|
||||
editTestPlan(testPlanDTO, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -863,12 +866,13 @@ public class TestPlanService {
|
|||
* @return
|
||||
*/
|
||||
public String runScenarioCase(SchedulePlanScenarioExecuteRequest request) {
|
||||
String returnId = "";
|
||||
MsTestPlan testPlan = new MsTestPlan();
|
||||
testPlan.setHashTree(new LinkedList<>());
|
||||
HashTree jmeterHashTree = new ListedHashTree();
|
||||
Map<String, Map<String, String>> testPlanScenarioIdMap = request.getTestPlanScenarioIDMap();
|
||||
|
||||
for (Map.Entry<String, Map<String, String>> entry : testPlanScenarioIdMap.entrySet()) {
|
||||
String testPlanID = entry.getKey();
|
||||
Map<String, String> planScenarioIdMap = entry.getValue();
|
||||
List<ApiScenarioWithBLOBs> apiScenarios = extApiScenarioMapper.selectIds(new ArrayList<>(planScenarioIdMap.keySet()));
|
||||
try {
|
||||
|
@ -918,23 +922,27 @@ public class TestPlanService {
|
|||
scenarios.add(scenario);
|
||||
// 创建场景报告
|
||||
//不同的运行模式,第二个参数入参不同
|
||||
apiAutomationService.createScenarioReport(group.getName(),
|
||||
APIScenarioReportResult report = apiAutomationService.createScenarioReport(group.getName(),
|
||||
planScenarioID + ":" + request.getTestPlanReportId(),
|
||||
item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
|
||||
request.getExecuteType(), item.getProjectId(), request.getReportUserID());
|
||||
group.setHashTree(scenarios);
|
||||
testPlan.getHashTree().add(group);
|
||||
|
||||
apiScenarioReportMapper.insert(report);
|
||||
returnId = request.getId();
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
MSException.throwException(ex.getMessage());
|
||||
}
|
||||
|
||||
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
|
||||
String runMode = ApiRunMode.SCHEDULE_SCENARIO_PLAN.name();
|
||||
// 调用执行方法
|
||||
jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode);
|
||||
}
|
||||
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
|
||||
String runMode = ApiRunMode.SCHEDULE_SCENARIO_PLAN.name();
|
||||
// 调用执行方法
|
||||
jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode);
|
||||
return request.getId();
|
||||
|
||||
return returnId;
|
||||
}
|
||||
|
||||
public void run(String testPlanID, String projectID, String userId, String triggerMode) {
|
||||
|
@ -966,37 +974,21 @@ public class TestPlanService {
|
|||
|
||||
LogUtil.info("-------------- start testplan schedule ----------");
|
||||
TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class);
|
||||
//首先创建testPlanReport,然后返回的ID重新赋值为resourceID,作为后续的参数
|
||||
TestPlanReport testPlanReport = testPlanReportService.genTestPlanReport(testPlanID, userId, triggerMode);
|
||||
//执行接口案例任务
|
||||
for (Map.Entry<String, String> entry : apiTestCaseIdMap.entrySet()) {
|
||||
String apiCaseID = entry.getKey();
|
||||
String planCaseID = entry.getValue();
|
||||
ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID);
|
||||
//需要更新这里来保证PlanCase的状态能正常更改
|
||||
apiTestCaseService.run(blobs, UUID.randomUUID().toString(), testPlanReport.getId(), testPlanID, ApiRunMode.SCHEDULE_API_PLAN.name());
|
||||
}
|
||||
|
||||
//执行场景执行任务
|
||||
if (!planScenarioIdMap.isEmpty()) {
|
||||
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
|
||||
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest();
|
||||
String senarionReportID = UUID.randomUUID().toString();
|
||||
scenarioRequest.setId(senarionReportID);
|
||||
scenarioRequest.setReportId(senarionReportID);
|
||||
scenarioRequest.setProjectId(projectID);
|
||||
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
|
||||
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
|
||||
Map<String, Map<String, String>> testPlanScenarioIdMap = new HashMap<>();
|
||||
testPlanScenarioIdMap.put(testPlanID, planScenarioIdMap);
|
||||
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap);
|
||||
scenarioRequest.setReportUserID(userId);
|
||||
scenarioRequest.setTestPlanID(testPlanID);
|
||||
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
|
||||
scenarioRequest.setTestPlanReportId(testPlanReport.getId());
|
||||
this.runScenarioCase(scenarioRequest);
|
||||
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
|
||||
}
|
||||
boolean apiCaseIsExcuting = false;
|
||||
boolean scenarioIsExcuting = false;
|
||||
boolean performaceIsExcuting = false;
|
||||
String apiCaseIdArray = "";
|
||||
String scenarioCaseIdArray = "";
|
||||
String performanceCaseIdArray = "";
|
||||
String planReportId = UUID.randomUUID().toString();
|
||||
//创建测试报告,然后返回的ID重新赋值为resourceID,作为后续的参数
|
||||
TestPlanReportSaveRequest saveRequest = new TestPlanReportSaveRequest(planReportId,testPlanID,userId,triggerMode,
|
||||
apiTestCaseIdMap.size()>0,planScenarioIdMap.size()>0,performanceIdMap.size()>0,
|
||||
JSONArray.toJSONString(new ArrayList<>(apiTestCaseIdMap.keySet())),JSONArray.toJSONString(new ArrayList<>(planScenarioIdMap.keySet())),JSONArray.toJSONString(new ArrayList<>(performanceIdMap.values())));
|
||||
|
||||
TestPlanReport testPlanReport = testPlanReportService.genTestPlanReport(saveRequest);
|
||||
|
||||
//执行性能测试任务
|
||||
List<String> performaneReportIDList = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : performanceIdMap.entrySet()) {
|
||||
|
@ -1016,20 +1008,76 @@ public class TestPlanService {
|
|||
testPlanLoadCase.setId(performanceRequest.getTestPlanLoadId());
|
||||
testPlanLoadCase.setLoadReportId(reportId);
|
||||
testPlanLoadCaseService.update(testPlanLoadCase);
|
||||
|
||||
//更新关联处的报告
|
||||
TestPlanLoadCase loadCase = new TestPlanLoadCaseDTO();
|
||||
loadCase.setId(id);
|
||||
loadCase.setLoadReportId(reportId);
|
||||
testPlanLoadCaseService.update(loadCase);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//更新关联处的报告
|
||||
TestPlanLoadCase loadCase = new TestPlanLoadCaseDTO();
|
||||
loadCase.setId(id);
|
||||
loadCase.setLoadReportId(reportId);
|
||||
testPlanLoadCaseService.update(loadCase);
|
||||
if(StringUtils.isEmpty(reportId)){
|
||||
performaceIsExcuting = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(performaceIsExcuting){
|
||||
performanceCaseIdArray= JSONArray.toJSONString(new ArrayList<>(performanceIdMap.values()));
|
||||
}
|
||||
|
||||
if (!performaneReportIDList.isEmpty()) {
|
||||
//性能测试时保存性能测试报告ID,在结果返回时用于捕捉并进行
|
||||
testPlanReportService.updatePerformanceInfo(testPlanReport, performaneReportIDList, ReportTriggerMode.SCHEDULE.name());
|
||||
}
|
||||
|
||||
|
||||
//执行接口案例任务
|
||||
for (Map.Entry<String, String> entry : apiTestCaseIdMap.entrySet()) {
|
||||
String apiCaseID = entry.getKey();
|
||||
String planCaseID = entry.getValue();
|
||||
ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID);
|
||||
//需要更新这里来保证PlanCase的状态能正常更改
|
||||
apiTestCaseService.run(blobs, UUID.randomUUID().toString(), planReportId, testPlanID, ApiRunMode.SCHEDULE_API_PLAN.name());
|
||||
apiCaseIsExcuting = true;
|
||||
}
|
||||
if(apiCaseIsExcuting){
|
||||
apiCaseIdArray = JSONArray.toJSONString(new ArrayList<>(apiTestCaseIdMap.keySet()));
|
||||
}
|
||||
|
||||
//执行场景执行任务
|
||||
if (!planScenarioIdMap.isEmpty()) {
|
||||
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
|
||||
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest();
|
||||
String senarionReportID = UUID.randomUUID().toString();
|
||||
scenarioRequest.setId(senarionReportID);
|
||||
scenarioRequest.setReportId(senarionReportID);
|
||||
scenarioRequest.setProjectId(projectID);
|
||||
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
|
||||
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
|
||||
Map<String, Map<String, String>> testPlanScenarioIdMap = new HashMap<>();
|
||||
testPlanScenarioIdMap.put(testPlanID, planScenarioIdMap);
|
||||
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap);
|
||||
scenarioRequest.setReportUserID(userId);
|
||||
scenarioRequest.setTestPlanID(testPlanID);
|
||||
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
|
||||
scenarioRequest.setTestPlanReportId(planReportId);
|
||||
String scenarioReportID = this.runScenarioCase(scenarioRequest);
|
||||
if(StringUtils.isNotEmpty(scenarioReportID)){
|
||||
scenarioIsExcuting = true;
|
||||
scenarioCaseIdArray= JSONArray.toJSONString(new ArrayList<>(planScenarioIdMap.keySet()));
|
||||
}
|
||||
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
|
||||
}
|
||||
|
||||
|
||||
//如果report参数和预期不对(某些原因执行失败),则更新report
|
||||
if(saveRequest.isApiCaseIsExecuting() != apiCaseIsExcuting ||saveRequest.isScenarioIsExecuting()!=scenarioIsExcuting ||saveRequest.isPerformanceIsExecuting() != performaceIsExcuting){
|
||||
testPlanReport.setIsApiCaseExecuting(apiCaseIsExcuting);
|
||||
testPlanReport.setIsScenarioExecuting(scenarioIsExcuting);
|
||||
testPlanReport.setIsPerformanceExecuting(performaceIsExcuting);
|
||||
testPlanReportService.update(testPlanReport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
|
||||
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
|
||||
import MsApiScenarioList from "@/business/components/api/automation/scenario/ApiScenarioList";
|
||||
import {getUUID, downloadFile, checkoutTestManagerOrTestUser} from "@/common/js/utils";
|
||||
import {getUUID, downloadFile, checkoutTestManagerOrTestUser,getCurrentUser} from "@/common/js/utils";
|
||||
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
|
||||
import MsEditApiScenario from "./scenario/EditApiScenario";
|
||||
|
||||
|
@ -127,7 +127,6 @@
|
|||
'$route'(to, from) { // 路由改变时,把接口定义界面中的 ctrl s 保存快捷键监听移除
|
||||
if (to.path.indexOf('/api/automation') == -1) {
|
||||
if (this.$refs && this.$refs.autoScenarioConfig) {
|
||||
// console.log(this.$refs.autoScenarioConfig);
|
||||
this.$refs.autoScenarioConfig.forEach(item => {
|
||||
item.removeListener();
|
||||
});
|
||||
|
@ -189,7 +188,16 @@
|
|||
let label = this.$t('api_test.automation.add_scenario');
|
||||
let name = getUUID().substring(0, 8);
|
||||
this.activeName = name;
|
||||
this.tabs.push({label: label, name: name, currentScenario: {apiScenarioModuleId: "", id: getUUID()}});
|
||||
let currentScenario = {
|
||||
status: "Underway", principal: getCurrentUser().id,
|
||||
apiScenarioModuleId: "root", id: getUUID(),
|
||||
modulePath: "/" + this.$t("commons.module_title")
|
||||
};
|
||||
if (this.nodeTree && this.nodeTree.length > 0) {
|
||||
currentScenario.apiScenarioModuleId = this.nodeTree[0].id;
|
||||
currentScenario.modulePath = this.nodeTree[0].path;
|
||||
}
|
||||
this.tabs.push({label: label, name: name, currentScenario: currentScenario});
|
||||
}
|
||||
if (tab.name === 'edit') {
|
||||
let label = this.$t('api_test.automation.add_scenario');
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,32 +16,9 @@
|
|||
ref="nodeTree">
|
||||
|
||||
<template v-slot:header>
|
||||
<el-input :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small">
|
||||
<template v-slot:append>
|
||||
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')"
|
||||
v-tester
|
||||
@command="handleCommand" trigger="click">
|
||||
<el-button icon="el-icon-folder-add" @click="addScenario"></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="add-scenario">{{ $t('api_test.automation.add_scenario') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="exports">
|
||||
<el-dropdown placement="right-start" @command="chooseExportType">
|
||||
<span>
|
||||
{{ $t('report.export') }} <i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<template>
|
||||
<el-dropdown-item command="export">{{ $t('report.export_to_ms_format') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="exportJmx">{{ $t('report.export') }} JMETER 格式</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-input>
|
||||
<ms-search-bar
|
||||
:condition="condition"
|
||||
:commands="operators"/>
|
||||
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
|
||||
</template>
|
||||
|
||||
|
@ -52,7 +29,7 @@
|
|||
@refresh="refresh"
|
||||
ref="basisScenario"/>
|
||||
|
||||
<api-import ref="apiImport" :moduleOptions="moduleOptions" @refreshAll="$emit('refreshAll')"/>
|
||||
<api-import ref="apiImport" :moduleOptions="extendTreeNodes" @refreshAll="$emit('refreshAll')"/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
@ -61,13 +38,15 @@
|
|||
import SelectMenu from "../../../track/common/SelectMenu";
|
||||
import MsAddBasisScenario from "@/business/components/api/automation/scenario/AddBasisScenario";
|
||||
import MsNodeTree from "../../../track/common/NodeTree";
|
||||
import {buildNodePath} from "../../definition/model/NodeTree";
|
||||
import {buildNodePath, buildTree} from "../../definition/model/NodeTree";
|
||||
import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton";
|
||||
import ApiImport from "./common/ScenarioImport";
|
||||
import MsSearchBar from "@/business/components/common/components/search/MsSearchBar";
|
||||
|
||||
export default {
|
||||
name: 'MsApiScenarioModule',
|
||||
components: {
|
||||
MsSearchBar,
|
||||
ApiImport,
|
||||
ModuleTrashButton,
|
||||
MsNodeTree,
|
||||
|
@ -103,8 +82,36 @@
|
|||
trashEnable: false
|
||||
},
|
||||
data: [],
|
||||
extendTreeNodes: [],
|
||||
currentModule: undefined,
|
||||
moduleOptions: [],
|
||||
operators: [
|
||||
{
|
||||
label: this.$t('api_test.automation.add_scenario'),
|
||||
callback: this.addScenario
|
||||
},
|
||||
{
|
||||
label: this.$t('api_test.api_import.label'),
|
||||
callback: this.handleImport
|
||||
},
|
||||
{
|
||||
label: this.$t('report.export'),
|
||||
children: [
|
||||
{
|
||||
label: this.$t('report.export_to_ms_format'),
|
||||
callback: () => {
|
||||
this.$emit('exportAPI');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('report.export') + 'JMETER 格式',
|
||||
callback: () => {
|
||||
this.$emit('exportJmx');
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -151,14 +158,19 @@
|
|||
break;
|
||||
}
|
||||
},
|
||||
chooseExportType(e) {
|
||||
switch (e) {
|
||||
case "export":
|
||||
this.$emit('exportAPI');
|
||||
break;
|
||||
case "exportJmx":
|
||||
this.$emit('exportJmx');
|
||||
break;
|
||||
handleImport() {
|
||||
if (this.projectId) {
|
||||
this.result = this.$get("/api/automation/module/list/" + this.projectId, response => {
|
||||
if (response.data != undefined && response.data != null) {
|
||||
this.data = response.data;
|
||||
let moduleOptions = [];
|
||||
this.data.forEach(node => {
|
||||
buildNodePath(node, {path: ''}, moduleOptions);
|
||||
});
|
||||
this.moduleOptions = moduleOptions
|
||||
}
|
||||
});
|
||||
this.$refs.apiImport.open(this.currentModule);
|
||||
}
|
||||
},
|
||||
list(projectId) {
|
||||
|
@ -176,11 +188,17 @@
|
|||
this.result = this.$get(url, response => {
|
||||
if (response.data != undefined && response.data != null) {
|
||||
this.data = response.data;
|
||||
let moduleOptions = [];
|
||||
this.data.forEach(node => {
|
||||
buildNodePath(node, {path: ''}, moduleOptions);
|
||||
this.extendTreeNodes = [];
|
||||
this.extendTreeNodes.unshift({
|
||||
"id": "root",
|
||||
"name": this.$t('commons.module_title'),
|
||||
"level": 0,
|
||||
"children": this.data,
|
||||
});
|
||||
this.$emit('setModuleOptions', moduleOptions);
|
||||
this.extendTreeNodes.forEach(node => {
|
||||
buildTree(node, {path: ''});
|
||||
});
|
||||
this.$emit('setModuleOptions', this.extendTreeNodes);
|
||||
this.$emit('setNodeTree', this.data);
|
||||
if (this.$refs.nodeTree) {
|
||||
this.$refs.nodeTree.filter(this.condition.filterText);
|
||||
|
|
|
@ -22,9 +22,7 @@
|
|||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId">
|
||||
<el-select class="ms-scenario-input" size="small" v-model="currentScenario.apiScenarioModuleId">
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</el-select>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="currentScenario.apiScenarioModuleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
|
@ -201,7 +199,8 @@
|
|||
@closePage="close" @unFullScreen="unFullScreen" @showAllBtn="showAllBtn" @runDebug="runDebug" @setProjectEnvMap="setProjectEnvMap" @showScenarioParameters="showScenarioParameters" @setCookieShare="setCookieShare" ref="maximizeHeader"/>
|
||||
</template>
|
||||
|
||||
<maximize-scenario :scenario-definition="scenarioDefinition" :envMap="projectEnvMap" :moduleOptions="moduleOptions" :currentScenario="currentScenario" :type="type" ref="maximizeScenario" @openScenario="openScenario"/>
|
||||
<maximize-scenario :scenario-definition="scenarioDefinition" :envMap="projectEnvMap" :moduleOptions="moduleOptions"
|
||||
:currentScenario="currentScenario" :type="type" ref="maximizeScenario" @openScenario="openScenario"/>
|
||||
</ms-drawer>
|
||||
|
||||
</div>
|
||||
|
@ -238,6 +237,7 @@
|
|||
import MaximizeScenario from "./maximize/MaximizeScenario";
|
||||
import ScenarioHeader from "./maximize/ScenarioHeader";
|
||||
import MsDrawer from "../../../common/components/MsDrawer";
|
||||
import MsSelectTree from "../../../common/select-tree/SelectTree";
|
||||
|
||||
let jsonPath = require('jsonpath');
|
||||
export default {
|
||||
|
@ -260,7 +260,8 @@
|
|||
EnvPopover,
|
||||
MaximizeScenario,
|
||||
ScenarioHeader,
|
||||
MsDrawer
|
||||
MsDrawer,
|
||||
MsSelectTree
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -268,6 +269,10 @@
|
|||
label: "label",
|
||||
children: "hashTree"
|
||||
},
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
|
@ -442,6 +447,10 @@
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
setModule(id,data) {
|
||||
this.currentScenario.apiScenarioModuleId = id;
|
||||
this.currentScenario.modulePath = data.path;
|
||||
},
|
||||
setHideBtn() {
|
||||
this.isBtnHide = false;
|
||||
},
|
||||
|
@ -577,13 +586,14 @@
|
|||
recursiveSorting(arr, scenarioProjectId) {
|
||||
for (let i in arr) {
|
||||
arr[i].index = Number(i) + 1;
|
||||
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) {
|
||||
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
|
||||
arr[i].countController.proceed = true;
|
||||
}
|
||||
if (!arr[i].projectId) {
|
||||
// 如果自身没有ID并且场景有ID则赋值场景ID,否则赋值当前项目ID
|
||||
arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId;
|
||||
}
|
||||
|
||||
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
|
||||
this.recursiveSorting(arr[i].hashTree, arr[i].projectId);
|
||||
}
|
||||
|
@ -606,6 +616,7 @@
|
|||
if (!this.scenarioDefinition[i].projectId) {
|
||||
this.scenarioDefinition[i].projectId = this.projectId;
|
||||
}
|
||||
|
||||
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
|
||||
this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId);
|
||||
}
|
||||
|
@ -753,6 +764,7 @@
|
|||
if (!sign) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$refs['currentScenario'].validate((valid) => {
|
||||
if (valid) {
|
||||
Promise.all([
|
||||
|
@ -841,15 +853,6 @@
|
|||
this.expandedNode.splice(this.expandedNode.indexOf(data.resourceId), 1);
|
||||
}
|
||||
},
|
||||
getPath(id) {
|
||||
if (id === null) {
|
||||
return null;
|
||||
}
|
||||
let path = this.moduleOptions.filter(function (item) {
|
||||
return item.id === id ? item.path : "";
|
||||
});
|
||||
return path[0].path;
|
||||
},
|
||||
setFiles(item, bodyUploadFiles, obj) {
|
||||
if (item.body) {
|
||||
if (item.body.kvs) {
|
||||
|
@ -926,7 +929,7 @@
|
|||
return bodyUploadFiles;
|
||||
},
|
||||
editScenario() {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise((resolve) => {
|
||||
document.getElementById("inputDelay").focus(); // 保存前在input框自动失焦,以免保存失败
|
||||
this.$refs['currentScenario'].validate((valid) => {
|
||||
if (valid) {
|
||||
|
@ -1008,7 +1011,6 @@
|
|||
setParameter() {
|
||||
this.currentScenario.stepTotal = this.scenarioDefinition.length;
|
||||
this.currentScenario.projectId = this.projectId;
|
||||
this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId);
|
||||
// 构建一个场景对象 方便引用处理
|
||||
let scenario = {
|
||||
id: this.currentScenario.id,
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
v-model="visible"
|
||||
placement="bottom"
|
||||
width="400"
|
||||
:disabled="isReadOnly"
|
||||
@show="showPopover"
|
||||
trigger="click">
|
||||
<env-select :project-ids="projectIds" :env-map="envMap" @close="visible = false"
|
||||
|
@ -24,6 +25,12 @@ export default {
|
|||
envMap: Map,
|
||||
projectIds: Set,
|
||||
projectList: Array,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
<el-option v-for="(environment, index) in pe.envs" :key="index"
|
||||
:label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')"
|
||||
:value="environment.id"/>
|
||||
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
|
||||
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
|
||||
{{ $t('api_test.environment.environment_config') }}
|
||||
</el-button>
|
||||
<template v-slot:empty>
|
||||
<div class="empty-environment">
|
||||
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
|
||||
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
|
||||
{{ $t('api_test.environment.environment_config') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
@ -78,12 +78,12 @@ export default {
|
|||
const project = this.projectList.find(p => p.id === id);
|
||||
return project ? project.name : "";
|
||||
},
|
||||
openEnvironmentConfig(projectId) {
|
||||
openEnvironmentConfig(projectId, envId) {
|
||||
if (!projectId) {
|
||||
this.$error(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
this.$refs.environmentConfig.open(projectId);
|
||||
this.$refs.environmentConfig.open(projectId, envId);
|
||||
},
|
||||
handleConfirm() {
|
||||
let map = new Map();
|
||||
|
|
|
@ -18,7 +18,7 @@ export const ELEMENTS = new Map([
|
|||
['CustomizeReq', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['MaxSamplerProxy', ["JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['AllSamplerProxy', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler"]],
|
||||
|
||||
['AllCanExecType', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "JSR223Processor"]]
|
||||
])
|
||||
|
||||
export const ELEMENT_TYPE = {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
|
||||
<ms-select-tree size="small" :data="moduleOptions" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="httpForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('commons.description')" prop="description" style="margin-bottom: 29px">
|
||||
|
@ -64,6 +64,7 @@
|
|||
import {createComponent, Request} from "../../../definition/components/jmeter/components";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import MsSelectTree from "@/business/components/common/select-tree/SelectTree";
|
||||
import {buildTree} from "../../../definition/model/NodeTree";
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -83,7 +84,7 @@
|
|||
callback();
|
||||
};
|
||||
return {
|
||||
httpForm: {environmentId: ""},
|
||||
httpForm: {environmentId: "", moduleId: "root"},
|
||||
moduleOptions: [],
|
||||
httpVisible: false,
|
||||
currentModule: {},
|
||||
|
@ -250,13 +251,23 @@
|
|||
let url = "/api/module/list/" + getCurrentProjectID() + "/" + data.protocol;
|
||||
this.result = this.$get(url, response => {
|
||||
if (response.data != undefined && response.data != null) {
|
||||
this.moduleOptions = response.data;
|
||||
let data = response.data;
|
||||
this.moduleOptions = [];
|
||||
this.moduleOptions.unshift({
|
||||
"id": "root",
|
||||
"name": this.$t('commons.module_title'),
|
||||
"level": 0,
|
||||
"children": data,
|
||||
});
|
||||
this.moduleOptions.forEach(node => {
|
||||
buildTree(node, {path: ''});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
setModule(id) {
|
||||
setModule(id, data) {
|
||||
this.httpForm.moduleId = id;
|
||||
//this.reload();
|
||||
this.httpForm.modulePath = data.path;
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
|
@ -271,7 +282,7 @@
|
|||
data.protocol = "DUBBO";
|
||||
}
|
||||
data.id = getUUID();
|
||||
this.httpForm = {id: data.id, name: data.name, protocol: data.protocol, path: data.path, method: api.method, userId: getCurrentUser().id, request: data};
|
||||
this.httpForm = {id: data.id, name: data.name, protocol: data.protocol, path: data.path, method: api.method, userId: getCurrentUser().id, request: data, moduleId: "root"};
|
||||
this.getMaintainerOptions();
|
||||
this.list(data);
|
||||
this.httpVisible = true;
|
||||
|
|
|
@ -101,25 +101,25 @@ export default {
|
|||
|
||||
let requestObj = JSON.parse(item.request);
|
||||
if(requestObj.esbDataStruct != null ){
|
||||
//ESB接口
|
||||
let param = {};
|
||||
param.request = requestObj;
|
||||
param.method = "ESB";
|
||||
param.esbDataStruct = JSON.stringify(requestObj.esbDataStruct);
|
||||
if(requestObj.backEsbDataStruct != null){
|
||||
param.backEsbDataStruct = JSON.stringify(requestObj.backEsbDataStruct);
|
||||
}else{
|
||||
param.backEsbDataStruct = "";
|
||||
}
|
||||
// //ESB接口
|
||||
// let param = {};
|
||||
// param.request = requestObj;
|
||||
// param.method = "ESB";
|
||||
// param.esbDataStruct = JSON.stringify(requestObj.esbDataStruct);
|
||||
// if(requestObj.backEsbDataStruct != null){
|
||||
// param.backEsbDataStruct = JSON.stringify(requestObj.backEsbDataStruct);
|
||||
// }else{
|
||||
// param.backEsbDataStruct = "";
|
||||
// }
|
||||
|
||||
this.$post("/api/definition/updateEsbRequest", param, response => {
|
||||
if(response.data!=null){
|
||||
if(response.data.request!=null){
|
||||
item.request = JSON.stringify(response.data.request);
|
||||
param.method = "TCP";
|
||||
}
|
||||
}
|
||||
})
|
||||
// this.$post("/api/definition/updateEsbRequest", param, response => {
|
||||
// if(response.data!=null){
|
||||
// if(response.data.request!=null){
|
||||
// item.request = JSON.stringify(response.data.request);
|
||||
// param.method = "TCP";
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
}
|
||||
});
|
||||
this.$emit('save', apiCases, 'CASE', reference);
|
||||
|
|
|
@ -28,9 +28,12 @@
|
|||
<div class="header-right" @click.stop>
|
||||
<slot name="message"></slot>
|
||||
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top" v-if="showBtn">
|
||||
<el-switch v-model="data.enable" class="enable-switch" size="mini" :disabled="data.disabled && !data.root"/>
|
||||
<el-switch v-model="data.enable" class="enable-switch" size="mini" :disabled="data.disabled && !data.root" style="width: 30px"/>
|
||||
</el-tooltip>
|
||||
<slot name="button"></slot>
|
||||
<el-tooltip content="Copy" placement="top">
|
||||
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="padding: 5px"/>
|
||||
</el-tooltip>
|
||||
<step-extend-btns style="display: contents" :data="data" @copy="copyRow" @remove="remove" @openScenario="openScenario" v-if="showBtn && (!data.disabled || data.root)"/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -22,11 +22,9 @@
|
|||
<el-row>
|
||||
<el-col :span="11">
|
||||
<el-form-item :label="$t('commons.import_module')">
|
||||
<el-select size="small" v-model="formData.moduleId" class="project-select" clearable>
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</el-select>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!isHar" :label="$t('commons.import_mode')">
|
||||
<el-form-item v-if="!isHar" :label="$t('commons.import_mode')">
|
||||
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
|
@ -70,16 +68,17 @@
|
|||
<script>
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
|
||||
export default {
|
||||
name: "ScenarioImport",
|
||||
components: {MsDialogFooter},
|
||||
components: {MsDialogFooter, MsSelectTree},
|
||||
props: {
|
||||
saved: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
moduleOptions: {}
|
||||
moduleOptions: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -140,7 +139,11 @@
|
|||
},
|
||||
rules: {},
|
||||
currentModule: {},
|
||||
fileList: []
|
||||
fileList: [],
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
|
@ -257,7 +260,11 @@
|
|||
this.fileList = [];
|
||||
removeGoBackListener(this.close);
|
||||
this.visible = false;
|
||||
}
|
||||
},
|
||||
setModule(id, data) {
|
||||
this.formData.moduleId = id;
|
||||
this.formData.modulePath = data.path;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<template v-slot:button>
|
||||
<el-tooltip :content="$t('api_test.run')" placement="top">
|
||||
<el-button @click="run" icon="el-icon-video-play" class="ms-btn" size="mini" circle/>
|
||||
<el-button @click="run" icon="el-icon-video-play" style="padding: 5px" class="ms-btn" size="mini" circle/>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
|
@ -289,6 +289,9 @@
|
|||
},
|
||||
active(item) {
|
||||
this.request.active = !this.request.active;
|
||||
if (this.node) {
|
||||
this.node.expanded = this.request.active;
|
||||
}
|
||||
this.reload();
|
||||
},
|
||||
run() {
|
||||
|
|
|
@ -105,11 +105,11 @@
|
|||
remove() {
|
||||
this.$emit('remove', this.scenario, this.node);
|
||||
},
|
||||
active(item) {
|
||||
if (item && item.active) {
|
||||
item.active = !item.active;
|
||||
active() {
|
||||
if (this.node) {
|
||||
this.node.expanded = !this.node.expanded;
|
||||
}
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
copyRow() {
|
||||
this.$emit('copyRow', this.scenario, this.node);
|
||||
|
|
|
@ -61,6 +61,9 @@
|
|||
},
|
||||
active() {
|
||||
this.request.active = !this.request.active;
|
||||
if (this.node) {
|
||||
this.node.expanded = this.request.active;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</template>
|
||||
|
||||
<template v-slot:button>
|
||||
<el-button @click="runDebug" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;" size="mini" circle/>
|
||||
<el-button @click="runDebug" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;padding: 5px" size="mini" circle/>
|
||||
</template>
|
||||
<div v-if="controller.loopType==='LOOP_COUNT'" draggable>
|
||||
<el-row>
|
||||
|
@ -246,6 +246,9 @@
|
|||
},
|
||||
active(item) {
|
||||
item.active = !item.active;
|
||||
if (this.node) {
|
||||
this.node.expanded = item.active;
|
||||
}
|
||||
this.reload();
|
||||
},
|
||||
changeRadio() {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<el-icon class="el-icon-more"></el-icon>
|
||||
</el-link>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="copy">复制步骤</el-dropdown-item>
|
||||
<!--<el-dropdown-item command="copy">复制步骤</el-dropdown-item>-->
|
||||
<el-dropdown-item command="remove" v-tester>删除步骤</el-dropdown-item>
|
||||
<el-dropdown-item command="scenarioVar" v-tester v-if="data.type==='scenario'">查看场景变量</el-dropdown-item>
|
||||
<el-dropdown-item command="openScenario" v-tester v-if="data.type==='scenario' && data.referenced==='REF'">打开场景</el-dropdown-item>
|
||||
|
@ -45,7 +45,7 @@
|
|||
this.$emit('remove');
|
||||
break;
|
||||
case "scenarioVar":
|
||||
this.$refs.scenarioParameters.open(this.data.variables, this.data.headers, true);
|
||||
this.$refs.scenarioParameters.open(this.data.variables, this.data.headers, this.data.referenced === 'REF');
|
||||
break;
|
||||
case "openScenario":
|
||||
this.getScenario();
|
||||
|
|
|
@ -50,39 +50,41 @@
|
|||
</div>
|
||||
</ms-aside-container>
|
||||
|
||||
<ms-main-container v-if="!loading">
|
||||
<!-- 第一层当前节点内容-->
|
||||
<ms-component-config
|
||||
:isMax="false"
|
||||
:showBtn="false"
|
||||
:type="selectedTreeNode.type"
|
||||
:scenario="selectedTreeNode"
|
||||
:response="response"
|
||||
:currentScenario="currentScenario"
|
||||
:currentEnvironmentId="currentEnvironmentId"
|
||||
:node="selectedNode"
|
||||
:project-list="projectList"
|
||||
:env-map="projectEnvMap"
|
||||
:draggable="false"
|
||||
@remove="remove" @copyRow="copyRow" @suggestClick="suggestClick" @refReload="refReload" @openScenario="openScenario"
|
||||
v-if="selectedTreeNode && selectedNode"/>
|
||||
<!-- 请求下还有的子步骤-->
|
||||
<div v-if="selectedTreeNode && selectedTreeNode.hashTree && showNode(selectedTreeNode)">
|
||||
<div v-for="item in selectedTreeNode.hashTree" :key="item.id" class="ms-col-one">
|
||||
<ms-component-config
|
||||
:showBtn="false"
|
||||
:isMax="false"
|
||||
:type="item.type"
|
||||
:scenario="item"
|
||||
:response="response"
|
||||
:currentScenario="currentScenario"
|
||||
:currentEnvironmentId="currentEnvironmentId"
|
||||
:project-list="projectList"
|
||||
:env-map="projectEnvMap"
|
||||
:draggable="false"
|
||||
@remove="remove" @copyRow="copyRow" @suggestClick="suggestClick"
|
||||
@refReload="refReload" @openScenario="openScenario"
|
||||
v-if="selectedTreeNode && selectedNode"/>
|
||||
<ms-main-container v-loading="loading">
|
||||
<div v-if="!loading">
|
||||
<!-- 第一层当前节点内容-->
|
||||
<ms-component-config
|
||||
:isMax="false"
|
||||
:showBtn="false"
|
||||
:type="selectedTreeNode.type"
|
||||
:scenario="selectedTreeNode"
|
||||
:response="response"
|
||||
:currentScenario="currentScenario"
|
||||
:currentEnvironmentId="currentEnvironmentId"
|
||||
:node="selectedNode"
|
||||
:project-list="projectList"
|
||||
:env-map="projectEnvMap"
|
||||
:draggable="false"
|
||||
@remove="remove" @copyRow="copyRow" @suggestClick="suggestClick" @refReload="refReload" @openScenario="openScenario"
|
||||
v-if="selectedTreeNode && selectedNode"/>
|
||||
<!-- 请求下还有的子步骤-->
|
||||
<div v-if="selectedTreeNode && selectedTreeNode.hashTree && showNode(selectedTreeNode)">
|
||||
<div v-for="item in selectedTreeNode.hashTree" :key="item.id" class="ms-col-one">
|
||||
<ms-component-config
|
||||
:showBtn="false"
|
||||
:isMax="false"
|
||||
:type="item.type"
|
||||
:scenario="item"
|
||||
:response="response"
|
||||
:currentScenario="currentScenario"
|
||||
:currentEnvironmentId="currentEnvironmentId"
|
||||
:project-list="projectList"
|
||||
:env-map="projectEnvMap"
|
||||
:draggable="false"
|
||||
@remove="remove" @copyRow="copyRow" @suggestClick="suggestClick"
|
||||
@refReload="refReload" @openScenario="openScenario"
|
||||
v-if="selectedTreeNode && selectedNode"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ms-main-container>
|
||||
|
@ -474,6 +476,7 @@
|
|||
}
|
||||
this.selectedTreeNode = data;
|
||||
this.selectedNode = node;
|
||||
this.reload();
|
||||
},
|
||||
suggestClick(node) {
|
||||
this.response = {};
|
||||
|
@ -493,7 +496,7 @@
|
|||
recursiveSorting(arr, scenarioProjectId) {
|
||||
for (let i in arr) {
|
||||
arr[i].index = Number(i) + 1;
|
||||
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) {
|
||||
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
|
||||
arr[i].countController.proceed = true;
|
||||
}
|
||||
if (!arr[i].projectId) {
|
||||
|
@ -521,6 +524,7 @@
|
|||
if (!this.scenarioDefinition[i].projectId) {
|
||||
this.scenarioDefinition[i].projectId = this.projectId;
|
||||
}
|
||||
|
||||
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
|
||||
this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId);
|
||||
}
|
||||
|
|
|
@ -296,8 +296,12 @@
|
|||
}
|
||||
let api = {
|
||||
status: "Underway", method: "GET", userId: getCurrentUser().id,
|
||||
url: "", protocol: this.currentProtocol, environmentId: ""
|
||||
url: "", protocol: this.currentProtocol, environmentId: "", moduleId: 'root', modulePath: "/" + this.$t("commons.module_title")
|
||||
};
|
||||
if (this.nodeTree && this.nodeTree.length > 0) {
|
||||
api.moduleId = this.nodeTree[0].id;
|
||||
api.modulePath = this.nodeTree[0].path;
|
||||
}
|
||||
this.handleTabsEdit(this.$t('api_test.definition.request.title'), e, api);
|
||||
},
|
||||
handleTabClose() {
|
||||
|
|
|
@ -185,9 +185,6 @@ export default {
|
|||
}
|
||||
this.response.body = body;
|
||||
}
|
||||
if (this.currentApi.moduleId && this.currentApi.moduleId === "root") {
|
||||
this.currentApi.moduleId = "";
|
||||
}
|
||||
},
|
||||
saveApi(data) {
|
||||
this.setParameters(data);
|
||||
|
|
|
@ -68,8 +68,8 @@
|
|||
|
||||
let projectId = "";
|
||||
// 如果envMap不存在,是单接口调用
|
||||
if (!this.envMap) {
|
||||
projectId = getCurrentProjectID();
|
||||
if (!this.envMap || this.envMap.size === 0) {
|
||||
projectId = this.$store.state.projectId;
|
||||
} else {
|
||||
// 场景步骤下接口调用
|
||||
projectId = this.runData.projectId;
|
||||
|
|
|
@ -275,19 +275,13 @@
|
|||
}
|
||||
},
|
||||
addModule(row) {
|
||||
let url = '/api/module/getModuleByName/' + getCurrentProjectID() + "/" + this.api.protocol;
|
||||
this.$get(url, response => {
|
||||
if (response.data) {
|
||||
this.$emit('refreshModule');
|
||||
this.saveApi(row, response.data);
|
||||
}
|
||||
});
|
||||
this.saveApi(row, "root");
|
||||
},
|
||||
saveApi(row, module) {
|
||||
let data = this.api;
|
||||
data.name = this.apiCase.name;
|
||||
data.moduleId = module.id;
|
||||
data.modulePath = '/bug';
|
||||
data.moduleId = module;
|
||||
data.modulePath ="/"+ this.$t('commons.module_title');
|
||||
this.setParameters(data);
|
||||
let bodyFiles = this.getBodyUploadFiles(data);
|
||||
this.$fileUpload("/api/definition/create", null, bodyFiles, data, () => {
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
this.$error(this.$t('api_test.select_project'));
|
||||
return;
|
||||
}
|
||||
this.$refs.environmentConfig.open(this.projectId);
|
||||
this.$refs.environmentConfig.open(this.projectId, this.environmentId);
|
||||
},
|
||||
environmentChange(value) {
|
||||
for (let i in this.environments) {
|
||||
|
|
|
@ -10,20 +10,7 @@
|
|||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
|
||||
<el-select class="ms-http-input" size="small" v-model="basicForm.moduleId" style="width: 100%" @change="reload">
|
||||
<div v-if="moduleOptions.length>0">
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-option :key="0" :value="''">
|
||||
<div style="margin-left: 40px">
|
||||
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
|
||||
</span>
|
||||
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
|
||||
</div>
|
||||
</el-option>
|
||||
</div>
|
||||
</el-select>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="basicForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -73,70 +60,79 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {API_STATUS} from "../../model/JsonData";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import {API_STATUS} from "../../model/JsonData";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
|
||||
export default {
|
||||
name: "MsBasisApi",
|
||||
components: {MsInputTag},
|
||||
props: {
|
||||
currentProtocol: {
|
||||
type: String,
|
||||
default: "HTTP"
|
||||
},
|
||||
moduleOptions: Array,
|
||||
basisData: {},
|
||||
},
|
||||
created() {
|
||||
this.getMaintainerOptions();
|
||||
this.basicForm = this.basisData;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
basicForm: {},
|
||||
httpVisible: false,
|
||||
currentModule: {},
|
||||
maintainerOptions: [],
|
||||
loading: false,
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
|
||||
],
|
||||
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
|
||||
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
|
||||
export default {
|
||||
name: "MsBasisApi",
|
||||
components: {MsInputTag, MsSelectTree},
|
||||
props: {
|
||||
currentProtocol: {
|
||||
type: String,
|
||||
default: "HTTP"
|
||||
},
|
||||
moduleOptions: Array,
|
||||
basisData: {},
|
||||
},
|
||||
created() {
|
||||
this.getMaintainerOptions();
|
||||
this.basicForm = this.basisData;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
basicForm: {},
|
||||
httpVisible: false,
|
||||
currentModule: {},
|
||||
maintainerOptions: [],
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
loading: false,
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
|
||||
],
|
||||
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
|
||||
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
|
||||
},
|
||||
value: API_STATUS[0].id,
|
||||
options: API_STATUS,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
setModule(id,data) {
|
||||
this.basicForm.moduleId = id;
|
||||
this.basisData.modulePath = data.path;
|
||||
},
|
||||
validate() {
|
||||
this.$refs['basicForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('callback');
|
||||
}
|
||||
})
|
||||
},
|
||||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
value: API_STATUS[0].id,
|
||||
options: API_STATUS,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
validate() {
|
||||
this.$refs['basicForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('callback');
|
||||
}
|
||||
})
|
||||
},
|
||||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -49,20 +49,7 @@
|
|||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
|
||||
<el-select class="ms-http-select" size="small" v-model="httpForm.moduleId">
|
||||
<div v-if="moduleOptions.length>0">
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-option :key="0" :value="''">
|
||||
<div style="margin-left: 40px">
|
||||
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
|
||||
</span>
|
||||
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
|
||||
</div>
|
||||
</el-option>
|
||||
</div>
|
||||
</el-select>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="httpForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
@ -109,25 +96,26 @@
|
|||
|
||||
<script>
|
||||
|
||||
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
|
||||
import MsResponseText from "../response/ResponseText";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
|
||||
import {KeyValue} from "../../model/ApiTestModel";
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
|
||||
import MsResponseText from "../response/ResponseText";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
|
||||
import {KeyValue} from "../../model/ApiTestModel";
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
|
||||
export default {
|
||||
name: "MsAddCompleteHttpApi",
|
||||
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag},
|
||||
data() {
|
||||
let validateURL = (rule, value, callback) => {
|
||||
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
|
||||
callback(this.$t('api_test.definition.request.path_valid_info'));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
export default {
|
||||
name: "MsAddCompleteHttpApi",
|
||||
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag, MsSelectTree},
|
||||
data() {
|
||||
let validateURL = (rule, value, callback) => {
|
||||
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
|
||||
callback(this.$t('api_test.definition.request.path_valid_info'));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
|
@ -147,6 +135,11 @@ export default {
|
|||
currentModule: {},
|
||||
reqOptions: REQ_METHOD,
|
||||
options: API_STATUS,
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
props: {moduleOptions: {}, request: {}, response: {}, basisData: {}, syncTabs: Array},
|
||||
|
@ -190,7 +183,6 @@ export default {
|
|||
});
|
||||
},
|
||||
setParameter() {
|
||||
this.httpForm.modulePath = this.getPath(this.httpForm.moduleId);
|
||||
this.request.path = this.httpForm.path;
|
||||
this.request.method = this.httpForm.method;
|
||||
this.httpForm.request.useEnvironment = undefined;
|
||||
|
@ -211,15 +203,6 @@ export default {
|
|||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
getPath(id) {
|
||||
if (id === null) {
|
||||
return null;
|
||||
}
|
||||
let path = this.moduleOptions.filter(function (item) {
|
||||
return item.id === id ? item.path : "";
|
||||
});
|
||||
return path[0].path;
|
||||
},
|
||||
urlChange() {
|
||||
if (!this.httpForm.path || this.httpForm.path.indexOf('?') === -1) return;
|
||||
let url = this.getURL(this.addProtocol(this.httpForm.path));
|
||||
|
@ -248,6 +231,10 @@ export default {
|
|||
this.$error(this.$t('api_test.request.url_invalid'), 2000);
|
||||
}
|
||||
},
|
||||
setModule(id,data) {
|
||||
this.httpForm.moduleId = id;
|
||||
this.httpForm.modulePath = data.path;
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
|
@ -255,7 +242,6 @@ export default {
|
|||
if (!this.basisData.environmentId) {
|
||||
this.basisData.environmentId = "";
|
||||
}
|
||||
|
||||
this.httpForm = JSON.parse(JSON.stringify(this.basisData));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('commons.name')" prop="name">
|
||||
<!-- <el-input class="ms-http-input" size="small" v-model="basicForm.name"/>-->
|
||||
<!-- <el-input class="ms-http-input" size="small" v-model="basicForm.name"/>-->
|
||||
<el-input v-model="basicForm.name" class="ms-http-input" size="small">
|
||||
<el-select v-model="basicForm.method" slot="prepend" style="width: 100px" size="small" @change="methodChange">
|
||||
<el-option v-for="item in methodTypes" :key="item" :label="item" :value="item"/>
|
||||
|
@ -16,20 +16,22 @@
|
|||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
|
||||
<el-select class="ms-http-input" size="small" v-model="basicForm.moduleId" style="width: 100%" @change="reload">
|
||||
<div v-if="moduleOptions.length>0">
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-option :key="0" :value="''">
|
||||
<div style="margin-left: 40px">
|
||||
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
|
||||
</span>
|
||||
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
|
||||
</div>
|
||||
</el-option>
|
||||
</div>
|
||||
</el-select>
|
||||
<!--<el-select class="ms-http-input" size="small" v-model="basicForm.moduleId" style="width: 100%" @change="reload">-->
|
||||
<!--<div v-if="moduleOptions.length>0">-->
|
||||
<!--<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>-->
|
||||
<!--</div>-->
|
||||
<!--<div v-else>-->
|
||||
<!--<el-option :key="0" :value="''">-->
|
||||
<!--<div style="margin-left: 40px">-->
|
||||
<!--<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},-->
|
||||
<!--</span>-->
|
||||
<!--<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>-->
|
||||
<!--</div>-->
|
||||
<!--</el-option>-->
|
||||
<!--</div>-->
|
||||
<!--</el-select>-->
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="basicForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -79,77 +81,87 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {API_STATUS} from "../../model/JsonData";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import {API_STATUS} from "../../model/JsonData";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
|
||||
export default {
|
||||
name: "MsTcpBasicApi",
|
||||
components: {MsInputTag},
|
||||
props: {
|
||||
currentProtocol: {
|
||||
type: String,
|
||||
default: "HTTP"
|
||||
},
|
||||
moduleOptions: Array,
|
||||
methodTypes: Array,
|
||||
basisData: {},
|
||||
},
|
||||
created() {
|
||||
this.getMaintainerOptions();
|
||||
this.basicForm = this.basisData;
|
||||
if(this.basicForm.protocol == null){
|
||||
this.basicForm.protocol = "TCP";
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
basicForm: {},
|
||||
httpVisible: false,
|
||||
currentModule: {},
|
||||
maintainerOptions: [],
|
||||
loading: false,
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
|
||||
],
|
||||
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
|
||||
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
|
||||
export default {
|
||||
name: "MsTcpBasicApi",
|
||||
components: {MsInputTag, MsSelectTree},
|
||||
props: {
|
||||
currentProtocol: {
|
||||
type: String,
|
||||
default: "HTTP"
|
||||
},
|
||||
moduleOptions: Array,
|
||||
methodTypes: Array,
|
||||
basisData: {},
|
||||
},
|
||||
created() {
|
||||
this.getMaintainerOptions();
|
||||
this.basicForm = this.basisData;
|
||||
if (this.basicForm.protocol == null) {
|
||||
this.basicForm.protocol = "TCP";
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
basicForm: {},
|
||||
httpVisible: false,
|
||||
currentModule: {},
|
||||
maintainerOptions: [],
|
||||
loading: false,
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
|
||||
],
|
||||
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
|
||||
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
|
||||
},
|
||||
value: API_STATUS[0].id,
|
||||
options: API_STATUS,
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
validate() {
|
||||
this.$refs['basicForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('callback');
|
||||
}
|
||||
})
|
||||
},
|
||||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
methodChange() {
|
||||
this.$emit("changeApiProtocol", this.basicForm.method);
|
||||
},
|
||||
setModule(id,data) {
|
||||
this.basisData.modulePath = data.path;
|
||||
this.basisData.moduleId = id;
|
||||
},
|
||||
value: API_STATUS[0].id,
|
||||
options: API_STATUS,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
validate() {
|
||||
this.$refs['basicForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$emit('callback');
|
||||
}
|
||||
})
|
||||
},
|
||||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
methodChange() {
|
||||
this.$emit("changeApiProtocol",this.basicForm.method);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -178,6 +178,10 @@
|
|||
<div v-else-if="apiInfo.requestBodyParamType == 'JSON-SCHEMA'" style="margin-left: 10px">
|
||||
<ms-json-code-edit :body="apiInfo.jsonSchemaBody" ref="jsonCodeEdit"/>
|
||||
</div>
|
||||
<!-- <div v-else-if="apiInfo.requestBodyParamType == 'XML'" style="margin-left: 10px">-->
|
||||
<!-- <ms-json-code-edit :body="apiInfo.jsonSchemaBody" ref="jsonCodeEdit"/>-->
|
||||
<!-- <editor v-model="formatData" :lang="mode" @init="editorInit" :theme="theme" :height="height"/>-->
|
||||
<!-- </div>-->
|
||||
<div v-else class="showDataDiv">
|
||||
<br/>
|
||||
<p style="margin: 0px 20px;"
|
||||
|
@ -423,7 +427,12 @@ export default {
|
|||
formatRowData(dataType, data) {
|
||||
var returnData = data;
|
||||
if (data) {
|
||||
returnData = data.replace(/\n/g, '<br>');
|
||||
|
||||
if(dataType === 'XML'){
|
||||
returnData = "<xmp>"+returnData+"</xmp>";
|
||||
}else{
|
||||
returnData = data.replace(/\n/g, '<br>');
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
|
@ -643,7 +652,7 @@ export default {
|
|||
|
||||
if(lastIndex < this.currentApiIndexInApiShowArray){
|
||||
//上移
|
||||
if(this.needAsyncSelect){
|
||||
// if(this.needAsyncSelect){
|
||||
//进行判断:是否还需要为apiShowArray 增加数据。 由于在当前数据前后最多展现2条数据,
|
||||
//可得: apiStepIndex-1- 2 < apiInfoArray,需要添加数据
|
||||
let dataIndex = this.apiStepIndex -3;
|
||||
|
@ -657,11 +666,11 @@ export default {
|
|||
if(this.apiShowArray.length > (this.currentApiIndexInApiShowArray+3)){
|
||||
this.apiShowArray.pop();
|
||||
}
|
||||
}
|
||||
// }
|
||||
this.apiStepIndex --;
|
||||
}else if(lastIndex > this.currentApiIndexInApiShowArray){
|
||||
//下滚
|
||||
if(this.needAsyncSelect){
|
||||
// if(this.needAsyncSelect){
|
||||
//进行判断:是否还需要为apiShowArray 增加数据。 由于在当前数据前后最多展现2条数据,
|
||||
//可得: apiStepIndex+1+ 2 < apiInfoArray,需要添加数据
|
||||
let dataIndex = this.apiStepIndex +3;
|
||||
|
@ -678,7 +687,7 @@ export default {
|
|||
let itemHeight = this.$refs.apiDocInfoDivItem[0].offsetHeight+10;
|
||||
this.$refs.apiDocInfoDiv.scrollTop = (apiDocDivScrollTop-itemHeight);
|
||||
}
|
||||
}
|
||||
// }
|
||||
this.apiStepIndex ++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,13 +46,15 @@
|
|||
icon: 'el-icon-delete',
|
||||
func: this.deleteEnvironment
|
||||
}
|
||||
]
|
||||
],
|
||||
selectEnvironmentId: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open: function (projectId) {
|
||||
open: function (projectId, envId) {
|
||||
this.visible = true;
|
||||
this.projectId = projectId;
|
||||
this.selectEnvironmentId = envId;
|
||||
this.getEnvironments();
|
||||
listenGoBack(this.close);
|
||||
},
|
||||
|
@ -114,7 +116,16 @@
|
|||
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
this.environments = response.data;
|
||||
if (this.environments.length > 0) {
|
||||
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
|
||||
if (this.selectEnvironmentId) {
|
||||
const index = this.environments.findIndex(e => e.id === this.selectEnvironmentId);
|
||||
if (index !== -1) {
|
||||
this.$refs.environmentItems.itemSelected(index, this.environments[index]);
|
||||
} else {
|
||||
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
|
||||
}
|
||||
} else {
|
||||
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
|
||||
}
|
||||
} else {
|
||||
let item = new Environment({
|
||||
projectId: this.projectId
|
||||
|
|
|
@ -25,12 +25,10 @@
|
|||
<el-row>
|
||||
<el-col :span="11">
|
||||
<el-form-item :label="$t('commons.import_module')" prop="moduleId">
|
||||
<el-select size="small" v-model="formData.moduleId" class="project-select" clearable>
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</el-select>
|
||||
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!isScenarioModel&&showImportModel" :label="$t('commons.import_mode')" prop="modeId">
|
||||
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
|
||||
<el-select size="small" v-model="formData.modeId" clearable style="width: 100%">
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
@ -59,7 +57,7 @@
|
|||
<el-switch
|
||||
v-model="swaggerSynchronization"
|
||||
@click.native="scheduleEdit"
|
||||
>
|
||||
>
|
||||
</el-switch>
|
||||
<span style="color: #6C317C;cursor: pointer;font-weight: bold;margin-left: 10px" @click="scheduleEditByText">{{$t('api_test.api_import.timing_synchronization')}}</span>
|
||||
</el-form-item>
|
||||
|
@ -98,337 +96,338 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||
import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit";
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||
import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
|
||||
export default {
|
||||
name: "ApiImport",
|
||||
components: {ScheduleImport, MsDialogFooter},
|
||||
props: {
|
||||
saved: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
moduleOptions: {},
|
||||
propotal:String,
|
||||
model: {
|
||||
type: String,
|
||||
default: 'definition'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
swaggerUrlEnable: false,
|
||||
swaggerSynchronization: false,
|
||||
showEnvironmentSelect: true,
|
||||
modeOptions: [{
|
||||
id: 'fullCoverage',
|
||||
name: this.$t('commons.cover')
|
||||
export default {
|
||||
name: "ApiImport",
|
||||
components: {ScheduleImport, MsDialogFooter, MsSelectTree},
|
||||
props: {
|
||||
saved: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
id: 'incrementalMerge',
|
||||
name: this.$t('commons.not_cover')
|
||||
}],
|
||||
protocol: "",
|
||||
platforms: [
|
||||
{
|
||||
name: 'MeterSphere',
|
||||
value: 'Metersphere',
|
||||
tip: this.$t('api_test.api_import.ms_tip'),
|
||||
exportTip: this.$t('api_test.api_import.ms_export_tip'),
|
||||
moduleOptions: Array,
|
||||
propotal: String,
|
||||
model: {
|
||||
type: String,
|
||||
default: 'definition'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
swaggerUrlEnable: false,
|
||||
swaggerSynchronization: false,
|
||||
showEnvironmentSelect: true,
|
||||
moduleObj: {
|
||||
id: 'id',
|
||||
label: 'name',
|
||||
},
|
||||
modeOptions: [{
|
||||
id: 'fullCoverage',
|
||||
name: this.$t('commons.cover')
|
||||
},
|
||||
{
|
||||
id: 'incrementalMerge',
|
||||
name: this.$t('commons.not_cover')
|
||||
}],
|
||||
protocol: "",
|
||||
platforms: [
|
||||
{
|
||||
name: 'MeterSphere',
|
||||
value: 'Metersphere',
|
||||
tip: this.$t('api_test.api_import.ms_tip'),
|
||||
exportTip: this.$t('api_test.api_import.ms_export_tip'),
|
||||
suffixes: new Set(['json'])
|
||||
},
|
||||
],
|
||||
postmanPlanform: {
|
||||
name: 'Postman',
|
||||
value: 'Postman',
|
||||
tip: this.$t('api_test.api_import.postman_tip'),
|
||||
exportTip: this.$t('api_test.api_import.post_export_tip'),
|
||||
suffixes: new Set(['json'])
|
||||
},
|
||||
],
|
||||
postmanPlanform:{
|
||||
name: 'Postman',
|
||||
value: 'Postman',
|
||||
tip: this.$t('api_test.api_import.postman_tip'),
|
||||
exportTip: this.$t('api_test.api_import.post_export_tip'),
|
||||
suffixes: new Set(['json'])
|
||||
},
|
||||
swaggerPlanform:{
|
||||
name: 'Swagger',
|
||||
value: 'Swagger2',
|
||||
tip: this.$t('api_test.api_import.swagger_tip'),
|
||||
exportTip: this.$t('api_test.api_import.swagger_export_tip'),
|
||||
suffixes: new Set(['json'])
|
||||
},
|
||||
harPlanform:{
|
||||
name: 'HAR',
|
||||
value: 'Har',
|
||||
tip: this.$t('api_test.api_import.har_tip'),
|
||||
exportTip: this.$t('api_test.api_import.har_export_tip'),
|
||||
suffixes: new Set(['har'])
|
||||
},
|
||||
esbPlanform : {
|
||||
name: 'ESB',
|
||||
value: 'ESB',
|
||||
tip: this.$t('api_test.api_import.esb_tip'),
|
||||
exportTip: this.$t('api_test.api_import.esb_export_tip'),
|
||||
suffixes: new Set(['xlsx','xls'])
|
||||
},
|
||||
selectedPlatform: {},
|
||||
selectedPlatformValue: 'Metersphere',
|
||||
result: {},
|
||||
projects: [],
|
||||
environments: [],
|
||||
useEnvironment: false,
|
||||
formData: {
|
||||
file: undefined,
|
||||
swaggerUrl: '',
|
||||
modeId: this.$t('commons.not_cover'),
|
||||
moduleId: '',
|
||||
},
|
||||
rules: {
|
||||
modeId: [
|
||||
{required: true, message: this.$t('commons.please_select_import_mode'), trigger: 'change'},
|
||||
],
|
||||
moduleId: [
|
||||
{required: true, message: this.$t('commons.please_select_import_module'), trigger: 'change'},
|
||||
],
|
||||
},
|
||||
currentModule: {},
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
this.selectedPlatform = this.platforms[0];
|
||||
},
|
||||
created() {
|
||||
this.platforms.push(this.postmanPlanform);
|
||||
this.platforms.push(this.swaggerPlanform);
|
||||
this.platforms.push(this.harPlanform);
|
||||
},
|
||||
watch: {
|
||||
selectedPlatformValue() {
|
||||
for (let i in this.platforms) {
|
||||
if (this.platforms[i].value === this.selectedPlatformValue) {
|
||||
this.selectedPlatform = this.platforms[i];
|
||||
break;
|
||||
}
|
||||
swaggerPlanform: {
|
||||
name: 'Swagger',
|
||||
value: 'Swagger2',
|
||||
tip: this.$t('api_test.api_import.swagger_tip'),
|
||||
exportTip: this.$t('api_test.api_import.swagger_export_tip'),
|
||||
suffixes: new Set(['json'])
|
||||
},
|
||||
harPlanform: {
|
||||
name: 'HAR',
|
||||
value: 'Har',
|
||||
tip: this.$t('api_test.api_import.har_tip'),
|
||||
exportTip: this.$t('api_test.api_import.har_export_tip'),
|
||||
suffixes: new Set(['har'])
|
||||
},
|
||||
esbPlanform: {
|
||||
name: 'ESB',
|
||||
value: 'ESB',
|
||||
tip: this.$t('api_test.api_import.esb_tip'),
|
||||
exportTip: this.$t('api_test.api_import.esb_export_tip'),
|
||||
suffixes: new Set(['xlsx', 'xls'])
|
||||
},
|
||||
selectedPlatform: {},
|
||||
selectedPlatformValue: 'Metersphere',
|
||||
result: {},
|
||||
projects: [],
|
||||
environments: [],
|
||||
useEnvironment: false,
|
||||
formData: {
|
||||
file: undefined,
|
||||
swaggerUrl: '',
|
||||
modeId: this.$t('commons.not_cover'),
|
||||
moduleId: '',
|
||||
},
|
||||
rules: {
|
||||
modeId: [
|
||||
{required: true, message: this.$t('commons.please_select_import_mode'), trigger: 'change'},
|
||||
],
|
||||
},
|
||||
currentModule: {},
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
propotal(){
|
||||
let postmanIndex = this.platforms.indexOf(this.postmanPlanform);
|
||||
let swaggerPlanformIndex = this.platforms.indexOf(this.swaggerPlanform);
|
||||
let harPlanformIndex = this.platforms.indexOf(this.harPlanform);
|
||||
let esbPlanformIndex = this.platforms.indexOf(this.esbPlanform);
|
||||
if(postmanIndex>=0){
|
||||
this.platforms.splice(this.platforms.indexOf(this.postmanPlanform),1);
|
||||
}
|
||||
if(swaggerPlanformIndex>=0){
|
||||
this.platforms.splice(this.platforms.indexOf(this.swaggerPlanform),1);
|
||||
}
|
||||
if(harPlanformIndex>=0){
|
||||
this.platforms.splice(this.platforms.indexOf(this.harPlanform),1);
|
||||
}
|
||||
if(esbPlanformIndex>=0){
|
||||
this.platforms.splice(this.platforms.indexOf(this.esbPlanform),1);
|
||||
}
|
||||
if(this.propotal === 'TCP'){
|
||||
this.platforms.push(this.esbPlanform);
|
||||
return true;
|
||||
}else if(this.propotal === 'HTTP'){
|
||||
this.platforms.push(this.postmanPlanform);
|
||||
this.platforms.push(this.swaggerPlanform);
|
||||
this.platforms.push(this.harPlanform);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSwagger2() {
|
||||
return this.selectedPlatformValue === 'Swagger2';
|
||||
activated() {
|
||||
this.selectedPlatform = this.platforms[0];
|
||||
},
|
||||
showImportModel() {
|
||||
return this.selectedPlatformValue != 'Har' && this.selectedPlatformValue != 'ESB';
|
||||
created() {
|
||||
this.platforms.push(this.postmanPlanform);
|
||||
this.platforms.push(this.swaggerPlanform);
|
||||
this.platforms.push(this.harPlanform);
|
||||
},
|
||||
showTemplate() {
|
||||
return this.selectedPlatformValue === 'ESB';
|
||||
},
|
||||
isScenarioModel() {
|
||||
return this.model === 'scenario';
|
||||
},
|
||||
projectId() {
|
||||
return this.$store.state.projectId
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
scheduleEdit() {
|
||||
if (!this.formData.swaggerUrl) {
|
||||
this.$warning(this.$t('commons.please_fill_path'));
|
||||
this.swaggerSynchronization = !this.swaggerSynchronization
|
||||
} else {
|
||||
if (this.swaggerSynchronization) {
|
||||
this.$refs.scheduleEdit.open(this.buildParam());
|
||||
}
|
||||
}
|
||||
},
|
||||
scheduleEditByText(){
|
||||
this.$refs.scheduleEdit.open(this.buildParam());
|
||||
},
|
||||
open(module) {
|
||||
this.currentModule = module;
|
||||
this.visible = true;
|
||||
listenGoBack(this.close);
|
||||
|
||||
},
|
||||
upload(file) {
|
||||
this.formData.file = file.file;
|
||||
},
|
||||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
this.formData.file = undefined;
|
||||
},
|
||||
downloadTemplate(){
|
||||
if(this.selectedPlatformValue == "ESB"){
|
||||
this.$fileDownload('/api/definition/export/esbExcelTemplate');
|
||||
}
|
||||
},
|
||||
uploadValidate(file, fileList) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
|
||||
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
save() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEnable)) && !this.formData.file) {
|
||||
this.$warning(this.$t('commons.please_upload'));
|
||||
return;
|
||||
watch: {
|
||||
selectedPlatformValue() {
|
||||
for (let i in this.platforms) {
|
||||
if (this.platforms[i].value === this.selectedPlatformValue) {
|
||||
this.selectedPlatform = this.platforms[i];
|
||||
break;
|
||||
}
|
||||
let url = '/api/definition/import';
|
||||
if (this.isScenarioModel) {
|
||||
url = '/api/automation/import';
|
||||
}
|
||||
let param = this.buildParam();
|
||||
this.result = this.$fileUpload(url, param.file, null, this.buildParam(), response => {
|
||||
let res = response.data;
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.visible = false;
|
||||
this.$emit('refresh', res);
|
||||
});
|
||||
} else {
|
||||
}
|
||||
},
|
||||
propotal() {
|
||||
let postmanIndex = this.platforms.indexOf(this.postmanPlanform);
|
||||
let swaggerPlanformIndex = this.platforms.indexOf(this.swaggerPlanform);
|
||||
let harPlanformIndex = this.platforms.indexOf(this.harPlanform);
|
||||
let esbPlanformIndex = this.platforms.indexOf(this.esbPlanform);
|
||||
if (postmanIndex >= 0) {
|
||||
this.platforms.splice(this.platforms.indexOf(this.postmanPlanform), 1);
|
||||
}
|
||||
if (swaggerPlanformIndex >= 0) {
|
||||
this.platforms.splice(this.platforms.indexOf(this.swaggerPlanform), 1);
|
||||
}
|
||||
if (harPlanformIndex >= 0) {
|
||||
this.platforms.splice(this.platforms.indexOf(this.harPlanform), 1);
|
||||
}
|
||||
if (esbPlanformIndex >= 0) {
|
||||
this.platforms.splice(this.platforms.indexOf(this.esbPlanform), 1);
|
||||
}
|
||||
if (this.propotal === 'TCP') {
|
||||
this.platforms.push(this.esbPlanform);
|
||||
return true;
|
||||
} else if (this.propotal === 'HTTP') {
|
||||
this.platforms.push(this.postmanPlanform);
|
||||
this.platforms.push(this.swaggerPlanform);
|
||||
this.platforms.push(this.harPlanform);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
buildParam() {
|
||||
let param = {};
|
||||
Object.assign(param, this.formData);
|
||||
param.platform = this.selectedPlatformValue;
|
||||
param.saved = this.saved;
|
||||
param.model = this.model;
|
||||
if (this.currentModule) {
|
||||
param.moduleId = this.formData.moduleId
|
||||
this.moduleOptions.filter(item => {
|
||||
if (item.id === this.formData.moduleId) {
|
||||
param.modulePath = item.path
|
||||
computed: {
|
||||
isSwagger2() {
|
||||
return this.selectedPlatformValue === 'Swagger2';
|
||||
},
|
||||
showImportModel() {
|
||||
return this.selectedPlatformValue != 'Har' && this.selectedPlatformValue != 'ESB';
|
||||
},
|
||||
showTemplate() {
|
||||
return this.selectedPlatformValue === 'ESB';
|
||||
},
|
||||
isScenarioModel() {
|
||||
return this.model === 'scenario';
|
||||
},
|
||||
projectId() {
|
||||
return this.$store.state.projectId
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
scheduleEdit() {
|
||||
if (!this.formData.swaggerUrl) {
|
||||
this.$warning(this.$t('commons.please_fill_path'));
|
||||
this.swaggerSynchronization = !this.swaggerSynchronization
|
||||
} else {
|
||||
if (this.swaggerSynchronization) {
|
||||
this.$refs.scheduleEdit.open(this.buildParam());
|
||||
}
|
||||
})
|
||||
param.modeId = this.formData.modeId
|
||||
}
|
||||
},
|
||||
scheduleEditByText() {
|
||||
this.$refs.scheduleEdit.open(this.buildParam());
|
||||
},
|
||||
open(module) {
|
||||
this.currentModule = module;
|
||||
this.visible = true;
|
||||
listenGoBack(this.close);
|
||||
|
||||
},
|
||||
upload(file) {
|
||||
this.formData.file = file.file;
|
||||
},
|
||||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
this.formData.file = undefined;
|
||||
},
|
||||
downloadTemplate() {
|
||||
if (this.selectedPlatformValue == "ESB") {
|
||||
this.$fileDownload('/api/definition/export/esbExcelTemplate');
|
||||
}
|
||||
},
|
||||
uploadValidate(file, fileList) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (this.selectedPlatform.suffixes && !this.selectedPlatform.suffixes.has(suffix)) {
|
||||
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
save() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEnable)) && !this.formData.file) {
|
||||
this.$warning(this.$t('commons.please_upload'));
|
||||
return;
|
||||
}
|
||||
let url = '/api/definition/import';
|
||||
if (this.isScenarioModel) {
|
||||
url = '/api/automation/import';
|
||||
}
|
||||
let param = this.buildParam();
|
||||
this.result = this.$fileUpload(url, param.file, null, this.buildParam(), response => {
|
||||
let res = response.data;
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.visible = false;
|
||||
this.$emit('refresh', res);
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
setModule(id, data) {
|
||||
this.formData.moduleId = id;
|
||||
this.formData.modulePath = data.path;
|
||||
},
|
||||
buildParam() {
|
||||
let param = {};
|
||||
Object.assign(param, this.formData);
|
||||
param.platform = this.selectedPlatformValue;
|
||||
param.saved = this.saved;
|
||||
param.model = this.model;
|
||||
if (this.currentModule) {
|
||||
param.moduleId = this.formData.moduleId
|
||||
param.modeId = this.formData.modeId
|
||||
}
|
||||
param.projectId = this.projectId;
|
||||
if (!this.swaggerUrlEnable) {
|
||||
param.swaggerUrl = undefined;
|
||||
}
|
||||
return param;
|
||||
},
|
||||
close() {
|
||||
this.formData = {
|
||||
file: undefined,
|
||||
swaggerUrl: ''
|
||||
};
|
||||
this.fileList = [];
|
||||
removeGoBackListener(this.close);
|
||||
this.visible = false;
|
||||
}
|
||||
param.projectId = this.projectId;
|
||||
if (!this.swaggerUrlEnable) {
|
||||
param.swaggerUrl = undefined;
|
||||
}
|
||||
return param;
|
||||
},
|
||||
close() {
|
||||
this.formData = {
|
||||
file: undefined,
|
||||
swaggerUrl: ''
|
||||
};
|
||||
this.fileList = [];
|
||||
removeGoBackListener(this.close);
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.api-import >>> .el-dialog {
|
||||
min-width: 750px;
|
||||
}
|
||||
.api-import >>> .el-dialog {
|
||||
min-width: 750px;
|
||||
}
|
||||
|
||||
.format-tip {
|
||||
background: #EDEDED;
|
||||
}
|
||||
.format-tip {
|
||||
background: #EDEDED;
|
||||
}
|
||||
|
||||
.api-upload {
|
||||
text-align: center;
|
||||
margin: auto 0;
|
||||
}
|
||||
.api-upload {
|
||||
text-align: center;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.api-upload >>> .el-upload {
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
}
|
||||
.api-upload >>> .el-upload {
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.api-upload >>> .el-upload-dragger {
|
||||
width: 100%;
|
||||
}
|
||||
.api-upload >>> .el-upload-dragger {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-radio-group {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.el-radio-group {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
margin-right: 20px;
|
||||
}
|
||||
.el-radio {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.header-bar, .format-tip, .el-form {
|
||||
border: solid #E1E1E1 1px;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.header-bar, .format-tip, .el-form {
|
||||
border: solid #E1E1E1 1px;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.header-bar {
|
||||
padding: 10px 30px;
|
||||
}
|
||||
.header-bar {
|
||||
padding: 10px 30px;
|
||||
}
|
||||
|
||||
.api-import >>> .el-dialog__body {
|
||||
padding: 15px 25px;
|
||||
}
|
||||
.api-import >>> .el-dialog__body {
|
||||
padding: 15px 25px;
|
||||
}
|
||||
|
||||
.operate-button {
|
||||
float: right;
|
||||
}
|
||||
.operate-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.save-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.el-form {
|
||||
padding: 30px 10px;
|
||||
}
|
||||
.el-form {
|
||||
padding: 30px 10px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
float: right;
|
||||
}
|
||||
.dialog-footer {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.swagger-url-disable {
|
||||
margin-top: 10px;
|
||||
.swagger-url-disable {
|
||||
margin-top: 10px;
|
||||
|
||||
margin-left: 80px;
|
||||
}
|
||||
margin-left: 80px;
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
height: 200px;
|
||||
}
|
||||
.el-divider {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -235,7 +235,7 @@
|
|||
import {
|
||||
_handleSelect,
|
||||
_handleSelectAll, buildBatchParam, getLabel,
|
||||
getSelectDataCounts, getSystemLabel, initCondition,
|
||||
getSelectDataCounts, initCondition,
|
||||
setUnSelectIds, toggleAllSelection
|
||||
} from "@/common/js/tableUtils";
|
||||
import {_filter, _sort} from "@/common/js/tableUtils";
|
||||
|
@ -380,16 +380,16 @@
|
|||
} else {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
}
|
||||
this.getSystemLabel(this.type)
|
||||
this.initTable();
|
||||
this.getMaintainerOptions();
|
||||
getLabel(this, API_LIST);
|
||||
},
|
||||
watch: {
|
||||
selectNodeIds() {
|
||||
initCondition(this.condition,false);
|
||||
this.initTable();
|
||||
},
|
||||
currentProtocol() {
|
||||
initCondition(this.condition,false);
|
||||
this.initTable();
|
||||
},
|
||||
trashEnable() {
|
||||
|
@ -399,21 +399,12 @@
|
|||
} else {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
}
|
||||
initCondition(this.condition,false);
|
||||
this.initTable();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getSystemLabel(type) {
|
||||
let param = {}
|
||||
param.type = type
|
||||
this.$post('/system/system/header', param, response => {
|
||||
if (response.data != null) {
|
||||
this.tableLabel = eval(response.data.props);
|
||||
}
|
||||
})
|
||||
},
|
||||
customHeader() {
|
||||
getLabel(this, API_LIST);
|
||||
this.$refs.headerCustom.open(this.tableLabel)
|
||||
},
|
||||
handleBatchMove() {
|
||||
|
@ -421,7 +412,7 @@
|
|||
},
|
||||
initTable() {
|
||||
this.selectRows = new Set();
|
||||
initCondition(this.condition);
|
||||
initCondition(this.condition,this.condition.selectAll);
|
||||
this.selectDataCounts = 0;
|
||||
this.condition.moduleIds = this.selectNodeIds;
|
||||
this.condition.projectId = this.projectId;
|
||||
|
@ -464,13 +455,42 @@
|
|||
}
|
||||
})
|
||||
if (this.$refs.apiDefinitionTable) {
|
||||
this.$refs.apiDefinitionTable.doLayout()
|
||||
setTimeout(() => {
|
||||
this.$refs.apiDefinitionTable.doLayout();
|
||||
this.result.loading = false;
|
||||
}, 500)
|
||||
}
|
||||
|
||||
// nexttick:表格加载完成之后触发。判断是否需要勾选行
|
||||
this.$nextTick(function(){
|
||||
this.checkTableRowIsSelect();
|
||||
})
|
||||
});
|
||||
}
|
||||
getLabel(this, API_LIST);
|
||||
},
|
||||
checkTableRowIsSelect(){
|
||||
//如果默认全选的话,则选中应该选中的行
|
||||
if(this.condition.selectAll){
|
||||
let unSelectIds = this.condition.unSelectIds;
|
||||
this.tableData.forEach(row=>{
|
||||
if(unSelectIds.indexOf(row.id)<0){
|
||||
this.$refs.apiDefinitionTable.toggleRowSelection(row,true);
|
||||
|
||||
//默认全选,需要把选中对行添加到selectRows中。不然会影响到勾选函数统计
|
||||
if (!this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", true);
|
||||
this.selectRows.add(row);
|
||||
}
|
||||
}else{
|
||||
//不勾选的行,也要判断是否被加入了selectRow中。加入了的话就去除。
|
||||
if (this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", false);
|
||||
this.selectRows.delete(row);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
genProtocalFilter(protocalType) {
|
||||
if (protocalType === "HTTP") {
|
||||
this.methodFilters = [
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
:condition="condition"
|
||||
:current-module="currentModule"
|
||||
:is-read-only="isReadOnly"
|
||||
:moduleOptions="extendTreeNodes"
|
||||
@exportAPI="exportAPI"
|
||||
@saveAsEdit="saveAsEdit"
|
||||
@refreshTable="$emit('refreshTable')"
|
||||
|
@ -34,25 +35,25 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsAddBasisApi from "../basis/AddBasisApi";
|
||||
import SelectMenu from "../../../../track/common/SelectMenu";
|
||||
import {OPTIONS} from "../../model/JsonData";
|
||||
import ApiImport from "../import/ApiImport";
|
||||
import MsNodeTree from "../../../../track/common/NodeTree";
|
||||
import ApiModuleHeader from "./ApiModuleHeader";
|
||||
import {buildNodePath} from "../../model/NodeTree";
|
||||
import MsAddBasisApi from "../basis/AddBasisApi";
|
||||
import SelectMenu from "../../../../track/common/SelectMenu";
|
||||
import {OPTIONS} from "../../model/JsonData";
|
||||
import ApiImport from "../import/ApiImport";
|
||||
import MsNodeTree from "../../../../track/common/NodeTree";
|
||||
import ApiModuleHeader from "./ApiModuleHeader";
|
||||
import {buildNodePath, buildTree} from "../../model/NodeTree";
|
||||
|
||||
export default {
|
||||
name: 'MsApiModule',
|
||||
components: {
|
||||
ApiModuleHeader,
|
||||
MsNodeTree,
|
||||
MsAddBasisApi,
|
||||
SelectMenu,
|
||||
ApiImport
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
export default {
|
||||
name: 'MsApiModule',
|
||||
components: {
|
||||
ApiModuleHeader,
|
||||
MsNodeTree,
|
||||
MsAddBasisApi,
|
||||
SelectMenu,
|
||||
ApiImport
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
condition: {
|
||||
protocol: OPTIONS[0].value,
|
||||
|
@ -61,6 +62,7 @@ export default {
|
|||
},
|
||||
data: [],
|
||||
currentModule: {},
|
||||
extendTreeNodes: [],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -129,11 +131,17 @@ export default {
|
|||
this.result = this.$get(url, response => {
|
||||
if (response.data != undefined && response.data != null) {
|
||||
this.data = response.data;
|
||||
let moduleOptions = [];
|
||||
this.data.forEach(node => {
|
||||
buildNodePath(node, {path: ''}, moduleOptions);
|
||||
this.extendTreeNodes = [];
|
||||
this.extendTreeNodes.unshift({
|
||||
"id": "root",
|
||||
"name": this.$t('commons.module_title'),
|
||||
"level": 0,
|
||||
"children": this.data,
|
||||
});
|
||||
this.$emit('setModuleOptions', moduleOptions);
|
||||
this.extendTreeNodes.forEach(node => {
|
||||
buildTree(node, {path: ''});
|
||||
});
|
||||
this.$emit('setModuleOptions', this.extendTreeNodes);
|
||||
this.$emit('setNodeTree', this.data);
|
||||
if (this.$refs.nodeTree) {
|
||||
this.$refs.nodeTree.filter(this.condition.filterText);
|
||||
|
|
|
@ -1,46 +1,23 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-select class="protocol-select" size="small" v-model="condition.protocol">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:name="item.name"
|
||||
:value="item.value"
|
||||
:disabled="item.disabled">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-input class="filter-input" :class="{'read-only': isReadOnly}" :placeholder="$t('test_track.module.search')" v-model="condition.filterText"
|
||||
size="small">
|
||||
<template v-slot:append>
|
||||
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')"
|
||||
v-tester
|
||||
@command="handleCommand"
|
||||
:hide-on-click='false'
|
||||
trigger="click">
|
||||
<el-button icon="el-icon-folder-add" @click="addApi"></el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="add-api">{{ $t('api_test.definition.request.title') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="debug">{{ $t('api_test.definition.request.fast_debug') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="export">
|
||||
<el-dropdown placement="right-start" @command="chooseExportType">
|
||||
<span>
|
||||
{{ $t('report.export') }} <i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<template>
|
||||
<el-dropdown-item command="export-MS">{{ $t('report.export_to_ms_format') }}</el-dropdown-item>
|
||||
<el-dropdown-item command="export-Swagger" v-show="condition.protocol=='HTTP'">
|
||||
{{ $t('report.export_to_swagger3_format') }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-row>
|
||||
<el-col class="protocol-col" :span="9">
|
||||
<el-select class="protocol-select" size="small" v-model="condition.protocol">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="item.value"
|
||||
:name="item.name"
|
||||
:value="item.value"
|
||||
:disabled="item.disabled">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="15">
|
||||
<ms-search-bar
|
||||
:condition="condition"
|
||||
:commands="operators"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
|
||||
|
||||
|
@ -54,127 +31,128 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {OPTIONS} from "../../model/JsonData";
|
||||
import MsAddBasisApi from "../basis/AddBasisApi";
|
||||
import ApiImport from "../import/ApiImport";
|
||||
import ModuleTrashButton from "./ModuleTrashButton";
|
||||
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
|
||||
import TemplateComponent from "../../../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
|
||||
|
||||
export default {
|
||||
name: "ApiModuleHeader",
|
||||
components: {TemplateComponent, ModuleTrashButton, ApiImport, MsAddBasisApi},
|
||||
data() {
|
||||
return {
|
||||
options: OPTIONS,
|
||||
moduleOptions: {}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
condition: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
currentModule: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
projectId() {
|
||||
return this.$store.state.projectId
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleCommand(e) {
|
||||
switch (e) {
|
||||
case "debug":
|
||||
this.$emit('debug');
|
||||
break;
|
||||
case "add-api":
|
||||
this.addApi();
|
||||
break;
|
||||
case "add-module":
|
||||
break;
|
||||
case "import":
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
import {OPTIONS} from "../../model/JsonData";
|
||||
import MsAddBasisApi from "../basis/AddBasisApi";
|
||||
import ApiImport from "../import/ApiImport";
|
||||
import ModuleTrashButton from "./ModuleTrashButton";
|
||||
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
|
||||
import TemplateComponent from "../../../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
|
||||
import MsSearchBar from "@/business/components/common/components/search/MsSearchBar";
|
||||
export default {
|
||||
name: "ApiModuleHeader",
|
||||
components: {MsSearchBar, TemplateComponent, ModuleTrashButton, ApiImport, MsAddBasisApi},
|
||||
data() {
|
||||
return {
|
||||
options: OPTIONS,
|
||||
operators: [
|
||||
{
|
||||
label: this.$t('api_test.definition.request.title'),
|
||||
callback: this.addApi
|
||||
},
|
||||
{
|
||||
label: this.$t('api_test.definition.request.fast_debug'),
|
||||
callback: () => {this.$emit('debug')}
|
||||
},
|
||||
{
|
||||
label: this.$t('api_test.api_import.label'),
|
||||
callback: this.handleImport
|
||||
},
|
||||
{
|
||||
label: this.$t('report.export'),
|
||||
children: [
|
||||
{
|
||||
label: this.$t('report.export_to_ms_format') ,
|
||||
callback: () => {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
}
|
||||
this.$emit('exportAPI', 'MS');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t('report.export_to_swagger3_format'),
|
||||
callback: () => {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
}
|
||||
this.$emit('exportAPI', 'Swagger');
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
this.protocol = "HTTP";
|
||||
this.result = this.$get("/api/module/list/" + this.projectId + "/" + this.condition.protocol, response => {
|
||||
if (response.data != undefined && response.data != null) {
|
||||
this.data = response.data;
|
||||
let moduleOptions = [];
|
||||
this.data.forEach(node => {
|
||||
buildNodePath(node, {path: ''}, moduleOptions);
|
||||
});
|
||||
this.moduleOptions = moduleOptions
|
||||
}
|
||||
this.$refs.apiImport.open(this.moduleOptions);
|
||||
});
|
||||
break;
|
||||
]
|
||||
}
|
||||
},
|
||||
chooseExportType(e) {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
props: {
|
||||
condition: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
moduleOptions: Array,
|
||||
currentModule: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
return false
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
projectId() {
|
||||
return this.$store.state.projectId
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleImport() {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
}
|
||||
this.protocol = "HTTP";
|
||||
this.$refs.apiImport.open(this.moduleOptions);
|
||||
},
|
||||
addApi() {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
}
|
||||
this.$refs.basisApi.open(this.currentModule);
|
||||
},
|
||||
saveAsEdit(data) {
|
||||
this.$emit('saveAsEdit', data);
|
||||
},
|
||||
refresh() {
|
||||
this.$emit('refresh');
|
||||
},
|
||||
enableTrash() {
|
||||
this.condition.trashEnable = true;
|
||||
}
|
||||
switch (e) {
|
||||
case "export-MS":
|
||||
this.$emit('exportAPI', 'MS');
|
||||
break;
|
||||
case "export-Swagger":
|
||||
this.$emit('exportAPI', 'Swagger');
|
||||
break;
|
||||
}
|
||||
},
|
||||
addApi() {
|
||||
if (!this.projectId) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
return;
|
||||
}
|
||||
this.$refs.basisApi.open(this.currentModule);
|
||||
},
|
||||
saveAsEdit(data) {
|
||||
this.$emit('saveAsEdit', data);
|
||||
},
|
||||
refresh() {
|
||||
this.$emit('refresh');
|
||||
},
|
||||
enableTrash() {
|
||||
this.condition.trashEnable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.protocol-select {
|
||||
width: 92px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.read-only {
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
width: 174px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.protocol-select {
|
||||
width: 92px;
|
||||
height: 30px;
|
||||
}
|
||||
.protocol-col {
|
||||
min-width: 93px;
|
||||
}
|
||||
.read-only {
|
||||
width: 150px !important;
|
||||
}
|
||||
.filter-input {
|
||||
width: 174px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -9,4 +9,16 @@ export function buildNodePath(node, option, moduleOptions) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// 构建树
|
||||
export function buildTree(node, option) {
|
||||
option.id = node.id;
|
||||
option.name = node.name;
|
||||
option.path = option.path + '/' + node.name;
|
||||
node.path = option.path;
|
||||
if (node.children) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
buildTree(node.children[i], {path: option.path});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,13 +46,15 @@
|
|||
icon: 'el-icon-delete',
|
||||
func: this.deleteEnvironment
|
||||
}
|
||||
]
|
||||
],
|
||||
selectEnvironmentId: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open: function (projectId) {
|
||||
open: function (projectId, envId) {
|
||||
this.visible = true;
|
||||
this.projectId = projectId;
|
||||
this.selectEnvironmentId = envId;
|
||||
this.getEnvironments();
|
||||
listenGoBack(this.close);
|
||||
},
|
||||
|
@ -114,7 +116,16 @@
|
|||
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
this.environments = response.data;
|
||||
if (this.environments.length > 0) {
|
||||
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
|
||||
if (this.selectEnvironmentId) {
|
||||
const index = this.environments.findIndex(e => e.id === this.selectEnvironmentId);
|
||||
if (index !== -1) {
|
||||
this.$refs.environmentItems.itemSelected(index, this.environments[index]);
|
||||
} else {
|
||||
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
|
||||
}
|
||||
} else {
|
||||
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
|
||||
}
|
||||
} else {
|
||||
let item = new Environment({
|
||||
projectId: this.projectId
|
||||
|
|
|
@ -155,7 +155,7 @@ export default {
|
|||
width: 100%;
|
||||
background: white;
|
||||
height: 100vh;
|
||||
z-index: 999999;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.full-screen >>> .minder-container {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<el-input class="ms-search-bar" :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small">
|
||||
<template v-slot:append>
|
||||
<el-dropdown>
|
||||
<el-button type="primary">
|
||||
<span class="tip-font">{{ $t('commons.more_operator') }}</span>
|
||||
<i class="el-icon-arrow-down el-icon--right"/>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="(item, index) in commands" :key="index" @click.native.stop="click(item)">
|
||||
<span class="tip-font" v-if="!item.children">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
<el-dropdown placement="right-start" v-else>
|
||||
<span class="tip-font">
|
||||
{{ item.label }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<template>
|
||||
<el-dropdown-item v-for="(child, index) in item.children" :key="index" @click.native.stop="click(child)">
|
||||
<span class="tip-font">
|
||||
{{child.label}}
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
</template>
|
||||
</el-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsSearchBar",
|
||||
props: {
|
||||
condition: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
commands: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('api_test.api_import.label'),
|
||||
callback: () => {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click(item) {
|
||||
if (item.callback) {
|
||||
item.callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tip-font {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.el-dropdown .el-button {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -52,6 +52,10 @@ export default {
|
|||
this.defaultCheckedKeys.push(i.id)
|
||||
}
|
||||
)
|
||||
if(this.type==='api_list'||this.type==='api_case_list'||this.type==='api_scenario_list'||this.type==='test_plan_function_test_case'
|
||||
||this.type==='test_plan_api_case'||this.type==='test_plan_load_case'||this.type==='test_plan_scenario_case'){
|
||||
this.fieldSelected=items
|
||||
}
|
||||
},
|
||||
saveHeader() {
|
||||
let param = {
|
||||
|
|
|
@ -81,7 +81,6 @@ export const Test_Case_Review_Case_List = [
|
|||
{id: 'name', label: i18n.t('commons.name')},
|
||||
{id: 'priority', label: i18n.t('test_track.case.priority')},
|
||||
{id: 'type', label: i18n.t('test_track.case.type')},
|
||||
{id: 'method', label: i18n.t('test_track.case.method')},
|
||||
{id: 'nodePath', label: i18n.t('test_track.case.module')},
|
||||
{id: 'projectName', label: i18n.t('test_track.review.review_project')},
|
||||
{id: 'reviewerName', label: i18n.t('test_track.review.reviewer')},
|
||||
|
@ -107,7 +106,7 @@ export const Test_Plan_Function_Test_Case = [
|
|||
//测试计划-api用例
|
||||
export const Test_Plan_Api_Case = [
|
||||
{id: 'num', label: i18n.t('commons.id')},
|
||||
{id: 'name', label: i18n.t('commons.name')},
|
||||
{id: 'name', label: i18n.t('api_test.definition.api_name')},
|
||||
{id: 'priority', label: i18n.t('test_track.case.priority')},
|
||||
{id: 'path', label: i18n.t('api_test.definition.api_path')},
|
||||
{id: 'createUser', label: '创建人'},
|
||||
|
@ -124,12 +123,12 @@ export const Test_Plan_Load_Case = [
|
|||
{id: 'createTime', label: i18n.t('commons.create_time')},
|
||||
{id: 'status', label: i18n.t('commons.status')},
|
||||
{id: 'caseStatus', label: i18n.t('test_track.plan.load_case.execution_status')},
|
||||
{id: 'loadReportId', label: i18n.t('test_track.plan.load_case.view_report')},
|
||||
{id: 'loadReportId', label: i18n.t('test_track.plan.load_case.report')},
|
||||
]
|
||||
//测试计划-场景用例
|
||||
export const Test_Plan_Scenario_Case = [
|
||||
{id: 'num', label: i18n.t('commons.id')},
|
||||
{id: 'name', label: i18n.t('commons.name')},
|
||||
{id: 'name', label: i18n.t('api_test.automation.scenario_name')},
|
||||
{id: 'level', label: i18n.t('api_test.automation.case_level')},
|
||||
{id: 'tagNames', label: i18n.t('api_test.automation.tag')},
|
||||
{id: 'userId', label: i18n.t('api_test.automation.creator')},
|
||||
|
|
|
@ -152,10 +152,13 @@
|
|||
return JSON.stringify(this.data).indexOf(this.obj.children) !== -1 ? this.data : this.switchTree();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
methods: {
|
||||
outsideClick(e) {
|
||||
e.stopPropagation();
|
||||
this.isShowSelect=false;
|
||||
this.isShowSelect = false;
|
||||
},
|
||||
init() {
|
||||
if (this.defaultKey != undefined && this.defaultKey.length > 0) {
|
||||
|
@ -233,7 +236,9 @@
|
|||
setKey(thisKey) {
|
||||
this.$refs.tree.setCurrentKey(thisKey);
|
||||
let node = this.$refs.tree.getNode(thisKey);
|
||||
this.setData(node.data);
|
||||
if (node && node.data) {
|
||||
this.setData(node.data);
|
||||
}
|
||||
},
|
||||
//单选:设置、初始化对象
|
||||
setData(data) {
|
||||
|
@ -284,7 +289,7 @@
|
|||
},
|
||||
//下拉框关闭执行
|
||||
popoverHide() {
|
||||
this.$emit('getValue', this.returnDataKeys, this.returnDatas);
|
||||
this.$emit('getValue', this.returnDataKeys, this.returnDatas ? this.returnDatas : {});
|
||||
},
|
||||
// 多选,清空所有勾选
|
||||
clearSelectedNodes() {
|
||||
|
@ -340,10 +345,13 @@
|
|||
// 隐藏select自带的下拉框
|
||||
this.$refs.select.blur();
|
||||
},
|
||||
treeData() {//监听tree数据
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
})
|
||||
treeData: {//监听tree数据
|
||||
handler: function () {
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
})
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
filterText(val) {
|
||||
this.$nextTick(() => {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<template>
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card>
|
||||
|
||||
</el-card>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsContainer from "@/business/components/common/components/MsContainer";
|
||||
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
|
||||
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "PerformanceReportCompare",
|
||||
components: {MsMainContainer, MsContainer},
|
||||
mounted() {
|
||||
let reportId = this.$route.path.split('/')[4];
|
||||
console.log(reportId);
|
||||
let items = localStorage.getItem("compareReportIds");
|
||||
console.log(JSON.parse(items));
|
||||
},
|
||||
computed: {
|
||||
isReadOnly() {
|
||||
return !checkoutTestManagerOrTestUser();
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -28,10 +28,6 @@
|
|||
<el-button :disabled="isReadOnly" type="warning" plain size="mini" @click="downloadJtl()">
|
||||
{{ $t('report.downloadJtl') }}
|
||||
</el-button>
|
||||
|
||||
<!--<el-button :disabled="isReadOnly" type="warning" plain size="mini">-->
|
||||
<!--{{$t('report.compare')}}-->
|
||||
<!--</el-button>-->
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
|
@ -83,6 +79,7 @@
|
|||
</div>
|
||||
</el-dialog>
|
||||
</ms-main-container>
|
||||
<same-test-reports ref="compareReports"/>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
|
@ -99,11 +96,13 @@ import {checkoutTestManagerOrTestUser, exportPdf} from "@/common/js/utils";
|
|||
import html2canvas from 'html2canvas';
|
||||
import MsPerformanceReportExport from "./PerformanceReportExport";
|
||||
import {Message} from "element-ui";
|
||||
import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
|
||||
|
||||
|
||||
export default {
|
||||
name: "PerformanceReportView",
|
||||
components: {
|
||||
SameTestReports,
|
||||
MsPerformanceReportExport,
|
||||
MsReportErrorLog,
|
||||
MsReportLogDetails,
|
||||
|
@ -312,6 +311,9 @@ export default {
|
|||
Message.error({message: JSON.parse(data).message || e.message, showClose: true});
|
||||
});
|
||||
});
|
||||
},
|
||||
compareReports() {
|
||||
this.$refs.compareReports.open(this.report);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -25,37 +25,32 @@
|
|||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="testName"
|
||||
:label="$t('report.test_name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="projectName"
|
||||
:label="$t('report.project_name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="userName"
|
||||
:label="$t('report.user_name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="createTime"
|
||||
sortable
|
||||
width="250"
|
||||
:label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode"
|
||||
<el-table-column prop="triggerMode" width="150" :label="$t('test_track.report.list.trigger_mode')" column-key="triggerMode"
|
||||
:filters="triggerFilters">
|
||||
<template v-slot:default="scope">
|
||||
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
|
||||
|
@ -85,6 +80,7 @@
|
|||
:total="total"/>
|
||||
</el-card>
|
||||
</ms-main-container>
|
||||
<same-test-reports ref="compareReports"/>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
|
@ -101,11 +97,14 @@ import MsTableHeader from "../../common/components/MsTableHeader";
|
|||
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
|
||||
import ShowMoreBtn from "../../track/case/components/ShowMoreBtn";
|
||||
import {_filter, _sort} from "@/common/js/tableUtils";
|
||||
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
|
||||
|
||||
export default {
|
||||
name: "PerformanceTestReportList",
|
||||
components: {
|
||||
SameTestReports,
|
||||
MsDialogFooter,
|
||||
MsTableHeader,
|
||||
ReportTriggerModeItem,
|
||||
MsTableOperatorButton,
|
||||
|
@ -200,6 +199,9 @@ export default {
|
|||
}
|
||||
});
|
||||
},
|
||||
handleDiff(report) {
|
||||
this.$refs.compareReports.open(report);
|
||||
},
|
||||
_handleDeleteNoMsg(report) {
|
||||
this.result = this.$post(this.deletePath + report.id, {}, () => {
|
||||
this.initTableData();
|
||||
|
|
|
@ -1,24 +1,32 @@
|
|||
<template>
|
||||
<div v-loading="result.loading" class="pressure-config-container">
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-col :span="12">
|
||||
|
||||
<el-collapse v-model="activeNames" accordion>
|
||||
<el-collapse-item :name="index"
|
||||
v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')"
|
||||
:key="index">
|
||||
<template slot="title">
|
||||
<div style="padding-right: 10px">
|
||||
{{ threadGroup.attributes.testname }}
|
||||
</div>
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'">
|
||||
{{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }}
|
||||
</el-tag>
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'ITERATION'">
|
||||
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }}
|
||||
</el-tag>
|
||||
<el-row>
|
||||
<el-col :span="14">
|
||||
<el-tooltip :content="threadGroup.attributes.testname" placement="top">
|
||||
<div style="padding-right:20px; font-size: 16px;" class="wordwrap">
|
||||
{{ threadGroup.attributes.testname }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'">
|
||||
{{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }}
|
||||
</el-tag>
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'ITERATION'">
|
||||
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }}
|
||||
</el-tag>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-form :inline="true">
|
||||
<el-form-item :label="$t('load_test.thread_num')">
|
||||
|
@ -134,7 +142,7 @@
|
|||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-col :span="12">
|
||||
<ms-chart class="chart-container" ref="chart1" :options="options" :autoresize="true"></ms-chart>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -578,4 +586,9 @@ export default {
|
|||
.title {
|
||||
margin-left: 60px;
|
||||
}
|
||||
.wordwrap {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<el-table-column
|
||||
prop="ko"
|
||||
label="KO"
|
||||
label="FAIL"
|
||||
align="center"
|
||||
/>
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<el-dialog :close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
:title="$t('已完成的测试报告')" width="60%"
|
||||
:visible.sync="loadReportVisible">
|
||||
<el-table v-loading="reportLoadingResult.loading"
|
||||
class="basic-config"
|
||||
:data="compareReports"
|
||||
@select-all="handleSelectAll"
|
||||
@select="handleSelectionChange">
|
||||
|
||||
<el-table-column type="selection"/>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="userName"
|
||||
:label="$t('report.user_name')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column prop="triggerMode"
|
||||
:label="$t('test_track.report.list.trigger_mode')">
|
||||
<template v-slot:default="scope">
|
||||
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<i class="el-icon-time"/>
|
||||
<span class="last-modified">{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ms-table-pagination :change="getCompareReports" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
|
||||
<template v-slot:footer>
|
||||
<ms-dialog-footer @cancel="close" @confirm="handleCompare"/>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import ReportTriggerModeItem from "@/business/components/common/tableItem/ReportTriggerModeItem";
|
||||
|
||||
export default {
|
||||
name: "SameTestReports",
|
||||
components: {ReportTriggerModeItem, MsDialogFooter, MsTablePagination},
|
||||
data() {
|
||||
return {
|
||||
loadReportVisible: false,
|
||||
reportLoadingResult: {},
|
||||
compareReports: [],
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
selectIds: new Set,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open(report) {
|
||||
this.getCompareReports(report.testId);
|
||||
this.loadReportVisible = true;
|
||||
},
|
||||
close() {
|
||||
this.loadReportVisible = false;
|
||||
},
|
||||
getCompareReports(testId) {
|
||||
|
||||
let condition = {
|
||||
testId: testId,
|
||||
filters: {status: ["Completed"]}
|
||||
};
|
||||
this.reportLoadingResult = this.$post('/performance/report/list/all/' + this.currentPage + "/" + this.pageSize, condition, res => {
|
||||
let data = res.data;
|
||||
this.total = data.itemCount;
|
||||
this.compareReports = data.listObject;
|
||||
})
|
||||
},
|
||||
handleCompare() {
|
||||
let reportIds = [...this.selectIds];
|
||||
localStorage.setItem("compareReportIds", JSON.stringify(reportIds));
|
||||
this.close();
|
||||
this.$router.push({path: '/performance/report/compare/' + reportIds[0]});
|
||||
},
|
||||
handleSelectAll(selection) {
|
||||
if (selection.length > 0) {
|
||||
this.compareReports.forEach(item => {
|
||||
this.selectIds.add(item.id);
|
||||
});
|
||||
} else {
|
||||
this.compareReports.forEach(item => {
|
||||
if (this.selectIds.has(item.id)) {
|
||||
this.selectIds.delete(item.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
handleSelectionChange(selection, row) {
|
||||
if (this.selectIds.has(row.id)) {
|
||||
this.selectIds.delete(row.id);
|
||||
} else {
|
||||
this.selectIds.add(row.id);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,5 +1,3 @@
|
|||
import MsProject from "@/business/components/settings/project/MsProject";
|
||||
|
||||
const PerformanceTest = () => import('@/business/components/performance/PerformanceTest')
|
||||
const PerformanceTestHome = () => import('@/business/components/performance/home/PerformanceTestHome')
|
||||
const EditPerformanceTest = () => import('@/business/components/performance/test/EditPerformanceTest')
|
||||
|
@ -7,6 +5,7 @@ const PerformanceTestList = () => import('@/business/components/performance/test
|
|||
const PerformanceTestReportList = () => import('@/business/components/performance/report/PerformanceTestReportList')
|
||||
const PerformanceChart = () => import('@/business/components/performance/report/components/PerformanceChart')
|
||||
const PerformanceReportView = () => import('@/business/components/performance/report/PerformanceReportView')
|
||||
const PerformanceReportCompare = () => import('@/business/components/performance/report/PerformanceReportCompare')
|
||||
|
||||
export default {
|
||||
path: "/performance",
|
||||
|
@ -62,6 +61,11 @@ export default {
|
|||
path: "report/view/:reportId",
|
||||
name: "perReportView",
|
||||
component: PerformanceReportView
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "report/compare/:reportId",
|
||||
name: "ReportCompare",
|
||||
component: PerformanceReportCompare,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ export default {
|
|||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
if (this.tableData.filter(f => f.name === row.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
this.$error(this.$t('load_test.delete_file') + ', name: ' + row.name);
|
||||
this.selectIds.clear();
|
||||
return;
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ export default {
|
|||
}
|
||||
|
||||
if (this.tableData.filter(f => f.name === file.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
this.$error(this.$t('load_test.delete_file') + ', name: ' + file.name);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -121,7 +121,7 @@ export default {
|
|||
for (let i = 0; i < rows.length; i++) {
|
||||
let row = rows[i];
|
||||
if (this.tableData.filter(f => f.name === row.name + ".jmx").length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
this.$error(this.$t('load_test.delete_file') + ', name: ' + row.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<el-col>
|
||||
<el-form :inline="true">
|
||||
<el-form-item :label="$t('load_test.select_resource_pool')">
|
||||
<el-select v-model="resourcePool" :disabled="isReadOnly" size="mini">
|
||||
<el-select v-model="resourcePool" :disabled="isReadOnly" size="mini" @change="resourcePoolChange">
|
||||
<el-option
|
||||
v-for="item in resourcePools"
|
||||
:key="item.id"
|
||||
|
@ -23,17 +23,25 @@
|
|||
v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')"
|
||||
:key="index">
|
||||
<template slot="title">
|
||||
<div style="padding-right: 20px; font-size: 16px;">
|
||||
{{ threadGroup.attributes.testname }}
|
||||
</div>
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'">
|
||||
{{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }}
|
||||
</el-tag>
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'ITERATION'">
|
||||
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
|
||||
{{$t('load_test.iterate_num')}} {{threadGroup.iterateNum}}
|
||||
</el-tag>
|
||||
<el-row>
|
||||
<el-col :span="14">
|
||||
<el-tooltip :content="threadGroup.attributes.testname" placement="top">
|
||||
<div style="padding-right:20px; font-size: 16px;" class="wordwrap">
|
||||
{{ threadGroup.attributes.testname }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'">
|
||||
{{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }}
|
||||
</el-tag>
|
||||
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'ITERATION'">
|
||||
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
|
||||
{{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }}
|
||||
</el-tag>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-form :inline="true">
|
||||
<el-form-item :label="$t('load_test.thread_num')">
|
||||
|
@ -42,6 +50,7 @@
|
|||
v-model="threadGroup.threadNumber"
|
||||
@change="calculateTotalChart(threadGroup)"
|
||||
:min="resourcePoolResourceLength"
|
||||
:max="maxThreadNumbers"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
|
@ -109,6 +118,7 @@
|
|||
<el-input-number
|
||||
:disabled="isReadOnly"
|
||||
:min="1"
|
||||
:max="threadGroup.duration"
|
||||
v-model="threadGroup.rampUpTime"
|
||||
@change="calculateTotalChart(threadGroup)"
|
||||
size="mini"/>
|
||||
|
@ -219,7 +229,8 @@ export default {
|
|||
resourcePools: [],
|
||||
activeNames: ["0"],
|
||||
threadGroups: [],
|
||||
resourcePoolResourceLength: 1
|
||||
resourcePoolResourceLength: 1,
|
||||
maxThreadNumbers: 5000,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -336,6 +347,22 @@ export default {
|
|||
});
|
||||
}
|
||||
},
|
||||
resourcePoolChange() {
|
||||
let result = this.resourcePools.filter(p => p.id === this.resourcePool);
|
||||
if (result.length === 1) {
|
||||
let threadNumber = 0;
|
||||
result[0].resources.forEach(resource => {
|
||||
threadNumber += JSON.parse(resource.configuration).maxConcurrency;
|
||||
})
|
||||
this.maxThreadNumbers = threadNumber;
|
||||
this.threadGroups.forEach(tg => {
|
||||
if (tg.threadNumber > threadNumber) {
|
||||
this.$set(tg, "threadNumber", threadNumber);
|
||||
}
|
||||
})
|
||||
this.calculateTotalChart();
|
||||
}
|
||||
},
|
||||
calculateTotalChart() {
|
||||
let handler = this;
|
||||
if (handler.duration < handler.rampUpTime) {
|
||||
|
@ -344,6 +371,11 @@ export default {
|
|||
if (handler.rampUpTime < handler.step) {
|
||||
handler.step = handler.rampUpTime;
|
||||
}
|
||||
// 线程数不能小于资源池节点的数量
|
||||
let resourcePool = this.resourcePools.filter(v => v.id === this.resourcePool)[0];
|
||||
if (resourcePool) {
|
||||
this.resourcePoolResourceLength = resourcePool.resources.length;
|
||||
}
|
||||
let color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
|
||||
handler.options = {
|
||||
color: color,
|
||||
|
@ -649,7 +681,7 @@ export default {
|
|||
border-bottom: 1px solid #DCDFE6;
|
||||
}
|
||||
|
||||
/deep/ .el-collapse-item__content{
|
||||
/deep/ .el-collapse-item__content {
|
||||
padding-left: 10px;
|
||||
padding-bottom: 5px;
|
||||
border-left-width: 8px;
|
||||
|
@ -682,4 +714,10 @@ export default {
|
|||
.title {
|
||||
margin-left: 60px;
|
||||
}
|
||||
|
||||
.wordwrap {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -216,8 +216,36 @@
|
|||
})
|
||||
}
|
||||
this.total = data.itemCount;
|
||||
|
||||
this.$nextTick(function(){
|
||||
this.checkTableRowIsSelect();
|
||||
});
|
||||
})
|
||||
},
|
||||
|
||||
checkTableRowIsSelect(){
|
||||
//如果默认全选的话,则选中应该选中的行
|
||||
if(this.condition.selectAll){
|
||||
let unSelectIds = this.condition.unSelectIds;
|
||||
this.tableData.forEach(row=>{
|
||||
if(unSelectIds.indexOf(row.id)<0){
|
||||
this.$refs.userTable.toggleRowSelection(row,true);
|
||||
|
||||
//默认全选,需要把选中对行添加到selectRows中。不然会影响到勾选函数统计
|
||||
if (!this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", true);
|
||||
this.selectRows.add(row);
|
||||
}
|
||||
}else{
|
||||
//不勾选的行,也要判断是否被加入了selectRow中。加入了的话就去除。
|
||||
if (this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", false);
|
||||
this.selectRows.delete(row);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
buildPagePath(path) {
|
||||
return path + "/" + this.currentPage + "/" + this.pageSize;
|
||||
},
|
||||
|
|
|
@ -598,7 +598,7 @@ export default {
|
|||
return;
|
||||
}
|
||||
this.selectRows = new Set();
|
||||
this.condition.selectAll = false;
|
||||
// this.condition.selectAll = false;
|
||||
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
|
||||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
|
@ -615,8 +615,38 @@ export default {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.$nextTick(function(){
|
||||
this.checkTableRowIsSelect();
|
||||
});
|
||||
|
||||
})
|
||||
},
|
||||
|
||||
checkTableRowIsSelect(){
|
||||
//如果默认全选的话,则选中应该选中的行
|
||||
if(this.condition.selectAll){
|
||||
let unSelectIds = this.condition.unSelectIds;
|
||||
this.tableData.forEach(row=>{
|
||||
if(unSelectIds.indexOf(row.id)<0){
|
||||
this.$refs.userTable.toggleRowSelection(row,true);
|
||||
|
||||
//默认全选,需要把选中对行添加到selectRows中。不然会影响到勾选函数统计
|
||||
if (!this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", true);
|
||||
this.selectRows.add(row);
|
||||
}
|
||||
}else{
|
||||
//不勾选的行,也要判断是否被加入了selectRow中。加入了的话就去除。
|
||||
if (this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", false);
|
||||
this.selectRows.delete(row);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
handleClose() {
|
||||
this.form = {roles: [{id: ''}]};
|
||||
this.btnAddRole = false;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue