Merge remote-tracking branch 'origin/master'

This commit is contained in:
Captain.B 2021-01-06 15:11:32 +08:00
commit f02477ce05
40 changed files with 916 additions and 333 deletions

View File

@ -14,7 +14,6 @@ import io.metersphere.api.service.*;
import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CronUtils;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
@ -59,7 +58,7 @@ public class APITestController {
@Resource
private ScheduleService scheduleService;
@Resource
private APIReportService apiReportService;
private HistoricalDataUpgradeService historicalDataUpgradeService;
@GetMapping("recent/{count}")
public List<APITestResult> recentTest(@PathVariable int count) {
@ -359,4 +358,8 @@ public class APITestController {
schedule.setEnable(request.isEnable());
apiAutomationService.updateSchedule(schedule);
}
@PostMapping(value = "/historicalDataUpgrade")
public String historicalDataUpgrade(@RequestBody SaveHistoricalDataUpgrade request) {
return historicalDataUpgradeService.upgrade(request);
}
}

View File

@ -121,5 +121,6 @@ public class ApiAutomationController {
public void createSchedule(@RequestBody Schedule request) {
apiAutomationService.createSchedule(request);
}
}

View File

@ -0,0 +1,18 @@
package io.metersphere.api.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class SaveHistoricalDataUpgrade {
private List<String> testIds;
private String projectId;
private String modulePath;
private String moduleId;
}

View File

@ -31,7 +31,7 @@ public class MsDubboSampler extends MsTestElement {
private String type = "DubboSampler";
@JSONField(ordinal = 52)
private String protocol;
private String protocol = "DUBBO";
@JsonProperty(value = "interface")
@JSONField(ordinal = 53, name = "interface")
private String _interface;

View File

@ -172,7 +172,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
LogUtil.error(e);
}
// REST参数
if (CollectionUtils.isNotEmpty(this.getArguments())) {
if (CollectionUtils.isNotEmpty(this.getRest())) {
sampler.setArguments(httpArguments(this.getRest()));
}
// 请求参数
@ -186,7 +186,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
if (StringUtils.isNotEmpty(this.body.getType()) && this.body.getType().equals("Form Data")) {
sampler.setDoMultipart(true);
}
sampler.setArguments(httpArguments(bodyParams));
if (CollectionUtils.isNotEmpty(bodyParams)) {
sampler.setArguments(httpArguments(bodyParams));
}
}
}

View File

