Conflicts:
	frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue
	frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue
	frontend/src/business/components/api/definition/components/list/ApiList.vue
This commit is contained in:
wenyann 2021-02-19 16:42:14 +08:00
commit 6e6ca3c0b9
125 changed files with 2775 additions and 1144 deletions

View File

@ -48,7 +48,7 @@ curl -sSL https://github.com/metersphere/metersphere/releases/latest/download/qu
文档和演示视频:
- [完整文档](https://metersphere.io/docs/)
- [演示视频](http://video.fit2cloud.com/%E3%80%90%E6%BC%94%E7%A4%BA%E8%A7%86%E9%A2%91%E3%80%91202006%20MeterSphere%20v1.0%20%E5%8A%9F%E8%83%BD%E6%BC%94%E7%A4%BA.mp4)
- [演示视频](https://www.bilibili.com/video/BV1yp4y1p72C/)
## MeterSphere 企业版

View File

@ -2,9 +2,13 @@ package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.JmxInfoDTO;
import io.metersphere.api.dto.automation.*;
import io.metersphere.api.dto.definition.ApiBatchRequest;
import io.metersphere.api.dto.definition.ApiExportResult;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
@ -103,7 +107,7 @@ public class ApiAutomationController {
@PostMapping("/batch/edit")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void bathEdit(@RequestBody SaveApiScenarioRequest request) {
public void bathEdit(@RequestBody ApiScenarioBatchRequest request) {
apiAutomationService.bathEdit(request);
}
@ -146,5 +150,18 @@ public class ApiAutomationController {
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"")
.body(bytes);
}
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public ApiDefinitionImport testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
return apiAutomationService.scenarioImport(file, request);
}
@PostMapping(value = "/export")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public ApiScenrioExportResult export(@RequestBody ApiScenarioBatchRequest request) {
return apiAutomationService.export(request);
}
}

View File

@ -6,8 +6,6 @@ import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.datacount.request.ScheduleInfoRequest;
import io.metersphere.api.dto.datacount.response.TaskInfoResult;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.ScheduleInfoSwaggerUrlRequest;
@ -92,7 +90,7 @@ public class ApiDefinitionController {
}
@PostMapping("/deleteBatchByParams")
public void deleteBatchByParams(@RequestBody ApiDefinitionBatchProcessingRequest request) {
public void deleteBatchByParams(@RequestBody ApiBatchRequest request) {
apiDefinitionService.deleteByParams(request);
}
@ -104,7 +102,7 @@ public class ApiDefinitionController {
@PostMapping("/removeToGcByParams")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void removeToGcByParams(@RequestBody ApiDefinitionBatchProcessingRequest request) {
public void removeToGcByParams(@RequestBody ApiBatchRequest request) {
apiDefinitionService.removeToGcByParams(request);
}
@ -149,6 +147,13 @@ public class ApiDefinitionController {
public ApiDefinitionImport testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
return apiDefinitionService.apiTestImport(file, request);
}
@PostMapping(value = "/export")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public ApiExportResult export(@RequestBody ApiBatchRequest request) {
return apiDefinitionService.export(request);
}
//定时任务创建
@PostMapping(value = "/schedule/create")
public void createSchedule(@RequestBody Schedule request) {

View File

@ -0,0 +1,48 @@
package io.metersphere.api.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiDocumentInfoDTO;
import io.metersphere.api.dto.ApiDocumentRequest;
import io.metersphere.api.dto.ApiDocumentSimpleInfoDTO;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiDocumentService;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
import org.python.antlr.ast.Str;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author song.tianyang
* @Date 2021/2/5 6:25 下午
* @Description
*/
@RestController
@RequestMapping(value = "/api/document")
public class ApiDocumentController {
@Resource
ApiDocumentService apiDocumentService;
@Resource
ApiDefinitionService apiDefinitionService;
@PostMapping("/selectApiSimpleInfo")
public List<ApiDocumentSimpleInfoDTO> list(@RequestBody ApiDocumentRequest request) {
List<ApiDocumentSimpleInfoDTO> returnList = apiDocumentService.findApiDocumentSimpleInfoByRequest(request);
return returnList;
}
@GetMapping("/selectApiInfoById/{id}")
public ApiDocumentInfoDTO selectApiInfoById(@PathVariable String id) {
ApiDefinitionWithBLOBs apiModel = apiDefinitionService.getBLOBs(id);
ApiDocumentInfoDTO returnDTO = apiDocumentService.conversionModelToDTO(apiModel);
return returnDTO;
}
}

View File

@ -0,0 +1,34 @@
package io.metersphere.api.dto;
import lombok.Getter;
import lombok.Setter;
/**
* @author song.tianyang
* @Date 2021/2/8 4:37 下午
* @Description
*/
@Getter
@Setter
public class ApiDocumentInfoDTO {
private String id;
private String method;
private String uri;
private String name;
private String status;
private String requestHead;
private String urlParams;
private String requestBodyParamType;
private String requestBodyFormData;
private String requestBodyStrutureData;
private String responseHead;
private String responseBody;
private String responseBodyParamType;
private String responseBodyFormData;
private String responseBodyStrutureData;
private String responseCode;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.api.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
/**
* @author song.tianyang
* @Date 2021/2/7 10:32 上午
* @Description
*/
@Getter
@Setter
public class ApiDocumentRequest {
private String projectId;
private List<String> moduleIds;
private String shareId;
}

View File

@ -0,0 +1,17 @@
package io.metersphere.api.dto;
import lombok.Getter;
import lombok.Setter;
/**
* 接口定义的简易信息
* @author song.tianyang
* @Date 2021/2/7 10:15 上午
* @Description
*/
@Getter
@Setter
public class ApiDocumentSimpleInfoDTO {
private String id;
private String name;
}

View File

@ -13,8 +13,6 @@ public class ApiTestImportRequest {
private String projectId;
private String platform;
private Boolean useEnvironment;
// 来自场景的导入不需要存储
private boolean saved = true;
private String swaggerUrl;
//导入策略
private String modeId;

View File

@ -0,0 +1,18 @@
package io.metersphere.api.dto.automation;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ApiScenarioBatchRequest extends ApiScenarioWithBLOBs {
private List<String> ids;
private String projectId;
private String environmentId;
private ApiScenarioRequest condition;
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.automation;
import io.metersphere.controller.request.BaseQueryRequest;
import io.metersphere.controller.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
@ -9,21 +10,14 @@ import java.util.Map;
@Getter
@Setter
public class ApiScenarioRequest {
public class ApiScenarioRequest extends BaseQueryRequest {
private String id;
private String excludeId;
private String projectId;
private String moduleId;
private List<String> moduleIds;
private String name;
private String workspaceId;
private String userId;
private String planId;
private boolean recent = false;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
private List<String> ids;
private boolean isSelectThisWeedData;
private long createTime = 0;
private String executeStatus;

View File

@ -0,0 +1,15 @@
package io.metersphere.api.dto.automation;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ApiScenrioExportResult {
private String projectId;
private String version;
private List<ApiScenarioWithBLOBs> data;
}

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.automation;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import lombok.Getter;
import lombok.Setter;
@ -8,14 +9,10 @@ import java.util.Map;
@Setter
@Getter
public class RunScenarioRequest {
private String id;
public class RunScenarioRequest extends ApiScenarioWithBLOBs {
private String reportId;
private String projectId;
private String environmentId;
private String triggerMode;
@ -29,28 +26,12 @@ public class RunScenarioRequest {
private List<String> planCaseIds;
private String reportUserID;
private List<String> ids;
private List<String> scenarioIds;
private String reportUserID;
private Map<String,String> scenarioTestPlanIdMap;
/**
* isSelectAllDate选择的数据是否是全部数据全部数据是不受分页影响的数据
* filters: 数据状态
* name如果是全部数据那么表格如果历经查询查询参数是什么
* moduleIds 哪些模块的数据
* unSelectIds是否在页面上有未勾选的数据有的话他们的ID是哪些
* filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要为了让程序能明确批量的范围
*/
private boolean isSelectAllDate;
private Map<String, List<String>> filters;
private String name;
private List<String> moduleIds;
private List<String> unSelectIds;
private ApiScenarioRequest condition;
}

View File

@ -6,7 +6,6 @@ import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@ -14,25 +13,8 @@ public class ApiBatchRequest extends ApiDefinitionWithBLOBs {
private List<String> ids;
private List<OrderRequest> orders;
private String projectId;
/**
* isSelectAllDate选择的数据是否是全部数据全部数据是不受分页影响的数据
* filters: 数据状态
* name如果是全部数据那么表格如果历经查询查询参数是什么
* moduleIds 哪些模块的数据
* unSelectIds是否在页面上有未勾选的数据有的话他们的ID是哪些
* filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要为了让程序能明确批量的范围
*/
private boolean isSelectAllDate;
private Map<String, List<String>> filters;
private String name;
private List<String> moduleIds;
private List<String> unSelectIds;
private String moduleId;
private String protocol;
private ApiDefinitionRequest condition;
}

View File

@ -1,42 +0,0 @@
package io.metersphere.api.dto.definition;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
/**
* 接口定义模块-批量处理请求类
* @author song.tianyang
* @Date 2020/12/29 4:13 下午
* @Description
*/
@Getter
@Setter
public class ApiDefinitionBatchProcessingRequest {
/**
* isSelectAllDate选择的数据是否是全部数据全部数据是不受分页影响的数据
* filters: 数据状态
* name如果是全部数据那么表格如果历经查询查询参数是什么
* moduleIds 哪些模块的数据
* unSelectIds是否在页面上有未勾选的数据有的话他们的ID是哪些
* filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要为了让程序能明确批量的范围
*/
private boolean isSelectAllDate;
private Map<String, List<String>> filters;
private String name;
private List<String> moduleIds;
private List<String> unSelectIds;
private String projectId;
private List<String> dataIds;
private String protocol;
}

View File

@ -1,31 +1,21 @@
package io.metersphere.api.dto.definition;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.controller.request.BaseQueryRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class ApiDefinitionRequest {
public class ApiDefinitionRequest extends BaseQueryRequest {
private String id;
private String excludeId;
private String projectId;
private String moduleId;
private List<String> moduleIds;
private String protocol;
private String name;
private String workspaceId;
private String userId;
private String planId;
private boolean recent = false;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
private List<String> ids;
private boolean isSelectThisWeedData = false;
private long createTime = 0;
private String status;

View File

@ -0,0 +1,17 @@
package io.metersphere.api.dto.definition;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ApiExportResult {
private String projectName;
private String protocol;
private List<ApiDefinitionWithBLOBs> data;
private List<ApiTestCaseWithBLOBs> cases;
}

View File

@ -1,6 +1,8 @@
package io.metersphere.api.dto.definition.parse;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.request.MsScenario;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import lombok.Data;
import java.util.List;
@ -9,5 +11,11 @@ import java.util.List;
public class ApiDefinitionImport {
private String projectName;
private String protocol;
private List<ApiDefinitionResult> data;
private List<ApiDefinitionWithBLOBs> data;
//导入场景
private MsScenario scenarioDefinition;
// 新版本带用例导出
private List<ApiTestCaseWithBLOBs> cases;
}

View File

@ -49,6 +49,9 @@ public class MsScenario extends MsTestElement {
@JSONField(ordinal = 24)
private boolean enableCookieShare;
@JSONField(ordinal = 26)
private List<KeyValue> headers;
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
@Override
@ -87,7 +90,7 @@ public class MsScenario extends MsTestElement {
}
// 场景变量和环境变量
tree.add(arguments(config));
//this.addCsvDataSet(tree, variables);
this.addCsvDataSet(tree, variables);
this.addCounter(tree, variables);
this.addRandom(tree, variables);
if (CollectionUtils.isNotEmpty(hashTree)) {
@ -131,6 +134,12 @@ public class MsScenario extends MsTestElement {
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
);
}
if (CollectionUtils.isNotEmpty(this.headers)) {
this.headers.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
);
}
return arguments;
}

View File

@ -79,13 +79,13 @@ public class MsIfController extends MsTestElement {
}
if (StringUtils.equals(operator, "is empty")) {
variable = "!empty(" + variable + ")";
variable = variable + "==" + "\"\\" + this.variable + "\"" + "|| empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "empty(" + variable + ")";
variable = variable + "!=" + "\"\\" + this.variable + "\"" + "&& !empty(" + variable + ")";
operator = "";
value = "";
}

View File

@ -123,13 +123,13 @@ public class MsLoopController extends MsTestElement {
}
if (StringUtils.equals(operator, "is empty")) {
variable = "!empty(" + variable + ")";
variable = variable + "==" + "\"\\" + this.whileController.getVariable() + "\"" + "|| empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "empty(" + variable + ")";
variable = variable + "!=" + "\"\\" + this.whileController.getVariable() + "\"" + "&& !empty(" + variable + ")";
operator = "";
value = "";
}

View File

@ -1,7 +1,6 @@
package io.metersphere.api.parse;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiModuleDTO;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.scenario.Body;
@ -9,6 +8,7 @@ import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
@ -75,7 +75,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
return null;
}
protected ApiModule buildModule(ApiModule parentModule, String name, boolean isSaved) {
protected ApiModule buildModule(ApiModule parentModule, String name) {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module;
if (parentModule != null) {
@ -84,9 +84,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
} else {
module = apiModuleService.getNewModule(name, this.projectId, 1);
}
if (isSaved) {
createModule(module);
}
createModule(module);
return module;
}
@ -151,8 +149,8 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
}
}
protected ApiDefinitionResult buildApiDefinition(String id, String name, String path, String method, ApiTestImportRequest importRequest) {
ApiDefinitionResult apiDefinition = new ApiDefinitionResult();
protected ApiDefinitionWithBLOBs buildApiDefinition(String id, String name, String path, String method, ApiTestImportRequest importRequest) {
ApiDefinitionWithBLOBs apiDefinition = new ApiDefinitionWithBLOBs();
apiDefinition.setName(name);
apiDefinition.setPath(formatPath(path));
apiDefinition.setProtocol(RequestType.HTTP);

View File

@ -0,0 +1,15 @@
package io.metersphere.api.parse;
import io.metersphere.commons.constants.ApiImportPlatform;
import org.apache.commons.lang3.StringUtils;
public class ApiScenarioImportParserFactory {
public static ApiImportParser getApiImportParser(String platform) {
if (StringUtils.equals(ApiImportPlatform.Metersphere.name(), platform)) {
return new MsParser();
} else if (StringUtils.equals(ApiImportPlatform.Postman.name(), platform)) {
return new ScenarioPostmanParser();
}
return null;
}
}

View File

@ -6,13 +6,16 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiExportResult;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.commons.constants.ApiImportPlatform;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
@ -41,34 +44,36 @@ public class MsParser extends ApiImportAbstractParser {
}
private ApiDefinitionImport parseMsFormat(String testStr, ApiTestImportRequest importRequest) {
ApiDefinitionImport apiDefinitionImport = JSON.parseObject(testStr, ApiDefinitionImport.class);
List<ApiDefinitionResult> data = apiDefinitionImport.getData();
data.forEach(apiDefinition -> {
String id = UUID.randomUUID().toString();
if (StringUtils.isBlank(apiDefinition.getModulePath())) {
apiDefinition.setModuleId(null);
}
parseModule(apiDefinition, importRequest);
apiDefinition.setId(id);
apiDefinition.setProjectId(this.projectId);
String request = apiDefinition.getRequest();
JSONObject requestObj = JSONObject.parseObject(request);
requestObj.put("id", id);
apiDefinition.setRequest(JSONObject.toJSONString(requestObj));
apiDefinitionImport.getData().forEach(apiDefinition -> {
parseApiDefinition(apiDefinition, importRequest);
});
return apiDefinitionImport;
}
private void parseApiDefinition(ApiDefinitionWithBLOBs apiDefinition, ApiTestImportRequest importRequest) {
String id = UUID.randomUUID().toString();
if (StringUtils.isBlank(apiDefinition.getModulePath())) {
apiDefinition.setModuleId(null);
}
parseModule(apiDefinition, importRequest);
apiDefinition.setId(id);
apiDefinition.setProjectId(this.projectId);
String request = apiDefinition.getRequest();
JSONObject requestObj = JSONObject.parseObject(request);
requestObj.put("id", id);
apiDefinition.setRequest(JSONObject.toJSONString(requestObj));
}
private ApiDefinitionImport parsePluginFormat(JSONObject testObject, ApiTestImportRequest importRequest) {
List<ApiDefinitionResult> results = new ArrayList<>();
List<ApiDefinitionWithBLOBs> results = new ArrayList<>();
ApiDefinitionImport apiImport = new ApiDefinitionImport();
apiImport.setProtocol(RequestType.HTTP);
apiImport.setData(results);
testObject.keySet().forEach(tag -> {
ApiModule parentModule = getSelectModule(importRequest.getModuleId());
ApiModule module = buildModule(parentModule, tag, importRequest.isSaved());
ApiModule module = buildModule(parentModule, tag);
JSONObject requests = testObject.getJSONObject(tag);
requests.keySet().forEach(requestName -> {
@ -78,7 +83,7 @@ public class MsParser extends ApiImportAbstractParser {
String method = requestObject.getString("method");
MsHTTPSamplerProxy request = buildRequest(requestName, path, method);
ApiDefinitionResult apiDefinition = buildApiDefinition(request.getId(), requestName, path, method,importRequest);
ApiDefinitionWithBLOBs apiDefinition = buildApiDefinition(request.getId(), requestName, path, method,importRequest);
apiDefinition.setModuleId(module.getId());
apiDefinition.setProjectId(this.projectId);
parseBody(requestObject, request.getBody());
@ -91,7 +96,7 @@ public class MsParser extends ApiImportAbstractParser {
return apiImport;
}
private void parsePath(MsHTTPSamplerProxy request, ApiDefinitionResult apiDefinition) {
private void parsePath(MsHTTPSamplerProxy request, ApiDefinitionWithBLOBs apiDefinition) {
if (StringUtils.isNotBlank(request.getPath())) {
String[] split = request.getPath().split("\\?");
String path = split[0];
@ -160,7 +165,7 @@ public class MsParser extends ApiImportAbstractParser {
}
private void parseModule(ApiDefinitionResult apiDefinition, ApiTestImportRequest importRequest) {
private void parseModule(ApiDefinitionWithBLOBs apiDefinition, ApiTestImportRequest importRequest) {
String modulePath = apiDefinition.getModulePath();
if (StringUtils.isBlank(modulePath)) {
return;
@ -176,7 +181,7 @@ public class MsParser extends ApiImportAbstractParser {
Iterator<String> iterator = modules.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
parent = buildModule(parent, item, importRequest.isSaved());
parent = buildModule(parent, item);
if (!iterator.hasNext()) {
apiDefinition.setModuleId(parent.getId());
}

View File

@ -3,7 +3,6 @@ package io.metersphere.api.parse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
@ -11,6 +10,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.parse.postman.*;
import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.constants.MsRequestBodyType;
import io.metersphere.commons.constants.PostmanRequestBodyMode;
@ -32,20 +32,23 @@ public class PostmanParser extends ApiImportAbstractParser {
PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class);
List<PostmanKeyValue> variables = postmanCollection.getVariable();
ApiDefinitionImport apiImport = new ApiDefinitionImport();
List<ApiDefinitionResult> results = new ArrayList<>();
parseItem(postmanCollection.getItem(), variables, results, buildModule(getSelectModule(request.getModuleId()), postmanCollection.getInfo().getName(), request.isSaved()), request.isSaved());
List<ApiDefinitionWithBLOBs> results = new ArrayList<>();
parseItem(postmanCollection.getItem(), variables, results, buildModule(getSelectModule(request.getModuleId()), postmanCollection.getInfo().getName()), true);
apiImport.setData(results);
return apiImport;
}
private void parseItem(List<PostmanItem> items, List<PostmanKeyValue> variables, List<ApiDefinitionResult> results, ApiModule parentModule, boolean isSaved) {
protected void parseItem(List<PostmanItem> items, List<PostmanKeyValue> variables, List<ApiDefinitionWithBLOBs> results, ApiModule parentModule, Boolean isCreateModule) {
for (PostmanItem item : items) {
List<PostmanItem> childItems = item.getItem();
if (childItems != null) {
ApiModule module = buildModule(parentModule, item.getName() , isSaved);
parseItem(childItems, variables, results, module, isSaved);
ApiModule module = null;
if (isCreateModule) {
module = buildModule(parentModule, item.getName());
}
parseItem(childItems, variables, results, module, isCreateModule);
} else {
ApiDefinitionResult request = parsePostman(item);
ApiDefinitionWithBLOBs request = parsePostman(item);
if (request != null) {
results.add(request);
}
@ -56,7 +59,7 @@ public class PostmanParser extends ApiImportAbstractParser {
}
}
private ApiDefinitionResult parsePostman(PostmanItem requestItem) {
private ApiDefinitionWithBLOBs parsePostman(PostmanItem requestItem) {
PostmanRequest requestDesc = requestItem.getRequest();
if (requestDesc == null) {
return null;
@ -64,7 +67,7 @@ public class PostmanParser extends ApiImportAbstractParser {
requestDesc.getAuth(); // todo 认证方式等待优化
PostmanUrl url = requestDesc.getUrl();
MsHTTPSamplerProxy request = buildRequest(requestItem.getName(), url.getRaw(), requestDesc.getMethod());
ApiDefinitionResult apiDefinition =
ApiDefinitionWithBLOBs apiDefinition =
buildApiDefinition(request.getId(), requestItem.getName(), url.getRaw(), requestDesc.getMethod(),new ApiTestImportRequest());
if (StringUtils.isNotBlank(request.getPath())) {
String path = request.getPath().split("\\?")[0];

View File

@ -0,0 +1,40 @@
package io.metersphere.api.parse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.MsScenario;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.parse.postman.PostmanCollection;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ScenarioPostmanParser extends PostmanParser {
@Override
public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) {
this.projectId = request.getProjectId();
ApiDefinitionImport apiImport = new ApiDefinitionImport();
List<ApiDefinitionWithBLOBs> results = new ArrayList<>();
PostmanCollection postmanCollection = JSON.parseObject(getApiTestStr(source), PostmanCollection.class);
parseItem(postmanCollection.getItem(), postmanCollection.getVariable(), results, null, false);
MsScenario msScenario = new MsScenario();
LinkedList<MsTestElement> msHTTPSamplerProxies = new LinkedList<>();
results.forEach(res -> {
msHTTPSamplerProxies.add(JSONObject.parseObject(res.getRequest(), MsHTTPSamplerProxy.class));
});
msScenario.setHashTree(msHTTPSamplerProxies);
msScenario.setType("scenario");
msScenario.setName(postmanCollection.getInfo().getName());
apiImport.setScenarioDefinition(msScenario);
return apiImport;
}
}

View File

@ -4,13 +4,13 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.response.HttpResponse;
import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.constants.SwaggerParameterType;
import io.swagger.models.*;
@ -49,13 +49,13 @@ public class Swagger2Parser extends SwaggerAbstractParser {
return definitionImport;
}
private List<ApiDefinitionResult> parseRequests(Swagger swagger, ApiTestImportRequest importRequest) {
private List<ApiDefinitionWithBLOBs> parseRequests(Swagger swagger, ApiTestImportRequest importRequest) {
Map<String, Path> paths = swagger.getPaths();
Set<String> pathNames = paths.keySet();
this.definitions = swagger.getDefinitions();
List<ApiDefinitionResult> results = new ArrayList<>();
List<ApiDefinitionWithBLOBs> results = new ArrayList<>();
ApiModule parentNode = getSelectModule(importRequest.getModuleId());
@ -66,12 +66,12 @@ public class Swagger2Parser extends SwaggerAbstractParser {
for (HttpMethod method : httpMethods) {
Operation operation = operationMap.get(method);
MsHTTPSamplerProxy request = buildRequest(operation, pathName, method.name());
ApiDefinitionResult apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method.name(),importRequest);
ApiDefinitionWithBLOBs apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method.name(),importRequest);
parseParameters(operation, request);
addBodyHeader(request);
apiDefinition.setRequest(JSON.toJSONString(request));
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses())));
buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved());
buildModule(parentNode, apiDefinition, operation.getTags());
results.add(apiDefinition);
}
}
@ -80,7 +80,7 @@ public class Swagger2Parser extends SwaggerAbstractParser {
return results;
}
private ApiDefinitionResult buildApiDefinition(String id, Operation operation, String path, String method,ApiTestImportRequest importRequest) {
private ApiDefinitionWithBLOBs buildApiDefinition(String id, Operation operation, String path, String method,ApiTestImportRequest importRequest) {
String name = "";
if (StringUtils.isNotBlank(operation.getSummary())) {
name = operation.getSummary();

View File

@ -4,13 +4,13 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.response.HttpResponse;
import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
@ -69,14 +69,14 @@ public class Swagger3Parser extends SwaggerAbstractParser {
return definitionImport;
}
private List<ApiDefinitionResult> parseRequests(OpenAPI openAPI, ApiTestImportRequest importRequest) {
private List<ApiDefinitionWithBLOBs> parseRequests(OpenAPI openAPI, ApiTestImportRequest importRequest) {
Paths paths = openAPI.getPaths();
Set<String> pathNames = paths.keySet();
this.components = openAPI.getComponents();
List<ApiDefinitionResult> results = new ArrayList<>();
List<ApiDefinitionWithBLOBs> results = new ArrayList<>();
ApiModule parentNode = getSelectModule(importRequest.getModuleId());
@ -97,13 +97,13 @@ public class Swagger3Parser extends SwaggerAbstractParser {
Operation operation = operationsMap.get(method);
if (operation != null) {
MsHTTPSamplerProxy request = buildRequest(operation, pathName, method);
ApiDefinitionResult apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method,importRequest);
ApiDefinitionWithBLOBs apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method,importRequest);
parseParameters(operation, request);
parseRequestBody(operation.getRequestBody(), request.getBody());
addBodyHeader(request);
apiDefinition.setRequest(JSON.toJSONString(request));
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved());
buildModule(parentNode, apiDefinition, operation.getTags());
results.add(apiDefinition);
}
}
@ -112,7 +112,7 @@ public class Swagger3Parser extends SwaggerAbstractParser {
return results;
}
private ApiDefinitionResult buildApiDefinition(String id, Operation operation, String path, String method,ApiTestImportRequest importRequest) {
private ApiDefinitionWithBLOBs buildApiDefinition(String id, Operation operation, String path, String method,ApiTestImportRequest importRequest) {
String name = "";
if (StringUtils.isNotBlank(operation.getSummary())) {
name = operation.getSummary();

View File

@ -1,16 +1,16 @@
package io.metersphere.api.parse;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiModule;
import java.util.List;
public abstract class SwaggerAbstractParser extends ApiImportAbstractParser {
protected void buildModule(ApiModule parentModule, ApiDefinitionResult apiDefinition, List<String> tags, boolean isSaved) {
protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, List<String> tags) {
if (tags != null) {
tags.forEach(tag -> {
ApiModule module = buildModule(parentModule, tag, isSaved);
ApiModule module = buildModule(parentModule, tag);
apiDefinition.setModuleId(module.getId());
});
}

View File

@ -3,7 +3,6 @@ package io.metersphere.api.parse.old;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager;
@ -16,6 +15,7 @@ import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.api.dto.scenario.request.HttpRequest;
import io.metersphere.api.dto.scenario.request.Request;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.commons.constants.MsRequestBodyType;
import io.metersphere.commons.constants.PostmanRequestBodyMode;
import org.apache.commons.lang3.StringUtils;
@ -54,20 +54,20 @@ public class PostmanParser extends ApiImportAbstractParser {
PostmanCollection postmanCollection = JSON.parseObject(testStr, PostmanCollection.class);
List<PostmanKeyValue> variables = postmanCollection.getVariable();
ApiDefinitionImport apiImport = new ApiDefinitionImport();
List<ApiDefinitionResult> requests = new ArrayList<>();
List<ApiDefinitionWithBLOBs> requests = new ArrayList<>();
parseItem(postmanCollection.getItem(), variables, requests);
apiImport.setData(requests);
return apiImport;
}
private void parseItem(List<PostmanItem> items, List<PostmanKeyValue> variables, List<ApiDefinitionResult> scenarios) {
private void parseItem(List<PostmanItem> items, List<PostmanKeyValue> variables, List<ApiDefinitionWithBLOBs> scenarios) {
for (PostmanItem item : items) {
List<PostmanItem> childItems = item.getItem();
if (childItems != null) {
parseItem(childItems, variables, scenarios);
} else {
ApiDefinitionResult request = parsePostman(item);
ApiDefinitionWithBLOBs request = parsePostman(item);
if (request != null) {
scenarios.add(request);
}
@ -75,13 +75,13 @@ public class PostmanParser extends ApiImportAbstractParser {
}
}
private ApiDefinitionResult parsePostman(PostmanItem requestItem) {
private ApiDefinitionWithBLOBs parsePostman(PostmanItem requestItem) {
PostmanRequest requestDesc = requestItem.getRequest();
if (requestDesc == null) {
return null;
}
PostmanUrl url = requestDesc.getUrl();
ApiDefinitionResult request = new ApiDefinitionResult();
ApiDefinitionWithBLOBs request = new ApiDefinitionWithBLOBs();
request.setName(requestItem.getName());
request.setPath(url.getRaw());
request.setMethod(requestDesc.getMethod());

View File

@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager;
@ -16,6 +15,7 @@ import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.api.dto.scenario.request.HttpRequest;
import io.metersphere.api.dto.scenario.request.Request;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.commons.constants.MsRequestBodyType;
import io.metersphere.commons.constants.SwaggerParameterType;
import io.swagger.models.*;
@ -54,13 +54,13 @@ public class Swagger2Parser extends ApiImportAbstractParser {
return definitionImport;
}
private List<ApiDefinitionResult> parseSwagger(ApiImport apiImport) {
List<ApiDefinitionResult> results = new LinkedList<>();
private List<ApiDefinitionWithBLOBs> parseSwagger(ApiImport apiImport) {
List<ApiDefinitionWithBLOBs> results = new LinkedList<>();
apiImport.getScenarios().forEach(item -> {
item.getRequests().forEach(childItem -> {
if (childItem instanceof HttpRequest) {
HttpRequest res = (HttpRequest) childItem;
ApiDefinitionResult request = new ApiDefinitionResult();
ApiDefinitionWithBLOBs request = new ApiDefinitionWithBLOBs();
request.setName(res.getName());
request.setPath(res.getPath());
request.setMethod(res.getMethod());

View File

@ -5,15 +5,20 @@ import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.JmxInfoDTO;
import io.metersphere.api.dto.automation.*;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.definition.ApiExportResult;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.*;
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.parse.ApiImportParser;
import io.metersphere.api.parse.ApiScenarioImportParserFactory;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiScenarioReportMapper;
@ -62,8 +67,6 @@ public class ApiAutomationService {
@Resource
private ApiScenarioMapper apiScenarioMapper;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
@Resource
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@ -103,13 +106,13 @@ public class ApiAutomationService {
if (setDefultOrders) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
}
if(StringUtils.isNotEmpty(request.getExecuteStatus())){
Map<String,List<String>> statusFilter = new HashMap<>();
if (StringUtils.isNotEmpty(request.getExecuteStatus())) {
Map<String, List<String>> statusFilter = new HashMap<>();
List<String> list = new ArrayList<>();
list.add("Prepare");
list.add("Underway");
list.add("Completed");
statusFilter.put("status",list);
statusFilter.put("status", list);
request.setFilters(statusFilter);
}
if (checkThisWeekData) {
@ -145,36 +148,15 @@ public class ApiAutomationService {
request.setId(UUID.randomUUID().toString());
checkNameExist(request);
final ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs();
scenario.setId(request.getId());
scenario.setName(request.getName());
scenario.setProjectId(request.getProjectId());
scenario.setTags(request.getTags());
scenario.setApiScenarioModuleId(request.getApiScenarioModuleId());
scenario.setModulePath(request.getModulePath());
scenario.setLevel(request.getLevel());
scenario.setFollowPeople(request.getFollowPeople());
scenario.setPrincipal(request.getPrincipal());
scenario.setStepTotal(request.getStepTotal());
scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition()));
final ApiScenarioWithBLOBs scenario = buildSaveScenario(request);
scenario.setCreateTime(System.currentTimeMillis());
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setNum(getNextNum(request.getProjectId()));
if (StringUtils.isNotEmpty(request.getStatus())) {
scenario.setStatus(request.getStatus());
} else {
scenario.setStatus(ScenarioStatus.Underway.name());
}
if (request.getUserId() == null) {
scenario.setUserId(SessionUtils.getUserId());
} else {
scenario.setUserId(request.getUserId());
}
scenario.setDescription(request.getDescription());
apiScenarioMapper.insert(scenario);
List<String> bodyUploadIds = request.getBodyUploadIds();
apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
return scenario;
}
@ -190,9 +172,14 @@ public class ApiAutomationService {
public void update(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles) {
checkNameExist(request);
List<String> bodyUploadIds = request.getBodyUploadIds();
apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
final ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs();
final ApiScenarioWithBLOBs scenario = buildSaveScenario(request);
apiScenarioMapper.updateByPrimaryKeySelective(scenario);
}
public ApiScenarioWithBLOBs buildSaveScenario(SaveApiScenarioRequest request) {
ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs();
scenario.setId(request.getId());
scenario.setName(request.getName());
scenario.setProjectId(request.getProjectId());
@ -203,16 +190,19 @@ public class ApiAutomationService {
scenario.setFollowPeople(request.getFollowPeople());
scenario.setPrincipal(request.getPrincipal());
scenario.setStepTotal(request.getStepTotal());
scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition()));
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition()));
if (StringUtils.isNotEmpty(request.getStatus())) {
scenario.setStatus(request.getStatus());
} else {
scenario.setStatus(ScenarioStatus.Underway.name());
}
scenario.setUserId(request.getUserId());
scenario.setDescription(request.getDescription());
apiScenarioMapper.updateByPrimaryKeySelective(scenario);
if (request.getUserId() == null) {
scenario.setUserId(SessionUtils.getUserId());
} else {
scenario.setUserId(request.getUserId());
}
return scenario;
}
public void delete(String id) {
@ -454,11 +444,11 @@ public class ApiAutomationService {
* @return
*/
public String run(RunScenarioRequest request) {
List<String> ids = request.getScenarioIds();
if (request.isSelectAllDate()) {
ids = this.getAllScenarioIdsByFontedSelect(
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
}
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
List<String> ids = request.getIds();
//检查是否有正在执行中的情景
this.checkScenarioIsRunnng(ids);
List<ApiScenarioWithBLOBs> apiScenarios = extApiScenarioMapper.selectIds(ids);
@ -480,8 +470,8 @@ public class ApiAutomationService {
public void checkScenarioIsRunnng(List<String> ids) {
List<ApiScenarioReport> lastReportStatusByIds = apiReportService.selectLastReportByIds(ids);
for (ApiScenarioReport report : lastReportStatusByIds) {
if(StringUtils.equals(report.getStatus(),APITestStatus.Running.name())){
MSException.throwException(report.getName()+" Is Running!");
if (StringUtils.equals(report.getStatus(), APITestStatus.Running.name())) {
MSException.throwException(report.getName() + " Is Running!");
}
}
}
@ -518,7 +508,7 @@ public class ApiAutomationService {
*/
public String debugRun(RunDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
EnvironmentConfig envConfig = null;
if (request.getEnvironmentId() != null) {
ApiTestEnvironmentWithBLOBs environment = environmentService.get(request.getEnvironmentId());
@ -667,11 +657,7 @@ public class ApiAutomationService {
public JmxInfoDTO genPerformanceTestJmx(RunScenarioRequest request) throws Exception {
List<ApiScenarioWithBLOBs> apiScenarios = null;
List<String> ids = request.getScenarioIds();
if (request.isSelectAllDate()) {
ids = this.getAllScenarioIdsByFontedSelect(
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
}
List<String> ids = request.getIds();
apiScenarios = extApiScenarioMapper.selectIds(ids);
String testName = "";
if (!apiScenarios.isEmpty()) {
@ -699,20 +685,17 @@ public class ApiAutomationService {
return dto;
}
public void bathEdit(SaveApiScenarioRequest request) {
if (CollectionUtils.isEmpty(request.getScenarioIds())) {
return;
}
if (request.isSelectAllDate()) {
request.setScenarioIds(this.getAllScenarioIdsByFontedSelect(
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds()));
}
public void bathEdit(ApiScenarioBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
if (StringUtils.isNotBlank(request.getEnvironmentId())) {
bathEditEnv(request);
return;
}
ApiScenarioExample apiScenarioExample = new ApiScenarioExample();
apiScenarioExample.createCriteria().andIdIn(request.getScenarioIds());
apiScenarioExample.createCriteria().andIdIn(request.getIds());
ApiScenarioWithBLOBs apiScenarioWithBLOBs = new ApiScenarioWithBLOBs();
BeanUtils.copyBean(apiScenarioWithBLOBs, request);
apiScenarioWithBLOBs.setUpdateTime(System.currentTimeMillis());
@ -721,9 +704,9 @@ public class ApiAutomationService {
apiScenarioExample);
}
public void bathEditEnv(SaveApiScenarioRequest request) {
public void bathEditEnv(ApiScenarioBatchRequest request) {
if (StringUtils.isNotBlank(request.getEnvironmentId())) {
List<ApiScenarioWithBLOBs> apiScenarios = selectByIdsWithBLOBs(request.getScenarioIds());
List<ApiScenarioWithBLOBs> apiScenarios = selectByIdsWithBLOBs(request.getIds());
apiScenarios.forEach(item -> {
JSONObject object = JSONObject.parseObject(item.getScenarioDefinition());
object.put("environmentId", request.getEnvironmentId());
@ -734,4 +717,40 @@ public class ApiAutomationService {
});
}
}
public ApiDefinitionImport scenarioImport(MultipartFile file, ApiTestImportRequest request) {
ApiImportParser apiImportParser = ApiScenarioImportParserFactory.getApiImportParser(request.getPlatform());
ApiDefinitionImport apiImport = null;
try {
apiImport = apiImportParser.parse(file == null ? null : file.getInputStream(), request);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("parse_data_error"));
}
SaveApiScenarioRequest saveReq = new SaveApiScenarioRequest();
saveReq.setScenarioDefinition(apiImport.getScenarioDefinition());
saveReq.setName(saveReq.getScenarioDefinition().getName());
saveReq.setProjectId(request.getProjectId());
saveReq.setApiScenarioModuleId(request.getModuleId());
if (StringUtils.isNotBlank(request.getUserId())) {
saveReq.setPrincipal(request.getUserId());
} else {
saveReq.setPrincipal(SessionUtils.getUserId());
}
create(saveReq, new ArrayList<>());
return apiImport;
}
public ApiScenrioExportResult export(ApiScenarioBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(request.getIds());
List<ApiScenarioWithBLOBs> apiScenarioWithBLOBs = apiScenarioMapper.selectByExampleWithBLOBs(example);
ApiScenrioExportResult result = new ApiScenrioExportResult();
result.setData(apiScenarioWithBLOBs);
result.setProjectId(request.getProjectId());
result.setVersion(System.getenv("MS_VERSION"));
return result;
}
}

View File

@ -131,7 +131,7 @@ public class ApiDefinitionService {
public void create(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
createTest(request);
createBodyFiles(bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
}
public void update(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) {
@ -141,27 +141,7 @@ public class ApiDefinitionService {
List<String> bodyUploadIds = request.getBodyUploadIds();
request.setBodyUploadIds(null);
updateTest(request);
createBodyFiles(bodyUploadIds, bodyFiles);
}
public void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (CollectionUtils.isNotEmpty(bodyUploadIds) && CollectionUtils.isNotEmpty(bodyFiles)) {
File testDir = new File(BODY_FILE_DIR);
if (!testDir.exists()) {
testDir.mkdirs();
}
for (int i = 0; i < bodyUploadIds.size(); i++) {
MultipartFile item = bodyFiles.get(i);
File file = new File(BODY_FILE_DIR + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(Translator.get("upload_fail"));
}
}
}
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
}
public void delete(String apiId) {
@ -298,24 +278,23 @@ public class ApiDefinitionService {
}
}
private ApiDefinition importCreate(ApiDefinitionResult request, ApiDefinitionMapper batchMapper, ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest) {
private ApiDefinition importCreate(ApiDefinitionWithBLOBs apiDefinition, ApiDefinitionMapper batchMapper,
ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List<ApiTestCaseWithBLOBs> cases) {
SaveApiDefinitionRequest saveReq = new SaveApiDefinitionRequest();
BeanUtils.copyBean(saveReq, request);
final ApiDefinitionWithBLOBs apiDefinition = new ApiDefinitionWithBLOBs();
BeanUtils.copyBean(apiDefinition, request);
BeanUtils.copyBean(saveReq, apiDefinition);
apiDefinition.setCreateTime(System.currentTimeMillis());
apiDefinition.setUpdateTime(System.currentTimeMillis());
apiDefinition.setStatus(APITestStatus.Underway.name());
if (request.getUserId() == null) {
if (apiDefinition.getUserId() == null) {
apiDefinition.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
} else {
apiDefinition.setUserId(request.getUserId());
apiDefinition.setUserId(apiDefinition.getUserId());
}
apiDefinition.setDescription(request.getDescription());
apiDefinition.setDescription(apiDefinition.getDescription());
List<ApiDefinition> sameRequest = getSameRequest(saveReq);
if (StringUtils.equals("fullCoverage", apiTestImportRequest.getModeId())) {
_importCreate(sameRequest, batchMapper, apiDefinition, apiTestCaseMapper, apiTestImportRequest);
_importCreate(sameRequest, batchMapper, apiDefinition, apiTestCaseMapper, apiTestImportRequest, cases);
} else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) {
if (CollectionUtils.isEmpty(sameRequest)) {
//postman 可能含有前置脚本接口定义去掉脚本
@ -325,25 +304,34 @@ public class ApiDefinitionService {
importApiCase(apiDefinition, apiTestCaseMapper, apiTestImportRequest, true);
}
} else {
_importCreate(sameRequest, batchMapper, apiDefinition, apiTestCaseMapper, apiTestImportRequest);
_importCreate(sameRequest, batchMapper, apiDefinition, apiTestCaseMapper, apiTestImportRequest, cases);
}
return apiDefinition;
}
private void _importCreate(List<ApiDefinition> sameRequest, ApiDefinitionMapper batchMapper, ApiDefinitionWithBLOBs apiDefinition,
ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest) {
ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List<ApiTestCaseWithBLOBs> cases) {
if (CollectionUtils.isEmpty(sameRequest)) {
String request = setImportHashTree(apiDefinition);
batchMapper.insert(apiDefinition);
apiDefinition.setRequest(request);
importApiCase(apiDefinition, apiTestCaseMapper, apiTestImportRequest, true);
} else {
String originId = apiDefinition.getId();
//如果存在则修改
apiDefinition.setId(sameRequest.get(0).getId());
String request = setImportHashTree(apiDefinition);
apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition);
apiDefinition.setRequest(request);
importApiCase(apiDefinition, apiTestCaseMapper, apiTestImportRequest, false);
// 如果是带用例导出重新设置接口id
if (CollectionUtils.isNotEmpty(cases)) {
cases.forEach(item -> {
if (StringUtils.equals(item.getApiDefinitionId(), originId)) {
item.setApiDefinitionId(apiDefinition.getId());
}
});
}
}
}
@ -356,10 +344,30 @@ public class ApiDefinitionService {
return request;
}
/**
* 导入是插件或者postman时创建用例
* postman考虑是否有前置脚本
*/
private void importMsCase(ApiDefinitionImport apiImport, SqlSession sqlSession, ApiTestCaseMapper apiTestCaseMapper) {
List<ApiTestCaseWithBLOBs> cases = apiImport.getCases();
if (CollectionUtils.isNotEmpty(cases)) {
int batchCount = 0;
cases.forEach(item -> {
item.setId(UUID.randomUUID().toString());
item.setCreateTime(System.currentTimeMillis());
item.setUpdateTime(System.currentTimeMillis());
item.setCreateUserId(SessionUtils.getUserId());
item.setUpdateUserId(SessionUtils.getUserId());
item.setProjectId(SessionUtils.getCurrentProjectId());
item.setNum(getNextNum(item.getApiDefinitionId()));
apiTestCaseMapper.insert(item);
});
if (batchCount % 300 == 0) {
sqlSession.flushStatements();
}
}
}
/**
* 导入是插件或者postman时创建用例
* postman考虑是否有前置脚本
*/
private void importApiCase(ApiDefinitionWithBLOBs apiDefinition, ApiTestCaseMapper apiTestCaseMapper,
ApiTestImportRequest apiTestImportRequest, Boolean isInsert) {
try {
@ -372,6 +380,7 @@ public class ApiDefinitionService {
apiTestCase.setUpdateTime(System.currentTimeMillis());
apiTestCase.setCreateUserId(SessionUtils.getUserId());
apiTestCase.setUpdateUserId(SessionUtils.getUserId());
apiTestCase.setNum(getNextNum(apiTestCase.getApiDefinitionId()));
apiTestCase.setPriority("P0");
if (!isInsert) {
apiTestCase.setName(apiTestCase.getName() + "_" + apiTestCase.getId().substring(0, 5));
@ -404,7 +413,7 @@ public class ApiDefinitionService {
*/
public String run(RunDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
createBodyFiles(bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
HashTree hashTree = request.getTestElement().generateHashTree();
String runMode = ApiRunMode.DEFINITION.name();
@ -489,28 +498,27 @@ public class ApiDefinitionService {
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("parse_data_error"));
}
if (request.isSaved()) {
importApi(request, apiImport);
}
importApi(request, apiImport);
return apiImport;
}
private void importApi(ApiTestImportRequest request, ApiDefinitionImport apiImport) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
List<ApiDefinitionWithBLOBs> data = apiImport.getData();
ApiDefinitionMapper batchMapper = sqlSession.getMapper(ApiDefinitionMapper.class);
ApiTestCaseMapper apiTestCaseMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
List<ApiDefinitionResult> data = apiImport.getData();
int num = 0;
if (!CollectionUtils.isEmpty(data) && data.get(0) != null && data.get(0).getProjectId() != null) {
num = getNextNum(data.get(0).getProjectId());
}
for (int i = 0; i < data.size(); i++) {
ApiDefinitionResult item = data.get(i);
ApiDefinitionWithBLOBs item = data.get(i);
if (item.getName().length() > 255) {
item.setName(item.getName().substring(0, 255));
}
item.setNum(num++);
importCreate(item, batchMapper, apiTestCaseMapper, request);
importCreate(item, batchMapper, apiTestCaseMapper, request, apiImport.getCases());
importMsCase(apiImport, sqlSession, apiTestCaseMapper);
if (i % 300 == 0) {
sqlSession.flushStatements();
}
@ -518,6 +526,7 @@ public class ApiDefinitionService {
sqlSession.flushStatements();
}
public ReferenceDTO getReference(ApiScenarioRequest request) {
ReferenceDTO dto = new ReferenceDTO();
dto.setScenarioList(extApiScenarioMapper.selectReference(request));
@ -539,20 +548,12 @@ public class ApiDefinitionService {
}
public void editApiByParam(ApiBatchRequest request) {
List<String> ids = request.getIds();
if (request.isSelectAllDate()) {
ids = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(),request.getProtocol());
}
//name在这里只是查询参数
request.setName(null);
ApiDefinitionExample definitionExample = new ApiDefinitionExample();
definitionExample.createCriteria().andIdIn(ids);
ApiDefinitionWithBLOBs definitionWithBLOBs = new ApiDefinitionWithBLOBs();
BeanUtils.copyBean(definitionWithBLOBs, request);
definitionWithBLOBs.setUpdateTime(System.currentTimeMillis());
apiDefinitionMapper.updateByExampleSelective(definitionWithBLOBs, definitionExample);
apiDefinitionMapper.updateByExampleSelective(definitionWithBLOBs, getBatchExample(request));
}
public void testPlanRelevance(ApiCaseRelevanceRequest request) {
@ -602,14 +603,16 @@ public class ApiDefinitionService {
return apiDefinitionMapper.selectByExample(example);
}
public void deleteByParams(ApiDefinitionBatchProcessingRequest request) {
List<String> apiIds = request.getDataIds();
if (request.isSelectAllDate()) {
apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(),request.getProtocol());
}
public void deleteByParams(ApiBatchRequest request) {
apiDefinitionMapper.deleteByExample(getBatchExample(request));
}
public ApiDefinitionExample getBatchExample(ApiBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiDefinitionMapper.selectIds(query));
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(apiIds);
apiDefinitionMapper.deleteByExample(example);
example.createCriteria().andIdIn(request.getIds());
return example;
}
private List<String> getAllApiIdsByFontedSelect(Map<String, List<String>> filters, String name, List<String> moduleIds, String projectId, List<String> unSelectIds,String protocol) {
@ -629,12 +632,10 @@ public class ApiDefinitionService {
return ids;
}
public void removeToGcByParams(ApiDefinitionBatchProcessingRequest request) {
List<String> apiIds = request.getDataIds();
if (request.isSelectAllDate()) {
apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(),request.getProtocol());
}
extApiDefinitionMapper.removeToGc(apiIds);
public void removeToGcByParams(ApiBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiDefinitionMapper.selectIds(query));
extApiDefinitionMapper.removeToGc(request.getIds());
}
public List<ApiDefinitionResult> listRelevance(ApiDefinitionRequest request) {
@ -732,4 +733,17 @@ public class ApiDefinitionService {
private void addOrUpdateSwaggerImportCronJob(Schedule request) {
scheduleService.addOrUpdateCronJob(request, SwaggerUrlImportJob.getJobKey(request.getResourceId()), SwaggerUrlImportJob.getTriggerKey(request.getResourceId()), SwaggerUrlImportJob.class);
}
public ApiExportResult export(ApiBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiDefinitionMapper.selectIds(query));
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(request.getIds());
ApiExportResult apiExportResult = new ApiExportResult();
apiExportResult.setData(apiDefinitionMapper.selectByExampleWithBLOBs(example));
apiExportResult.setCases(apiTestCaseService.selectCasesBydApiIds(request.getIds()));
apiExportResult.setProjectName(request.getProjectId());
apiExportResult.setProtocol(request.getProtocol());
return apiExportResult;
}
}

View File

@ -0,0 +1,222 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiDocumentInfoDTO;
import io.metersphere.api.dto.ApiDocumentRequest;
import io.metersphere.api.dto.ApiDocumentSimpleInfoDTO;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.mapper.ext.ExtApiDocumentMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author song.tianyang
* @Date 2021/2/7 10:37 上午
* @Description
*/
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiDocumentService {
@Resource
ExtApiDocumentMapper extApiDocumentMapper;
public List<ApiDocumentSimpleInfoDTO> findApiDocumentSimpleInfoByRequest(ApiDocumentRequest request) {
return extApiDocumentMapper.findApiDocumentSimpleInfoByRequest(request);
}
public ApiDocumentInfoDTO conversionModelToDTO(ApiDefinitionWithBLOBs apiModel) {
ApiDocumentInfoDTO apiInfoDTO = new ApiDocumentInfoDTO();
if (apiModel != null) {
apiInfoDTO.setId(apiModel.getId());
apiInfoDTO.setName(apiModel.getName());
apiInfoDTO.setMethod(apiModel.getMethod());
apiInfoDTO.setUri(apiModel.getPath());
apiInfoDTO.setStatus(apiModel.getStatus());
JSONObject requestJsonObj = JSONObject.parseObject(apiModel.getRequest());
//head赋值
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);
}
}
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");
apiInfoDTO.setRequestBodyParamType(type);
if (StringUtils.equalsAny(type, "JSON", "XML", "Raw")) {
if (bodyObj.containsKey("raw")) {
String raw = bodyObj.getString("raw");
apiInfoDTO.setRequestBodyStrutureData(raw);
}
} 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);
}
}
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("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.setRequestBodyFormData(JSONArray.toJSONString(bodyParamList));
}
}
}
}
JSONObject responseJsonObj = JSONObject.parseObject(apiModel.getResponse());
//赋值响应头
if (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);
}
}
apiInfoDTO.setResponseHead(responseHeadDataArr.toJSONString());
}
// 赋值响应体
if (responseJsonObj.containsKey("body")) {
JSONObject bodyObj = responseJsonObj.getJSONObject("body");
if (bodyObj.containsKey("type")) {
String type = bodyObj.getString("type");
apiInfoDTO.setResponseBodyParamType(type);
if (StringUtils.equalsAny(type, "JSON", "XML", "Raw")) {
if (bodyObj.containsKey("raw")) {
String raw = bodyObj.getString("raw");
apiInfoDTO.setResponseBodyStrutureData(raw);
}
} 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);
}
}
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));
}
}
}
}
// 赋值响应码
if (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);
}
}
apiInfoDTO.setResponseCode(responseStatusDataArr.toJSONString());
}
}
return apiInfoDTO;
}
}

View File

@ -46,10 +46,6 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
@Resource
TestPlanProjectService testPlanProjectService;
@Resource
private ApiTestCaseService apiTestCaseService;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ProjectService projectService;
public ApiScenarioModuleService() {

View File

@ -38,7 +38,7 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.aspectj.util.FileUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@ -72,7 +72,7 @@ public class ApiTestCaseService {
@Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@Resource
TestPlanApiCaseMapper testPlanApiCaseMapper;
private TestPlanApiCaseMapper testPlanApiCaseMapper;
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
@ -136,7 +136,7 @@ public class ApiTestCaseService {
public ApiTestCase create(SaveApiTestCaseRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
ApiTestCase test = createTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
return test;
}
@ -152,31 +152,10 @@ public class ApiTestCaseService {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
request.setBodyUploadIds(null);
ApiTestCase test = updateTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles);
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
return test;
}
private void createBodyFiles(ApiTestCase test, List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (bodyUploadIds.size() > 0) {
String dir = BODY_FILE_DIR + "/" + test.getId();
File testDir = new File(dir);
if (!testDir.exists()) {
testDir.mkdirs();
}
for (int i = 0; i < bodyUploadIds.size(); i++) {
MultipartFile item = bodyFiles.get(i);
File file = new File(testDir + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(Translator.get("upload_fail"));
}
}
}
}
public void delete(String testId) {
extTestPlanTestCaseMapper.deleteByTestCaseID(testId);
deleteFileByTestId(testId);
@ -279,19 +258,14 @@ public class ApiTestCaseService {
}
}
private void saveFile(String testId, MultipartFile file) {
final FileMetadata fileMetadata = fileService.saveFile(file);
ApiTestFile apiTestFile = new ApiTestFile();
apiTestFile.setTestId(testId);
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
}
private void deleteFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
final List<ApiTestFile> ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample);
apiTestFileMapper.deleteByExample(ApiTestFileExample);
if (CollectionUtils.isNotEmpty(ApiTestFiles)) {
apiTestFileMapper.deleteByExample(ApiTestFileExample);
}
if (!CollectionUtils.isEmpty(ApiTestFiles)) {
final List<String> fileIds = ApiTestFiles.stream().map(ApiTestFile::getFileId).collect(Collectors.toList());
@ -386,6 +360,12 @@ public class ApiTestCaseService {
return apiTestCaseMapper.selectByExample(example);
}
public List<ApiTestCaseWithBLOBs> selectCasesBydApiIds(List<String> apiIds) {
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andApiDefinitionIdIn(apiIds);
return apiTestCaseMapper.selectByExampleWithBLOBs(example);
}
public Map<String, String> getRequest(ApiTestCaseRequest request) {
List<ApiTestCaseWithBLOBs> list = extApiTestCaseMapper.getRequest(request);
return list.stream().collect(Collectors.toMap(ApiTestCaseWithBLOBs::getId, ApiTestCaseWithBLOBs::getRequest));

View File

@ -7,6 +7,7 @@ import io.metersphere.api.dto.SaveHistoricalDataUpgrade;
import io.metersphere.api.dto.automation.ScenarioStatus;
import io.metersphere.api.dto.definition.request.MsScenario;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.assertions.MsAssertionDuration;
import io.metersphere.api.dto.definition.request.assertions.MsAssertions;
import io.metersphere.api.dto.definition.request.controller.MsIfController;
import io.metersphere.api.dto.definition.request.extract.MsExtract;
@ -84,6 +85,7 @@ public class HistoricalDataUpgradeService {
scenario.setReferenced("Upgrade");
scenario.setId(oldScenario.getId());
scenario.setResourceId(UUID.randomUUID().toString());
scenario.setHeaders(oldScenario.getHeaders());
LinkedList<MsTestElement> testElements = new LinkedList<>();
int index = 1;
for (Request request : oldScenario.getRequests()) {
@ -202,6 +204,23 @@ public class HistoricalDataUpgradeService {
if (StringUtils.isEmpty(msAssertions.getName())) {
msAssertions.setName("Assertions");
}
// 给初始值
if (msAssertions.getDuration() == null) {
msAssertions.setDuration(new MsAssertionDuration());
}
if (CollectionUtils.isEmpty(msAssertions.getJsr223())) {
msAssertions.setJsr223(new LinkedList<>());
}
if (CollectionUtils.isEmpty(msAssertions.getXpath2())) {
msAssertions.setXpath2(new LinkedList<>());
}
if (CollectionUtils.isEmpty(msAssertions.getJsonPath())) {
msAssertions.setJsonPath(new LinkedList<>());
}
if (CollectionUtils.isEmpty(msAssertions.getRegex())) {
msAssertions.setRegex(new LinkedList<>());
}
msAssertions.setType("Assertions");
msAssertions.setIndex(index + "");
msAssertions.setResourceId(UUID.randomUUID().toString());
@ -216,6 +235,16 @@ public class HistoricalDataUpgradeService {
if (StringUtils.isEmpty(extract.getName())) {
extract.setName("Extract");
}
// 默认给初始值
if (CollectionUtils.isEmpty(extract.getJson())) {
extract.setJson(new LinkedList<>());
}
if (CollectionUtils.isEmpty(extract.getXpath())) {
extract.setXpath(new LinkedList<>());
}
if (CollectionUtils.isEmpty(extract.getRegex())) {
extract.setRegex(new LinkedList<>());
}
extract.setType("Extract");
extract.setIndex(index + "");
extract.setHashTree(new LinkedList<>());

View File

@ -7,7 +7,7 @@ import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiSwaggerUrlDTO;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.ApiDefinitionExample;
import io.metersphere.base.domain.ApiModuleExample;
import io.metersphere.controller.request.BaseQueryRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -36,4 +36,6 @@ public interface ExtApiDefinitionMapper {
ApiDefinition getNextNum(@Param("projectId") String projectId);
List<ApiDefinitionResult> listRelevance(@Param("request")ApiDefinitionRequest request);
List<String> selectIds(@Param("request") BaseQueryRequest query);
}

View File

@ -210,85 +210,7 @@
from api_definition
left join project on api_definition.project_id = project.id
left join user on api_definition.user_id = user.id
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
</include>
</if>
<if test="request.name != null">
and (api_definition.name like CONCAT('%', #{request.name},'%')
or api_definition.tags like CONCAT('%', #{request.name},'%')
or api_definition.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.protocol != null">
AND api_definition.protocol = #{request.protocol}
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.projectId != null">
AND project.id = #{request.projectId}
</if>
<if test="request.id != null">
AND api_definition.id = #{request.id}
</if>
<if test="request.userId != null">
AND api_definition.user_id = #{request.userId}
</if>
<if test="request.createTime >0">
AND api_definition.create_time >= #{request.createTime}
</if>
<if test="request.moduleId != null">
AND api_definition.module_id = #{request.moduleId}
</if>
<if test="request.projectId != null">
AND api_definition.project_id = #{request.projectId}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api_definition.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and api_definition.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='method'">
and api_definition.method in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_id'">
and api_definition.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
<if test="request.apiCaseCoverage == 'uncoverage' ">
and api_definition.id not in
(SELECT api_definition_id FROM api_test_case)
</if>
<if test="request.apiCaseCoverage == 'coverage' ">
and api_definition.id in
(SELECT api_definition_id FROM api_test_case)
</if>
</where>
<include refid="queryWhereCondition"/>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
@ -446,4 +368,93 @@
WHERE sch.`enable` = true
AND apiScene.project_id = #{0,jdbcType=VARCHAR}
</select>
<select id="selectIds" resultType="java.lang.String">
select api_definition.id
from api_definition
left join project on api_definition.project_id = project.id
<include refid="queryWhereCondition"/>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
</include>
</if>
<if test="request.name != null">
and (api_definition.name like CONCAT('%', #{request.name},'%')
or api_definition.tags like CONCAT('%', #{request.name},'%')
or api_definition.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.protocol != null">
AND api_definition.protocol = #{request.protocol}
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
</if>
<if test="request.projectId != null">
AND project.id = #{request.projectId}
</if>
<if test="request.id != null">
AND api_definition.id = #{request.id}
</if>
<if test="request.userId != null">
AND api_definition.user_id = #{request.userId}
</if>
<if test="request.createTime >0">
AND api_definition.create_time >= #{request.createTime}
</if>
<if test="request.moduleId != null">
AND api_definition.module_id = #{request.moduleId}
</if>
<if test="request.projectId != null">
AND api_definition.project_id = #{request.projectId}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api_definition.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and api_definition.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='method'">
and api_definition.method in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='user_id'">
and api_definition.user_id in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
<if test="request.apiCaseCoverage == 'uncoverage' ">
and api_definition.id not in
(SELECT api_definition_id FROM api_test_case)
</if>
<if test="request.apiCaseCoverage == 'coverage' ">
and api_definition.id in
(SELECT api_definition_id FROM api_test_case)
</if>
</where>
</sql>
</mapper>

View File

@ -0,0 +1,11 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.ApiDocumentRequest;
import io.metersphere.api.dto.ApiDocumentSimpleInfoDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiDocumentMapper {
List<ApiDocumentSimpleInfoDTO> findApiDocumentSimpleInfoByRequest(@Param("request") ApiDocumentRequest request);
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ext.ExtApiDocumentMapper">
<select id="findApiDocumentSimpleInfoByRequest" resultType="io.metersphere.api.dto.ApiDocumentSimpleInfoDTO">
SELECT api.id,api.name FROM Api_definition api
<where>
<if test="request.projectId != null">
api.project_Id = #{request.projectId}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</where>
ORDER BY api.create_time DESC
</select>
</mapper>

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioExample;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.controller.request.BaseQueryRequest;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -34,4 +35,6 @@ public interface ExtApiScenarioMapper {
List<String> selectIdsNotExistsInPlan(String projectId, String planId);
ApiScenario getNextNum(@Param("projectId") String projectId);
List<String> selectIdsByQuery(@Param("request") ApiScenarioRequest request);
}

View File

@ -134,6 +134,11 @@
from api_scenario
left join project on api_scenario.project_id = project.id
left join user on api_scenario.user_id = user.id
<include refid="queryWhereCondition"/>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<sql id="queryWhereCondition">
<where>
<if test="request.combine != null">
<include refid="combine">
@ -144,8 +149,8 @@
<if test="request.name != null">
and (api_scenario.name like CONCAT('%', #{request.name},'%')
or api_scenario.tags like CONCAT('%', #{request.name},'%')
or api_scenario.num like CONCAT('%', #{request.name},'%'))
or api_scenario.tags like CONCAT('%', #{request.name},'%')
or api_scenario.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId}
@ -223,19 +228,13 @@
</if>
<if test="request.notInTestPlan == true ">
and api_scenario.id not in (
select pc.api_scenario_id
from test_plan_api_scenario pc
where pc.test_plan_id = #{request.planId}
select pc.api_scenario_id
from test_plan_api_scenario pc
where pc.test_plan_id = #{request.planId}
)
</if>
</where>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
api_scenario.${order.name} ${order.type}
</foreach>
</if>
</select>
</sql>
<select id="selectByTagId" resultType="io.metersphere.base.domain.ApiScenarioWithBLOBs">
select * from api_scenario where tags like CONCAT('%', #{id},'%')
@ -321,4 +320,12 @@
SELECT * FROM api_scenario WHERE api_scenario.project_id = #{projectId} ORDER BY num DESC LIMIT 1;
</select>
<select id="selectIdsByQuery" resultType="java.lang.String">
select api_scenario.id
from api_scenario
left join project on api_scenario.project_id = project.id
<include refid="queryWhereCondition"/>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
</mapper>

View File

@ -0,0 +1,34 @@
package io.metersphere.commons.utils;
import io.metersphere.commons.exception.MSException;
import io.metersphere.i18n.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.aspectj.util.FileUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.List;
public class FileUtils {
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
public static void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (CollectionUtils.isNotEmpty(bodyUploadIds) && CollectionUtils.isNotEmpty(bodyFiles)) {
File testDir = new File(BODY_FILE_DIR);
if (!testDir.exists()) {
testDir.mkdirs();
}
for (int i = 0; i < bodyUploadIds.size(); i++) {
MultipartFile item = bodyFiles.get(i);
File file = new File(BODY_FILE_DIR + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(Translator.get("upload_fail"));
}
}
}
}
}

View File

@ -1,8 +1,11 @@
package io.metersphere.commons.utils;
import io.metersphere.commons.exception.MSException;
import io.metersphere.controller.request.BaseQueryRequest;
import io.metersphere.controller.request.OrderRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
@ -28,7 +31,7 @@ public class ServiceUtils {
* @param func 查询id列表的数据库查询
* @return
*/
public static void getSelectAllIds(BaseQueryRequest queryRequest, Function<BaseQueryRequest, List<String>> func) {
public static<T> void getSelectAllIds( T batchRequest, BaseQueryRequest queryRequest, Function<BaseQueryRequest, List<String>> func) {
if (queryRequest.isSelectAll()) {
List<String> ids = func.apply(queryRequest);
if (!ids.isEmpty()) {
@ -37,6 +40,13 @@ public class ServiceUtils {
.collect(Collectors.toList());
}
queryRequest.setIds(ids);
try {
Method setIds = batchRequest.getClass().getDeclaredMethod("setIds", List.class);
setIds.invoke(batchRequest, ids);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("请求没有setIds方法");
}
}
}
}

View File

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
@RestControllerAdvice
@ -30,6 +31,12 @@ public class RestControllerExceptionHandler {
}
@ExceptionHandler(SQLException.class)
public ResultHolder sqlExceptionHandler(HttpServletRequest request, HttpServletResponse response, MSException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return ResultHolder.error("SQL error happened, please check logs.");
}
@ExceptionHandler(MSException.class)
public ResultHolder msExceptionHandler(HttpServletRequest request, HttpServletResponse response, MSException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

View File

@ -22,6 +22,15 @@ public class BaseQueryRequest {
private List<String> nodeIds;
/**
* selectAll选择的数据是否是全部数据全部数据是不受分页影响的数据
* filters: 数据状态
* name如果是全部数据那么表格如果历经查询查询参数是什么
* moduleIds 哪些模块的数据
* unSelectIds是否在页面上有未勾选的数据有的话他们的ID是哪些
* filters/name/moduleIds/unSeelctIds 只在isSelectAllDate为true时需要为了让程序能明确批量的范围
*/
/**
* 是否选中所有数据
*/

View File

@ -56,7 +56,7 @@ public class ApiScenarioTestJob extends MsScheduleJob {
request.setProjectId(projectID);
request.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
request.setExecuteType(ExecuteType.Saved.name());
request.setScenarioIds(this.scenarioIds);
request.setIds(this.scenarioIds);
request.setReportUserID(this.userId);
apiAutomationService.run(request);

View File

@ -29,7 +29,6 @@ public class SwaggerUrlImportJob extends MsScheduleJob {
request.setSwaggerUrl(swaggerUrlProject.getSwaggerUrl());
request.setModuleId(swaggerUrlProject.getModuleId());
request.setPlatform("Swagger2");
request.setSaved(true);
request.setUserId(jobDataMap.getString("userId"));
request.setType("schedule");
apiDefinitionService.apiTestImport(null, request);

View File

@ -0,0 +1,32 @@
package io.metersphere.performance.engine.producer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.commons.utils.LogUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class LoadTestProducer {
@Value("${kafka.topic}")
private String topic;
@Resource
private KafkaTemplate<String, Object> kafkaTemplate;
@Resource
private ObjectMapper objectMapper;
public void sendMessage(String reportId) {
Metric metric = new Metric();
metric.setReportId(reportId);
metric.setThreadName("tearDown Thread Group"); // 发送停止消息
try {
this.kafkaTemplate.send(topic, objectMapper.writeValueAsString(metric));
} catch (JsonProcessingException e) {
LogUtil.error("发送停止消息失败", e);
}
}
}

View File

@ -0,0 +1,77 @@
package io.metersphere.performance.engine.producer;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class Metric {
@JsonProperty("test.id")
private String testId;
@JsonProperty("test.name")
private String testName;
@JsonProperty("test.startTime")
private Long clusterStartTime;
@JsonProperty("test.reportId")
private String reportId;
@JsonProperty("ContentType")
private String contentType;
@JsonProperty("IdleTime")
private Integer idleTime;
@JsonProperty("ElapsedTime")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
private Date elapsedTime;
@JsonProperty("ErrorCount")
private Integer errorCount;
@JsonProperty("Timestamp")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ")
private Date timestamp;
@JsonProperty("URL")
private String url;
@JsonProperty("SampleStartTime")
private String sampleStartTime;
@JsonProperty("Success")
private Boolean success;
@JsonProperty("Bytes")
private Integer bytes;
@JsonProperty("SentBytes")
private Integer sentBytes;
@JsonProperty("AllThreads")
private Integer allThreads;
@JsonProperty("TestElement.name")
private String testElementName;
@JsonProperty("DataType")
private String dataType;
@JsonProperty("ResponseTime")
private Integer responseTime;
@JsonProperty("SampleCount")
private Integer sampleCount;
@JsonProperty("FailureMessage")
private String failureMessage;
@JsonProperty("ConnectTime")
private Integer connectTime;
@JsonProperty("ResponseCode")
private String responseCode;
@JsonProperty("TestStartTime")
private Long testStartTime;
@JsonProperty("AssertionResults")
private List<Object> assertionResults;
@JsonProperty("Latency")
private Integer latency;
@JsonProperty("InjectorHostname")
private String injectorHostname;
@JsonProperty("GrpThreads")
private Integer grpThreads;
@JsonProperty("SampleEndTime")
private String sampleEndTime;
@JsonProperty("BodySize")
private Long bodySize;
@JsonProperty("ThreadName")
private String threadName;
@JsonProperty("SampleLabel")
private String sampleLabel;
}

View File

@ -21,6 +21,7 @@ import io.metersphere.i18n.Translator;
import io.metersphere.job.sechedule.PerformanceTestJob;
import io.metersphere.performance.engine.Engine;
import io.metersphere.performance.engine.EngineFactory;
import io.metersphere.performance.engine.producer.LoadTestProducer;
import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService;
import io.metersphere.service.ScheduleService;
@ -75,6 +76,8 @@ public class PerformanceTestService {
private TestCaseService testCaseService;
@Resource
private TestResourcePoolMapper testResourcePoolMapper;
@Resource
private LoadTestProducer loadTestProducer;
public List<LoadTestDTO> list(QueryTestPlanRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
@ -437,6 +440,8 @@ public class PerformanceTestService {
reportService.deleteReport(reportId);
} else {
stopEngine(reportId);
// 发送测试停止消息
loadTestProducer.sendMessage(reportId);
// 停止测试之后设置报告的状态
reportService.updateStatus(reportId, PerformanceTestStatus.Completed.name());
}

View File

@ -1,8 +1,8 @@
package io.metersphere.track.controller;
import io.metersphere.base.domain.Issues;
import io.metersphere.track.issue.PlatformUser;
import io.metersphere.track.issue.ZentaoBuild;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.issue.domain.ZentaoBuild;
import io.metersphere.track.service.IssuesService;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.springframework.web.bind.annotation.*;

View File

@ -1,6 +1,7 @@
package io.metersphere.track.issue;
import io.metersphere.base.domain.Issues;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.request.testcase.IssuesRequest;
import java.util.List;

View File

@ -7,6 +7,7 @@ import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import org.commonmark.node.Node;

View File

@ -5,6 +5,7 @@ import io.metersphere.base.domain.TestCaseIssues;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.request.testcase.IssuesRequest;
import java.util.List;

View File

@ -7,9 +7,9 @@ import io.metersphere.base.domain.*;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.RestTemplateUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.ResultHolder;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;
@ -194,9 +194,9 @@ public class TapdPlatform extends AbstractIssuePlatform {
HttpHeaders header = auth(account, password);
if (httpMethod.equals(HttpMethod.GET)) {
responseJson = RestTemplateUtils.get(url, header);
responseJson = TapdRestUtils.get(url, header);
} else {
responseJson = RestTemplateUtils.post(url, params, header);
responseJson = TapdRestUtils.post(url, params, header);
}
ResultHolder result = JSON.parseObject(responseJson, ResultHolder.class);

View File

@ -1,6 +1,8 @@
package io.metersphere.commons.utils;
package io.metersphere.track.issue;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@ -8,7 +10,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
public class RestTemplateUtils {
public class TapdRestUtils {
private static RestTemplate restTemplate;

View File

@ -7,6 +7,8 @@ import io.metersphere.base.domain.*;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.issue.domain.ZentaoBuild;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpEntity;

View File

@ -1,4 +1,4 @@
package io.metersphere.track.issue;
package io.metersphere.track.issue.domain;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package io.metersphere.track.domain;
package io.metersphere.track.issue.domain;
import lombok.Data;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.metersphere.track.issue;
package io.metersphere.track.issue.domain;
import lombok.Data;

View File

@ -1,7 +1,6 @@
package io.metersphere.track.request.testcase;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.controller.request.BaseQueryRequest;
import io.metersphere.controller.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
@ -15,5 +14,5 @@ public class TestCaseBatchRequest extends TestCaseWithBLOBs {
private List<OrderRequest> orders;
private String projectId;
private BaseQueryRequest condition;
private QueryTestCaseRequest condition;
}

View File

@ -16,6 +16,8 @@ import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.IntegrationService;
import io.metersphere.service.ProjectService;
import io.metersphere.track.issue.*;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.issue.domain.ZentaoBuild;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

View File

@ -180,14 +180,16 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
List<String> projectIds = testPlanProjectService.getProjectIdsByPlanId(planId);
projectIds.forEach(id -> {
Project project = projectMapper.selectByPrimaryKey(id);
String name = project.getName();
List<TestCaseNodeDTO> nodeList = getNodeDTO(id, planId);
TestCaseNodeDTO testCaseNodeDTO = new TestCaseNodeDTO();
testCaseNodeDTO.setId(project.getId());
testCaseNodeDTO.setName(name);
testCaseNodeDTO.setLabel(name);
testCaseNodeDTO.setChildren(nodeList);
list.add(testCaseNodeDTO);
if (project != null) {
String name = project.getName();
List<TestCaseNodeDTO> nodeList = getNodeDTO(id, planId);
TestCaseNodeDTO testCaseNodeDTO = new TestCaseNodeDTO();
testCaseNodeDTO.setId(project.getId());
testCaseNodeDTO.setName(name);
testCaseNodeDTO.setLabel(name);
testCaseNodeDTO.setChildren(nodeList);
list.add(testCaseNodeDTO);
}
});
return list;

View File

@ -5,6 +5,7 @@ import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.ApiBatchRequest;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
@ -528,33 +529,25 @@ public class TestCaseService {
public void editTestCaseBath(TestCaseBatchRequest request) {
getSelectAllIds(request);
TestCaseExample testCaseExample = new TestCaseExample();
testCaseExample.createCriteria().andIdIn(request.getIds());
TestCaseExample example = this.getBatchExample(request);
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, request);
testCase.setUpdateTime(System.currentTimeMillis());
testCaseMapper.updateByExampleSelective(
testCase,
testCaseExample);
testCaseMapper.updateByExampleSelective(testCase, example);
}
public void deleteTestCaseBath(TestCaseBatchRequest request) {
getSelectAllIds(request);
TestCaseExample example = this.getBatchExample(request);
deleteTestPlanTestCaseBath(request.getIds());
TestCaseExample example = new TestCaseExample();
example.createCriteria().andIdIn(request.getIds());
testCaseMapper.deleteByExample(example);
}
public void getSelectAllIds(TestCaseBatchRequest request) {
if (request.getCondition().isSelectAll()) {
ServiceUtils.getSelectAllIds(request.getCondition(),
(query) -> extTestCaseMapper.selectIds(query));
request.setIds(request.getCondition().getIds());
}
public TestCaseExample getBatchExample(TestCaseBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extTestCaseMapper.selectIds(query));
TestCaseExample example = new TestCaseExample();
example.createCriteria().andIdIn(request.getIds());
return example;
}
public void deleteTestPlanTestCaseBath(List<String> caseIds) {

View File

@ -11,6 +11,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -26,23 +27,15 @@ public class TestPlanProjectService {
private TestPlanMapper testPlanMapper;
public List<String> getProjectIdsByPlanId(String planId) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId);
TestPlanProjectExample example = new TestPlanProjectExample();
example.createCriteria().andTestPlanIdEqualTo(planId);
List<String> projectIds = testPlanProjectMapper.selectByExample(example)
.stream()
.map(TestPlanProject::getProjectId)
.collect(Collectors.toList());
if (testPlan != null && StringUtils.isNotBlank(testPlan.getProjectId())) {
if (!projectIds.contains(testPlan.getProjectId())) {
projectIds.add(testPlan.getProjectId());
}
TestPlan plan = testPlanMapper.selectByPrimaryKey(planId);
String workspaceId = plan.getWorkspaceId();
if (StringUtils.isNotBlank(workspaceId)) {
ProjectExample example = new ProjectExample();
example.createCriteria().andWorkspaceIdEqualTo(workspaceId);
List<Project> projects = projectMapper.selectByExample(example);
return projects.stream().map(Project::getId).collect(Collectors.toList());
}
if (projectIds.isEmpty()) {
return null;
}
return projectIds;
return new ArrayList<>();
}
public List<Project> getProjectByPlanId(TestCaseRelevanceRequest request) {
@ -62,15 +55,16 @@ public class TestPlanProjectService {
}
public List<String> getPlanIdByProjectId(String projectId) {
TestPlanProjectExample testPlanProjectExample = new TestPlanProjectExample();
testPlanProjectExample.createCriteria().andProjectIdEqualTo(projectId);
List<TestPlanProject> testPlanProjects = testPlanProjectMapper.selectByExample(testPlanProjectExample);
if (CollectionUtils.isEmpty(testPlanProjects)) {
TestPlanExample testPlanExample = new TestPlanExample();
testPlanExample.createCriteria().andProjectIdEqualTo(projectId);
List<TestPlan> testPlans = testPlanMapper.selectByExample(testPlanExample);
if (CollectionUtils.isEmpty(testPlans)) {
return null;
}
return testPlanProjects
return testPlans
.stream()
.map(TestPlanProject::getTestPlanId)
.map(TestPlan::getId)
.collect(Collectors.toList());
}
}

View File

@ -96,7 +96,7 @@ public class TestPlanScenarioCaseService {
scenarioIds.add(apiScenario.getApiScenarioId());
scenarioIdApiScarionMap.put(apiScenario.getApiScenarioId(),apiScenario.getId());
}
request.setScenarioIds(scenarioIds);
request.setIds(scenarioIds);
request.setScenarioTestPlanIdMap(scenarioIdApiScarionMap);
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
return apiAutomationService.run(request);

View File

@ -119,15 +119,6 @@ public class TestPlanService {
}
String testPlanId = UUID.randomUUID().toString();
List<String> projectIds = testPlan.getProjectIds();
projectIds.forEach(id -> {
TestPlanProject testPlanProject = new TestPlanProject();
testPlanProject.setProjectId(id);
testPlanProject.setTestPlanId(testPlanId);
testPlanProjectMapper.insertSelective(testPlanProject);
});
testPlan.setId(testPlanId);
testPlan.setStatus(TestPlanStatus.Prepare.name());
testPlan.setCreateTime(System.currentTimeMillis());
@ -166,7 +157,6 @@ public class TestPlanService {
}
public int editTestPlan(TestPlanDTO testPlan) {
editTestPlanProject(testPlan);
checkTestPlanExist(testPlan);
TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库
if (!res.getStatus().equals(testPlan.getStatus())) { // 若有改变才更新时间
@ -213,70 +203,6 @@ public class TestPlanService {
return i;
}
private void editTestPlanProject(TestPlanDTO testPlan) {
// 将要进行关联的项目ID
List<String> projectIds = testPlan.getProjectIds();
// 如果将要关联的项目ID中包含测试计划所属ID则进行剔除
if (!CollectionUtils.isEmpty(projectIds)) {
if (projectIds.contains(testPlan.getProjectId())) {
projectIds.remove(testPlan.getProjectId());
}
}
// todo 优化 TestPlanList intoPlan 方法会触发此更新
if (StringUtils.isNotBlank(testPlan.getProjectId())) {
TestPlanProjectExample testPlanProjectExample1 = new TestPlanProjectExample();
testPlanProjectExample1.createCriteria().andTestPlanIdEqualTo(testPlan.getId());
List<TestPlanProject> testPlanProjects = testPlanProjectMapper.selectByExample(testPlanProjectExample1);
// 已经关联的项目idList
List<String> dbProjectIds = testPlanProjects.stream().map(TestPlanProject::getProjectId).collect(Collectors.toList());
// 修改后传过来的项目idList如果还未关联进行关联
projectIds.forEach(projectId -> {
if (!dbProjectIds.contains(projectId)) {
TestPlanProject testPlanProject = new TestPlanProject();
testPlanProject.setTestPlanId(testPlan.getId());
testPlanProject.setProjectId(projectId);
testPlanProjectMapper.insert(testPlanProject);
}
});
TestPlanProjectExample testPlanProjectExample = new TestPlanProjectExample();
TestPlanProjectExample.Criteria criteria1 = testPlanProjectExample.createCriteria();
criteria1.andTestPlanIdEqualTo(testPlan.getId());
if (!CollectionUtils.isEmpty(projectIds)) {
criteria1.andProjectIdNotIn(projectIds);
}
testPlanProjectMapper.deleteByExample(testPlanProjectExample);
// 关联的项目下的用例idList
List<String> caseIds = null;
// 测试计划所属项目下的用例不解除关联
projectIds.add(testPlan.getProjectId());
if (!CollectionUtils.isEmpty(projectIds)) {
TestCaseExample example = new TestCaseExample();
example.createCriteria().andProjectIdIn(projectIds);
List<TestCase> caseList = testCaseMapper.selectByExample(example);
caseIds = caseList.stream().map(TestCase::getId).collect(Collectors.toList());
}
// 取消关联项目下的用例和计划的关系
TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample();
TestPlanTestCaseExample.Criteria criteria = testPlanTestCaseExample.createCriteria().andPlanIdEqualTo(testPlan.getId());
if (!CollectionUtils.isEmpty(caseIds)) {
criteria.andCaseIdNotIn(caseIds);
}
testPlanTestCaseMapper.deleteByExample(testPlanTestCaseExample);
List<String> relevanceProjectIds = new ArrayList<>();
relevanceProjectIds.add(testPlan.getProjectId());
if (!CollectionUtils.isEmpty(testPlan.getProjectIds())) {
relevanceProjectIds.addAll(testPlan.getProjectIds());
}
testPlanApiCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
testPlanScenarioCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
testPlanLoadCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
}
}
//计划内容
private Map<String, Object> getTestPlanParamMap(TestPlan testPlan) {
Long startTime = testPlan.getPlannedStartTime();
@ -331,7 +257,6 @@ public class TestPlanService {
public int deleteTestPlan(String planId) {
TestPlan testPlan = getTestPlan(planId);
deleteTestCaseByPlanId(planId);
testPlanProjectService.deleteTestPlanProjectByPlanId(planId);
testPlanApiCaseService.deleteByPlanId(planId);
testPlanScenarioCaseService.deleteByPlanId(planId);

View File

@ -277,12 +277,16 @@ public class XmindCaseParser {
String tc = title.replace("", ":");
String[] tcArr = tc.split(":");
if (tcArr.length != 2) {
if (tcArr.length < 1) {
process.add(Translator.get("test_case_name") + Translator.get("incorrect_format"), title);
return;
}
// 用例名称
testCase.setName(this.replace(tcArr[1], TC_REGEX));
StringBuffer name = new StringBuffer();
for (int i = 1; i < tcArr.length; i++) {
name.append(tcArr[i]);
}
testCase.setName(name.toString());
testCase.setNodePath(nodePath);
// 用例等级和用例性质处理

@ -1 +1 @@
Subproject commit b35af517d888268abd3a8f2b58d6ea94335138a5
Subproject commit 44bcc9893033900f95c99068cd4edec740465dfe

View File

@ -0,0 +1,4 @@
ALTER TABLE schedule
MODIFY COLUMN id VARCHAR(255);
ALTER TABLE message_task
MODIFY COLUMN id VARCHAR(255);

View File

@ -0,0 +1,4 @@
ALTER TABLE schedule
MODIFY COLUMN resource_id VARCHAR(255);
ALTER TABLE message_task
MODIFY COLUMN test_id VARCHAR(255);

View File

@ -8,6 +8,7 @@
@setModuleOptions="setModuleOptions"
@setNodeTree="setNodeTree"
@enableTrash="enableTrash"
@exportAPI="exportAPI"
:type="'edit'"
ref="nodeTree"/>
</ms-aside-container>
@ -267,6 +268,9 @@
},
enableTrash(data) {
this.trashEnable = data;
},
exportAPI() {
this.$refs.apiScenarioList.exportApi();
}
}
}

View File

@ -9,7 +9,8 @@
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all-fixed"
@sort-change="sort"
@filter-change="filter"
@select-all="select" @select="select"
@select-all="handleSelectAll"
@select="handleSelect"
@header-dragend="headerDragend"
:height="screenHeight"
v-loading="loading">
@ -24,7 +25,7 @@
<el-table-column v-if="!referenced" width="30" min-width="30" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="isSelect(scope.row)" :buttons="buttons" :size="selectDataCounts"/>
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
</template>
</el-table-column>
<template v-for="(item, index) in tableLabel">
@ -170,6 +171,7 @@ import MsTableHeader from "@/business/components/common/components/MsTableHeader
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import MsTag from "../../../common/components/MsTag";
import {downloadFile, getCurrentProjectID, getUUID} from "@/common/js/utils";
import {getCurrentProjectID, getCurrentUser, getUUID} from "@/common/js/utils";
import MsApiReportDetail from "../report/ApiReportDetail";
import MsTableMoreBtn from "./TableMoreBtn";
@ -182,11 +184,20 @@ import PriorityTableItem from "../../../track/common/tableItems/planview/Priorit
import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem";
import BatchEdit from "../../../track/case/components/BatchEdit";
import {API_SCENARIO_LIST, TEST_CASE_LIST, TEST_PLAN_LIST, WORKSPACE_ID} from "../../../../../common/js/constants";
import {PROJECT_NAME, WORKSPACE_ID} from "../../../../../common/js/constants";
import EnvironmentSelect from "../../definition/components/environment/EnvironmentSelect";
import BatchMove from "../../../track/case/components/BatchMove";
import {_filter, _sort} from "@/common/js/tableUtils";
import {Api_Scenario_List, Track_Test_Case} from "@/business/components/common/model/JsonData";
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
import {
_filter,
_handleSelect,
_handleSelectAll,
_sort,
getSelectDataCounts,
setUnSelectIds, toggleAllSelection
} from "@/common/js/tableUtils";
export default {
name: "MsApiScenarioList",
@ -276,7 +287,7 @@ export default {
}
],
isSelectAllDate: false,
unSelection: [],
selectRows: new Set(),
selectDataCounts: 0,
typeArr: [
{id: 'level', name: this.$t('test_track.case.priority')},
@ -355,6 +366,7 @@ export default {
this.search();
},
search() {
this.selectRows = new Set();
this.getLabel()
this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
@ -524,11 +536,9 @@ export default {
}
},
buildBatchParam(param) {
param.scenarioIds = this.selection;
param.ids = Array.from(this.selectRows).map(row => row.id);
param.projectId = getCurrentProjectID();
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
param.condition = this.condition;
},
handleBatchExecute() {
this.infoDb = false;
@ -542,16 +552,21 @@ export default {
this.batchReportId = run.id;
});
},
select(selection) {
this.selection = selection.map(s => s.id);
//
this.selectRowsCount(this.selection)
this.$emit('selection', selection);
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
},
isSelect(row) {
return this.selection.includes(row.id)
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
},
isSelectDataAll(data) {
this.condition.selectAll = data;
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
toggleAllSelection(this.$refs.scenarioTable, this.tableData, this.selectRows);
},
edit(row) {
let data = JSON.parse(JSON.stringify(row));
@ -592,28 +607,6 @@ export default {
this.infoDb = true;
this.reportId = row.reportId;
},
//
isSelectDataAll(dataType) {
this.isSelectAllDate = dataType;
this.selectRowsCount(this.selection);
//
if (this.selection.length != this.tableData.length) {
this.$refs.scenarioTable.toggleAllSelection(true);
}
},
//
selectRowsCount(selection) {
let selectedIDs = selection;
let allIDs = this.tableData.map(s => s.id);
this.unSelection = allIDs.filter(function (val) {
return selectedIDs.indexOf(val) === -1
});
if (this.isSelectAllDate) {
this.selectDataCounts = this.total - this.unSelection.length;
} else {
this.selectDataCounts = this.selection.length;
}
},
//
isSelectThissWeekData() {
let dataRange = this.$route.params.dataSelectRange;
@ -666,6 +659,20 @@ export default {
openScenario(item) {
this.$emit('openScenario', item)
},
exportApi() {
let param = {};
this.buildBatchParam(param);
this.loading = true;
if (param.ids === undefined || param.ids.length < 1) {
this.$warning(this.$t("api_test.automation.scenario.check_case"));
return;
}
this.result = this.$post("/api/automation/export", param, response => {
this.loading = false;
let obj = response.data;
downloadFile("Metersphere_Scenario_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
});
}
}
}
</script>

View File

@ -11,16 +11,20 @@
@edit="edit"
@drag="drag"
@remove="remove"
@refresh="list"
@nodeSelectEvent="nodeChange"
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-button v-if="!isReadOnly" icon="el-icon-folder-add" @click="addScenario" v-tester/>
</template>
</el-input>
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
<api-scenario-module-header
:condition="condition"
:current-module="currentModule"
:is-read-only="isReadOnly"
:project-id="projectId"
@exportAPI="exportAPI"
@addScenario="addScenario"
@refreshTable="$emit('refreshTable')"
@refresh="refresh"/>
</template>
</ms-node-tree>
@ -34,24 +38,26 @@
</template>
<script>
import SelectMenu from "../../../track/common/SelectMenu";
import MsAddBasisScenario from "@/business/components/api/automation/scenario/AddBasisScenario";
import {getCurrentProjectID} from "@/common/js/utils";
import MsNodeTree from "../../../track/common/NodeTree";
import {buildNodePath} from "../../definition/model/NodeTree";
import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton";
import SelectMenu from "../../../track/common/SelectMenu";
import MsAddBasisScenario from "@/business/components/api/automation/scenario/AddBasisScenario";
import {getCurrentProjectID} from "@/common/js/utils";
import MsNodeTree from "../../../track/common/NodeTree";
import {buildNodePath} from "../../definition/model/NodeTree";
import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton";
import ApiScenarioModuleHeader from "@/business/components/api/automation/scenario/module/ApiScenarioModuleHeader";
export default {
name: 'MsApiScenarioModule',
components: {
ModuleTrashButton,
MsNodeTree,
MsAddBasisScenario,
SelectMenu,
},
props: {
isReadOnly: {
type: Boolean,
export default {
name: 'MsApiScenarioModule',
components: {
ApiScenarioModuleHeader,
ModuleTrashButton,
MsNodeTree,
MsAddBasisScenario,
SelectMenu,
},
props: {
isReadOnly: {
type: Boolean,
default() {
return false
}
@ -174,9 +180,9 @@
this.$emit("nodeSelectEvent", node, nodeIds, pNodes);
}
},
// exportAPI() {
// this.$emit('exportAPI');
// },
exportAPI() {
this.$emit('exportAPI');
},
// debug() {
// this.$emit('debug');
// },

View File

@ -106,7 +106,7 @@
</el-col>
<el-col :span="3" class="ms-col-one ms-font">
<el-link class="head" @click="showScenarioParameters">{{$t('api_test.automation.scenario_total')}}</el-link>
{{this.currentScenario.variables!=undefined?this.currentScenario.variables.length: 0}}
{{getVariableSize()}}
</el-col>
<el-col :span="3" class="ms-col-one ms-font">
<el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox>
@ -196,7 +196,7 @@
</el-drawer>
<!--场景公共参数-->
<ms-variable-list @setVariables="setVariables" ref="scenarioParameters"/>
<ms-variable-list @setVariables="setVariables" ref="scenarioParameters" class="ms-sc-variable-header"/>
<!--外部导入-->
<api-import ref="apiImport" :saved="false" @refresh="apiImport"/>
</div>
@ -427,8 +427,9 @@
getIdx(index) {
return index - 0.33
},
setVariables(v) {
setVariables(v, headers) {
this.currentScenario.variables = v;
this.currentScenario.headers = headers;
if (this.path.endsWith("/update")) {
//
this.editScenario();
@ -503,7 +504,7 @@
this.sort();
},
nodeClick(data, node) {
if (data.referenced != 'REF' && data.referenced != 'Deleted') {
if (data.referenced != 'REF' && data.referenced != 'Deleted' && !data.disabled) {
this.operatingElements = ELEMENTS.get(data.type);
} else {
this.operatingElements = [];
@ -679,7 +680,7 @@
this.editScenario();
this.debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare,
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare, headers: this.currentScenario.headers,
environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition
};
this.reportId = getUUID().substring(0, 8);
@ -863,6 +864,9 @@
if (!this.currentScenario.variables) {
this.currentScenario.variables = [];
}
if (!this.currentScenario.headers) {
this.currentScenario.headers = [];
}
if (this.currentScenario.id) {
this.result = this.$get("/api/automation/getApiScenario/" + this.currentScenario.id, response => {
if (response.data) {
@ -887,6 +891,9 @@
}
})
}
if (obj.headers) {
this.currentScenario.headers = obj.headers;
}
this.enableCookieShare = obj.enableCookieShare;
this.scenarioDefinition = obj.hashTree;
}
@ -905,8 +912,9 @@
this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId);
// 便
let scenario = {
id: this.currentScenario.id, enableCookieShare: this.enableCookieShare, name: this.currentScenario.name, variables: this.currentScenario.variables,
type: "scenario", referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition
id: this.currentScenario.id, enableCookieShare: this.enableCookieShare, name: this.currentScenario.name, type: "scenario",
variables: this.currentScenario.variables, headers: this.currentScenario.headers,
referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition
};
this.currentScenario.scenarioDefinition = scenario;
if (this.currentScenario.tags instanceof Array) {
@ -924,7 +932,7 @@
this.loading = false;
},
showScenarioParameters() {
this.$refs.scenarioParameters.open(this.currentScenario.variables);
this.$refs.scenarioParameters.open(this.currentScenario.variables, this.currentScenario.headers);
},
apiImport(importData) {
if (importData && importData.data) {
@ -934,6 +942,16 @@
this.sort();
this.reload();
}
},
getVariableSize() {
let size = 0;
if (this.currentScenario.variables) {
size += this.currentScenario.variables.length;
}
if (this.currentScenario.headers && this.currentScenario.headers.length > 1) {
size += this.currentScenario.headers.length - 1;
}
return size;
}
}
}
@ -1066,4 +1084,7 @@
content: "\e722";
font-size: 20px;
}
.ms-sc-variable-header >>> .el-dialog__body {
padding: 0px 20px;
}
</style>

View File

@ -48,7 +48,7 @@
let scenarioIds = [];
scenarioIds.push(row.id);
run.projectId = getCurrentProjectID();
run.scenarioIds = scenarioIds;
run.ids = scenarioIds;
run.id = getUUID();
run.name = row.name;
this.$post(url, run, response => {

View File

@ -107,7 +107,8 @@
},
methods: {
active() {
this.$set(this.data, 'active', !this.data.active);
//
//this.$set(this.data, 'active', !this.data.active);
this.$emit('active');
},
copyRow() {

View File

@ -1,12 +1,12 @@
<template>
<div>
<div v-if="request.protocol === 'HTTP'">
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-if="request.url" v-model="request.url" style="width: 85%;margin-top: 10px" size="small">
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-if="request.url" v-model="request.url" style="width: 85%;margin-top: 10px" size="small" @blur="urlChange">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-input>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-else v-model="request.path" style="width: 85%;margin-top: 10px" size="small">
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-else v-model="request.path" style="width: 85%;margin-top: 10px" size="small" @blur="pathChange">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
@ -18,14 +18,14 @@
<el-row>
<el-col :span="8">
<el-form-item :label="$t('api_test.request.tcp.server')" prop="server">
<el-input class="server-input" v-model="request.server" maxlength="300" show-word-limit size="small"/>
<el-input class="server-input" v-model="request.server" maxlength="300" show-word-limit size="small"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('api_test.request.tcp.port')" prop="port" label-width="60px">
<el-input-number v-model="request.port" controls-position="right" :min="0" :max="65535" size="small"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('api_test.request.tcp.port')" prop="port" label-width="60px">
<el-input-number v-model="request.port" controls-position="right" :min="0" :max="65535" size="small"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
@ -33,17 +33,55 @@
</template>
<script>
import {REQ_METHOD} from "@/business/components/api/definition/model/JsonData";
import {REQ_METHOD} from "@/business/components/api/definition/model/JsonData";
import {KeyValue} from "../../../definition/model/ApiTestModel";
export default {
name: "CustomizeReqInfo",
props: ['request', 'isCustomizeReq'],
data() {
return {
reqOptions: REQ_METHOD,
export default {
name: "CustomizeReqInfo",
props: ['request', 'isCustomizeReq'],
data() {
return {
reqOptions: REQ_METHOD,
}
},
methods: {
pathChange() {
if (!this.request.path || this.request.path.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.request.path));
if (url) {
this.request.path = decodeURIComponent(this.request.path.substr(0, this.request.path.indexOf("?")));
}
},
urlChange() {
if (!this.request.url || this.request.url.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.request.url));
if (url) {
this.request.url = decodeURIComponent(this.request.url.substr(0, this.request.url.indexOf("?")));
}
},
addProtocol(url) {
if (url) {
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
return "https://" + url;
}
}
return url;
},
getURL(urlStr) {
try {
let url = new URL(urlStr);
url.searchParams.forEach((value, key) => {
if (key && value) {
this.request.arguments.splice(0, 0, new KeyValue({name: key, required: false, value: value}));
}
});
return url;
} catch (e) {
this.$error(this.$t('api_test.request.url_invalid'), 2000);
}
},
}
}
}
</script>
<style scoped>

View File

@ -3,6 +3,7 @@
v-loading="loading"
@copy="copyRow"
@remove="remove"
@active="active"
:is-show-name-input="!isDeletedOrRef"
:data="request"
:draggable="true"
@ -34,7 +35,16 @@
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'" :showScript="false"/>
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
<api-response-component :currentProtocol="request.protocol" :result="request.requestResult"/>
<div v-if="request.result">
<el-tabs v-model="request.activeName" closable class="ms-tabs">
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in request.result.scenarios" :key="index">
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
<api-response-component v-if="result.name===request.name" :result="result"/>
</div>
</el-tab-pane>
</el-tabs>
</div>
<api-response-component :currentProtocol="request.protocol" :result="request.requestResult" v-else/>
<!-- 保存操作 -->
<el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)" v-if="!request.referenced">
@ -155,7 +165,7 @@
return true
}
return false;
}
},
},
methods: {
remove() {
@ -238,8 +248,8 @@
this.request.customizeReq = this.isCustomizeReq;
let debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare,
environmentId: this.currentEnvironmentId, hashTree: [this.request]
variables: this.currentScenario.variables, referenced: 'Created', headers: this.currentScenario.headers,
enableCookieShare: this.enableCookieShare, environmentId: this.currentEnvironmentId, hashTree: [this.request]
};
this.runData.push(debugData);
/*触发执行操作*/
@ -247,6 +257,7 @@
},
runRefresh(data) {
this.request.requestResult = data;
this.request.result = undefined;
this.loading = false;
},
reload() {
@ -289,4 +300,9 @@
.icon.is-active {
transform: rotate(90deg);
}
.ms-tabs >>> .el-icon-close:before {
content: "";
}
</style>

View File

@ -3,6 +3,7 @@
v-loading="loading"
@copy="copyRow"
@remove="remove"
@active="active"
:data="scenario"
:show-collapse="false"
:is-show-name-input="!isDeletedOrRef"

View File

@ -2,11 +2,12 @@
<api-base-component
@copy="copyRow"
@remove="remove"
@active="active"
:data="jsr223Processor"
:draggable="draggable"
:color="color"
:background-color="backgroundColor"
:title="title">
:title="title" v-loading="loading">
<jsr233-processor-content
:jsr223-processor="jsr223Processor"
@ -50,6 +51,9 @@
backgroundColor: String,
node: {},
},
data() {
return {loading: false}
},
methods: {
remove() {
this.$emit('remove', this.jsr223Processor, this.node);
@ -57,6 +61,16 @@
copyRow() {
this.$emit('copyRow', this.jsr223Processor, this.node);
},
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
active() {
this.jsr223Processor.active = !this.jsr223Processor.active;
this.reload();
},
}
}
</script>

View File

@ -79,17 +79,17 @@
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="3000" :step="1000"/>
<span class="ms-span ms-radio">ms</span>
</div>
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
<div>
<el-tabs v-model="activeName" closable class="ms-tabs">
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
<api-response-component :result="result"/>
</div>
</el-tab-pane>
</el-tabs>
<!--<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>-->
<!--<div>-->
<!--<el-tabs v-model="activeName" closable class="ms-tabs">-->
<!--<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">-->
<!--<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">-->
<!--<api-response-component :result="result"/>-->
<!--</div>-->
<!--</el-tab-pane>-->
<!--</el-tabs>-->
</div>
<!--</div>-->
</api-base-component>
@ -117,7 +117,7 @@
},
},
created() {
this.initResult();
// this.initResult();
},
data() {
return {
@ -202,12 +202,11 @@
this.$warning("当前循环下没有请求,不能执行")
return;
}
this.controller.active = true;
this.loading = true;
this.debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare,
environmentId: this.currentEnvironmentId, hashTree: [this.controller]
variables: this.currentScenario.variables, headers: this.currentScenario.headers,
referenced: 'Created', enableCookieShare: this.enableCookieShare, environmentId: this.currentEnvironmentId, hashTree: [this.controller]
};
this.reportId = getUUID().substring(0, 8);
},
@ -224,7 +223,7 @@
},
changeRadio() {
this.controller.active = true;
this.initResult();
//this.initResult();
this.reload();
},
change(value) {
@ -258,6 +257,20 @@
this.success = this.requestResult.scenarios && this.requestResult.scenarios != null ? this.requestResult.scenarios.length - this.error : 0;
}
},
setResult(hashTree) {
if (hashTree) {
hashTree.forEach(item => {
if (item.type === "HTTPSamplerProxy" || item.type === "DubboSampler" || item.type === "JDBCSampler" || item.type === "TCPSampler") {
item.result = this.requestResult;
item.activeName = this.activeName;
item.requestResult = undefined;
}
if (item.hashTree && item.hashTree.length > 0) {
this.setResult(item.hashTree);
}
})
}
},
getReport() {
if (this.reportId) {
let url = "/api/scenario/report/get/" + this.reportId;
@ -285,11 +298,15 @@
break;
}
this.getFails();
this.activeName = this.requestResult && this.requestResult.scenarios && this.requestResult.scenarios != null && this.requestResult.scenarios.length > 0 ? this.requestResult.scenarios[0].name : "";
//
this.setResult(this.controller.hashTree);
this.$emit('refReload');
} catch (e) {
throw e;
}
this.loading = false;
this.activeName = this.requestResult && this.requestResult.scenarios && this.requestResult.scenarios != null && this.requestResult.scenarios.length > 0 ? this.requestResult.scenarios[0].name : "";
this.reload();
} else {
setTimeout(this.getReport, 2000)
}
@ -333,11 +350,6 @@
margin: 20px 0;
}
.ms-tabs >>> .el-icon-close:before {
content: "";
}
.icon.is-active {
transform: rotate(90deg);
}

View File

@ -0,0 +1,110 @@
<template>
<div>
<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">
<el-button icon="el-icon-folder-add" @click="addScenario"></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
<el-dropdown-item command="export">{{ $t('report.export') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-input>
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
<api-import :model="'scenario'" ref="apiImport" :moduleOptions="moduleOptions" @refresh="$emit('refresh')"/>
</div>
</template>
<script>
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
import ModuleTrashButton from "@/business/components/api/definition/components/module/ModuleTrashButton";
import ApiImport from "@/business/components/api/definition/components/import/ApiImport";
export default {
name: "ApiScenarioModuleHeader",
components: {ApiImport, ModuleTrashButton},
data() {
return {
moduleOptions: {}
}
},
props: {
condition: {
type: Object,
default() {
return {}
}
},
currentModule: {
type: Object,
default() {
return {}
}
},
isReadOnly: {
type: Boolean,
default() {
return false
}
},
projectId: String
},
methods: {
handleCommand(e) {
switch (e) {
case "import":
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
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);
break;
default:
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.$emit('exportAPI');
break;
}
},
addScenario() {
this.$emit('addScenario');
},
refresh() {
this.$emit('refresh');
},
enableTrash() {
this.condition.trashEnable = true;
}
}
}
</script>
<style scoped>
.read-only {
width: 150px !important;
}
.filter-input {
width: 174px;
padding-left: 3px;
}
</style>

View File

@ -1,57 +1,88 @@
<template>
<el-dialog :title="$t('api_test.scenario.variables')" :close-on-click-modal="false"
<el-dialog title="场景变量" :close-on-click-modal="false"
:visible.sync="visible" class="visible-dialog" width="60%"
@close="close" v-loading="loading">
<div>
<el-input placeholder="变量名称搜索" style="width: 50%;margin: 0px 0px 10px" v-model="selectVariable" size="small" @change="filter" @keyup.enter="filter">
<el-select v-model="searchType" slot="prepend" placeholder="类型" style="width: 90px" @change="filter">
<el-option value="CONSTANT" label="常量"></el-option>
<el-option value="LIST" label="列表"></el-option>
<el-option value="CSV" label="CSV"></el-option>
<el-option value="COUNTER" label="计数器"></el-option>
<el-option value="RANDOM" label="随机数"></el-option>
</el-select>
</el-input>
<el-row>
<el-col :span="12">
<div style="border:1px #DCDFE6 solid; min-height: 400px;border-radius: 4px ;width: 100% ;">
<el-table ref="table" border :data="variables" class="adjust-table" @select-all="select" @select="select"
v-loading="loading" @row-click="edit" height="400px" :row-class-name="tableRowClassName">
<el-table-column type="selection" width="38"/>
<el-table-column prop="num" label="ID" sortable/>
<el-table-column prop="name" :label="$t('api_test.variable_name')" sortable show-overflow-tooltip/>
<el-table-column prop="type" :label="$t('test_track.case.type')">
<template v-slot:default="scope">
<span>{{types.get(scope.row.type)}}</span>
</template>
</el-table-column>
<el-table-column prop="value" :label="$t('api_test.value')" show-overflow-tooltip/>
</el-table>
</div>
</el-col>
<el-col :span="12">
<ms-edit-constant v-if="editData.type=='CONSTANT'" ref="parameters" :editData.sync="editData"/>
<ms-edit-counter v-if="editData.type=='COUNTER'" ref="counter" :editData.sync="editData"/>
<ms-edit-random v-if="editData.type=='RANDOM'" ref="random" :editData.sync="editData"/>
<ms-edit-list-value v-if="editData.type=='LIST'" ref="listValue" :editData="editData"/>
<ms-edit-csv v-if="editData.type=='CSV'" ref="csv" :editData.sync="editData"/>
</el-col>
</el-row>
</div>
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.scenario.variables')" name="variable">
<div style="margin-top: 10px">
<el-row style="margin-bottom: 10px">
<el-col :span="8">
<el-input placeholder="变量名称搜索" v-model="selectVariable" size="small" @change="filter" @keyup.enter="filter">
<el-select v-model="searchType" slot="prepend" placeholder="类型" style="width: 90px" @change="filter">
<el-option value="CONSTANT" label="常量"></el-option>
<el-option value="LIST" label="列表"></el-option>
<el-option value="CSV" label="CSV"></el-option>
<el-option value="COUNTER" label="计数器"></el-option>
<el-option value="RANDOM" label="随机数"></el-option>
</el-select>
</el-input>
</el-col>
<el-col :span="6">
<el-dropdown split-button type="primary" @command="handleClick" @click="handleClick('CONSTANT')" size="small" style="margin-left: 10px">
{{$t('commons.add')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="CONSTANT">常量</el-dropdown-item>
<el-dropdown-item command="LIST">列表</el-dropdown-item>
<el-dropdown-item command="CSV">CSV</el-dropdown-item>
<el-dropdown-item command="COUNTER">计数器</el-dropdown-item>
<el-dropdown-item command="RANDOM">随机数</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button size="small" style="margin-left: 10px" @click="deleteVariable">{{$t('commons.delete')}}</el-button>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<div style="border:1px #DCDFE6 solid; min-height: 400px;border-radius: 4px ;width: 100% ;">
<el-table ref="table" border :data="variables" class="adjust-table" @select-all="select" @select="select"
v-loading="loading" @row-click="edit" height="400px" :row-class-name="tableRowClassName">
<el-table-column type="selection" width="38"/>
<el-table-column prop="num" label="ID" sortable/>
<el-table-column prop="name" :label="$t('api_test.variable_name')" sortable show-overflow-tooltip/>
<el-table-column prop="type" :label="$t('test_track.case.type')">
<template v-slot:default="scope">
<span>{{types.get(scope.row.type)}}</span>
</template>
</el-table-column>
<el-table-column prop="value" :label="$t('api_test.value')" show-overflow-tooltip/>
</el-table>
</div>
</el-col>
<el-col :span="12">
<ms-edit-constant v-if="editData.type=='CONSTANT'" ref="parameters" :editData.sync="editData"/>
<ms-edit-counter v-if="editData.type=='COUNTER'" ref="counter" :editData.sync="editData"/>
<ms-edit-random v-if="editData.type=='RANDOM'" ref="random" :editData.sync="editData"/>
<ms-edit-list-value v-if="editData.type=='LIST'" ref="listValue" :editData="editData"/>
<ms-edit-csv v-if="editData.type=='CSV'" ref="csv" :editData.sync="editData"/>
</el-col>
</el-row>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">
<!-- 请求头-->
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.request.headers')" placement="top-start" slot="label">
<span>{{$t('api_test.request.headers')}}
<div class="el-step__icon is-text ms-api-col ms-variable-header" v-if="headers.length>1">
<div class="el-step__icon-inner">{{headers.length-1}}</div>
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-variable-link" @click="batchAdd" style="color: #783887"> {{$t("commons.batch_add")}}</el-link>
</el-row>
<div style="min-height: 400px">
<ms-api-key-value :items="headers"/>
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
</div>
</el-tab-pane>
</el-tabs>
<template v-slot:footer>
<div>
<el-button style="margin-right:10px" @click="deleteVariable">{{$t('commons.delete')}}</el-button>
<el-dropdown split-button type="primary" @command="handleClick" @click="handleClick('CONSTANT')" placement="top-end">
{{$t('commons.add')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="CONSTANT">常量</el-dropdown-item>
<el-dropdown-item command="LIST">列表</el-dropdown-item>
<el-dropdown-item command="CSV">CSV</el-dropdown-item>
<el-dropdown-item command="COUNTER">计数器</el-dropdown-item>
<el-dropdown-item command="RANDOM">随机数</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button type="primary" @click="save">{{$t('commons.confirm')}}</el-button>
</div>
</template>
</el-dialog>
@ -67,6 +98,9 @@
import MsEditListValue from "./EditListValue";
import MsEditCsv from "./EditCsv";
import {getUUID} from "@/common/js/utils";
import MsApiKeyValue from "../../../definition/components/ApiKeyValue";
import BatchAddParameter from "../../../definition/components/basis/BatchAddParameter";
import {KeyValue} from "../../../definition/model/ApiTestModel";
export default {
name: "MsVariableList",
@ -78,11 +112,15 @@
MsEditCounter,
MsEditRandom,
MsEditListValue,
MsEditCsv
MsEditCsv,
MsApiKeyValue,
BatchAddParameter
},
data() {
return {
variables: [],
headers: [],
activeName: "variable",
searchType: "",
selectVariable: "",
condition: {},
@ -103,6 +141,26 @@
}
},
methods: {
batchAdd() {
this.$refs.batchAddParameter.open();
},
batchSave(data) {
if (data) {
let params = data.split("\n");
let keyValues = [];
params.forEach(item => {
let line = item.split(/|,/);
let required = false;
if (line[1] === '必填' || line[1] === 'true') {
required = true;
}
keyValues.push(new KeyValue({name: line[0], required: required, value: line[2], description: line[3], type: "text", valid: false, file: false, encode: true, enable: true, contentType: "text/plain"}));
})
keyValues.forEach(item => {
this.headers.unshift(item);
})
}
},
handleClick(command) {
this.editData = {};
this.editData.type = command;
@ -135,12 +193,16 @@
isSelect(row) {
return this.selection.includes(row.id)
},
open: function (variables) {
open: function (variables, headers) {
this.variables = variables;
this.headers = headers;
this.visible = true;
this.editData = {type: "CONSTANT"};
this.addParameters(this.editData);
},
save() {
this.visible = false;
},
close() {
this.visible = false;
let saveVariables = [];
@ -152,7 +214,7 @@
})
this.selectVariable = "";
this.searchType = "";
this.$emit('setVariables', saveVariables);
this.$emit('setVariables', saveVariables, this.headers);
},
deleteVariable() {
let ids = Array.from(this.selection);
@ -209,4 +271,16 @@
.ms-variable-hidden-row {
display: none;
}
.ms-variable-header {
background: #783887;
color: white;
height: 18px;
border-radius: 42%;
}
.ms-variable-link {
float: right;
margin-right: 45px;
}
</style>

View File

@ -26,7 +26,7 @@
:name="item.name">
<!-- 列表集合 -->
<ms-api-list
v-if="item.type === 'list' && isApiListEnable"
v-if="item.type === 'list' && activeDom==='api' "
:module-tree="nodeTree"
:module-options="moduleOptions"
:current-protocol="currentProtocol"
@ -35,29 +35,42 @@
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:is-api-list-enable="isApiListEnable"
:active-dom="activeDom"
:queryDataType="queryDataType"
:selectDataRange="selectDataRange"
@changeSelectDataRangeAll="changeSelectDataRangeAll"
@editApi="editApi"
@handleCase="handleCase"
@showExecResult="showExecResult"
@activeDomChange="activeDomChange"
@isApiListEnableChange="isApiListEnableChange"
ref="apiList"/>
<!--测试用例列表-->
<api-case-simple-list
v-if="item.type === 'list' && !isApiListEnable"
v-if="item.type === 'list' && activeDom==='testCase' "
:current-protocol="currentProtocol"
:visible="visible"
:currentRow="currentRow"
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:is-api-list-enable="isApiListEnable"
:active-dom="activeDom"
:queryDataType="queryDataType"
@changeSelectDataRangeAll="changeSelectDataRangeAll"
@isApiListEnableChange="isApiListEnableChange"
@activeDomChange="activeDomChange"
@handleCase="handleCase"
@showExecResult="showExecResult"
ref="apiList"/>
<api-documents-page class="api-doc-page"
v-if="item.type === 'list' && activeDom==='doc' "
:is-api-list-enable="isApiListEnable"
:active-dom="activeDom"
:project-id="projectId"
:module-ids="selectNodeIds"
@activeDomChange="activeDomChange"
@isApiListEnableChange="isApiListEnableChange"
/>
<!-- 添加/编辑测试窗口-->
<div v-else-if="item.type=== 'ADD'" class="ms-api-div">
@ -114,24 +127,25 @@
</div>
</template>
<script>
import MsApiList from './components/list/ApiList';
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsAsideContainer from "../../common/components/MsAsideContainer";
import MsApiConfig from "./components/ApiConfig";
import MsDebugHttpPage from "./components/debug/DebugHttpPage";
import MsDebugJdbcPage from "./components/debug/DebugJdbcPage";
import MsDebugTcpPage from "./components/debug/DebugTcpPage";
import MsDebugDubboPage from "./components/debug/DebugDubboPage";
import MsApiList from './components/list/ApiList';
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsAsideContainer from "../../common/components/MsAsideContainer";
import MsApiConfig from "./components/ApiConfig";
import MsDebugHttpPage from "./components/debug/DebugHttpPage";
import MsDebugJdbcPage from "./components/debug/DebugJdbcPage";
import MsDebugTcpPage from "./components/debug/DebugTcpPage";
import MsDebugDubboPage from "./components/debug/DebugDubboPage";
import MsRunTestHttpPage from "./components/runtest/RunTestHTTPPage";
import MsRunTestTcpPage from "./components/runtest/RunTestTCPPage";
import MsRunTestSqlPage from "./components/runtest/RunTestSQLPage";
import MsRunTestDubboPage from "./components/runtest/RunTestDubboPage";
import {downloadFile, getCurrentUser, getUUID, getCurrentProjectID} from "@/common/js/utils";
import MsApiModule from "./components/module/ApiModule";
import ApiCaseSimpleList from "./components/list/ApiCaseSimpleList";
import {PROJECT_NAME} from "../../../../common/js/constants";
import MsRunTestHttpPage from "./components/runtest/RunTestHTTPPage";
import MsRunTestTcpPage from "./components/runtest/RunTestTCPPage";
import MsRunTestSqlPage from "./components/runtest/RunTestSQLPage";
import MsRunTestDubboPage from "./components/runtest/RunTestDubboPage";
import {getCurrentProjectID, getCurrentUser, getUUID} from "@/common/js/utils";
import MsApiModule from "./components/module/ApiModule";
import ApiCaseSimpleList from "./components/list/ApiCaseSimpleList";
import ApiDocumentsPage from "@/business/components/api/definition/components/list/ApiDocumentsPage";
export default {
name: "ApiDefinition",
@ -142,8 +156,10 @@
this.changeRedirectParam(redirectIDParam);
if (routeParam === 'apiTestCase') {
this.isApiListEnableChange(false);
this.activeDomChange("testCase");
} else {
this.isApiListEnableChange(true);
this.activeDomChange("api");
}
return routeParam;
},
@ -163,7 +179,8 @@
MsDebugDubboPage,
MsRunTestTcpPage,
MsRunTestSqlPage,
MsRunTestDubboPage
MsRunTestDubboPage,
ApiDocumentsPage
},
props: {
visible: {
@ -194,6 +211,7 @@
closable: false
}],
isApiListEnable: true,
activeDom: "testCase",
syncTabs: [],
projectId: "",
nodeTree: []
@ -231,6 +249,9 @@
isApiListEnableChange(data) {
this.isApiListEnable = data;
},
activeDomChange(tabType){
this.activeDom = tabType;
},
addTab(tab) {
if (tab.name === 'add') {
this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug");
@ -340,36 +361,7 @@
this.$warning('用例列表暂不支持导出,请切换成接口列表');
return;
}
let obj = {projectName: this.projectId, protocol: this.currentProtocol}
if (this.$refs.apiList[0].selectRows && this.$refs.apiList[0].selectRows.size > 0) {
let arr = Array.from(this.$refs.apiList[0].selectRows);
obj.data = arr;
this.buildApiPath(obj.data);
downloadFile("Metersphere_Api_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
} else {
let condition = {};
let url = "/api/definition/list/all";
condition.filters = new Map(
[
["status", ["Prepare", "Underway", "Completed"]],
]
);
condition.projectId = this.projectId;
this.$post(url, condition, response => {
obj.data = response.data;
this.buildApiPath(obj.data);
downloadFile("Metersphere_Api_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
});
}
},
buildApiPath(apis) {
apis.forEach((api) => {
this.moduleOptions.forEach(item => {
if (api.moduleId === item.id) {
api.modulePath = item.path;
}
});
});
this.$refs.apiList[0].exportApi();
},
refresh(data) {
this.$refs.apiList[0].initTable(data);

View File

@ -17,29 +17,29 @@
</template>
<script>
import MsEditCompleteHttpApi from "./complete/EditCompleteHTTPApi";
import MsEditCompleteTcpApi from "./complete/EditCompleteTCPApi";
import MsEditCompleteDubboApi from "./complete/EditCompleteDubboApi";
import MsEditCompleteSqlApi from "./complete/EditCompleteSQLApi";
import MsEditCompleteHttpApi from "./complete/EditCompleteHTTPApi";
import MsEditCompleteTcpApi from "./complete/EditCompleteTCPApi";
import MsEditCompleteDubboApi from "./complete/EditCompleteDubboApi";
import MsEditCompleteSqlApi from "./complete/EditCompleteSQLApi";
import {ResponseFactory, Body} from "../model/ApiTestModel";
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
import {createComponent, Request} from "./jmeter/components";
import Sampler from "./jmeter/components/sampler/sampler";
import {WORKSPACE_ID} from '@/common/js/constants';
import {handleCtrlSEvent} from "../../../../../common/js/utils";
import {Body} from "../model/ApiTestModel";
import {getUUID} from "@/common/js/utils";
import {createComponent, Request} from "./jmeter/components";
import Sampler from "./jmeter/components/sampler/sampler";
import {WORKSPACE_ID} from '@/common/js/constants';
import {handleCtrlSEvent} from "../../../../../common/js/utils";
export default {
name: "ApiConfig",
components: {MsEditCompleteHttpApi, MsEditCompleteTcpApi, MsEditCompleteDubboApi, MsEditCompleteSqlApi},
data() {
return {
reqUrl: "",
request: Sampler,
config: {},
response: {},
maintainerOptions: [],
}
export default {
name: "ApiConfig",
components: {MsEditCompleteHttpApi, MsEditCompleteTcpApi, MsEditCompleteDubboApi, MsEditCompleteSqlApi},
data() {
return {
reqUrl: "",
request: Sampler,
config: {},
response: {},
maintainerOptions: [],
}
},
props: {
currentApi: {},

View File

@ -131,7 +131,7 @@
})
},
active() {
// item.active = !item.active;
this.assertions.active = !this.assertions.active;
this.reload();
},
remove() {

View File

@ -54,8 +54,7 @@
import {CASE_ORDER} from "../../model/JsonData";
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
import MsBatchEdit from "../basis/BatchEdit";
// import {CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
import {CASE_PRIORITY,API_METHOD_COLOUR, API_STATUS, REQ_METHOD, TCP_METHOD, SQL_METHOD, DUBBO_METHOD} from "../../model/JsonData";
import {CASE_PRIORITY, REQ_METHOD, TCP_METHOD, SQL_METHOD, DUBBO_METHOD} from "../../model/JsonData";
export default {
name: 'ApiCaseList',
@ -270,10 +269,13 @@
this.singleLoading = true;
this.singleRunId = row.id;
row.request.name = row.id;
row.request.useEnvironment = this.environment.id;
this.runData.push(row.request);
/*触发执行操作*/
this.reportId = getUUID().substring(0, 8);
this.$get('/api/definition/get/' + row.request.id, response => {
row.request.path = response.data.path; // pathpath
row.request.useEnvironment = this.environment.id;
this.runData.push(row.request);
/*触发执行操作*/
this.reportId = getUUID().substring(0, 8);
});
},
batchRun() {

View File

@ -2,7 +2,8 @@
<div class="card-container" v-loading="loading">
<el-card class="card-content">
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand"
<el-button v-if="scenario" style="float: right;margin-right: 20px" size="small" type="primary" @click="handleCommand"> {{$t('commons.test')}}</el-button>
<el-dropdown v-else split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">

View File

@ -15,6 +15,7 @@
</el-form-item>
<el-form-item>
<el-button v-if="scenario" size="small" type="primary" @click="handleCommand"> {{$t('commons.test')}}</el-button>
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" v-if="testCase===undefined && !scenario">
{{$t('commons.test')}}
@ -198,7 +199,7 @@
},
urlChange() {
if (!this.debugForm.url) return;
let url = this.getURL(this.debugForm.url);
let url = this.getURL(this.addProtocol(this.debugForm.url));
if (url && url.pathname) {
if (this.debugForm.url.indexOf('?') != -1) {
this.debugForm.url = decodeURIComponent(this.debugForm.url.substr(0, this.debugForm.url.indexOf("?")));
@ -209,7 +210,14 @@
}
},
addProtocol(url) {
if (url) {
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
return "https://" + url;
}
}
return url;
},
getURL(urlStr) {
try {
let url = new URL(urlStr);

View File

@ -2,7 +2,9 @@
<div class="card-container" v-loading="loading">
<el-card class="card-content">
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand"
<el-button v-if="scenario" style="float: right;margin-right: 20px" size="small" type="primary" @click="handleCommand"> {{$t('commons.test')}}</el-button>
<el-dropdown v-else split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">
@ -49,7 +51,7 @@
export default {
name: "ApiConfig",
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters, MsJmxStep,MsApiCaseList},
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters, MsJmxStep, MsApiCaseList},
props: {
currentProtocol: String,
scenario: Boolean,

View File

@ -11,7 +11,9 @@
<el-input-number v-model="request.port" controls-position="right" :min="0" :max="65535" size="small"/>
</el-form-item>
<el-form-item>
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand"
<el-button v-if="scenario" size="small" type="primary" @click="handleCommand"> {{$t('commons.test')}}</el-button>
<el-dropdown v-else split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">

View File

@ -0,0 +1,119 @@
<template>
<div class="container">
<div class="wrapper">
<div class="section" style="width:80%;margin-left: 8px" v-for="(item, index) in list" :key="index">
<div class="border" style="width:100%;height:200px;font-size:30px;text-align:center;color:black;">
<el-divider><i class="el-icon-mobile-phone">111</i></el-divider>
{{item.name}}
</div>
</div>
</div>
<div id="nac" style="height:500px;"></div>
<nav style="position:fixed;right:30px;top:100px;">
<span class="nav1 hand" v-for="(item, index) in navList" :key="index" @click="jump(index)"
:class="index==0?'current':''">{{item}}</span>
</nav>
</div>
</template>
<script>
// import $ from 'jquery';
export default {
name:"MsAnchor",
data() {
return {
scroll: '',
list: [{
name: "第一条wwwwwwww",
backgroundcolor: "#90B2A3"
}, {
name: "第二条eeeeeeee",
backgroundcolor: "#A593B2"
}, {
name: "第三条",
backgroundcolor: "#A7B293"
}, {
name: "第四条",
backgroundcolor: "#0F2798"
}, {
name: "第五条",
backgroundcolor: "#0A464D"
}],
navList: [11111111, 2111111111111, 31111, 4111111, 5111111111]
}
},
methods: {
dataScroll: function () {
this.scroll = document.documentElement.scrollTop || document.body.scrollTop;
},
jump(index) {
let jump = document.getElementsByClassName('section');
//
let total = jump[index].offsetTop;
// Chrome
document.body.scrollTop = total;
// Firefox
document.documentElement.scrollTop = total;
// Safari
window.pageYOffset = total;
// $('html, body').animate({
// 'scrollTop': total
// }, 400);
},
loadSroll: function () {
// var self = this;
// var $navs = $(".nav1");
// var sections = document.getElementsByClassName('section');
// for (var i = sections.length - 1; i >= 0; i--) {
// if (self.scroll >= sections[i].offsetTop - 100) {
// $navs.eq(i).addClass("current").siblings().removeClass("current")
// break;
// }
// }
}
},
watch: {
scroll: function () {
this.loadSroll()
}
},
mounted() {
window.addEventListener('scroll', this.dataScroll);
}
}
</script>
<style>
* {
padding: 0;
margin: 0;
}
.nav1 {
display: block;
height: 40px;
text-align: center;
line-height: 40px;
background: #eee;
}
.navs1 .active {
color: #847ec3;
background-color: #e2e2e2;
}
.hand {
cursor: pointer;
}
.current {
color: #fff;
background: #847ec3;
}
.border {
border: 1px solid #eee;
border-top: 0
}
</style>

View File

@ -0,0 +1,422 @@
<template>
<el-container>
<el-main>
<div ref="apiDocInfoDiv">
<div style="margin-bottom: 50px">
<div style="font-size: 17px">
{{apiInfo.name}}
<span class="apiStatusTag">
<api-status :value="apiInfo.status"/>
</span>
</div>
<!--api请求信息-->
<el-row class="apiInfoRow">
<div class="tip">请求信息</div>
</el-row>
<el-row class="apiInfoRow">
<div class="simpleFontClass">
<el-tag size="medium"
:style="{'background-color': getColor(apiInfo.method), border: getColor(apiInfo.method),borderRadius:'0px', marginRight:'20px'}">
{{ apiInfo.method }}
</el-tag>
{{apiInfo.uri}}
</div>
</el-row>
<!--api请求头-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
请求头
<div v-if="getJsonArr(apiInfo.requestHead).length==0">
</div>
<div v-else>
<el-table border :show-header="false"
:data="getJsonArr(apiInfo.requestHead)" row-key="name" class="test-content adjust-table">
<el-table-column prop="name"
label="名称"
show-overflow-tooltip/>
<el-table-column prop="value"
label="值"
show-overflow-tooltip/>
</el-table>
</div>
</div>
</el-row>
<!--URL参数-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
URL参数
<div v-if="getJsonArr(apiInfo.urlParams).length==0">
</div>
<div v-else>
<el-table border
:data="getJsonArr(apiInfo.urlParams)" row-key="name" class="test-content adjust-table">
<el-table-column prop="name"
label="名称"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="isEnable"
label="是否必填"
min-width="80px"
show-overflow-tooltip/>
<el-table-column prop="value"
label="值"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="description"
label="描述"
min-width="450px"
show-overflow-tooltip/>
</el-table>
</div>
</div>
</el-row>
<!--api请求体 以及表格-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
请求体
</div>
<div class="smallFontClass">
类型:{{apiInfo.requestBodyParamType}}
</div>
<div>
<el-table border v-if="apiInfo.requestBodyParamType=='kv'"
:data="getJsonArr(apiInfo.requestBodyFormData)" row-key="id" class="test-content adjust-table">
<el-table-column prop="name"
label="名称"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="contentType"
label="类型"
min-width="80px"
show-overflow-tooltip/>
<el-table-column prop="description"
label="描述"
min-width="450px"
show-overflow-tooltip/>
<el-table-column label="必需"
min-width="80px"
show-overflow-tooltip>
<template v-slot:default="scope">
<div v-if="scope.enable"></div>
<div v-else-if="!scope.enable"></div>
</template>
</el-table-column>
<el-table-column prop="value"
label="默认值"
min-width="120px"
show-overflow-tooltip/>
</el-table>
</div>
</el-row>
<!--范例展示-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
范例展示
</div>
<div class="showDataDiv">
<br/>
<p style="margin: 0px 20px;">
{{ apiInfo.requestBodyStrutureData }}
</p>
<br/>
</div>
</el-row>
<!--响应信息-->
<el-row class="apiInfoRow">
<div class="tip">响应信息</div>
</el-row>
<el-row class="apiInfoRow">
</el-row>
<!--响应头-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
响应头:
<el-table border :show-header="false"
:data="getJsonArr(apiInfo.responseHead)" row-key="name" class="test-content adjust-table">
<el-table-column prop="name"
label="名称"
show-overflow-tooltip/>
<el-table-column prop="value"
label="值"
show-overflow-tooltip/>
</el-table>
</div>
</el-row>
<!--响应体-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
响应体
</div>
<div class="smallFontClass">
类型:{{apiInfo.responseBodyParamType}}
</div>
<div>
<el-table border v-if="apiInfo.responseBodyParamType=='kv'"
:data="getJsonArr(apiInfo.responseBodyFormData)" row-key="id" class="test-content adjust-table">
<el-table-column prop="name"
label="名称"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="contentType"
label="类型"
min-width="80px"
show-overflow-tooltip/>
<el-table-column prop="description"
label="描述"
min-width="450px"
show-overflow-tooltip/>
<el-table-column label="必需"
min-width="80px"
show-overflow-tooltip>
<template v-slot:default="scope">
<div v-if="scope.enable"></div>
<div v-else-if="!scope.enable"></div>
</template>
</el-table-column>
<el-table-column prop="value"
label="默认值"
min-width="120px"
show-overflow-tooltip/>
</el-table>
</div>
</el-row>
<!--响应状态码-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
响应状态码:
<el-table border :show-header="false"
:data="getJsonArr(apiInfo.responseCode)" row-key="name" class="test-content adjust-table">
<el-table-column prop="name"
label="名称"
show-overflow-tooltip/>
<el-table-column prop="value"
label="值"
show-overflow-tooltip/>
</el-table>
</div>
</el-row>
</div>
</div>
</el-main>
<!-- 右侧列表 -->
<el-aside width="200px">
<div ref="apiDocList">
<el-steps style="height: 40%" direction="vertical" :active="apiStepIndex">
<el-step v-for="(apiInfo) in apiSimpleInfoArray" :key="apiInfo.id" @click.native="clickStep(apiInfo.id)">
<el-link slot="title">{{apiInfo.name}}</el-link>
</el-step>
</el-steps>
</div>
</el-aside>
</el-container>
</template>
<script>
import MsAnchor from "./Anchor";
import {API_METHOD_COLOUR} from "@/business/components/api/definition/model/JsonData";
import {jsonToMap} from "@/common/js/utils";
import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus";
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
export default {
name: "ApiDocumentItem",
components: {
MsAnchor,ApiStatus,
},
data() {
return {
apiStepIndex:0,
apiSimpleInfoArray:[],
apiInfo:{
method:"无",
uri:"无",
name:"无",
id:"",
requestHead:"无",
urlParams:"无",
requestBodyParamType:"无",
requestBodyFormData:'[]',
requestBodyStrutureData:"",
responseHead:"无",
responseBody:"",
responseBodyParamType:"无",
responseBodyFormData:"无",
responseBodyStrutureData:"无",
responseCode:"无",
},
methodColorMap: new Map(API_METHOD_COLOUR),
clientHeight:'',//
}
},
props: {
projectId:String,
moduleIds:Array,
},
activated() {
this.initApiDocSimpleList();
this.clientHeight = `${document.documentElement.clientHeight}`;//
let that = this;
window.onresize = function(){
this.clientHeight = `${document.documentElement.clientHeight}`;
this.changeFixed(this.clientHeight);
}
},
created: function () {
this.initApiDocSimpleList();
this.clientHeight = `${document.documentElement.clientHeight}`;//
let that = this;
window.onresize = function(){
this.clientHeight = `${document.documentElement.clientHeight}`;
this.changeFixed(this.clientHeight);
}
},
mounted() {
let that = this;
window.onresize = function(){
this.clientHeight = `${document.documentElement.clientHeight}`;
if(that.$refs.apiDocInfoDiv){
that.$refs.apiDocInfoDiv.style.minHeight = this.clientHeight - 300 + 'px';
that.$refs.apiDocList.style.minHeight = this.clientHeight - 300 + 'px';
}
}
},
computed: {
documentId: function () {
return this.$route.params.documentId;
}
},
watch: {
'$route.params.documentId'() {
},
moduleIds(){
this.initApiDocSimpleList();
},
clientHeight(){ //clientHeight
this.changeFixed(this.clientHeight);
}
},
methods: {
changeFixed(clientHeight){
if(this.$refs.apiDocInfoDiv){
this.$refs.apiDocInfoDiv.style.height = clientHeight -300 + 'px';
this.$refs.apiDocInfoDiv.style.overflow = 'auto';
this.$refs.apiDocList.style.height = clientHeight -300 + 'px';
}
},
initApiDocSimpleList(){
let simpleRequest = {};
if(this.projectId!=null && this.projectId!= ""){
simpleRequest.projectId=this.projectId;
}
if(this.documentId!=null && this.documentId!= ""){
simpleRequest.documentId=this.documentId;
}
if(this.moduleIds.length>0){
simpleRequest.moduleIds=this.moduleIds;
}
let simpleInfoUrl = "/api/document/selectApiSimpleInfo";
this.$post(simpleInfoUrl, simpleRequest, response => {
this.apiSimpleInfoArray = response.data;
this.apiStepIndex = 0;
if(this.apiSimpleInfoArray.length>0){
this.selectApiInfo(this.apiSimpleInfoArray[0].id);
}
});
},
selectApiInfo(apiId){
let simpleInfoUrl = "/api/document/selectApiInfoById/"+apiId;
this.$get(simpleInfoUrl, response => {
this.apiInfo = response.data;
});
},
clickStep(apiId){
for (let index = 0; index < this.apiSimpleInfoArray.length; index++) {
if(apiId == this.apiSimpleInfoArray[index].id){
this.apiStepIndex = index;
break;
}
}
this.selectApiInfo(apiId);
},
stepClick(stepIndex){
this.apiStepIndex = stepIndex;
},
getColor(enable, method) {
return this.methodColorMap.get(method);
},
getJsonArr(jsonString){
let returnJsonArr = [];
if(jsonString == '无'){
return returnJsonArr;
}
let jsonArr = JSON.parse(jsonString);
//
for(var index = 0;index < jsonArr.length;index++){
var item = jsonArr[index];
if(item.name!="" && item.name!=null){
returnJsonArr.push(item);
}
}
return returnJsonArr;
}
},
}
</script>
<style scoped>
.simpleFontClass{
font-size: 14px;
}
.blackFontClass{
font-weight: bold;
font-size: 14px;
}
.smallFontClass{
font-size: 13px;
margin: 20px 0px;
}
.tip {
padding: 3px 5px;
font-size: 14px;
border-radius: 4px;
border-left: 4px solid #783887;
}
.apiInfoRow{
margin: 20px 10px;
}
.apiStatusTag{
margin: 20px 5px;
}
.showDataDiv{
background-color: #F5F7F9;
margin: 20px 0px;
}
/*
步骤条中已经完成后的节点样式和里面a标签的样式
*/
/deep/ .el-step__head.is-finish {
color: #C0C4CC;
border-color: #C0C4CC;
}
/deep/ .el-step__title.is-finish /deep/ .el-link.el-link--default{
color: #C0C4CC;
}
/*
步骤条中当前节点样式和当前a标签的样式
*/
/deep/ .el-step__head.is-process {
color: #783887;
border-color: #783887;
}
/deep/ .el-step__title.is-process /deep/ .el-link.el-link--default {
color: #783887;
}
</style>

View File

@ -2,12 +2,13 @@
<api-base-component
@copy="copyRow"
@remove="remove"
@active="active"
:data="extract"
:draggable="draggable"
color="#015478"
background-color="#E6EEF2"
:title="$t('api_test.definition.request.extract_param')">
<div style="margin: 20px">
<div style="margin: 20px" v-loading="loading">
<div class="extract-description">
{{$t('api_test.request.extract.description')}}
</div>
@ -25,7 +26,7 @@
<ms-api-extract-common :is-read-only="isReadOnly" :extract-type="type" :list="list" v-if="type" :callback="after"/>
</el-col>
<el-button v-if="!type" :disabled="true" type="primary" size="small">Add</el-button>
<el-button v-if="!type" :disabled="true" type="primary" size="small">{{$t('commons.add')}}</el-button>
</el-row>
</div>
@ -99,8 +100,8 @@
this.loading = false
})
},
active(item) {
item.active = !item.active;
active() {
this.extract.active = !this.extract.active;
this.reload();
},
suggestJsonOpen() {
@ -157,6 +158,10 @@
border-radius: 5px;
}
.icon.is-active {
transform: rotate(90deg);
}
/deep/ .el-card__body {
padding: 15px;
}

View File

@ -5,7 +5,9 @@
<div class="header-bar">
<div>{{ $t('api_test.api_import.data_format') }}</div>
<el-radio-group v-model="selectedPlatformValue">
<el-radio v-for="(item, index) in platforms" :key="index" :label="item.value">{{ item.name }}</el-radio>
<span v-for="(item, index) in platforms" :key="index">
<el-radio v-if="!isScenarioModel || item.name != 'Swagger'" :label="item.value">{{ item.name }}</el-radio>
</span>
</el-radio-group>
<div class="operate-button">
@ -26,14 +28,14 @@
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item :label="$t('commons.import_mode')">
<el-form-item v-if="!isScenarioModel" :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>
</el-form-item>
<el-form-item v-if="isSwagger2">
<el-switch
v-model="swaggerUrlEable"
v-model="swaggerUrlEnable"
:active-text="$t('api_test.api_import.swagger_url_import')">
</el-switch>
</el-form-item>
@ -42,7 +44,7 @@
<el-col :span="1">
<el-divider direction="vertical"/>
</el-col>
<el-col :span="12" v-show="isSwagger2 && swaggerUrlEable" style="margin-top: 40px">
<el-col :span="12" v-show="isSwagger2 && swaggerUrlEnable" style="margin-top: 40px">
<el-form-item :label="'Swagger URL'" prop="swaggerUrl" class="swagger-url">
<el-input size="small" v-model="formData.swaggerUrl" clearable show-word-limit/>
</el-form-item>
@ -56,7 +58,7 @@
</el-form-item>
</el-col>
<el-col :span="12"
v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEable)">
v-if="selectedPlatformValue != 'Swagger2' || (selectedPlatformValue == 'Swagger2' && !swaggerUrlEnable)">
<el-upload
class="api-upload"
drag
@ -93,7 +95,6 @@ import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {getCurrentProjectID} from "../../../../../../common/js/utils";
import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit";
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
export default {
name: "ApiImport",
@ -103,12 +104,16 @@ export default {
type: Boolean,
default: true,
},
moduleOptions: {}
moduleOptions: {},
model: {
type: String,
default: 'definition'
}
},
data() {
return {
visible: false,
swaggerUrlEable: false,
swaggerUrlEnable: false,
swaggerSynchronization: false,
showEnvironmentSelect: true,
modeOptions: [{
@ -176,6 +181,9 @@ export default {
computed: {
isSwagger2() {
return this.selectedPlatformValue === 'Swagger2';
},
isScenarioModel() {
return this.model === 'scenario';
}
},
methods: {
@ -222,12 +230,16 @@ export default {
save() {
this.$refs.form.validate(valid => {
if (valid) {
if ((this.selectedPlatformValue != 'Swagger2' || (this.selectedPlatformValue == 'Swagger2' && !this.swaggerUrlEable)) && !this.formData.file) {
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('/api/definition/import', param.file, null, this.buildParam(), response => {
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;
@ -243,7 +255,7 @@ export default {
Object.assign(param, this.formData);
param.platform = this.selectedPlatformValue;
param.saved = this.saved;
console.log(this.formData.moduleId)
param.model = this.model;
if (this.currentModule) {
param.moduleId = this.formData.moduleId
this.moduleOptions.filter(item => {
@ -254,7 +266,7 @@ export default {
param.modeId = this.formData.modeId
}
param.projectId = getCurrentProjectID();
if (!this.swaggerUrlEable) {
if (!this.swaggerUrlEnable) {
param.swaggerUrl = undefined;
}
return param;
@ -300,6 +312,10 @@ export default {
margin: 10px 0;
}
.el-radio {
margin-right: 20px;
}
.header-bar, .format-tip, .el-form {
border: solid #E1E1E1 1px;
margin: 10px 0;

View File

@ -1,7 +1,9 @@
<template>
<div>
<api-list-container
<api-list-container-with-doc
:is-api-list-enable="isApiListEnable"
:active-dom="activeDom"
@activeDomChange="activeDomChange"
@isApiListEnableChange="isApiListEnableChange">
<el-link type="primary" style="float:right;margin-top: 5px" @click="open">{{$t('commons.adv_search.title')}}</el-link>
@ -116,7 +118,7 @@
:type=type></header-custom>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</api-list-container>
</api-list-container-with-doc>
<api-case-list @showExecResult="showExecResult" @refresh="initTable" :currentApi="selectCase" ref="caseList"/>
<!--批量编辑-->
@ -148,6 +150,9 @@ import {API_METHOD_COLOUR, CASE_PRIORITY, DUBBO_METHOD, REQ_METHOD, SQL_METHOD,
import {getBodyUploadFiles, getCurrentProjectID, getCurrentUser} from "@/common/js/utils";
import ApiListContainer from "./ApiListContainer";
import {getBodyUploadFiles, getCurrentProjectID} from "@/common/js/utils";
// import ApiListContainer from "./ApiListContainer";
import ApiListContainerWithDoc from "@/business/components/api/definition/components/list/ApiListContainerWithDoc";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import MsApiCaseTableExtendBtns from "../reference/ApiCaseTableExtendBtns";
import MsReferenceView from "../reference/ReferenceView";
@ -177,6 +182,18 @@ export default {
MsTablePagination,
MsTag,
MsApiCaseList,
name: "ApiCaseSimpleList",
components: {
MsTableHeaderSelectPopover,
MsSetEnvironment,
ApiCaseList,
PriorityTableItem,
ApiListContainerWithDoc,
MsTableOperatorButton,
MsTableOperator,
MsTablePagination,
MsTag,
MsApiCaseList,
MsContainer,
MsBottomContainer,
ShowMoreBtn,
@ -187,9 +204,6 @@ export default {
},
data() {
return {
type: API_CASE_LIST,
headerItems: Api_Case_List,
tableLabel: Api_Case_List,
condition: {
components: API_CASE_CONFIGS
},
@ -235,6 +249,7 @@ export default {
props: {
currentProtocol: String,
selectNodeIds: Array,
activeDom:String,
visible: {
type: Boolean,
default: false,
@ -297,6 +312,9 @@ export default {
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
activeDomChange(tabType){
this.$emit("activeDomChange",tabType);
},
initTable() {
this.getLabel()
this.selectRows = new Set();

View File

@ -0,0 +1,60 @@
<template>
<div>
<api-list-container-with-doc
:is-api-list-enable="isApiListEnable"
:active-dom="activeDom"
@activeDomChange="activeDomChange"
@isApiListEnableChange="isApiListEnableChange">
<api-document-item :project-id="projectId" :module-ids="moduleIds"/>
</api-list-container-with-doc>
</div>
</template>
<script>
import ApiListContainerWithDoc from "@/business/components/api/definition/components/list/ApiListContainerWithDoc";
import ApiDocumentItem from "@/business/components/api/definition/components/document/ApiDocumentItem";
export default {
name: "ApiDocumentsPage",
components: {
ApiListContainerWithDoc,
ApiDocumentItem,
},
data() {
return {
}
},
props: {
projectId:String,
moduleIds:Array,
activeDom:String,
isApiListEnable: {
type: Boolean,
default: false,
},
},
created: function () {
},
watch: {
},
computed: {
},
methods: {
isApiListEnableChange(data){
this.$emit("isApiListEnableChange",data);
},
activeDomChange(data){
this.$emit("activeDomChange",data);
}
},
}
</script>
<style scoped>
</style>

View File

@ -1,7 +1,9 @@
<template>
<div>
<api-list-container
<api-list-container-with-doc
:is-api-list-enable="isApiListEnable"
:active-dom="activeDom"
@activeDomChange="activeDomChange"
@isApiListEnableChange="isApiListEnableChange">
<el-link type="primary" @click="open" style="float: right;margin-top: 5px">{{ $t('commons.adv_search.title') }}
@ -194,7 +196,7 @@
</el-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</api-list-container>
</api-list-container-with-doc>
<ms-api-case-list @refresh="initTable" @showExecResult="showExecResult" :currentApi="selectApi" ref="caseList"/>
<!--批量编辑-->
<ms-batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :value-arr="valueArr"/>
@ -219,6 +221,8 @@ import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, API_STATUS, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData";
import {downloadFile, getCurrentProjectID} from "@/common/js/utils";
import {PROJECT_NAME, WORKSPACE_ID} from '@/common/js/constants';
import {getCurrentProjectID, getCurrentUser} from "@/common/js/utils";
import {API_LIST, TEST_CASE_LIST, WORKSPACE_ID} from '@/common/js/constants';
import ApiListContainer from "./ApiListContainer";
@ -228,6 +232,15 @@ import MsTableAdvSearchBar from "@/business/components/common/components/search/
import {API_DEFINITION_CONFIGS} from "@/business/components/common/components/search/search-components";
import MsTipButton from "@/business/components/common/components/MsTipButton";
import CaseBatchMove from "@/business/components/api/definition/components/basis/BatchMove";
import ApiListContainerWithDoc from "@/business/components/api/definition/components/list/ApiListContainerWithDoc";
import {
_filter,
_handleSelect,
_handleSelectAll,
_sort,
getSelectDataCounts, initCondition,
setUnSelectIds, toggleAllSelection
} from "@/common/js/tableUtils";
import {_filter, _sort} from "@/common/js/tableUtils";
import {Api_List, Track_Test_Case} from "@/business/components/common/model/JsonData";
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
@ -240,7 +253,7 @@ export default {
CaseBatchMove,
ApiStatus,
MsTableHeaderSelectPopover,
ApiListContainer,
ApiListContainerWithDoc,
MsTableButton,
MsTableOperatorButton,
MsTableOperator,
@ -314,8 +327,6 @@ export default {
total: 0,
screenHeight: document.documentElement.clientHeight - 270,//,
environmentId: undefined,
selectAll: false,
unSelection: [],
selectDataCounts: 0,
}
},
@ -323,6 +334,7 @@ export default {
currentProtocol: String,
selectNodeIds: Array,
isSelectThisWeek: String,
activeDom:String,
visible: {
type: Boolean,
default: false,
@ -386,15 +398,16 @@ export default {
handleBatchMove() {
this.$refs.testCaseBatchMove.open(this.moduleTree, [], this.moduleOptions);
},
isApiListEnableChange(data) {
this.$emit('isApiListEnableChange', data);
},
activeDomChange(tabType){
this.$emit("activeDomChange",tabType);
},
initTable() {
this.getLabel()
this.selectRows = new Set();
this.selectAll = false;
this.unSelection = [];
initCondition(this.condition);
this.selectDataCounts = 0;
this.condition.moduleIds = this.selectNodeIds;
this.condition.projectId = getCurrentProjectID();
@ -431,7 +444,6 @@ export default {
this.genProtocalFilter(this.condition.protocol);
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
this.unSelection = response.data.listObject.map(s => s.id);
this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
@ -510,45 +522,21 @@ export default {
});
});
},
handleSelect(selection, row) {
row.hashTree = [];
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
} else {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
let arr = Array.from(this.selectRows);
// 1
if (this.selectRows.size === 1) {
this.$set(arr[0], "showMore", true);
} else if (this.selectRows.size === 2) {
arr.forEach(row => {
this.$set(row, "showMore", true);
})
}
this.selectRowsCount(this.selectRows)
},
handleSelectAll(selection) {
if (selection.length > 0) {
if (selection.length === 1) {
selection.hashTree = [];
this.selectRows.add(selection[0]);
} else {
this.tableData.forEach(item => {
item.hashTree = [];
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
}
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
}
this.selectRowsCount(this.selectRows)
_handleSelectAll(this, selection, this.tableData, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
},
isSelectDataAll(data) {
this.condition.selectAll = data;
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
toggleAllSelection(this.$refs.apiDefinitionTable, this.tableData, this.selectRows);
},
search() {
this.changeSelectDataRangeAll();
@ -580,14 +568,7 @@ export default {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let deleteParam = {};
let ids = Array.from(this.selectRows).map(row => row.id);
deleteParam.dataIds = ids;
deleteParam.projectId = getCurrentProjectID();
deleteParam.selectAllDate = this.isSelectAllDate;
deleteParam.unSelectIds = this.unSelection;
deleteParam = Object.assign(deleteParam, this.condition);
this.$post('/api/definition/deleteBatchByParams/', deleteParam, () => {
this.$post('/api/definition/deleteBatchByParams/', this.buildBatchParam(), () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
@ -600,14 +581,7 @@ export default {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
let deleteParam = {};
deleteParam.dataIds = ids;
deleteParam.projectId = getCurrentProjectID();
deleteParam.selectAllDate = this.isSelectAllDate;
deleteParam.unSelectIds = this.unSelection;
deleteParam = Object.assign(deleteParam, this.condition);
this.$post('/api/definition/removeToGcByParams/', deleteParam, () => {
this.$post('/api/definition/removeToGcByParams/', this.buildBatchParam(), () => {
this.selectRows.clear();
this.initTable();
this.$success(this.$t('commons.delete_success'));
@ -631,27 +605,27 @@ export default {
this.$refs.batchEdit.open();
},
batchEdit(form) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
let param = {};
let param = this.buildBatchParam();
param[form.type] = form.value;
param.ids = ids;
param.projectId = getCurrentProjectID();
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
this.$post('/api/definition/batch/editByParams', param, () => {
this.$success(this.$t('commons.save_success'));
this.initTable();
});
},
buildBatchParam() {
let param = {};
param.ids = Array.from(this.selectRows).map(row => row.id);
param.projectId = getCurrentProjectID();
param.condition = this.condition;
return param;
},
moveSave(param) {
let arr = Array.from(this.selectRows);
let ids = arr.map(row => row.id);
param.ids = ids;
param.projectId = getCurrentProjectID();
param.moduleId=param.nodeId;
param.condition = this.condition;
param.selectAllDate = this.isSelectAllDate;
param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition);
@ -706,26 +680,6 @@ export default {
showExecResult(row) {
this.$emit('showExecResult', row);
},
selectRowsCount(selection) {
let selectedIDs = this.getIds(selection);
let allIDs = this.tableData.map(s => s.id);
this.unSelection = allIDs.filter(function (val) {
return selectedIDs.indexOf(val) === -1
});
if (this.isSelectAllDate) {
this.selectDataCounts = this.total - this.unSelection.length;
} else {
this.selectDataCounts = selection.size;
}
},
isSelectDataAll(dataType) {
this.isSelectAllDate = dataType;
this.selectRowsCount(this.selectRows)
//
if (this.selectRows.size != this.tableData.length) {
this.$refs.apiDefinitionTable.toggleAllSelection(true);
}
},
//
getSelectDataRange() {
let dataRange = this.$route.params.dataSelectRange;
@ -744,6 +698,25 @@ export default {
let ids = rowArray.map(s => s.id);
return ids;
},
exportApi() {
let param = this.buildBatchParam();
param.protocol = this.currentProtocol;
this.result = this.$post("/api/definition/export", param, response => {
let obj = response.data;
obj.protocol = this.currentProtocol;
this.buildApiPath(obj.data);
downloadFile("Metersphere_Api_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
});
},
buildApiPath(apis) {
apis.forEach((api) => {
this.moduleOptions.forEach(item => {
if (api.moduleId === item.id) {
api.modulePath = item.path;
}
});
});
},
sort(column) {
//
if (this.condition.orders) {

Some files were not shown because too many files have changed in this diff Show More