Merge branch 'master' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
9d6cec56e1
|
@ -148,10 +148,10 @@ public class ApiDefinitionController {
|
||||||
return apiDefinitionService.apiTestImport(file, request);
|
return apiDefinitionService.apiTestImport(file, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/export")
|
@PostMapping(value = "/export/{type}")
|
||||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||||
public ApiExportResult export(@RequestBody ApiBatchRequest request) {
|
public ApiExportResult export(@RequestBody ApiBatchRequest request, @PathVariable String type) {
|
||||||
return apiDefinitionService.export(request);
|
return apiDefinitionService.export(request, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
//定时任务创建
|
//定时任务创建
|
||||||
|
|
|
@ -1,19 +1,10 @@
|
||||||
package io.metersphere.api.dto.definition;
|
package io.metersphere.api.dto.definition;
|
||||||
|
|
||||||
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
|
||||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
public class ApiExportResult {
|
public class ApiExportResult {
|
||||||
private String projectName;
|
|
||||||
private String protocol;
|
|
||||||
private String projectId;
|
|
||||||
private String version;
|
|
||||||
private List<ApiDefinitionWithBLOBs> data;
|
|
||||||
private List<ApiTestCaseWithBLOBs> cases;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
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 MsApiExportResult extends ApiExportResult {
|
||||||
|
private String projectName;
|
||||||
|
private String protocol;
|
||||||
|
private String projectId;
|
||||||
|
private String version;
|
||||||
|
private List<ApiDefinitionWithBLOBs> data;
|
||||||
|
private List<ApiTestCaseWithBLOBs> cases;
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package io.metersphere.api.dto.definition;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.metersphere.api.dto.definition.parse.swagger.SwaggerInfo;
|
||||||
|
import io.metersphere.api.dto.definition.parse.swagger.SwaggerTag;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SwaggerApiExportResult extends ApiExportResult{
|
||||||
|
private String openapi;
|
||||||
|
private SwaggerInfo info;
|
||||||
|
private String externalDocs;
|
||||||
|
private List<String> servers;
|
||||||
|
private List<SwaggerTag> tags;
|
||||||
|
private JSONObject paths; // Map<String, Object>, Object 里放 Operation 对象
|
||||||
|
private List<String> components;
|
||||||
|
}
|
|
@ -30,14 +30,14 @@ public class Swagger2Parser extends SwaggerAbstractParser {
|
||||||
public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) {
|
public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) {
|
||||||
Swagger swagger;
|
Swagger swagger;
|
||||||
String sourceStr = "";
|
String sourceStr = "";
|
||||||
if (StringUtils.isNotBlank(request.getSwaggerUrl())) {
|
if (StringUtils.isNotBlank(request.getSwaggerUrl())) { // 使用 url 导入 swagger
|
||||||
swagger = new SwaggerParser().read(request.getSwaggerUrl());
|
swagger = new SwaggerParser().read(request.getSwaggerUrl());
|
||||||
} else {
|
} else {
|
||||||
sourceStr = getApiTestStr(source);
|
sourceStr = getApiTestStr(source); // 导入的二进制文件转换为 String
|
||||||
swagger = new SwaggerParser().readWithInfo(sourceStr).getSwagger();
|
swagger = new SwaggerParser().readWithInfo(sourceStr).getSwagger();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swagger == null || swagger.getSwagger() == null) {
|
if (swagger == null || swagger.getSwagger() == null) { // 不是 2.0 版本,则尝试转换 3.0
|
||||||
Swagger3Parser swagger3Parser = new Swagger3Parser();
|
Swagger3Parser swagger3Parser = new Swagger3Parser();
|
||||||
return swagger3Parser.parse(sourceStr, request);
|
return swagger3Parser.parse(sourceStr, request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,18 @@ import com.alibaba.fastjson.JSON;
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import io.metersphere.api.dto.ApiTestImportRequest;
|
import io.metersphere.api.dto.ApiTestImportRequest;
|
||||||
|
import io.metersphere.api.dto.definition.SwaggerApiExportResult;
|
||||||
|
import io.metersphere.api.dto.definition.parse.swagger.*;
|
||||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||||
import io.metersphere.api.dto.definition.response.HttpResponse;
|
import io.metersphere.api.dto.definition.response.HttpResponse;
|
||||||
import io.metersphere.api.dto.scenario.Body;
|
import io.metersphere.api.dto.scenario.Body;
|
||||||
import io.metersphere.api.dto.scenario.KeyValue;
|
import io.metersphere.api.dto.scenario.KeyValue;
|
||||||
import io.metersphere.api.dto.scenario.request.RequestType;
|
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.ApiDefinitionWithBLOBs;
|
||||||
import io.metersphere.base.domain.ApiModule;
|
import io.metersphere.base.domain.ApiModule;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.commons.utils.XMLUtils;
|
import io.metersphere.commons.utils.XMLUtils;
|
||||||
import io.swagger.parser.OpenAPIParser;
|
import io.swagger.parser.OpenAPIParser;
|
||||||
|
@ -362,4 +366,119 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
||||||
QueryParameter queryParameter = (QueryParameter) parameter;
|
QueryParameter queryParameter = (QueryParameter) parameter;
|
||||||
arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()), parameter.getRequired()));
|
arguments.add(new KeyValue(queryParameter.getName(), "", getDefaultStringValue(queryParameter.getDescription()), parameter.getRequired()));
|
||||||
}
|
}
|
||||||
|
/* 导出的 swagger json描述文件样例
|
||||||
|
{
|
||||||
|
"openapi":"3.0.1",
|
||||||
|
"info":{},
|
||||||
|
"externalDocs":{},
|
||||||
|
"servers":{},
|
||||||
|
"tags":{},
|
||||||
|
"paths":{ // 对应 SwaggerApiExportResult 类的paths
|
||||||
|
"/lzx/test/{ball}":{ // key
|
||||||
|
"get":{ // 对应 SwaggerPath 类的 JSONObject 类型的成员,”get“为key
|
||||||
|
"tags":[
|
||||||
|
"subModule2"
|
||||||
|
],
|
||||||
|
"summary":"API",
|
||||||
|
"parameters":[
|
||||||
|
{
|
||||||
|
"name":"ballName",
|
||||||
|
"in":"query",// path,header,query都可选。
|
||||||
|
"description":"描述param",
|
||||||
|
"required":true // 是否必填参数
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody":{
|
||||||
|
"content":{
|
||||||
|
"application/octet-stream":{ // type
|
||||||
|
"schema":{
|
||||||
|
"type":null,
|
||||||
|
"format":null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // SwaggerApiInfo类,为 value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components":{}
|
||||||
|
} */
|
||||||
|
public SwaggerApiExportResult swagger3Export(List<ApiDefinitionWithBLOBs> apiDefinitionList) {
|
||||||
|
SwaggerApiExportResult result = new SwaggerApiExportResult();
|
||||||
|
|
||||||
|
result.setOpenapi("3.0.1");
|
||||||
|
result.setInfo(new SwaggerInfo());
|
||||||
|
result.setServers(new ArrayList<>());
|
||||||
|
result.setTags(new ArrayList<>());
|
||||||
|
result.setComponents(new ArrayList<>());
|
||||||
|
|
||||||
|
JSONObject paths = new JSONObject();
|
||||||
|
JSONObject swaggerPath = new JSONObject();
|
||||||
|
for(ApiDefinitionWithBLOBs apiDefinition : apiDefinitionList) {
|
||||||
|
SwaggerApiInfo swaggerApiInfo = new SwaggerApiInfo(); // {tags:, summary:, description:, parameters:}
|
||||||
|
swaggerApiInfo.setSummary(apiDefinition.getName());
|
||||||
|
// 设置导入后的模块名 (根据 api 的 moduleID 查库获得所属模块,作为导出的模块名)
|
||||||
|
ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
|
||||||
|
String moduleName = apiModuleService.getNode(apiDefinition.getModuleId()).getName();
|
||||||
|
swaggerApiInfo.setTags(Arrays.asList(moduleName));
|
||||||
|
// 设置请求体
|
||||||
|
JSONObject requestObject = JSON.parseObject(apiDefinition.getRequest()); // 将api的request属性转换成JSON对象以便获得参数
|
||||||
|
JSONObject requestBody = buildRequestBody(requestObject);
|
||||||
|
swaggerApiInfo.setRequestBody(requestBody);
|
||||||
|
// 设置请求参数列表
|
||||||
|
List<JSONObject> paramsList = buildParameters(requestObject);
|
||||||
|
swaggerApiInfo.setParameters(paramsList);
|
||||||
|
swaggerPath.put(apiDefinition.getMethod().toLowerCase(), JSON.parseObject(JSON.toJSONString(swaggerApiInfo))); // 设置api的请求类型和api定义、参数
|
||||||
|
paths.put(apiDefinition.getPath(), swaggerPath);
|
||||||
|
}
|
||||||
|
result.setPaths(paths);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<JSONObject> buildParameters(JSONObject request) {
|
||||||
|
List<JSONObject> paramsList = new ArrayList<>();
|
||||||
|
Hashtable<String, String> typeMap = new Hashtable<String, String>() {{
|
||||||
|
put("headers", "header");
|
||||||
|
put("rest", "path");
|
||||||
|
put("arguments", "query");
|
||||||
|
}};
|
||||||
|
Set<String> typeKeys = typeMap.keySet();
|
||||||
|
for(String type : typeKeys) {
|
||||||
|
JSONArray params = request.getJSONArray(type); // 获得请求参数列表
|
||||||
|
if(params != null) {
|
||||||
|
for(int i = 0; i < params.size(); ++i) {
|
||||||
|
JSONObject param = params.getJSONObject(i); // 对于每个参数:
|
||||||
|
SwaggerParams swaggerParam = new SwaggerParams();
|
||||||
|
swaggerParam.setIn(typeMap.get(type)); // 利用 map,根据 request 的 key 设置对应的参数类型
|
||||||
|
swaggerParam.setDescription((String) param.get("description"));
|
||||||
|
swaggerParam.setName((String) param.get("name"));
|
||||||
|
swaggerParam.setRequired((boolean) param.get("required"));
|
||||||
|
paramsList.add(JSON.parseObject(JSON.toJSONString(swaggerParam)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paramsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject buildRequestBody(JSONObject request) {
|
||||||
|
Hashtable<String, String> typeMap = new Hashtable<String, String>() {{
|
||||||
|
put("XML", org.springframework.http.MediaType.APPLICATION_XML_VALUE);
|
||||||
|
put("JSON", org.springframework.http.MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
put("Raw", "application/urlencoded");
|
||||||
|
put("BINARY", org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE);
|
||||||
|
put("Form Data", org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE);
|
||||||
|
put("WWW_FORM", org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||||
|
}};
|
||||||
|
String type = request.getJSONObject("body").getString("type");
|
||||||
|
JSONObject requestBody = new JSONObject();
|
||||||
|
JSONObject schema = new JSONObject();
|
||||||
|
JSONObject typeName = new JSONObject();
|
||||||
|
schema.put("type", null);
|
||||||
|
schema.put("format", null);
|
||||||
|
typeName.put("schema", schema);
|
||||||
|
JSONObject content = new JSONObject();
|
||||||
|
content.put(typeMap.get(type), typeName);
|
||||||
|
requestBody.put("content", content);
|
||||||
|
return requestBody;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package io.metersphere.api.dto.definition.parse.swagger;
|
||||||
|
import lombok.*;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/*
|
||||||
|
该类表示 swagger3 的 paths 字段下每个请求类型中的 value,即表示一个 api 定义
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SwaggerApiInfo {
|
||||||
|
private List<String> tags; // 对应一个 API 在MS项目中所在的 module 名称
|
||||||
|
private String summary; // 对应 API 的名字
|
||||||
|
private List<JSONObject> parameters; // 对应 API 的请求参数
|
||||||
|
private JSONObject requestBody;
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package io.metersphere.api.dto.definition.parse.swagger;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class SwaggerParams {
|
||||||
|
private String name; // 对应 API 请求参数名
|
||||||
|
private String in; // 参数类型,可选值为 path,header,query 等
|
||||||
|
private String description;
|
||||||
|
private boolean required; // 是否是必填参数
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import io.metersphere.api.dto.datacount.ApiDataCountResult;
|
||||||
import io.metersphere.api.dto.definition.*;
|
import io.metersphere.api.dto.definition.*;
|
||||||
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
||||||
import io.metersphere.api.dto.definition.parse.ApiDefinitionImportParserFactory;
|
import io.metersphere.api.dto.definition.parse.ApiDefinitionImportParserFactory;
|
||||||
|
import io.metersphere.api.dto.definition.parse.Swagger3Parser;
|
||||||
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||||
import io.metersphere.api.dto.definition.request.ScheduleInfoSwaggerUrlRequest;
|
import io.metersphere.api.dto.definition.request.ScheduleInfoSwaggerUrlRequest;
|
||||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||||
|
@ -762,18 +763,27 @@ public class ApiDefinitionService {
|
||||||
scheduleService.addOrUpdateCronJob(request, SwaggerUrlImportJob.getJobKey(request.getResourceId()), SwaggerUrlImportJob.getTriggerKey(request.getResourceId()), SwaggerUrlImportJob.class);
|
scheduleService.addOrUpdateCronJob(request, SwaggerUrlImportJob.getJobKey(request.getResourceId()), SwaggerUrlImportJob.getTriggerKey(request.getResourceId()), SwaggerUrlImportJob.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApiExportResult export(ApiBatchRequest request) {
|
public ApiExportResult export(ApiBatchRequest request, String type) {
|
||||||
|
ApiExportResult apiExportResult;
|
||||||
ServiceUtils.getSelectAllIds(request, request.getCondition(),
|
ServiceUtils.getSelectAllIds(request, request.getCondition(),
|
||||||
(query) -> extApiDefinitionMapper.selectIds(query));
|
(query) -> extApiDefinitionMapper.selectIds(query));
|
||||||
ApiDefinitionExample example = new ApiDefinitionExample();
|
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||||
example.createCriteria().andIdIn(request.getIds());
|
example.createCriteria().andIdIn(request.getIds());
|
||||||
ApiExportResult apiExportResult = new ApiExportResult();
|
|
||||||
apiExportResult.setData(apiDefinitionMapper.selectByExampleWithBLOBs(example));
|
if (StringUtils.equals(type, "MS")) { // 导出为 Metersphere 格式
|
||||||
apiExportResult.setCases(apiTestCaseService.selectCasesBydApiIds(request.getIds()));
|
apiExportResult = new MsApiExportResult();
|
||||||
apiExportResult.setProjectName(request.getProjectId());
|
((MsApiExportResult) apiExportResult).setData(apiDefinitionMapper.selectByExampleWithBLOBs(example));
|
||||||
apiExportResult.setProtocol(request.getProtocol());
|
((MsApiExportResult) apiExportResult).setCases(apiTestCaseService.selectCasesBydApiIds(request.getIds()));
|
||||||
apiExportResult.setProjectId(request.getProjectId());
|
((MsApiExportResult) apiExportResult).setProjectName(request.getProjectId());
|
||||||
apiExportResult.setVersion(System.getenv("MS_VERSION"));
|
((MsApiExportResult) apiExportResult).setProtocol(request.getProtocol());
|
||||||
|
((MsApiExportResult) apiExportResult).setProjectId(request.getProjectId());
|
||||||
|
((MsApiExportResult) apiExportResult).setVersion(System.getenv("MS_VERSION"));
|
||||||
|
}
|
||||||
|
else { // 导出为 Swagger 格式
|
||||||
|
Swagger3Parser swagger3Parser = new Swagger3Parser();
|
||||||
|
System.out.println(apiDefinitionMapper.selectByExampleWithBLOBs(example));
|
||||||
|
apiExportResult = swagger3Parser.swagger3Export(apiDefinitionMapper.selectByExampleWithBLOBs(example));
|
||||||
|
}
|
||||||
return apiExportResult;
|
return apiExportResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ edit_load_test_not_found=Cannot edit test, test not found=
|
||||||
run_load_test_not_found=Cannot run test, test not found=
|
run_load_test_not_found=Cannot run test, test not found=
|
||||||
run_load_test_file_not_found=Unable to run test, unable to get test file meta information, test ID=
|
run_load_test_file_not_found=Unable to run test, unable to get test file meta information, test ID=
|
||||||
run_load_test_file_content_not_found=Cannot run test, cannot get test file content, test ID=
|
run_load_test_file_content_not_found=Cannot run test, cannot get test file content, test ID=
|
||||||
run_load_test_file_init_error=Failed to run test, please check current end point configuration
|
run_load_test_file_init_error=Failed to run the test, please go to [Settings-System-System Parameter Setting] to check the current site configuration. For details, see https://metersphere.io/docs/faq/load_ test/#url
|
||||||
load_test_is_running=Load test is running, please wait.
|
load_test_is_running=Load test is running, please wait.
|
||||||
load_test_kafka_invalid=Kafka is not available, please check the configuration
|
load_test_kafka_invalid=Kafka is not available, please check the configuration
|
||||||
cannot_edit_load_test_running=Cannot modify the running test
|
cannot_edit_load_test_running=Cannot modify the running test
|
||||||
|
|
|
@ -34,7 +34,7 @@ edit_load_test_not_found=无法编辑测试,未找到测试:
|
||||||
run_load_test_not_found=无法运行测试,未找到测试:
|
run_load_test_not_found=无法运行测试,未找到测试:
|
||||||
run_load_test_file_not_found=无法运行测试,无法获取测试文件元信息,测试ID:
|
run_load_test_file_not_found=无法运行测试,无法获取测试文件元信息,测试ID:
|
||||||
run_load_test_file_content_not_found=无法运行测试,无法获取测试文件内容,测试ID:
|
run_load_test_file_content_not_found=无法运行测试,无法获取测试文件内容,测试ID:
|
||||||
run_load_test_file_init_error=无法运行测试,请前往 [系统设置-系统-系统参数设置] 检查当前站点配置,详情见https://metersphere.io/docs/faq/load_test/#url
|
run_load_test_file_init_error=无法运行测试,请前往 [系统设置-系统-系统参数设置] 检查当前站点配置,详情见 https://metersphere.io/docs/faq/load_test/#url
|
||||||
load_test_is_running=测试正在运行, 请等待
|
load_test_is_running=测试正在运行, 请等待
|
||||||
load_test_kafka_invalid=Kafka 不可用,请检查配置
|
load_test_kafka_invalid=Kafka 不可用,请检查配置
|
||||||
cannot_edit_load_test_running=不能修改正在运行的测试
|
cannot_edit_load_test_running=不能修改正在运行的测试
|
||||||
|
|
|
@ -33,7 +33,7 @@ user_apikey_limit=最多能有5個Api key
|
||||||
edit_load_test_not_found=無法編輯測試,未找到測試:
|
edit_load_test_not_found=無法編輯測試,未找到測試:
|
||||||
run_load_test_not_found=無法運行測試,未找到測試:
|
run_load_test_not_found=無法運行測試,未找到測試:
|
||||||
run_load_test_file_not_found=無法運行測試,無法獲取測試文件元信息,測試ID:
|
run_load_test_file_not_found=無法運行測試,無法獲取測試文件元信息,測試ID:
|
||||||
run_load_test_file_content_not_found=無法運行測試,無法獲取測試文件內容,測試ID:
|
run_load_test_file_content_not_found=無法運行測試,請前往 [系統設置-系統-系統參數設置] 檢查當前站點配置,詳情見 https://metersphere.io/docs/faq/load_test/#url
|
||||||
run_load_test_file_init_error=無法運行測試,請檢查當前站點配置
|
run_load_test_file_init_error=無法運行測試,請檢查當前站點配置
|
||||||
load_test_is_running=測試正在運行, 請等待
|
load_test_is_running=測試正在運行, 請等待
|
||||||
load_test_kafka_invalid=Kafka 不可用,請檢查配置
|
load_test_kafka_invalid=Kafka 不可用,請檢查配置
|
||||||
|
|
|
@ -141,6 +141,7 @@ export default {
|
||||||
.header-top-menus {
|
.header-top-menus {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menus > a {
|
.menus > a {
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
:trash-enable="trashEnable"
|
:trash-enable="trashEnable"
|
||||||
:checkRedirectID="checkRedirectID"
|
:checkRedirectID="checkRedirectID"
|
||||||
:isRedirectEdit="isRedirectEdit"
|
:isRedirectEdit="isRedirectEdit"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
@openScenario="editScenario"
|
@openScenario="editScenario"
|
||||||
@edit="editScenario"
|
@edit="editScenario"
|
||||||
@changeSelectDataRangeAll="changeSelectDataRangeAll"
|
@changeSelectDataRangeAll="changeSelectDataRangeAll"
|
||||||
|
@ -66,7 +67,7 @@
|
||||||
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
|
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
|
||||||
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
|
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
|
||||||
import MsApiScenarioList from "@/business/components/api/automation/scenario/ApiScenarioList";
|
import MsApiScenarioList from "@/business/components/api/automation/scenario/ApiScenarioList";
|
||||||
import {getUUID, downloadFile} from "@/common/js/utils";
|
import {getUUID, downloadFile, checkoutTestManagerOrTestUser} from "@/common/js/utils";
|
||||||
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
|
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
|
||||||
import MsEditApiScenario from "./scenario/EditApiScenario";
|
import MsEditApiScenario from "./scenario/EditApiScenario";
|
||||||
import {getCurrentProjectID} from "../../../../common/js/utils";
|
import {getCurrentProjectID} from "../../../../common/js/utils";
|
||||||
|
@ -92,6 +93,9 @@
|
||||||
let redirectParam = this.$route.params.dataSelectRange;
|
let redirectParam = this.$route.params.dataSelectRange;
|
||||||
this.checkRedirectEditPage(redirectParam);
|
this.checkRedirectEditPage(redirectParam);
|
||||||
return redirectParam;
|
return redirectParam;
|
||||||
|
},
|
||||||
|
isReadOnly(){
|
||||||
|
return !checkoutTestManagerOrTestUser();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<el-table-column v-if="!referenced" width="30" min-width="30" :resizable="false" align="center">
|
<el-table-column v-if="!referenced" width="30" min-width="30" :resizable="false" align="center">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
|
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts" v-tester/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<template v-for="(item, index) in tableLabel">
|
<template v-for="(item, index) in tableLabel">
|
||||||
|
@ -33,7 +33,8 @@
|
||||||
min-width="120px"
|
min-width="120px"
|
||||||
show-overflow-tooltip :key="index">
|
show-overflow-tooltip :key="index">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tooltip content="编辑">
|
<span style="cursor:pointer" v-if="isReadOnly"> {{ scope.row.num }} </span>
|
||||||
|
<el-tooltip v-else content="编辑">
|
||||||
<a style="cursor:pointer" @click="edit(scope.row)"> {{ scope.row.num }} </a>
|
<a style="cursor:pointer" @click="edit(scope.row)"> {{ scope.row.num }} </a>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
@ -107,7 +108,7 @@
|
||||||
min-width="120px"
|
min-width="120px"
|
||||||
show-overflow-tooltip :key="index"/>
|
show-overflow-tooltip :key="index"/>
|
||||||
</template>
|
</template>
|
||||||
<el-table-column fixed="right" :label="$t('commons.operating')" width="190px" v-if="!referenced">
|
<el-table-column fixed="right" :label="$t('commons.operating')" width="190px" v-if="!referenced && !isReadOnly">
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<header-label-operate @exec="customHeader"/>
|
<header-label-operate @exec="customHeader"/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -232,6 +233,11 @@
|
||||||
default() {
|
default() {
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
//用于判断是否是只读用户
|
||||||
|
isReadOnly: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
:trash-enable="trashEnable"
|
:trash-enable="trashEnable"
|
||||||
:queryDataType="queryDataType"
|
:queryDataType="queryDataType"
|
||||||
:selectDataRange="selectDataRange"
|
:selectDataRange="selectDataRange"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
@changeSelectDataRangeAll="changeSelectDataRangeAll"
|
@changeSelectDataRangeAll="changeSelectDataRangeAll"
|
||||||
@editApi="editApi"
|
@editApi="editApi"
|
||||||
@handleCase="handleCase"
|
@handleCase="handleCase"
|
||||||
|
@ -60,6 +61,7 @@
|
||||||
:select-node-ids="selectNodeIds"
|
:select-node-ids="selectNodeIds"
|
||||||
:trash-enable="trashEnable"
|
:trash-enable="trashEnable"
|
||||||
:queryDataType="queryDataType"
|
:queryDataType="queryDataType"
|
||||||
|
:is-read-only="isReadOnly"
|
||||||
@changeSelectDataRangeAll="changeSelectDataRangeAll"
|
@changeSelectDataRangeAll="changeSelectDataRangeAll"
|
||||||
@handleCase="handleCase"
|
@handleCase="handleCase"
|
||||||
@showExecResult="showExecResult"
|
@showExecResult="showExecResult"
|
||||||
|
@ -138,7 +140,7 @@ import MsRunTestHttpPage from "./components/runtest/RunTestHTTPPage";
|
||||||
import MsRunTestTcpPage from "./components/runtest/RunTestTCPPage";
|
import MsRunTestTcpPage from "./components/runtest/RunTestTCPPage";
|
||||||
import MsRunTestSqlPage from "./components/runtest/RunTestSQLPage";
|
import MsRunTestSqlPage from "./components/runtest/RunTestSQLPage";
|
||||||
import MsRunTestDubboPage from "./components/runtest/RunTestDubboPage";
|
import MsRunTestDubboPage from "./components/runtest/RunTestDubboPage";
|
||||||
import {getCurrentProjectID, getCurrentUser, getUUID} from "@/common/js/utils";
|
import {checkoutTestManagerOrTestUser, getCurrentProjectID, getCurrentUser, getUUID} from "@/common/js/utils";
|
||||||
import MsApiModule from "./components/module/ApiModule";
|
import MsApiModule from "./components/module/ApiModule";
|
||||||
import ApiCaseSimpleList from "./components/list/ApiCaseSimpleList";
|
import ApiCaseSimpleList from "./components/list/ApiCaseSimpleList";
|
||||||
|
|
||||||
|
@ -155,6 +157,9 @@ import MsTabButton from "@/business/components/common/components/MsTabButton";
|
||||||
this.changeRedirectParam(redirectIDParam);
|
this.changeRedirectParam(redirectIDParam);
|
||||||
return routeParam;
|
return routeParam;
|
||||||
},
|
},
|
||||||
|
isReadOnly(){
|
||||||
|
return !checkoutTestManagerOrTestUser();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
MsTabButton,
|
MsTabButton,
|
||||||
|
@ -343,12 +348,12 @@ import MsTabButton from "@/business/components/common/components/MsTabButton";
|
||||||
apiCaseClose() {
|
apiCaseClose() {
|
||||||
this.showCasePage = true;
|
this.showCasePage = true;
|
||||||
},
|
},
|
||||||
exportAPI() {
|
exportAPI(type) {
|
||||||
if (!this.isApiListEnable) {
|
if (!this.isApiListEnable) {
|
||||||
this.$warning('用例列表暂不支持导出,请切换成接口列表');
|
this.$warning('用例列表暂不支持导出,请切换成接口列表');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$refs.apiList[0].exportApi();
|
this.$refs.apiList[0].exportApi(type);
|
||||||
},
|
},
|
||||||
refresh(data) {
|
refresh(data) {
|
||||||
this.$refs.apiList[0].initTable(data);
|
this.$refs.apiList[0].initTable(data);
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
全选/反选<i class="el-icon-arrow-down el-icon--right"></i>
|
{{ $t('api_test.select_or_invert') }} <i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item @click.native="selectAll">全选</el-dropdown-item>
|
<el-dropdown-item @click.native="selectAll">{{ $t('api_test.select_all') }}</el-dropdown-item>
|
||||||
<el-dropdown-item @click.native="invertSelect">反选</el-dropdown-item>
|
<el-dropdown-item @click.native="invertSelect">{{ $t('api_test.invert_select') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<div class="kv-row item" v-for="(item, index) in items" :key="index">
|
<div class="kv-row item" v-for="(item, index) in items" :key="index">
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
<span class="el-dropdown-link">
|
<span class="el-dropdown-link">
|
||||||
全选/反选<i class="el-icon-arrow-down el-icon--right"></i>
|
{{ $t('api_test.select_or_invert') }} <i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</span>
|
</span>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item @click.native="selectAll">全选</el-dropdown-item>
|
<el-dropdown-item @click.native="selectAll">{{ $t('api_test.select_all') }}</el-dropdown-item>
|
||||||
<el-dropdown-item @click.native="invertSelect">反选</el-dropdown-item>
|
<el-dropdown-item @click.native="invertSelect">{{ $t('api_test.invert_select') }}</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
<div class="item kv-row" v-for="(item, index) in parameters" :key="index">
|
<div class="item kv-row" v-for="(item, index) in parameters" :key="index">
|
||||||
|
|
|
@ -26,14 +26,17 @@
|
||||||
|
|
||||||
<el-table-column width="30" :resizable="false" min-width="30px" align="center">
|
<el-table-column width="30" :resizable="false" min-width="30px" align="center">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
|
<!-- 选中后浮现提供批量操作的按钮-->
|
||||||
|
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts" v-tester/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<template v-for="(item, index) in tableLabel">
|
<template v-for="(item, index) in tableLabel">
|
||||||
<el-table-column v-if="item.id == 'num'" prop="num" label="ID" min-width="120px" show-overflow-tooltip
|
<el-table-column v-if="item.id == 'num'" prop="num" label="ID" min-width="120px" show-overflow-tooltip
|
||||||
:key="index">
|
:key="index">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tooltip content="编辑">
|
<!-- 为只读用户的话不能编辑 -->
|
||||||
|
<span style="cursor:pointer" v-if="isReadOnly"> {{ scope.row.num }} </span>
|
||||||
|
<el-tooltip content="编辑" v-else>
|
||||||
<a style="cursor:pointer" @click="handleTestCase(scope.row)"> {{ scope.row.num }} </a>
|
<a style="cursor:pointer" @click="handleTestCase(scope.row)"> {{ scope.row.num }} </a>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
<el-table-column width="30" :resizable="false" align="center">
|
<el-table-column width="30" :resizable="false" align="center">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
|
<!-- 选中记录后浮现的按钮,提供对记录的批量操作 -->
|
||||||
|
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts" v-tester/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<template v-for="(item, index) in tableLabel">
|
<template v-for="(item, index) in tableLabel">
|
||||||
|
@ -40,9 +41,11 @@
|
||||||
sortable="custom"
|
sortable="custom"
|
||||||
:key="index">
|
:key="index">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tooltip content="编辑">
|
<!-- 判断为只读用户的话不可点击ID进行编辑操作 -->
|
||||||
|
<span style="cursor:pointer" v-if="isReadOnly"> {{ scope.row.num }} </span>
|
||||||
|
<el-tooltip v-else content="编辑">
|
||||||
<a style="cursor:pointer" @click="editApi(scope.row)"> {{ scope.row.num }} </a>
|
<a style="cursor:pointer" @click="editApi(scope.row)"> {{ scope.row.num }} </a>
|
||||||
</el-tooltip>
|
</el-tooltip >
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
|
@ -155,7 +158,7 @@
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
:key="index"/>
|
:key="index"/>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 操作 -->
|
||||||
<el-table-column fixed="right" v-if="!isReadOnly" min-width="180"
|
<el-table-column fixed="right" v-if="!isReadOnly" min-width="180"
|
||||||
align="center">
|
align="center">
|
||||||
|
|
||||||
|
@ -168,6 +171,7 @@
|
||||||
:tip="$t('api_test.automation.execute')"
|
:tip="$t('api_test.automation.execute')"
|
||||||
icon="el-icon-video-play"
|
icon="el-icon-video-play"
|
||||||
@exec="runApi(scope.row)"/>
|
@exec="runApi(scope.row)"/>
|
||||||
|
<!-- 回收站的恢复按钮 -->
|
||||||
<ms-table-operator-button :tip="$t('commons.reduction')" icon="el-icon-refresh-left"
|
<ms-table-operator-button :tip="$t('commons.reduction')" icon="el-icon-refresh-left"
|
||||||
@exec="reductionApi(scope.row)" v-if="trashEnable" v-tester/>
|
@exec="reductionApi(scope.row)" v-if="trashEnable" v-tester/>
|
||||||
<ms-table-operator-button :tip="$t('commons.edit')" icon="el-icon-edit" @exec="editApi(scope.row)" v-else
|
<ms-table-operator-button :tip="$t('commons.edit')" icon="el-icon-edit" @exec="editApi(scope.row)" v-else
|
||||||
|
@ -179,6 +183,7 @@
|
||||||
<el-button @click="handleTestCase(scope.row)"
|
<el-button @click="handleTestCase(scope.row)"
|
||||||
@keydown.enter.native.prevent
|
@keydown.enter.native.prevent
|
||||||
type="primary"
|
type="primary"
|
||||||
|
:disabled="isReadOnly"
|
||||||
circle
|
circle
|
||||||
style="color:white;padding: 0px 0.1px;font-size: 11px;width: 28px;height: 28px;"
|
style="color:white;padding: 0px 0.1px;font-size: 11px;width: 28px;height: 28px;"
|
||||||
size="mini">case
|
size="mini">case
|
||||||
|
@ -218,7 +223,7 @@ import MsBottomContainer from "../BottomContainer";
|
||||||
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
|
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
|
||||||
import MsBatchEdit from "../basis/BatchEdit";
|
import MsBatchEdit from "../basis/BatchEdit";
|
||||||
import {API_METHOD_COLOUR, API_STATUS, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData";
|
import {API_METHOD_COLOUR, API_STATUS, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData";
|
||||||
import {downloadFile, getUUID} from "@/common/js/utils";
|
import {checkoutTestManagerOrTestUser, downloadFile, getUUID} from "@/common/js/utils";
|
||||||
import {PROJECT_NAME} from '@/common/js/constants';
|
import {PROJECT_NAME} from '@/common/js/constants';
|
||||||
import {getCurrentProjectID, getCurrentUser} from "@/common/js/utils";
|
import {getCurrentProjectID, getCurrentUser} from "@/common/js/utils";
|
||||||
import {API_LIST, TEST_CASE_LIST, WORKSPACE_ID} from '@/common/js/constants';
|
import {API_LIST, TEST_CASE_LIST, WORKSPACE_ID} from '@/common/js/constants';
|
||||||
|
@ -693,18 +698,23 @@ export default {
|
||||||
let ids = rowArray.map(s => s.id);
|
let ids = rowArray.map(s => s.id);
|
||||||
return ids;
|
return ids;
|
||||||
},
|
},
|
||||||
exportApi() {
|
exportApi(type) {
|
||||||
let param = buildBatchParam(this);
|
let param = buildBatchParam(this);
|
||||||
param.protocol = this.currentProtocol;
|
param.protocol = this.currentProtocol;
|
||||||
if (param.ids === undefined || param.ids.length < 1) {
|
if (param.ids === undefined || param.ids.length < 1) {
|
||||||
this.$warning(this.$t("api_test.definition.check_select"));
|
this.$warning(this.$t("api_test.definition.check_select"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.result = this.$post("/api/definition/export", param, response => {
|
this.result = this.$post("/api/definition/export/" + type, param, response => {
|
||||||
let obj = response.data;
|
let obj = response.data;
|
||||||
obj.protocol = this.currentProtocol;
|
if(type == 'MS') {
|
||||||
this.buildApiPath(obj.data);
|
obj.protocol = this.currentProtocol;
|
||||||
downloadFile("Metersphere_Api_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
|
this.buildApiPath(obj.data);
|
||||||
|
downloadFile("Metersphere_Api_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
downloadFile("Swagger_Api_" + localStorage.getItem(PROJECT_NAME) + ".json", JSON.stringify(obj));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
buildApiPath(apis) {
|
buildApiPath(apis) {
|
||||||
|
|
|
@ -188,8 +188,8 @@ export default {
|
||||||
this.$refs.nodeTree.append({}, dataArr[0]);
|
this.$refs.nodeTree.append({}, dataArr[0]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
exportAPI() {
|
exportAPI(type) {
|
||||||
this.$emit('exportAPI');
|
this.$emit('exportAPI', type);
|
||||||
},
|
},
|
||||||
debug() {
|
debug() {
|
||||||
this.$emit('debug');
|
this.$emit('debug');
|
||||||
|
|
|
@ -14,13 +14,29 @@
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')"
|
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')"
|
||||||
v-tester
|
v-tester
|
||||||
@command="handleCommand">
|
@command="handleCommand"
|
||||||
|
:hide-on-click='false'
|
||||||
|
trigger="click">
|
||||||
<el-button icon="el-icon-folder-add" @click="addApi"></el-button>
|
<el-button icon="el-icon-folder-add" @click="addApi"></el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="add-api">{{ $t('api_test.definition.request.title') }}</el-dropdown-item>
|
<el-dropdown-item command="add-api">{{ $t('api_test.definition.request.title') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="debug">{{ $t('api_test.definition.request.fast_debug') }}</el-dropdown-item>
|
<el-dropdown-item command="debug">{{ $t('api_test.definition.request.fast_debug') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
|
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
|
||||||
<el-dropdown-item command="export">{{ $t('report.export') }}</el-dropdown-item>
|
<el-dropdown-item command="export">
|
||||||
|
<el-dropdown placement="right-start" @command="chooseExportType">
|
||||||
|
<span>
|
||||||
|
{{ $t('report.export') }} <i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
|
</span>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<template>
|
||||||
|
<el-dropdown-item command="export-MS">{{ $t('report.export_to_ms_format') }}</el-dropdown-item>
|
||||||
|
<el-dropdown-item command="export-Swagger" v-show="condition.protocol=='HTTP'">
|
||||||
|
{{ $t('report.export_to_swagger3_format') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</template>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
|
@ -44,10 +60,11 @@ import ApiImport from "../import/ApiImport";
|
||||||
import ModuleTrashButton from "./ModuleTrashButton";
|
import ModuleTrashButton from "./ModuleTrashButton";
|
||||||
import {getCurrentProjectID} from "../../../../../../common/js/utils";
|
import {getCurrentProjectID} from "../../../../../../common/js/utils";
|
||||||
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
|
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
|
||||||
|
import TemplateComponent from "../../../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ApiModuleHeader",
|
name: "ApiModuleHeader",
|
||||||
components: {ModuleTrashButton, ApiImport, MsAddBasisApi},
|
components: {TemplateComponent, ModuleTrashButton, ApiImport, MsAddBasisApi},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
options: OPTIONS,
|
options: OPTIONS,
|
||||||
|
@ -104,12 +121,19 @@ export default {
|
||||||
});
|
});
|
||||||
this.$refs.apiImport.open(this.currentModule);
|
this.$refs.apiImport.open(this.currentModule);
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
if (!getCurrentProjectID()) {
|
},
|
||||||
this.$warning(this.$t('commons.check_project_tip'));
|
chooseExportType(e) {
|
||||||
return;
|
if (!getCurrentProjectID()) {
|
||||||
}
|
this.$warning(this.$t('commons.check_project_tip'));
|
||||||
this.$emit('exportAPI');
|
return;
|
||||||
|
}
|
||||||
|
switch (e) {
|
||||||
|
case "export-MS":
|
||||||
|
this.$emit('exportAPI', 'MS');
|
||||||
|
break;
|
||||||
|
case "export-Swagger":
|
||||||
|
this.$emit('exportAPI', 'Swagger');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,11 @@
|
||||||
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')" width="80" show-overflow-tooltip/>
|
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')" width="80" show-overflow-tooltip/>
|
||||||
<el-table-column prop="name" :label="$t('commons.name')" width="200" >
|
<el-table-column prop="name" :label="$t('commons.name')" width="200" >
|
||||||
<template v-slot:default="{row}">
|
<template v-slot:default="{row}">
|
||||||
<el-link type="info" @click="redirect(row)">
|
<!-- 若为只读用户不可点击之后跳转-->
|
||||||
|
<span v-if="isReadOnly">
|
||||||
|
{{ row.name }}
|
||||||
|
</span>
|
||||||
|
<el-link v-else type="info" @click="redirect(row)">
|
||||||
{{ row.name }}
|
{{ row.name }}
|
||||||
</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
|
@ -25,9 +29,10 @@
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<div>
|
<div>
|
||||||
<el-switch
|
<el-switch
|
||||||
|
:disabled="isReadOnly"
|
||||||
v-model="scope.row.taskStatus"
|
v-model="scope.row.taskStatus"
|
||||||
class="captcha-img"
|
class="captcha-img"
|
||||||
@click.native="closeTaskConfirm(scope.row)"
|
@change="closeTaskConfirm(scope.row)"
|
||||||
></el-switch>
|
></el-switch>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -51,7 +56,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getCurrentProjectID} from "@/common/js/utils";
|
import {checkoutTestManagerOrTestUser, getCurrentProjectID} from "@/common/js/utils";
|
||||||
import MsTag from "@/business/components/common/components/MsTag";
|
import MsTag from "@/business/components/common/components/MsTag";
|
||||||
export default {
|
export default {
|
||||||
name: "MsRunningTaskList",
|
name: "MsRunningTaskList",
|
||||||
|
@ -69,6 +74,12 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed:{
|
||||||
|
isReadOnly(){
|
||||||
|
return !checkoutTestManagerOrTestUser();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
search() {
|
search() {
|
||||||
let projectID = getCurrentProjectID();
|
let projectID = getCurrentProjectID();
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<div class="minder">
|
||||||
|
<minder-editor v-if="isActive"
|
||||||
|
class="minder-container"
|
||||||
|
:import-json="importJson"
|
||||||
|
@save="save"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MsModuleMinder",
|
||||||
|
components: {},
|
||||||
|
props: {
|
||||||
|
treeNodes: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
importJsonTest: {
|
||||||
|
"root": {
|
||||||
|
"data": {
|
||||||
|
"text": "test111"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{ "data": { "text": "新闻"}},
|
||||||
|
{ "data": { "text": "网页"} },
|
||||||
|
{ "data": { "text": "贴吧"} },
|
||||||
|
{ "data": { "text": "知道"} },
|
||||||
|
{ "data": { "text": "音乐" } },
|
||||||
|
{ "data": { "text": "图片"} },
|
||||||
|
{ "data": { "text": "视频"} },
|
||||||
|
{ "data": { "text": "地图" } },
|
||||||
|
{ "data": { "text": "百科","expandState":"collapse"}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"template":"default"
|
||||||
|
},
|
||||||
|
importJson: {
|
||||||
|
root: {
|
||||||
|
data: {
|
||||||
|
text: "全部用例",
|
||||||
|
disable: true
|
||||||
|
},
|
||||||
|
children: []
|
||||||
|
},
|
||||||
|
"template":"default"
|
||||||
|
},
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.parse(this.importJson.root, this.treeNodes);
|
||||||
|
this.reload();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
save(data) {
|
||||||
|
console.log(data);
|
||||||
|
// console.log(this.treeNodes);
|
||||||
|
},
|
||||||
|
parse(root, children) {
|
||||||
|
if (children == null || children.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.children = [];
|
||||||
|
children.forEach((item) => {
|
||||||
|
let node = {
|
||||||
|
data: {
|
||||||
|
text: item.name,
|
||||||
|
id: item.id,
|
||||||
|
disable: true,
|
||||||
|
// resource: ['#']
|
||||||
|
},
|
||||||
|
}
|
||||||
|
root.children.push(node);
|
||||||
|
this.parse(node, item.children);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
reload() {
|
||||||
|
this.isActive = false;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.isActive = true;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -19,8 +19,8 @@
|
||||||
<ms-tab-button
|
<ms-tab-button
|
||||||
:active-dom.sync="activeDom"
|
:active-dom.sync="activeDom"
|
||||||
:left-tip="'用例列表'"
|
:left-tip="'用例列表'"
|
||||||
:left-content="'CAS'"
|
:left-content="'CASE'"
|
||||||
:right-tip="'E脑图'"
|
:right-tip="'脑图'"
|
||||||
:right-content="'脑图'"
|
:right-content="'脑图'"
|
||||||
:middle-button-enable="false">
|
:middle-button-enable="false">
|
||||||
<test-case-list
|
<test-case-list
|
||||||
|
@ -40,6 +40,7 @@
|
||||||
ref="testCaseList">
|
ref="testCaseList">
|
||||||
</test-case-list>
|
</test-case-list>
|
||||||
<testcase-minder
|
<testcase-minder
|
||||||
|
:tree-nodes="treeNodes"
|
||||||
v-if="activeDom === 'right'"
|
v-if="activeDom === 'right'"
|
||||||
ref="testCaseList"/>
|
ref="testCaseList"/>
|
||||||
</ms-tab-button>
|
</ms-tab-button>
|
||||||
|
@ -99,7 +100,7 @@ import MsMainContainer from "../../common/components/MsMainContainer";
|
||||||
import {checkoutTestManagerOrTestUser, getCurrentProjectID, getUUID, hasRoles} from "../../../../common/js/utils";
|
import {checkoutTestManagerOrTestUser, getCurrentProjectID, getUUID, hasRoles} from "../../../../common/js/utils";
|
||||||
import TestCaseNodeTree from "../common/TestCaseNodeTree";
|
import TestCaseNodeTree from "../common/TestCaseNodeTree";
|
||||||
import {TrackEvent,LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
import {TrackEvent,LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
||||||
import TestcaseMinder from "@/business/components/track/case/components/minder/TestcaseMinder";
|
import TestcaseMinder from "@/business/components/common/components/MsModuleMinder";
|
||||||
import MsTabButton from "@/business/components/common/components/MsTabButton";
|
import MsTabButton from "@/business/components/common/components/MsTabButton";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -335,4 +336,8 @@ export default {
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/deep/ .el-button-group>.el-button:first-child {
|
||||||
|
padding: 4px 1px !important;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
<template>
|
|
||||||
$END$
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: "TestcaseMinder"
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,41 +1,25 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="minder">
|
<ms-module-minder
|
||||||
<minder-editor class="minder-container" :import-json="importJson"/>
|
:tree-nodes="treeNodes"/>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import MsModuleMinder from "@/business/components/common/components/MsModuleMinder";
|
||||||
export default {
|
export default {
|
||||||
name: "TestcaseMinder",
|
name: "TestcaseMinder",
|
||||||
components: {},
|
components: {MsModuleMinder},
|
||||||
data() {
|
props: {
|
||||||
return {
|
treeNodes: {
|
||||||
importJson: {
|
type: Array,
|
||||||
"root": {
|
default() {
|
||||||
"data": {
|
return []
|
||||||
"text": "test111"
|
|
||||||
},
|
|
||||||
"children": [
|
|
||||||
{ "data": { "text": "新闻"}},
|
|
||||||
{ "data": { "text": "网页"} },
|
|
||||||
{ "data": { "text": "贴吧"} },
|
|
||||||
{ "data": { "text": "知道"} },
|
|
||||||
{ "data": { "text": "音乐" } },
|
|
||||||
{ "data": { "text": "图片"} },
|
|
||||||
{ "data": { "text": "视频"} },
|
|
||||||
{ "data": { "text": "地图" } },
|
|
||||||
{ "data": { "text": "百科","expandState":"collapse"}}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"template":"default"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -427,6 +427,8 @@ export default {
|
||||||
downloadJtl: 'Download JTL',
|
downloadJtl: 'Download JTL',
|
||||||
test_execute_again: 'Test Execute Again',
|
test_execute_again: 'Test Execute Again',
|
||||||
export: 'Export',
|
export: 'Export',
|
||||||
|
export_to_ms_format: 'Export to Metersphere format',
|
||||||
|
export_to_swagger3_format: 'Export to Swagger3.0 format',
|
||||||
compare: 'Compare',
|
compare: 'Compare',
|
||||||
generation_error: 'Report generation error, unable to view, please check log details!',
|
generation_error: 'Report generation error, unable to view, please check log details!',
|
||||||
being_generated: 'Report is being generated...',
|
being_generated: 'Report is being generated...',
|
||||||
|
@ -534,6 +536,9 @@ export default {
|
||||||
run: "Run",
|
run: "Run",
|
||||||
running: "Running",
|
running: "Running",
|
||||||
reset: "Rest",
|
reset: "Rest",
|
||||||
|
select_all: "Select all",
|
||||||
|
invert_select: "Invert select",
|
||||||
|
select_or_invert: "Select all/Invert select",
|
||||||
input_name: "Please enter the test name",
|
input_name: "Please enter the test name",
|
||||||
select_project: "Please select project",
|
select_project: "Please select project",
|
||||||
variable_name: "Variable name",
|
variable_name: "Variable name",
|
||||||
|
@ -597,7 +602,7 @@ export default {
|
||||||
save_as_case: "Save as new use case",
|
save_as_case: "Save as new use case",
|
||||||
update_api: "Update interface",
|
update_api: "Update interface",
|
||||||
body_form_data: "form-data",
|
body_form_data: "form-data",
|
||||||
body_x_www_from_urlencoded: "x-www-from-urlencoded",
|
body_x_www_from_urlencoded: "x-www-form-urlencoded",
|
||||||
body_raw: "raw",
|
body_raw: "raw",
|
||||||
body_binary: "binary",
|
body_binary: "binary",
|
||||||
body_json: "json",
|
body_json: "json",
|
||||||
|
|
|
@ -425,6 +425,8 @@ export default {
|
||||||
test_execute_again: '再次执行',
|
test_execute_again: '再次执行',
|
||||||
downloadJtl: '下载JTL',
|
downloadJtl: '下载JTL',
|
||||||
export: '导出',
|
export: '导出',
|
||||||
|
export_to_ms_format: '导出 Metersphere 格式',
|
||||||
|
export_to_swagger3_format: '导出 Swagger3.0 格式',
|
||||||
compare: '比较',
|
compare: '比较',
|
||||||
generation_error: '报告生成错误, 无法查看, 请检查日志详情!',
|
generation_error: '报告生成错误, 无法查看, 请检查日志详情!',
|
||||||
being_generated: '报告正在生成中...',
|
being_generated: '报告正在生成中...',
|
||||||
|
@ -533,6 +535,9 @@ export default {
|
||||||
run: "执行",
|
run: "执行",
|
||||||
running: "正在执行",
|
running: "正在执行",
|
||||||
reset: "重置",
|
reset: "重置",
|
||||||
|
select_all: "全选",
|
||||||
|
invert_select: "反选",
|
||||||
|
select_or_invert: "全选/反选",
|
||||||
input_name: "请输入测试名称",
|
input_name: "请输入测试名称",
|
||||||
select_project: "请选择项目",
|
select_project: "请选择项目",
|
||||||
variable_name: "变量名",
|
variable_name: "变量名",
|
||||||
|
@ -598,7 +603,7 @@ export default {
|
||||||
save_as_case: "另存为新用例",
|
save_as_case: "另存为新用例",
|
||||||
update_api: "更新接口",
|
update_api: "更新接口",
|
||||||
body_form_data: "form-data",
|
body_form_data: "form-data",
|
||||||
body_x_www_from_urlencoded: "x-www-from-urlencoded",
|
body_x_www_from_urlencoded: "x-www-form-urlencoded",
|
||||||
body_raw: "raw",
|
body_raw: "raw",
|
||||||
body_binary: "binary",
|
body_binary: "binary",
|
||||||
body_json: "json",
|
body_json: "json",
|
||||||
|
|
|
@ -425,6 +425,8 @@ export default {
|
||||||
test_execute_again: '再次執行',
|
test_execute_again: '再次執行',
|
||||||
downloadJtl: '下載JTL',
|
downloadJtl: '下載JTL',
|
||||||
export: '導出',
|
export: '導出',
|
||||||
|
export_to_ms_format: '導出 Metersphere 格式',
|
||||||
|
export_to_swagger3_format: '導出 Swagger3.0 格式',
|
||||||
compare: '比較',
|
compare: '比較',
|
||||||
generation_error: '報告生成錯誤, 無法查看, 請檢查日誌詳情!',
|
generation_error: '報告生成錯誤, 無法查看, 請檢查日誌詳情!',
|
||||||
being_generated: '報告正在生成中...',
|
being_generated: '報告正在生成中...',
|
||||||
|
@ -533,6 +535,9 @@ export default {
|
||||||
run: "執行",
|
run: "執行",
|
||||||
running: "正在執行",
|
running: "正在執行",
|
||||||
reset: "重置",
|
reset: "重置",
|
||||||
|
select_all: "全選",
|
||||||
|
invert_select: "反選",
|
||||||
|
select_or_invert: "全選/反選",
|
||||||
input_name: "請輸入測試名稱",
|
input_name: "請輸入測試名稱",
|
||||||
select_project: "請選擇項目",
|
select_project: "請選擇項目",
|
||||||
variable_name: "變量名",
|
variable_name: "變量名",
|
||||||
|
@ -597,7 +602,7 @@ export default {
|
||||||
save_as_case: "另存為新用例",
|
save_as_case: "另存為新用例",
|
||||||
update_api: "更新接口",
|
update_api: "更新接口",
|
||||||
body_form_data: "form-data",
|
body_form_data: "form-data",
|
||||||
body_x_www_from_urlencoded: "x-www-from-urlencoded",
|
body_x_www_from_urlencoded: "x-www-form-urlencoded",
|
||||||
body_raw: "raw",
|
body_raw: "raw",
|
||||||
body_binary: "binary",
|
body_binary: "binary",
|
||||||
body_json: "json",
|
body_json: "json",
|
||||||
|
|
Loading…
Reference in New Issue