@ -42,7 +42,11 @@ public class Body {
return true;
} else return false;
}
public boolean isOldKV() {
if (StringUtils.equals(type, KV)) {
return true;
} else return false;
}
public List<KeyValue> getBodyParams(HTTPSamplerProxy sampler, String requestId) {
List<KeyValue> body = new ArrayList<>();
if (this.isKV() || this.isBinary()) {

View File

@ -2,6 +2,7 @@ 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;
import io.metersphere.api.dto.scenario.KeyValue;
@ -11,6 +12,8 @@ import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import org.apache.commons.collections.CollectionUtils;
@ -60,6 +63,32 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
}
}
protected ApiModule getSelectModule(String moduleId) {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
if (StringUtils.isNotBlank(moduleId)) {
ApiModule module = new ApiModule();
ApiModuleDTO moduleDTO = apiModuleService.getNode(moduleId);
BeanUtils.copyBean(module, moduleDTO);
return module;
}
return null;
}
protected ApiModule buildModule(ApiModule parentModule, String name, boolean isSaved) {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module;
if (parentModule != null) {
module = apiModuleService.getNewModule(name, this.projectId, parentModule.getLevel() + 1);
module.setParentId(parentModule.getId());
} else {
module = apiModuleService.getNewModule(name, this.projectId, 1);
}
if (isSaved) {
createModule(module);
}
return module;
}
protected void createModule(ApiModule module) {
module.setProtocol(RequestType.HTTP);
List<ApiModule> apiModules = apiModuleService.selectSameModule(module);

View File

@ -6,6 +6,7 @@ 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.ApiModuleDTO;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.scenario.Body;
@ -31,7 +32,7 @@ public class MsParser extends ApiImportAbstractParser {
if (testObject.get("projectName") != null) {
return parseMsFormat(testStr, request);
} else {
return parsePluginFormat(testObject, request.isSaved());
return parsePluginFormat(testObject, request);
}
}
@ -44,7 +45,7 @@ public class MsParser extends ApiImportAbstractParser {
if (StringUtils.isBlank(apiDefinition.getModulePath())) {
apiDefinition.setModuleId(null);
}
parseModule(apiDefinition, importRequest.isSaved());
parseModule(apiDefinition, importRequest);
apiDefinition.setId(id);
apiDefinition.setProjectId(this.projectId);
String request = apiDefinition.getRequest();
@ -55,16 +56,16 @@ public class MsParser extends ApiImportAbstractParser {
return apiDefinitionImport;
}
private ApiDefinitionImport parsePluginFormat(JSONObject testObject, boolean isSaved) {
private ApiDefinitionImport parsePluginFormat(JSONObject testObject, ApiTestImportRequest importRequest) {
List<ApiDefinitionResult> results = new ArrayList<>();
ApiDefinitionImport apiImport = new ApiDefinitionImport();
apiImport.setProtocol(RequestType.HTTP);
apiImport.setData(results);
testObject.keySet().forEach(tag -> {
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
if (isSaved) {
createModule(module);
}
ApiModule parentModule = getSelectModule(importRequest.getModuleId());
ApiModule module = buildModule(parentModule, tag, importRequest.isSaved());
JSONObject requests = testObject.getJSONObject(tag);
requests.keySet().forEach(requestName -> {
@ -125,7 +126,7 @@ public class MsParser extends ApiImportAbstractParser {
}
private void parseModule(ApiDefinitionResult apiDefinition, Boolean isSaved) {
private void parseModule(ApiDefinitionResult apiDefinition, ApiTestImportRequest importRequest) {
String modulePath = apiDefinition.getModulePath();
if (StringUtils.isBlank(modulePath)) {
return;
@ -137,29 +138,14 @@ public class MsParser extends ApiImportAbstractParser {
modulePath = modulePath.substring(0, modulePath.length() - 1);
}
List<String> modules = Arrays.asList(modulePath.split("/"));
ApiModule parent = null;
ApiModule parent = getSelectModule(importRequest.getModuleId());
Iterator<String> iterator = modules.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
parent = buildModule(item, parent, isSaved);
parent = buildModule(parent, item, importRequest.isSaved());
if (!iterator.hasNext()) {
apiDefinition.setModuleId(parent.getId());
}
}
}
private ApiModule buildModule(String name, ApiModule parentModule, boolean isSaved) {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module;
if (parentModule != null) {
module = apiModuleService.getNewModule(name, this.projectId, parentModule.getLevel() + 1);
module.setParentId(parentModule.getId());
} else {
module = apiModuleService.getNewModule(name, this.projectId, 1);
}
if (isSaved) {
createModule(module);
}
return module;
}
}

View File

@ -9,11 +9,9 @@ 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.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.constants.MsRequestBodyType;
import io.metersphere.commons.constants.PostmanRequestBodyMode;
import io.metersphere.commons.utils.CommonBeanFactory;
import org.apache.commons.lang3.StringUtils;
import java.io.InputStream;
@ -30,7 +28,7 @@ public class PostmanParser extends ApiImportAbstractParser {
List<PostmanKeyValue> variables = postmanCollection.getVariable();
ApiDefinitionImport apiImport = new ApiDefinitionImport();
List<ApiDefinitionResult> results = new ArrayList<>();
parseItem(postmanCollection.getItem(), variables, results, buildModule(postmanCollection.getInfo().getName(), null, request.isSaved()), request.isSaved());
parseItem(postmanCollection.getItem(), variables, results, buildModule(getSelectModule(request.getModuleId()), postmanCollection.getInfo().getName(), request.isSaved()), request.isSaved());
apiImport.setData(results);
return apiImport;
}
@ -39,7 +37,7 @@ public class PostmanParser extends ApiImportAbstractParser {
for (PostmanItem item : items) {
List<PostmanItem> childItems = item.getItem();
if (childItems != null) {
ApiModule module = buildModule(item.getName(), parentModule, isSaved);
ApiModule module = buildModule(parentModule, item.getName() , isSaved);
parseItem(childItems, variables, results, module, isSaved);
} else {
ApiDefinitionResult request = parsePostman(item);
@ -53,21 +51,6 @@ public class PostmanParser extends ApiImportAbstractParser {
}
}
private ApiModule buildModule(String name, ApiModule parentModule, boolean isSaved) {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module;
if (parentModule != null) {
module = apiModuleService.getNewModule(name, this.projectId, parentModule.getLevel() + 1);
module.setParentId(parentModule.getId());
} else {
module = apiModuleService.getNewModule(name, this.projectId, 1);
}
if (isSaved) {
createModule(module);
}
return module;
}
private ApiDefinitionResult parsePostman(PostmanItem requestItem) {
PostmanRequest requestDesc = requestItem.getRequest();
if (requestDesc == null) {

View File

@ -11,10 +11,8 @@ 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.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.constants.SwaggerParameterType;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.swagger.models.*;
import io.swagger.models.parameters.*;
import io.swagger.models.properties.*;
@ -25,7 +23,7 @@ import org.apache.commons.lang3.StringUtils;
import java.io.InputStream;
import java.util.*;
public class Swagger2Parser extends ApiImportAbstractParser {
public class Swagger2Parser extends SwaggerAbstractParser {
private Map<String, Model> definitions = null;
@ -47,11 +45,11 @@ public class Swagger2Parser extends ApiImportAbstractParser {
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
this.projectId = request.getProjectId();
definitionImport.setData(parseRequests(swagger, request.isSaved()));
definitionImport.setData(parseRequests(swagger, request));
return definitionImport;
}
private List<ApiDefinitionResult> parseRequests(Swagger swagger, boolean isSaved) {
private List<ApiDefinitionResult> parseRequests(Swagger swagger, ApiTestImportRequest importRequest) {
Map<String, Path> paths = swagger.getPaths();
Set<String> pathNames = paths.keySet();
@ -59,6 +57,8 @@ public class Swagger2Parser extends ApiImportAbstractParser {
List<ApiDefinitionResult> results = new ArrayList<>();
ApiModule parentNode = getSelectModule(importRequest.getModuleId());
for (String pathName : pathNames) {
Path path = paths.get(pathName);
Map<HttpMethod, Operation> operationMap = path.getOperationMap();
@ -70,7 +70,7 @@ public class Swagger2Parser extends ApiImportAbstractParser {
parseParameters(operation, request);
apiDefinition.setRequest(JSON.toJSONString(request));
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
buildModule(apiDefinition, operation, isSaved);
buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved());
results.add(apiDefinition);
}
}
@ -79,20 +79,6 @@ public class Swagger2Parser extends ApiImportAbstractParser {
return results;
}
private void buildModule(ApiDefinitionResult apiDefinition, Operation operation, boolean isSaved) {
List<String> tags = operation.getTags();
if (tags != null) {
tags.forEach(tag -> {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
if (isSaved) {
createModule(module);
}
apiDefinition.setModuleId(module.getId());
});
}
}
private ApiDefinitionResult buildApiDefinition(String id, Operation operation, String path, String method) {
String name = "";
if (StringUtils.isNotBlank(operation.getSummary())) {

View File

@ -11,10 +11,8 @@ 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.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.XMLUtils;
import io.swagger.parser.OpenAPIParser;
@ -34,7 +32,7 @@ import java.io.InputStream;
import java.util.*;
public class Swagger3Parser extends ApiImportAbstractParser {
public class Swagger3Parser extends SwaggerAbstractParser {
private Components components;
@ -67,11 +65,11 @@ public class Swagger3Parser extends ApiImportAbstractParser {
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
this.projectId = request.getProjectId();
definitionImport.setData(parseRequests(openAPI, request.isSaved()));
definitionImport.setData(parseRequests(openAPI, request));
return definitionImport;
}
private List<ApiDefinitionResult> parseRequests(OpenAPI openAPI, boolean isSaved) {
private List<ApiDefinitionResult> parseRequests(OpenAPI openAPI, ApiTestImportRequest importRequest) {
Paths paths = openAPI.getPaths();
Set<String> pathNames = paths.keySet();
@ -80,6 +78,8 @@ public class Swagger3Parser extends ApiImportAbstractParser {
List<ApiDefinitionResult> results = new ArrayList<>();
ApiModule parentNode = getSelectModule(importRequest.getModuleId());
for (String pathName : pathNames) {
PathItem pathItem = paths.get(pathName);
@ -102,7 +102,7 @@ public class Swagger3Parser extends ApiImportAbstractParser {
parseRequestBody(operation.getRequestBody(), request.getBody());
apiDefinition.setRequest(JSON.toJSONString(request));
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
buildModule(apiDefinition, operation, isSaved);
buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved());
results.add(apiDefinition);
}
}
@ -111,20 +111,6 @@ public class Swagger3Parser extends ApiImportAbstractParser {
return results;
}
private void buildModule(ApiDefinitionResult apiDefinition, Operation operation, boolean isSaved) {
List<String> tags = operation.getTags();
if (tags != null) {
tags.forEach(tag -> {
apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
ApiModule module = apiModuleService.getNewModule(tag, this.projectId, 1);
if (isSaved) {
createModule(module);
}
apiDefinition.setModuleId(module.getId());
});
}
}
private ApiDefinitionResult buildApiDefinition(String id, Operation operation, String path, String method) {
String name = "";
if (StringUtils.isNotBlank(operation.getSummary())) {

View File

@ -0,0 +1,19 @@
package io.metersphere.api.parse;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
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) {
if (tags != null) {
tags.forEach(tag -> {
ApiModule module = buildModule(parentModule, tag, isSaved);
apiDefinition.setModuleId(module.getId());
});
}
}
}

View File

@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
@ -18,6 +17,7 @@ import io.metersphere.api.parse.ApiImportParserFactory;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionMapper;
import io.metersphere.base.mapper.ApiTestFileMapper;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
@ -71,6 +71,8 @@ public class ApiDefinitionService {
private ExtApiScenarioMapper extApiScenarioMapper;
@Resource
private ExtTestPlanMapper extTestPlanMapper;
@Resource
private ProjectMapper projectMapper;
private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24);
@ -178,7 +180,8 @@ public class ApiDefinitionService {
example.createCriteria().andMethodEqualTo(request.getMethod()).andStatusNotEqualTo("Trash")
.andProtocolEqualTo(request.getProtocol()).andPathEqualTo(request.getPath())
.andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId());
if (apiDefinitionMapper.countByExample(example) > 0) {
Project project = projectMapper.selectByPrimaryKey(request.getProjectId());
if (apiDefinitionMapper.countByExample(example) > 0 && !project.getRepeatable()) {
MSException.throwException(Translator.get("api_definition_url_not_repeating"));
}
} else {
@ -439,8 +442,8 @@ 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());
if (request.isSelectAllDate()) {
ids = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds());
}
//name在这里只是查询参数
request.setName(null);
@ -503,15 +506,15 @@ public class ApiDefinitionService {
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());
if (request.isSelectAllDate()) {
apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds());
}
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(apiIds);
apiDefinitionMapper.deleteByExample(example);
}
private List<String> getAllApiIdsByFontedSelect(List<String> filter,String name,List<String> moduleIds,String projectId,List<String>unSelectIds) {
private List<String> getAllApiIdsByFontedSelect(List<String> filter, String name, List<String> moduleIds, String projectId, List<String> unSelectIds) {
ApiDefinitionRequest request = new ApiDefinitionRequest();
request.setFilters(filter);
request.setName(name);
@ -529,8 +532,8 @@ public class ApiDefinitionService {
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());
if (request.isSelectAllDate()) {
apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds());
}
extApiDefinitionMapper.removeToGc(apiIds);
}

View File

@ -0,0 +1,335 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSON;
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.MsAssertions;
import io.metersphere.api.dto.definition.request.controller.MsIfController;
import io.metersphere.api.dto.definition.request.extract.MsExtract;
import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor;
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
import io.metersphere.api.dto.definition.request.sampler.MsDubboSampler;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.definition.request.timer.MsConstantTimer;
import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.api.dto.scenario.request.*;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiTestMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.*;
import java.net.URL;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
public class HistoricalDataUpgradeService {
@Resource
private ApiTestMapper apiTestMapper;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
@Resource
SqlSessionFactory sqlSessionFactory;
private int getNextNum(String projectId) {
ApiScenario apiScenario = extApiScenarioMapper.getNextNum(projectId);
if (apiScenario == null) {
return 100001;
} else {
return Optional.of(apiScenario.getNum() + 1).orElse(100001);
}
}
private MsScenario createScenario(Scenario oldScenario) {
MsScenario scenario = new MsScenario();
scenario.setVariables(oldScenario.getVariables());
scenario.setName(oldScenario.getName());
scenario.setEnableCookieShare(oldScenario.isEnableCookieShare());
scenario.setEnvironmentId(oldScenario.getEnvironmentId());
scenario.setReferenced("Upgrade");
scenario.setId(oldScenario.getId());
scenario.setResourceId(UUID.randomUUID().toString());
LinkedList<MsTestElement> testElements = new LinkedList<>();
int index = 1;
for (Request request : oldScenario.getRequests()) {
// 条件控制器
MsIfController ifController = null;
if (request.getController() != null && StringUtils.isNotEmpty(request.getController().getValue())
&& StringUtils.isNotEmpty(request.getController().getVariable())) {
ifController = new MsIfController();
BeanUtils.copyBean(ifController, request.getController());
ifController.setType("IfController");
ifController.setName("IfController");
ifController.setIndex(index + "");
ifController.setResourceId(UUID.randomUUID().toString());
}
// 等待控制器
if (request.getTimer() != null && StringUtils.isNotEmpty(request.getTimer().getDelay())) {
MsConstantTimer constantTimer = new MsConstantTimer();
BeanUtils.copyBean(constantTimer, request.getTimer());
constantTimer.setType("ConstantTimer");
constantTimer.setIndex(index + "");
constantTimer.setResourceId(UUID.randomUUID().toString());
testElements.add(constantTimer);
}
MsTestElement element = null;
if (request instanceof HttpRequest) {
element = new MsHTTPSamplerProxy();
HttpRequest request1 = (HttpRequest) request;
if (StringUtils.isEmpty(request1.getPath()) && StringUtils.isNotEmpty(request1.getUrl())) {
try {
URL urlObject = new URL(request1.getUrl());
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
request1.setPath(envPath);
} catch (Exception ex) {
LogUtil.error(ex.getMessage());
}
}
if (request1.getBody() != null && request1.getBody().isOldKV()) {
request1.getBody().setType(Body.FORM_DATA);
}
BeanUtils.copyBean(element, request1);
((MsHTTPSamplerProxy) element).setProtocol(RequestType.HTTP);
((MsHTTPSamplerProxy) element).setArguments(request1.getParameters());
element.setType("HTTPSamplerProxy");
}
if (request instanceof DubboRequest) {
String requestJson = JSON.toJSONString(request);
element = JSON.parseObject(requestJson, MsDubboSampler.class);
element.setType("DubboSampler");
}
if (request instanceof SqlRequest) {
element = new MsJDBCSampler();
SqlRequest request1 = (SqlRequest) request;
BeanUtils.copyBean(element, request1);
element.setType("JDBCSampler");
}
if (request instanceof TCPRequest) {
element = new MsTCPSampler();
TCPRequest request1 = (TCPRequest) request;
BeanUtils.copyBean(element, request1);
element.setType("TCPSampler");
}
element.setIndex(index + "");
element.setResourceId(UUID.randomUUID().toString());
LinkedList<MsTestElement> msTestElements = new LinkedList<>();
// 断言规则
if (request.getAssertions() != null && ((request.getAssertions().getDuration() != null && request.getAssertions().getDuration().getValue() > 0) ||
CollectionUtils.isNotEmpty(request.getAssertions().getJsonPath()) || CollectionUtils.isNotEmpty(request.getAssertions().getJsr223()) ||
CollectionUtils.isNotEmpty(request.getAssertions().getRegex()) || CollectionUtils.isNotEmpty(request.getAssertions().getXpath2()))) {
String assertions = JSON.toJSONString(request.getAssertions());
MsAssertions msAssertions = JSON.parseObject(assertions, MsAssertions.class);
msAssertions.setType("Assertions");
msAssertions.setIndex(index + "");
msAssertions.setResourceId(UUID.randomUUID().toString());
msTestElements.add(msAssertions);
}
// 提取规则
if (request.getExtract() != null && (CollectionUtils.isNotEmpty(request.getExtract().getJson()) ||
CollectionUtils.isNotEmpty(request.getExtract().getRegex()) || CollectionUtils.isNotEmpty(request.getExtract().getXpath()))) {
String extractJson = JSON.toJSONString(request.getExtract());
MsExtract extract = JSON.parseObject(extractJson, MsExtract.class);
extract.setType("Extract");
extract.setIndex(index + "");
extract.setResourceId(UUID.randomUUID().toString());
msTestElements.add(extract);
}
// 前置脚本
if (request.getJsr223PreProcessor() != null && StringUtils.isNotEmpty(request.getJsr223PreProcessor().getScript())) {
String preJson = JSON.toJSONString(request.getJsr223PreProcessor());
MsJSR223PreProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PreProcessor.class);
preProcessor.setType("JSR223PreProcessor");
preProcessor.setIndex(index + "");
preProcessor.setResourceId(UUID.randomUUID().toString());
msTestElements.add(preProcessor);
}
// 后置脚本
if (request.getJsr223PostProcessor() != null && StringUtils.isNotEmpty(request.getJsr223PostProcessor().getScript())) {
String preJson = JSON.toJSONString(request.getJsr223PostProcessor());
MsJSR223PostProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PostProcessor.class);
preProcessor.setType("JSR223PostProcessor");
preProcessor.setIndex(index + "");
preProcessor.setResourceId(UUID.randomUUID().toString());
msTestElements.add(preProcessor);
}
if (CollectionUtils.isNotEmpty(msTestElements)) {
element.setHashTree(msTestElements);
}
if (ifController != null) {
LinkedList<MsTestElement> elements = new LinkedList<>();
elements.add(element);
ifController.setHashTree(elements);
testElements.add(ifController);
} else {
testElements.add(element);
}
index++;
}
scenario.setHashTree(testElements);
return scenario;
}
private ApiScenarioWithBLOBs checkNameExist(Scenario oldScenario, String projectId, ApiScenarioMapper mapper) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdEqualTo(oldScenario.getId());
List<ApiScenarioWithBLOBs> list = mapper.selectByExampleWithBLOBs(example);
if (list.size() > 0) {
return list.get(0);
}
return null;
}
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
//文件的拷贝
private static void copyFile(String sourcePath, String newPath) {
File readfile = new File(sourcePath);
File newFile = new File(newPath);
BufferedWriter bufferedWriter = null;
Writer writer = null;
FileOutputStream fileOutputStream = null;
BufferedReader bufferedReader = null;
try {
fileOutputStream = new FileOutputStream(newFile, true);
writer = new OutputStreamWriter(fileOutputStream, "UTF-8");
bufferedWriter = new BufferedWriter(writer);
bufferedReader = new BufferedReader(new FileReader(readfile));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bufferedWriter != null) {
bufferedWriter.close();
}
if (bufferedReader != null) {
bufferedReader.close();
}
if (writer != null) {
writer.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void copyDir(String sourcePathDir, String newPathDir) {
File start = new File(sourcePathDir);
File end = new File(newPathDir);
String[] filePath = start.list();
if (!end.exists()) {
end.mkdir();
}
for (String temp : filePath) {
//添加满足情况的条件
if (new File(sourcePathDir + File.separator + temp).isFile()) {
//为文件则进行拷贝
copyFile(sourcePathDir + File.separator + temp, newPathDir + File.separator + temp);
}
}
}
private void createBodyFiles(String testId) {
String dir = BODY_FILE_DIR + "/" + testId;
File testDir = new File(dir);
if (testDir.exists()) {
testDir.mkdirs();
}
copyDir(dir, BODY_FILE_DIR);
}
private void createApiScenarioWithBLOBs(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade, Scenario oldScenario, String scenarioDefinition, ApiScenarioMapper mapper) {
if (StringUtils.isEmpty(oldScenario.getName())) {
oldScenario.setName("默认名称-" + DateUtils.getTimeStr(System.currentTimeMillis()));
}
ApiScenarioWithBLOBs scenario = checkNameExist(oldScenario, saveHistoricalDataUpgrade.getProjectId(), mapper);
if (scenario != null) {
scenario.setName(oldScenario.getName());
scenario.setProjectId(saveHistoricalDataUpgrade.getProjectId());
scenario.setTags(scenario.getTags());
scenario.setLevel("P0");
scenario.setModulePath(saveHistoricalDataUpgrade.getModulePath());
scenario.setApiScenarioModuleId(saveHistoricalDataUpgrade.getModuleId());
scenario.setPrincipal(Objects.requireNonNull(SessionUtils.getUser()).getId());
scenario.setStepTotal(oldScenario.getRequests().size());
scenario.setScenarioDefinition(scenarioDefinition);
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setStatus(ScenarioStatus.Underway.name());
scenario.setUserId(SessionUtils.getUserId());
scenario.setNum(getNextNum(saveHistoricalDataUpgrade.getProjectId()));
mapper.updateByPrimaryKeySelective(scenario);
} else {
scenario = new ApiScenarioWithBLOBs();
scenario.setId(oldScenario.getId());
scenario.setName(oldScenario.getName());
scenario.setProjectId(saveHistoricalDataUpgrade.getProjectId());
scenario.setTags(scenario.getTags());
scenario.setLevel("P0");
scenario.setModulePath(saveHistoricalDataUpgrade.getModulePath());
scenario.setApiScenarioModuleId(saveHistoricalDataUpgrade.getModuleId());
scenario.setPrincipal(Objects.requireNonNull(SessionUtils.getUser()).getId());
scenario.setStepTotal(oldScenario.getRequests().size());
scenario.setScenarioDefinition(scenarioDefinition);
scenario.setCreateTime(System.currentTimeMillis());
scenario.setUpdateTime(System.currentTimeMillis());
scenario.setStatus(ScenarioStatus.Underway.name());
scenario.setUserId(SessionUtils.getUserId());
scenario.setNum(getNextNum(saveHistoricalDataUpgrade.getProjectId()));
mapper.insert(scenario);
}
}
public String upgrade(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade) {
ApiTestExample example = new ApiTestExample();
example.createCriteria().andIdIn(saveHistoricalDataUpgrade.getTestIds());
List<ApiTest> blobs = apiTestMapper.selectByExampleWithBLOBs(example);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiScenarioMapper mapper = sqlSession.getMapper(ApiScenarioMapper.class);
for (ApiTest test : blobs) {
// 附件迁移
createBodyFiles(test.getId());
List<Scenario> scenarios = JSON.parseArray(test.getScenarioDefinition(), Scenario.class);
if (CollectionUtils.isNotEmpty(scenarios)) {
// 批量处理
for (Scenario scenario : scenarios) {
MsScenario scenario1 = createScenario(scenario);
String scenarioDefinition = JSON.toJSONString(scenario1);
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario, scenarioDefinition, mapper);
}
}
}
sqlSession.flushStatements();
return null;
}
}

View File

@ -1,8 +1,9 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
@Data
public class Project implements Serializable {
private String id;
@ -23,5 +24,7 @@ public class Project implements Serializable {
private String zentaoId;
private Boolean repeatable;
private static final long serialVersionUID = 1L;
}

View File

@ -713,6 +713,66 @@ public class ProjectExample {
addCriterion("zentao_id not between", value1, value2, "zentaoId");
return (Criteria) this;
}
public Criteria andRepeatableIsNull() {
addCriterion("`repeatable` is null");
return (Criteria) this;
}
public Criteria andRepeatableIsNotNull() {
addCriterion("`repeatable` is not null");
return (Criteria) this;
}
public Criteria andRepeatableEqualTo(Boolean value) {
addCriterion("`repeatable` =", value, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableNotEqualTo(Boolean value) {
addCriterion("`repeatable` <>", value, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableGreaterThan(Boolean value) {
addCriterion("`repeatable` >", value, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableGreaterThanOrEqualTo(Boolean value) {
addCriterion("`repeatable` >=", value, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableLessThan(Boolean value) {
addCriterion("`repeatable` <", value, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableLessThanOrEqualTo(Boolean value) {
addCriterion("`repeatable` <=", value, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableIn(List<Boolean> values) {
addCriterion("`repeatable` in", values, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableNotIn(List<Boolean> values) {
addCriterion("`repeatable` not in", values, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableBetween(Boolean value1, Boolean value2) {
addCriterion("`repeatable` between", value1, value2, "repeatable");
return (Criteria) this;
}
public Criteria andRepeatableNotBetween(Boolean value1, Boolean value2) {
addCriterion("`repeatable` not between", value1, value2, "repeatable");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -11,6 +11,7 @@
<result column="tapd_id" jdbcType="VARCHAR" property="tapdId" />
<result column="jira_key" jdbcType="VARCHAR" property="jiraKey" />
<result column="zentao_id" jdbcType="VARCHAR" property="zentaoId" />
<result column="repeatable" jdbcType="BIT" property="repeatable" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -72,7 +73,7 @@
</sql>
<sql id="Base_Column_List">
id, workspace_id, `name`, description, create_time, update_time, tapd_id, jira_key,
zentao_id
zentao_id, `repeatable`
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultMap="BaseResultMap">
select
@ -107,12 +108,12 @@
<insert id="insert" parameterType="io.metersphere.base.domain.Project">
insert into project (id, workspace_id, `name`,
description, create_time, update_time,
tapd_id, jira_key, zentao_id
)
tapd_id, jira_key, zentao_id,
`repeatable`)
values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{tapdId,jdbcType=VARCHAR}, #{jiraKey,jdbcType=VARCHAR}, #{zentaoId,jdbcType=VARCHAR}
)
#{tapdId,jdbcType=VARCHAR}, #{jiraKey,jdbcType=VARCHAR}, #{zentaoId,jdbcType=VARCHAR},
#{repeatable,jdbcType=BIT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Project">
insert into project
@ -144,6 +145,9 @@
<if test="zentaoId != null">
zentao_id,
</if>
<if test="repeatable != null">
`repeatable`,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -173,6 +177,9 @@
<if test="zentaoId != null">
#{zentaoId,jdbcType=VARCHAR},
</if>
<if test="repeatable != null">
#{repeatable,jdbcType=BIT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultType="java.lang.Long">
@ -211,6 +218,9 @@
<if test="record.zentaoId != null">
zentao_id = #{record.zentaoId,jdbcType=VARCHAR},
</if>
<if test="record.repeatable != null">
`repeatable` = #{record.repeatable,jdbcType=BIT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -226,7 +236,8 @@
update_time = #{record.updateTime,jdbcType=BIGINT},
tapd_id = #{record.tapdId,jdbcType=VARCHAR},
jira_key = #{record.jiraKey,jdbcType=VARCHAR},
zentao_id = #{record.zentaoId,jdbcType=VARCHAR}
zentao_id = #{record.zentaoId,jdbcType=VARCHAR},
`repeatable` = #{record.repeatable,jdbcType=BIT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -258,6 +269,9 @@
<if test="zentaoId != null">
zentao_id = #{zentaoId,jdbcType=VARCHAR},
</if>
<if test="repeatable != null">
`repeatable` = #{repeatable,jdbcType=BIT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -270,7 +284,8 @@
update_time = #{updateTime,jdbcType=BIGINT},
tapd_id = #{tapdId,jdbcType=VARCHAR},
jira_key = #{jiraKey,jdbcType=VARCHAR},
zentao_id = #{zentaoId,jdbcType=VARCHAR}
zentao_id = #{zentaoId,jdbcType=VARCHAR},
`repeatable` = #{repeatable,jdbcType=BIT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -4,7 +4,7 @@
<select id="getProjectWithWorkspace" resultType="io.metersphere.dto.ProjectDTO">
select p.id, p.workspace_id, p.name, p.description, p.update_time,
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id,p.repeatable
from project p
join workspace w on p.workspace_id = w.id
<where>
@ -42,11 +42,11 @@
</if>
</set>
where project.id in (select id from (select id
from project
where workspace_id in
(select workspace.id
from workspace
where organization_id = #{orgId})) as a)
from project
where workspace_id in
(select workspace.id
from workspace
where organization_id = #{orgId})) as a)
</update>
</mapper>

View File

@ -17,4 +17,6 @@ public class ProjectDTO {
private String tapdId;
private String jiraKey;
private String zentaoId;
private boolean repeatable;
}

View File

@ -13,7 +13,7 @@ import javax.validation.constraints.Pattern;
public class TestCaseExcelDataCn extends TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@Length(max = 255)
@ExcelProperty("用例名称")
private String name;

View File

@ -13,7 +13,7 @@ import javax.validation.constraints.Pattern;
public class TestCaseExcelDataTw extends TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@Length(max = 255)
@ExcelProperty("用例名稱")
private String name;

View File

@ -14,7 +14,7 @@ import javax.validation.constraints.Pattern;
public class TestCaseExcelDataUs extends TestCaseExcelData {
@NotBlank(message = "{cannot_be_null}")
@Length(max = 50)
@Length(max = 255)
@ExcelProperty("Name")
private String name;

@ -1 +1 @@
Subproject commit 068127ce59ea8b016434ed52a9de4a7a4b13bdb4
Subproject commit 9f4a9bbf46fc1333dbcccea21f83e27e3ec10b1f

View File

@ -1,5 +1,5 @@
alter table test_plan add project_id varchar(50) null comment '测试计划所属项目';
ALTER TABLE api_test_case MODIFY COLUMN name varchar(255) NOT NULL COMMENT 'Test name';
ALTER TABLE api_test_case MODIFY COLUMN name varchar(255) NOT NULL COMMENT 'Test case name';
DROP PROCEDURE IF EXISTS test_cursor;
DELIMITER //

View File

@ -0,0 +1 @@
ALTER TABLE project ADD repeatable tinyint(1) DEFAULT null;

View File

@ -0,0 +1 @@
ALTER TABLE test_case MODIFY COLUMN name varchar(255) NOT NULL COMMENT 'Test case name';

View File

@ -64,7 +64,7 @@
<!--要生成的数据库表 -->
<table tableName="api_scenario"/>
<table tableName="project"/>
<!--<table tableName="test_plan_api_scenario"/>-->
<!--<table tableName="test_plan"/>-->
<!--<table tableName="api_scenario_report"/>-->

View File

@ -87,7 +87,7 @@ export default {
isHide: true,
activeName: 'default',
currentModule: null,
moduleOptions: {},
moduleOptions: [],
tabs: [],
trashEnable: false,
selectNodeIds: [],
@ -114,7 +114,12 @@ export default {
this.$post(url, {id:scenarioId,projectId:projectId}, response => {
let data = response.data;
if(data!=null){
//
if(JSON.stringify(this.moduleOptions) === '{}' ){
this.$refs.nodeTree.list();
}
let row = data.listObject[0];
row.tags = JSON.parse(row.tags);
this.editScenario(row);
}
});

View File

@ -58,8 +58,8 @@
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
<ms-api-request-form :referenced="true" :headers="request.headers " :request="request" v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"/>
<ms-tcp-basis-parameters :request="request" v-if="request.protocol==='TCP'|| request.type==='TCPSampler'"/>
<ms-sql-basis-parameters :request="request" v-if="request.protocol==='SQL'|| request.type==='JDBCSampler'"/>
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'"/>
<ms-sql-basis-parameters :request="request" v-if="request.protocol==='SQL'|| request.type==='JDBCSampler'" :showScript="false"/>
<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>
<ms-request-result-tail :currentProtocol="request.protocol" :response="request.requestResult" ref="runResult"/>

View File

@ -55,7 +55,7 @@
import MsApiAssertionRegex from "./ApiAssertionRegex";
import MsApiAssertionDuration from "./ApiAssertionDuration";
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
import MsApiAssertionJsr223 from "@/business/components/api/test/components/assertion/ApiAssertionJsr223";
import MsApiAssertionJsr223 from "./ApiAssertionJsr223";
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
export default {

View File

@ -63,18 +63,19 @@
</el-tabs>
</el-form>
</div>
<div v-for="row in request.hashTree" :key="row.id" v-loading="isReloadData" style="margin-left: 20px;width: 100%">
<!-- 前置脚本 -->
<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"
:jsr223-processor="row"/>
<!--后置脚本-->
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/>
<!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
<div v-if="showScript">
<div v-for="row in request.hashTree" :key="row.id" v-loading="isReloadData" style="margin-left: 20px;width: 100%">
<!-- 前置脚本 -->
<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"
:jsr223-processor="row"/>
<!--后置脚本-->
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/>
<!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div>
</div>
</el-col>
<el-col :span="3" class="ms-left-cell" v-if="showScript">

View File

@ -41,19 +41,19 @@
</el-form>
</div>
<div v-for="row in request.hashTree" :key="row.id" v-loading="isReloadData" style="margin-left: 20px;width: 100%">
<!-- 前置脚本 -->
<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"
:jsr223-processor="row"/>
<!--后置脚本-->
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/>
<!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
<div v-if="showScript">
<div v-for="row in request.hashTree" :key="row.id" v-loading="isReloadData" style="margin-left: 20px;width: 100%">
<!-- 前置脚本 -->
<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"
:jsr223-processor="row"/>
<!--后置脚本-->
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/>
<!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div>
</div>
</el-col>

View File

@ -7,7 +7,7 @@
:title="$t('commons.test')"
@create="create" :createTip="$t('load_test.create')" :runTip="$t('load_test.run')"
:show-run="true"
@runTest="runTest"/>
@runTest="runTest" @historicalDataUpgrade="historicalDataUpgrade"/>
</template>
@ -54,7 +54,8 @@
</el-card>
<api-copy-dialog ref="apiCopy" @refresh="search"/>
<ms-upgrade ref="upgrade" :select-ids="selectIds"
:select-project-names="selectProjectNames" :select-project-id="selectProjectId"/>
</ms-main-container>
</ms-container>
</template>
@ -72,13 +73,14 @@
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ApiCopyDialog from "./components/ApiCopyDialog";
import MsUpgrade from "./Upgrade";
export default {
components: {
ApiCopyDialog,
OneClickOperation,
MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator, MsUpgrade
},
data() {
return {
@ -204,6 +206,13 @@
_filter(filters, this.condition);
this.init();
},
historicalDataUpgrade() {
if (this.selectIds.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
} else {
this.$refs.upgrade.openOneClickOperation();
}
}
},
created() {
this.init();

View File

@ -0,0 +1,109 @@
<template>
<el-dialog
title="选则模块"
:visible.sync="oneClickOperationVisible"
width="600px"
left
:destroy-on-close="true"
show-close
@closed="handleClose" v-loading="loading">
<el-form :model="ruleForm" label-position="right" label-width="80px" size="small" :rules="rule">
<el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId">
<el-select size="small" style="width: 80%" v-model="apiScenarioModuleId">
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="oneClickOperationVisible = false"
@confirm="confirm"/>
</span>
</el-dialog>
</template>
<script>
import MsDialogFooter from '../../common/components/MsDialogFooter'
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
import {buildNodePath} from "../definition/model/NodeTree";
export default {
name: "MsUpgrade",
components: {
MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig, MsDialogFooter
},
data() {
return {
oneClickOperationVisible: false,
apiScenarioModuleId: "",
moduleOptions: [],
ruleForm: {},
loading: false,
rule: {
apiScenarioModuleId: [
{required: true, message: this.$t('test_track.module.module'), trigger: 'blur'},
],
}
};
},
created() {
this.initModule();
},
props: {
selectIds: {
type: Set
},
selectProjectNames: {
type: Set
},
selectProjectId: {
type: Set
}
},
methods: {
openOneClickOperation() {
this.oneClickOperationVisible = true;
},
getPath(id) {
let path = this.moduleOptions.filter(function (item) {
return item.id === id ? item.path : "";
});
return path[0].path;
},
confirm() {
this.loading = true;
let arr = Array.from(this.selectIds);
let obj = {testIds: arr, projectId: getCurrentProjectID(), modulePath: this.getPath(this.apiScenarioModuleId), moduleId: this.apiScenarioModuleId};
this.$post("/api/historicalDataUpgrade", obj, response => {
this.loading = false;
this.$success(this.$t('organization.integration.successful_operation'));
this.oneClickOperationVisible = false;
})
},
initModule() {
let url = "/api/automation/module/list/" + getCurrentProjectID();
this.$get(url, response => {
if (response.data != undefined && response.data != null) {
this.data = response.data;
let modules = [];
this.data.forEach(node => {
buildNodePath(node, {path: ''}, modules);
});
this.moduleOptions = modules;
}
});
},
handleClose() {
this.ruleForm = {}
},
}
}
</script>
<style scoped>
</style>

View File

@ -13,6 +13,8 @@
<ms-table-button :is-tester-permission="isTesterPermission" v-if="showRun" icon="el-icon-video-play"
type="primary"
:content="runTip" @click="runTest"/>
<ms-table-button :is-tester-permission="isTesterPermission" v-if="showRun" icon="el-icon-circle-plus-outline"
content="转场景测试" @click="historicalDataUpgrade"/>
<slot name="button"></slot>
</span>
@ -84,6 +86,9 @@
},
runTest() {
this.$emit('runTest')
},
historicalDataUpgrade() {
this.$emit('historicalDataUpgrade');
}
},
computed: {

View File

@ -3,7 +3,12 @@
<el-card class="table-card">
<template v-slot:header>
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="search" @create="create"
:create-tip="btnTips" :title="$t('commons.project')"/>
:create-tip="btnTips" :title="$t('commons.project')">
<template v-slot:button>
<ms-table-button :is-tester-permission="true" icon="el-icon-box"
:content="$t('api_test.jar_config.title')" @click="openJarConfig"/>
</template>
</ms-table-header>
</template>
<el-table border class="adjust-table" :data="items" style="width: 100%" @sort-change="sort">
<el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip/>
@ -48,7 +53,7 @@
</el-card>
<el-dialog :close-on-click-modal="false" :title="title" :visible.sync="createVisible" destroy-on-close @close="handleClose">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="100px" size="small">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="140px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
@ -64,6 +69,9 @@
<el-form-item :label="$t('project.zentao_id')" v-if="zentao">
<el-input v-model="form.zentaoId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label="$t('project.repeatable')" prop="repeatable">
<el-switch v-model="form.repeatable"></el-switch>
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="dialog-footer">
@ -78,196 +86,203 @@
<api-environment-config ref="environmentConfig"/>
<ms-jar-config ref="jarConfig"/>
</div>
</template>
<script>
import MsCreateBox from "../CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {_sort, getCurrentProjectID, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsDeleteConfirm from "../../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {PROJECT_ID} from "@/common/js/constants";
export default {
name: "MsProject",
components: {
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
},
data() {
return {
createVisible: false,
result: {},
btnTips: this.$t('project.create'),
title: this.$t('project.create'),
condition: {},
items: [],
tapd: false,
jira: false,
zentao: false,
form: {},
currentPage: 1,
pageSize: 10,
total: 0,
rules: {
name: [
{required: true, message: this.$t('project.input_name'), trigger: 'blur'},
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'}
],
description: [
{max: 250, message: this.$t('commons.input_limit', [0, 250]), trigger: 'blur'}
],
},
}
},
props: {
baseUrl: {
type: String
}
},
mounted() {
if (this.$route.path.split('/')[2] === 'project' &&
this.$route.path.split('/')[3] === 'create') {
this.create();
this.$router.replace('/setting/project/all');
}
this.list();
},
activated() {
this.list();
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
destroyed() {
this.createVisible = false;
},
methods: {
create() {
let workspaceId = this.currentUser.lastWorkspaceId;
if (!workspaceId) {
this.$warning(this.$t('project.please_choose_workspace'));
return false;
import MsCreateBox from "../CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {_sort, getCurrentProjectID, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsDeleteConfirm from "../../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {PROJECT_ID} from "@/common/js/constants";
import MsJarConfig from "../../api/test/components/jar/JarConfig";
import MsTableButton from "../../common/components/MsTableButton";
export default {
name: "MsProject",
components: {
MsTableButton,
MsJarConfig,
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
},
data() {
return {
createVisible: false,
result: {},
btnTips: this.$t('project.create'),
title: this.$t('project.create'),
condition: {},
items: [],
tapd: false,
jira: false,
zentao: false,
form: {},
currentPage: 1,
pageSize: 10,
total: 0,
rules: {
name: [
{required: true, message: this.$t('project.input_name'), trigger: 'blur'},
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'}
],
description: [
{max: 250, message: this.$t('commons.input_limit', [0, 250]), trigger: 'blur'}
],
},
}
this.title = this.$t('project.create');
// listenGoBack(this.handleClose);
this.createVisible = true;
this.form = {};
},
edit(row) {
this.title = this.$t('project.edit');
this.createVisible = true;
listenGoBack(this.handleClose);
this.form = Object.assign({}, row);
this.$get("/service/integration/all/" + getCurrentUser().lastOrganizationId, response => {
let data = response.data;
let platforms = data.map(d => d.platform);
if (platforms.indexOf("Tapd") !== -1) {
this.tapd = true;
}
if (platforms.indexOf("Jira") !== -1) {
this.jira = true;
}
if (platforms.indexOf("Zentao") !== -1) {
this.zentao = true;
}
});
props: {
baseUrl: {
type: String
}
},
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let saveType = "add";
if (this.form.id) {
saveType = "update"
}
this.result = this.$post("/project/" + saveType, this.form, () => {
this.createVisible = false;
this.list();
Message.success(this.$t('commons.save_success'));
});
} else {
mounted() {
if (this.$route.path.split('/')[2] === 'project' &&
this.$route.path.split('/')[3] === 'create') {
this.create();
this.$router.replace('/setting/project/all');
}
this.list();
},
activated() {
this.list();
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
destroyed() {
this.createVisible = false;
},
methods: {
create() {
let workspaceId = this.currentUser.lastWorkspaceId;
if (!workspaceId) {
this.$warning(this.$t('project.please_choose_workspace'));
return false;
}
});
},
handleDelete(project) {
this.$refs.deleteConfirm.open(project);
},
_handleDelete(project) {
this.$confirm(this.$t('project.delete_tip'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.$get('/project/delete/' + project.id, () => {
if (project.id === getCurrentProjectID()) {
localStorage.removeItem(PROJECT_ID);
this.$post("/user/update/current", {id: getCurrentUser().id, lastProjectId: ''});
this.title = this.$t('project.create');
// listenGoBack(this.handleClose);
this.createVisible = true;
this.form = {};
},
edit(row) {
this.title = this.$t('project.edit');
this.createVisible = true;
listenGoBack(this.handleClose);
this.form = Object.assign({}, row);
this.$get("/service/integration/all/" + getCurrentUser().lastOrganizationId, response => {
let data = response.data;
let platforms = data.map(d => d.platform);
if (platforms.indexOf("Tapd") !== -1) {
this.tapd = true;
}
if (platforms.indexOf("Jira") !== -1) {
this.jira = true;
}
if (platforms.indexOf("Zentao") !== -1) {
this.zentao = true;
}
Message.success(this.$t('commons.delete_success'));
this.list();
});
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('commons.delete_cancelled')
},
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let saveType = "add";
if (this.form.id) {
saveType = "update"
}
this.result = this.$post("/project/" + saveType, this.form, () => {
this.createVisible = false;
this.list();
Message.success(this.$t('commons.save_success'));
});
} else {
return false;
}
});
});
},
openJarConfig() {
this.$refs.jarConfig.open();
},
handleDelete(project) {
this.$refs.deleteConfirm.open(project);
},
_handleDelete(project) {
this.$confirm(this.$t('project.delete_tip'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.$get('/project/delete/' + project.id, () => {
if (project.id === getCurrentProjectID()) {
localStorage.removeItem(PROJECT_ID);
this.$post("/user/update/current", {id: getCurrentUser().id, lastProjectId: ''});
}
Message.success(this.$t('commons.delete_success'));
this.list();
});
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('commons.delete_cancelled')
});
});
},
handleClose() {
removeGoBackListener(this.handleClose);
this.createVisible = false;
this.tapd = false;
this.jira = false;
this.zentao = false;
},
search() {
this.list();
},
list() {
let url = "/project/list/" + this.currentPage + '/' + this.pageSize;
this.result = this.$post(url, this.condition, (response) => {
let data = response.data;
this.items = data.listObject;
this.total = data.itemCount;
})
},
sort(column) {
_sort(column, this.condition);
this.list();
},
openEnvironmentConfig(project) {
this.$refs.environmentConfig.open(project.id);
},
handleEvent(event) {
if (event.keyCode === 13) {
this.submit('form')
}
},
},
handleClose() {
removeGoBackListener(this.handleClose);
this.createVisible = false;
this.tapd = false;
this.jira = false;
this.zentao = false;
created() {
document.addEventListener('keydown', this.handleEvent)
},
search() {
this.list();
},
list() {
let url = "/project/list/" + this.currentPage + '/' + this.pageSize;
this.result = this.$post(url, this.condition, (response) => {
let data = response.data;
this.items = data.listObject;
this.total = data.itemCount;
})
},
sort(column) {
_sort(column, this.condition);
this.list();
},
openEnvironmentConfig(project) {
this.$refs.environmentConfig.open(project.id);
},
handleEvent(event) {
if (event.keyCode === 13) {
this.submit('form')
}
},
},
created() {
document.addEventListener('keydown', this.handleEvent)
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleEvent);
beforeDestroy() {
document.removeEventListener('keydown', this.handleEvent);
}
}
}
</script>
<style scoped>
@ -275,5 +290,4 @@ export default {
margin: 0 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
}
</style>

@ -1 +1 @@
Subproject commit 7d43154a7c19732407a8e9ace8a7d1ea13c91f36
Subproject commit 010ad7a5f072a5e9d368c756a2473bbd20781433

View File

@ -308,7 +308,8 @@ export default {
zentao_id: 'Zentao Project ID',
manager: 'Manager',
no_data: 'No Data',
select: 'Select'
select: 'Select',
repeatable: 'Interface definition URL repeatable'
},
member: {
create: 'Create',

View File

@ -305,7 +305,8 @@ export default {
zentao_id: 'Zentao项目ID',
manager: '项目管理',
no_data: '无数据',
select: '选择项目'
select: '选择项目',
repeatable: '接口定义URL可重复'
},
member: {
create: '添加成员',

View File

@ -305,7 +305,8 @@ export default {
zentao_id: 'Zentao項目ID',
manager: '項目管理',
no_data: '無數據',
select: '選擇項目'
select: '選擇項目',
repeatable: '接口定义URL可重复'
},
member: {
create: '添加成員',