Merge branch 'main' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
db78596dba
|
@ -122,18 +122,16 @@ public class MsScenario extends MsTestElement {
|
|||
// 环境变量
|
||||
Arguments arguments = ElementUtil.getConfigArguments(this.isEnvironmentEnable() ?
|
||||
newConfig : config, this.getName(), this.getProjectId(), this.getVariables());
|
||||
if (arguments != null && ((this.variableEnable == null || this.variableEnable)
|
||||
|| (this.mixEnable == null || this.mixEnable))) {
|
||||
if (arguments != null && !arguments.getArguments().isEmpty()) {
|
||||
Arguments valueSupposeMock = ParameterConfig.valueSupposeMock(arguments);
|
||||
// 这里加入自定义变量解决ForEach循环控制器取值问题,循环控制器无法从vars中取值
|
||||
if ((this.variableEnable == null || this.variableEnable)
|
||||
|| (this.mixEnable == null || this.mixEnable)) {
|
||||
if (BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) {
|
||||
scenarioTree.add(ElementUtil.argumentsToUserParameters(valueSupposeMock));
|
||||
} else {
|
||||
} else if (config != null && StringUtils.equals(this.getId(), config.getScenarioId())) {
|
||||
scenarioTree.add(valueSupposeMock);
|
||||
}
|
||||
}
|
||||
if (this.variableEnable == null || this.variableEnable) {
|
||||
if (BooleanUtils.isTrue(this.variableEnable) || BooleanUtils.isTrue(this.mixEnable)) {
|
||||
ElementUtil.addCsvDataSet(scenarioTree, variables, this.isEnvironmentEnable() ? newConfig : config, "shareMode.group");
|
||||
ElementUtil.addCounter(scenarioTree, variables);
|
||||
ElementUtil.addRandom(scenarioTree, variables);
|
||||
|
|
|
@ -83,23 +83,21 @@ public class ApiCaseExecuteService {
|
|||
if (StringUtils.equals(EnvironmentType.GROUP.toString(), request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
|
||||
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
|
||||
}
|
||||
LoggerUtil.debug("开始查询测试计划用例");
|
||||
LoggerUtil.info("开始查询测试计划用例", request.getPlanIds().size());
|
||||
|
||||
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
|
||||
example.createCriteria().andIdIn(request.getPlanIds());
|
||||
example.setOrderByClause("`order` DESC");
|
||||
List<TestPlanApiCase> planApiCases = testPlanApiCaseMapper.selectByExample(example);
|
||||
List<TestPlanApiCase> planApiCases = this.selectByPlanApiCaseIds(request.getPlanIds());
|
||||
if (CollectionUtils.isEmpty(planApiCases)) {
|
||||
return responseDTOS;
|
||||
}
|
||||
if (StringUtils.isEmpty(request.getTriggerMode())) {
|
||||
request.setTriggerMode(ApiRunMode.API_PLAN.name());
|
||||
}
|
||||
LoggerUtil.debug("查询到测试计划用例 " + planApiCases.size());
|
||||
LoggerUtil.info("查询到测试计划用例 " + planApiCases.size());
|
||||
|
||||
Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue = request.isRerun() ? request.getExecuteQueue() : new LinkedHashMap<>();
|
||||
String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? ApiReportStatus.PENDING.name()
|
||||
: ApiReportStatus.RUNNING.name();
|
||||
|
||||
String status = StringUtils.equals(request.getConfig().getMode(), RunModeConstants.SERIAL.toString())
|
||||
? ApiReportStatus.PENDING.name() : ApiReportStatus.RUNNING.name();
|
||||
|
||||
// 查出用例
|
||||
List<String> apiCaseIds = planApiCases.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
|
||||
|
@ -117,23 +115,24 @@ public class ApiCaseExecuteService {
|
|||
//处理环境配置为空时的情况
|
||||
RunModeConfigDTO runModeConfigDTO = new RunModeConfigDTO();
|
||||
BeanUtils.copyBean(runModeConfigDTO, request.getConfig());
|
||||
if (MapUtils.isEmpty(runModeConfigDTO.getEnvMap())) {
|
||||
ApiTestCase testCase = caseMap.get(testPlanApiCase.getApiCaseId());
|
||||
if (testCase != null) {
|
||||
runModeConfigDTO.setEnvMap(new HashMap<>() {{
|
||||
this.put(testCase.getProjectId(), testPlanApiCase.getEnvironmentId());
|
||||
}});
|
||||
}
|
||||
ApiTestCase testCase = caseMap.get(testPlanApiCase.getApiCaseId());
|
||||
if (testCase == null) {
|
||||
continue;
|
||||
}
|
||||
ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.addResult(request, runModeConfigDTO, testPlanApiCase, status, caseMap, resourcePoolId);
|
||||
if (MapUtils.isEmpty(runModeConfigDTO.getEnvMap())) {
|
||||
runModeConfigDTO.setEnvMap(new HashMap<>() {{
|
||||
this.put(testCase.getProjectId(), testPlanApiCase.getEnvironmentId());
|
||||
}});
|
||||
}
|
||||
ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.addResult(request, runModeConfigDTO, testPlanApiCase, status, testCase, resourcePoolId);
|
||||
executeQueue.put(testPlanApiCase.getId(), report);
|
||||
responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode()));
|
||||
LoggerUtil.debug("预生成测试用例结果报告:" + report.getName() + ", ID " + report.getId());
|
||||
LoggerUtil.info("预生成测试用例结果报告:" + report.getName(), report.getId());
|
||||
}
|
||||
apiCaseResultService.batchSave(executeQueue);
|
||||
}
|
||||
|
||||
LoggerUtil.debug("开始生成测试计划队列");
|
||||
LoggerUtil.info("开始生成测试计划队列");
|
||||
String reportType = request.getConfig().getReportType();
|
||||
String poolId = request.getConfig().getResourcePoolId();
|
||||
String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name();
|
||||
|
@ -157,6 +156,16 @@ public class ApiCaseExecuteService {
|
|||
return responseDTOS;
|
||||
}
|
||||
|
||||
public List<TestPlanApiCase> selectByPlanApiCaseIds(List<String> planApiCaseIds) {
|
||||
if (CollectionUtils.isEmpty(planApiCaseIds)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
|
||||
example.createCriteria().andIdIn(planApiCaseIds);
|
||||
example.setOrderByClause("`order` DESC");
|
||||
return testPlanApiCaseMapper.selectByExample(example);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> checkEnv(List<ApiTestCaseWithBLOBs> caseList) {
|
||||
Map<String, List<String>> projectEnvMap = new HashMap<>();
|
||||
if (CollectionUtils.isNotEmpty(caseList)) {
|
||||
|
|
|
@ -297,8 +297,8 @@ public class ApiExecuteService {
|
|||
|
||||
// 线程组
|
||||
MsThreadGroup group = new MsThreadGroup();
|
||||
group.setLabel(testCaseWithBLOBs.getName());
|
||||
group.setName(testCaseWithBLOBs.getId());
|
||||
group.setLabel(request.getReportId());
|
||||
group.setName(request.getReportId());
|
||||
group.setOnSampleError(true);
|
||||
LinkedList<MsTestElement> hashTrees = new LinkedList<>();
|
||||
hashTrees.add(element);
|
||||
|
|
|
@ -30,9 +30,9 @@ public class MsKafkaListener {
|
|||
@Resource
|
||||
private TestResultService testResultService;
|
||||
// 线程池维护线程的最少数量
|
||||
private final static int CORE_POOL_SIZE = 20;
|
||||
private final static int CORE_POOL_SIZE = 5;
|
||||
// 线程池维护线程的最大数量
|
||||
private final static int MAX_POOL_SIZE = 20;
|
||||
private final static int MAX_POOL_SIZE = 5;
|
||||
// 线程池维护线程所允许的空闲时间
|
||||
private final static int KEEP_ALIVE_TIME = 1;
|
||||
// 线程池所使用的缓冲队列大小
|
||||
|
|
|
@ -46,10 +46,12 @@ import org.apache.commons.collections.CollectionUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.assertions.*;
|
||||
import org.apache.jmeter.config.ConfigTestElement;
|
||||
import org.apache.jmeter.extractor.BeanShellPostProcessor;
|
||||
import org.apache.jmeter.extractor.JSR223PostProcessor;
|
||||
import org.apache.jmeter.extractor.RegexExtractor;
|
||||
import org.apache.jmeter.extractor.XPath2Extractor;
|
||||
import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor;
|
||||
import org.apache.jmeter.modifiers.BeanShellPreProcessor;
|
||||
import org.apache.jmeter.modifiers.JSR223PreProcessor;
|
||||
import org.apache.jmeter.protocol.http.control.HeaderManager;
|
||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||
|
@ -110,10 +112,6 @@ public class JmeterDefinitionParser extends ApiImportAbstractParser<ApiDefinitio
|
|||
for (MsTestElement element : results) {
|
||||
ApiDefinitionWithBLOBs apiDefinitionWithBLOBs = buildApiDefinition(element);
|
||||
if (apiDefinitionWithBLOBs != null) {
|
||||
if (element.getHashTree() != null) {
|
||||
element.getHashTree().clear();
|
||||
}
|
||||
|
||||
element.setEnable(true);
|
||||
apiDefinitionWithBLOBs.setRequest(JSON.toJSONString(element));
|
||||
HttpResponse defaultHttpResponse = getDefaultHttpResponse();
|
||||
|
@ -394,6 +392,10 @@ public class JmeterDefinitionParser extends ApiImportAbstractParser<ApiDefinitio
|
|||
BeanUtils.copyBean(elementNode, jsr223Sampler);
|
||||
((MsJSR223PreProcessor) elementNode).setScript(jsr223Sampler.getPropertyAsString("script"));
|
||||
((MsJSR223PreProcessor) elementNode).setScriptLanguage(jsr223Sampler.getPropertyAsString("scriptLanguage"));
|
||||
} else if (key instanceof BeanShellPostProcessor) {
|
||||
elementNode = MsJmeterParser.getMsTestElement((BeanShellPostProcessor) key);
|
||||
} else if (key instanceof BeanShellPreProcessor) {
|
||||
elementNode = MsJmeterParser.getMsTestElement((BeanShellPreProcessor) key);
|
||||
}
|
||||
// 断言规则
|
||||
else if (key instanceof ResponseAssertion || key instanceof JSONPathAssertion || key instanceof XPath2Assertion || key instanceof JSR223Assertion || key instanceof DurationAssertion) {
|
||||
|
|
|
@ -345,7 +345,7 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
JsonSchemaItem jsonSchemaItem = parseSchema(schema, refSet);
|
||||
if (jsonSchemaItem == null) {
|
||||
jsonSchemaItem = new JsonSchemaItem();
|
||||
if (StringUtils.isNotBlank(schema.getType())) {
|
||||
if (schema != null && StringUtils.isNotBlank(schema.getType())) {
|
||||
jsonSchemaItem.setType(schema.getType());
|
||||
}
|
||||
}
|
||||
|
@ -575,7 +575,8 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
}
|
||||
|
||||
private Schema getSchema(Schema schema) {
|
||||
if (StringUtils.isBlank(schema.get$ref()) && StringUtils.isNotBlank(schema.getType()) && StringUtils.equals(schema.getType(),"string")) {
|
||||
|
||||
if (schema != null && StringUtils.isBlank(schema.get$ref()) && StringUtils.isNotBlank(schema.getType()) && StringUtils.equals(schema.getType(), "string")) {
|
||||
ObjectSchema objectSchema = new ObjectSchema();
|
||||
objectSchema.setExample(schema.getExample());
|
||||
schema = objectSchema;
|
||||
|
@ -702,7 +703,6 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
// 设置响应体
|
||||
JSONObject responseObject = JSONUtil.parseObject(apiDefinition.getResponse());
|
||||
JSONObject jsonObject = buildResponseBody(responseObject);
|
||||
|
||||
swaggerApiInfo.setResponses(JSONUtil.parseObjectNode(jsonObject.toString()));
|
||||
// 设置请求参数列表
|
||||
List<JSONObject> paramsList = buildParameters(requestObject);
|
||||
|
@ -835,12 +835,12 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
parsedParam.put(PropertyConstant.ITEMS, item);
|
||||
} else if (StringUtils.equals(type, PropertyConstant.OBJECT)) {
|
||||
parsedParam.put(PropertyConstant.TYPE, PropertyConstant.OBJECT);
|
||||
JSONObject properties = requestBody.optJSONObject(PropertyConstant.REQUIRED);
|
||||
JSONObject properties = requestBody.optJSONObject(PropertyConstant.PROPERTIES);
|
||||
JSONObject jsonObject = buildFormDataSchema(properties);
|
||||
if (StringUtils.isNotBlank(requestBody.optString("description"))) {
|
||||
parsedParam.put("description", requestBody.optString("description"));
|
||||
}
|
||||
parsedParam.put(PropertyConstant.REQUIRED, jsonObject.optJSONObject(PropertyConstant.REQUIRED));
|
||||
parsedParam.put(PropertyConstant.PROPERTIES, jsonObject.optJSONObject(PropertyConstant.PROPERTIES));
|
||||
} else if (StringUtils.equals(type, PropertyConstant.INTEGER)) {
|
||||
parsedParam.put(PropertyConstant.TYPE, PropertyConstant.INTEGER);
|
||||
parsedParam.put("format", "int64");
|
||||
|
@ -944,7 +944,7 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
}
|
||||
properties.put(key, property);
|
||||
}
|
||||
schema.put(PropertyConstant.REQUIRED, properties);
|
||||
schema.put(PropertyConstant.PROPERTIES, properties);
|
||||
return schema;
|
||||
}
|
||||
|
||||
|
@ -997,7 +997,12 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
statusCodeInfo.put("description", StringUtils.EMPTY);
|
||||
// 返回code
|
||||
JSONArray statusCode = response.optJSONArray("statusCode");
|
||||
responseBody.put(statusCode.toString(), statusCodeInfo);
|
||||
for (int i = 0; i < statusCode.length(); i++) {
|
||||
JSONObject jsonObject = statusCode.getJSONObject(i);
|
||||
jsonObject.get("name");
|
||||
statusCodeInfo.put("description", jsonObject.get("value"));
|
||||
responseBody.put(jsonObject.get("name").toString(), statusCodeInfo);
|
||||
}
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,14 +42,10 @@ import io.metersphere.commons.constants.LoopConstants;
|
|||
import io.metersphere.commons.constants.PropertyConstant;
|
||||
import io.metersphere.commons.constants.RequestTypeConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.environment.service.BaseEnvironmentService;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import io.metersphere.request.BodyFile;
|
||||
import io.metersphere.commons.utils.JSONUtil;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -59,10 +55,12 @@ import org.apache.jmeter.control.ForeachController;
|
|||
import org.apache.jmeter.control.LoopController;
|
||||
import org.apache.jmeter.control.TransactionController;
|
||||
import org.apache.jmeter.control.WhileController;
|
||||
import org.apache.jmeter.extractor.BeanShellPostProcessor;
|
||||
import org.apache.jmeter.extractor.JSR223PostProcessor;
|
||||
import org.apache.jmeter.extractor.RegexExtractor;
|
||||
import org.apache.jmeter.extractor.XPath2Extractor;
|
||||
import org.apache.jmeter.extractor.json.jsonpath.JSONPostProcessor;
|
||||
import org.apache.jmeter.modifiers.BeanShellPreProcessor;
|
||||
import org.apache.jmeter.modifiers.JSR223PreProcessor;
|
||||
import org.apache.jmeter.protocol.http.control.HeaderManager;
|
||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||
|
@ -746,6 +744,10 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
BeanUtils.copyBean(elementNode, jsr223Sampler);
|
||||
((MsJSR223PostProcessor) elementNode).setScript(jsr223Sampler.getPropertyAsString("script"));
|
||||
((MsJSR223PostProcessor) elementNode).setScriptLanguage(jsr223Sampler.getPropertyAsString("scriptLanguage"));
|
||||
} else if (key instanceof BeanShellPostProcessor) {
|
||||
elementNode = getMsTestElement((BeanShellPostProcessor) key);
|
||||
} else if (key instanceof BeanShellPreProcessor) {
|
||||
elementNode = getMsTestElement((BeanShellPreProcessor) key);
|
||||
}
|
||||
// 前置脚本
|
||||
else if (key instanceof JSR223PreProcessor) {
|
||||
|
@ -853,4 +855,26 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static MsTestElement getMsTestElement(BeanShellPreProcessor key) {
|
||||
MsTestElement elementNode;
|
||||
BeanShellPreProcessor beanShellPreProcessor = key;
|
||||
elementNode = new MsJSR223PreProcessor();
|
||||
BeanUtils.copyBean(elementNode, beanShellPreProcessor);
|
||||
((MsJSR223PreProcessor) elementNode).setJsrEnable(false);
|
||||
((MsJSR223PreProcessor) elementNode).setScript(beanShellPreProcessor.getPropertyAsString("script"));
|
||||
((MsJSR223PreProcessor) elementNode).setScriptLanguage(beanShellPreProcessor.getPropertyAsString("scriptLanguage"));
|
||||
return elementNode;
|
||||
}
|
||||
|
||||
public static MsTestElement getMsTestElement(BeanShellPostProcessor key) {
|
||||
MsTestElement elementNode;
|
||||
BeanShellPostProcessor beanShellPostProcessor = key;
|
||||
elementNode = new MsJSR223PostProcessor();
|
||||
((MsJSR223PostProcessor) elementNode).setJsrEnable(false);
|
||||
BeanUtils.copyBean(elementNode, beanShellPostProcessor);
|
||||
((MsJSR223PostProcessor) elementNode).setScript(beanShellPostProcessor.getPropertyAsString("script"));
|
||||
((MsJSR223PostProcessor) elementNode).setScriptLanguage(beanShellPostProcessor.getPropertyAsString("scriptLanguage"));
|
||||
return elementNode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,8 @@ public interface ExtApiDefinitionMapper {
|
|||
|
||||
List<ApiDefinition> selectApiBaseInfoByProjectIdAndProtocolAndStatus(@Param("projectId") String projectId, @Param("protocol") String protocol, @Param("versionId") String versionId, @Param("status") String status);
|
||||
|
||||
List<ApiDefinition> selectApiBaseInfoByCondition(@Param("projectId") String projectId, @Param("protocol") String protocol, @Param("versionId") String versionId, @Param("status") String status, @Param("request") ApiDefinitionRequest request);
|
||||
|
||||
void updateNoModuleApiToDefaultModule(@Param("projectId") String projectId, @Param("protocol") String protocol, @Param("status") String status, @Param("versionId") String versionId, @Param("moduleId") String moduleId);
|
||||
|
||||
List<String> selectApiIdInExecutionInfoByProjectIdIsNull();
|
||||
|
|
|
@ -290,6 +290,7 @@
|
|||
api_definition.status, api_definition.user_id, api_definition.create_time, api_definition.update_time,
|
||||
api_definition.delete_user_id, api_definition.create_user,api_definition.delete_time, api_definition.remark,
|
||||
api_definition.version_id,
|
||||
api_definition.latest,
|
||||
project_version.name as version_name, api_definition.ref_id, user.name as user_name
|
||||
from api_definition
|
||||
left join user on api_definition.user_id = user.id
|
||||
|
@ -1237,6 +1238,22 @@
|
|||
AND latest IS TRUE
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectApiBaseInfoByCondition" resultType="io.metersphere.base.domain.ApiDefinition">
|
||||
select id, module_id
|
||||
from api_definition
|
||||
<include refid="queryWhereCondition"/>
|
||||
AND project_id = #{projectId}
|
||||
AND protocol = #{protocol}
|
||||
AND status = #{status}
|
||||
<if test="versionId != null">
|
||||
AND version_id = #{versionId}
|
||||
</if>
|
||||
<if test="versionId == null">
|
||||
AND latest IS TRUE
|
||||
</if>
|
||||
</select>
|
||||
|
||||
<select id="selectApiIdInExecutionInfoByProjectIdIsNull" resultType="java.lang.String">
|
||||
SELECT DISTINCT source_id FROM api_execution_info
|
||||
WHERE project_id IS NULL
|
||||
|
|
|
@ -102,6 +102,8 @@ public interface ExtApiScenarioMapper {
|
|||
|
||||
List<ApiScenario> selectBaseInfoByProjectIdAndStatus(@Param("projectId") String projectId, @Param("status") String status);
|
||||
|
||||
List<ApiScenario> selectBaseInfoByCondition(@Param("projectId") String projectId, @Param("status") String status, @Param("request") ApiScenarioRequest request);
|
||||
|
||||
List<ApiCountChartResult> countByRequest(ApiCountRequest request);
|
||||
|
||||
List<ApiScenarioDTO> relevanceScenarioList(@Param("request") ApiScenarioRequest request);
|
||||
|
|
|
@ -189,7 +189,7 @@
|
|||
<select id="list" resultMap="BaseResultMap">
|
||||
select api_scenario.id, api_scenario.project_id, api_scenario.tags, api_scenario.user_id, api_scenario.num,
|
||||
api_scenario.custom_num, api_scenario.version, api_scenario.environment_type, api_scenario.environment_group_id,
|
||||
api_scenario.version_id, api_scenario.ref_id, project_version.name as version_name,
|
||||
api_scenario.version_id, api_scenario.ref_id,api_scenario.latest, project_version.name as version_name,
|
||||
<if test="request.selectEnvironment == true">
|
||||
api_scenario.environment_json as env,
|
||||
</if>
|
||||
|
@ -913,6 +913,15 @@
|
|||
AND latest IS TRUE
|
||||
</select>
|
||||
|
||||
<select id="selectBaseInfoByCondition" resultType="io.metersphere.base.domain.ApiScenario">
|
||||
select api_scenario.id, api_scenario.api_scenario_module_id
|
||||
from api_scenario
|
||||
<include refid="queryWhereCondition"/>
|
||||
AND api_scenario.project_id = #{projectId}
|
||||
AND api_scenario.status = #{status}
|
||||
AND api_scenario.latest IS TRUE
|
||||
</select>
|
||||
|
||||
<select id="countByRequest" resultType="io.metersphere.api.dto.ApiCountChartResult">
|
||||
select
|
||||
<if test="testCaseGroupColumn != null and testCaseGroupColumn != ''">
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
<if test="versionId != null">
|
||||
AND version_id = #{versionId}
|
||||
</if>
|
||||
<if test="versionId == null">
|
||||
AND latest = 1
|
||||
</if>
|
||||
)
|
||||
AND reference_id IS NOT NULL
|
||||
</select>
|
||||
|
|
|
@ -8,10 +8,10 @@ import io.metersphere.base.domain.TestPlanApiCase;
|
|||
import io.metersphere.commons.constants.ApiRunMode;
|
||||
import io.metersphere.commons.constants.ReportTypeConstants;
|
||||
import io.metersphere.commons.constants.TriggerMode;
|
||||
import io.metersphere.commons.enums.StorageEnums;
|
||||
import io.metersphere.dto.RunModeConfigDTO;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -27,7 +27,7 @@ public class ApiDefinitionExecResultUtil {
|
|||
apiResult.setStartTime(System.currentTimeMillis());
|
||||
apiResult.setEndTime(System.currentTimeMillis());
|
||||
apiResult.setTriggerMode(TriggerMode.BATCH.name());
|
||||
apiResult.setActuator("LOCAL");
|
||||
apiResult.setActuator(StorageEnums.LOCAL.name());
|
||||
if (config != null && GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()).isPool()) {
|
||||
apiResult.setActuator(config.getResourcePoolId());
|
||||
}
|
||||
|
@ -42,31 +42,33 @@ public class ApiDefinitionExecResultUtil {
|
|||
return apiResult;
|
||||
}
|
||||
|
||||
public static ApiDefinitionExecResultWithBLOBs addResult(BatchRunDefinitionRequest request, RunModeConfigDTO runModeConfigDTO, TestPlanApiCase key, String status,
|
||||
Map<String, ApiTestCase> caseMap, String poolId) {
|
||||
public static ApiDefinitionExecResultWithBLOBs addResult(
|
||||
BatchRunDefinitionRequest request,
|
||||
RunModeConfigDTO runModeConfigDTO,
|
||||
TestPlanApiCase key,
|
||||
String status,
|
||||
ApiTestCase testCase,
|
||||
String poolId) {
|
||||
|
||||
ApiDefinitionExecResultWithBLOBs apiResult = new ApiDefinitionExecResultWithBLOBs();
|
||||
apiResult.setId(UUID.randomUUID().toString());
|
||||
apiResult.setCreateTime(System.currentTimeMillis());
|
||||
apiResult.setStartTime(System.currentTimeMillis());
|
||||
apiResult.setEndTime(System.currentTimeMillis());
|
||||
apiResult.setReportType(ReportTypeConstants.API_INDEPENDENT.name());
|
||||
ApiTestCase testCase = caseMap.get(key.getApiCaseId());
|
||||
if (testCase != null) {
|
||||
apiResult.setName(testCase.getName());
|
||||
apiResult.setProjectId(testCase.getProjectId());
|
||||
apiResult.setVersionId(testCase.getVersionId());
|
||||
}
|
||||
apiResult.setTriggerMode(request.getTriggerMode());
|
||||
apiResult.setActuator("LOCAL");
|
||||
apiResult.setActuator(StorageEnums.LOCAL.name());
|
||||
if (StringUtils.isNotEmpty(poolId)) {
|
||||
apiResult.setActuator(poolId);
|
||||
}
|
||||
if (StringUtils.isEmpty(request.getUserId())) {
|
||||
if (SessionUtils.getUser() != null) {
|
||||
apiResult.setUserId(SessionUtils.getUser().getId());
|
||||
}
|
||||
} else {
|
||||
apiResult.setUserId(request.getUserId());
|
||||
apiResult.setUserId(request.getUserId());
|
||||
if (StringUtils.isEmpty(apiResult.getUserId())) {
|
||||
apiResult.setUserId(SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
apiResult.setResourceId(key.getId());
|
||||
|
@ -92,7 +94,7 @@ public class ApiDefinitionExecResultUtil {
|
|||
apiResult.setStartTime(System.currentTimeMillis());
|
||||
apiResult.setEndTime(System.currentTimeMillis());
|
||||
apiResult.setTriggerMode(TriggerMode.BATCH.name());
|
||||
apiResult.setActuator("LOCAL");
|
||||
apiResult.setActuator(StorageEnums.LOCAL.name());
|
||||
apiResult.setUserId(userId);
|
||||
apiResult.setResourceId(resourceId);
|
||||
apiResult.setStartTime(System.currentTimeMillis());
|
||||
|
|
|
@ -103,7 +103,7 @@ public class MockApiUtils {
|
|||
} else if (StringUtils.equalsIgnoreCase(type, "XML")) {
|
||||
if (bodyObj.has("raw")) {
|
||||
String xmlStr = bodyObj.optString("raw");
|
||||
xmlStr = xmlStr.replaceAll("\r","").replaceAll("\n","");
|
||||
xmlStr = xmlStr.replaceAll("\r", "").replaceAll("\n", "");
|
||||
JSONObject matchObj = XMLUtil.xmlStringToJSONObject(xmlStr);
|
||||
returnJson = matchObj;
|
||||
}
|
||||
|
@ -360,7 +360,10 @@ public class MockApiUtils {
|
|||
bodyParams = new JSONArray();
|
||||
bodyParams.put(paramJson);
|
||||
} else {
|
||||
bodyParams.put(((JSONObject) paramJson));
|
||||
JSONArray oldArray = returnParams.getBodyParams();
|
||||
if (!JsonStructUtils.checkJsonArrayCompliance(oldArray, ((JSONObject) paramJson))) {
|
||||
bodyParams.put(((JSONObject) paramJson));
|
||||
}
|
||||
}
|
||||
returnParams.setBodyParams(bodyParams);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.controller.definition;
|
||||
|
||||
import io.metersphere.api.dto.definition.ApiDefinitionRequest;
|
||||
import io.metersphere.api.dto.definition.ApiModuleDTO;
|
||||
import io.metersphere.api.dto.definition.DragModuleRequest;
|
||||
import io.metersphere.service.definition.ApiModuleService;
|
||||
|
@ -37,6 +38,13 @@ public class ApiModuleController {
|
|||
return apiModuleService.getNodeTreeByProjectId(projectId, protocol, versionId);
|
||||
}
|
||||
|
||||
@PostMapping("/list/{projectId}/{protocol}")
|
||||
public List<ApiModuleDTO> searchNodeByProjectId(@PathVariable String projectId, @PathVariable String protocol, @RequestBody ApiDefinitionRequest request) {
|
||||
String userId = SessionUtils.getUserId();
|
||||
ApiDefinitionDefaultApiTypeUtil.addUserSelectApiType(userId, protocol);
|
||||
return apiModuleService.getNodeTreeByCondition(projectId, protocol, null, request);
|
||||
}
|
||||
|
||||
@GetMapping("/trash/list/{projectId}/{protocol}/{versionId}")
|
||||
public List<ApiModuleDTO> getTrashNodeByProtocolAndProjectId(@PathVariable String projectId, @PathVariable String protocol,
|
||||
@PathVariable String versionId) {
|
||||
|
@ -48,6 +56,11 @@ public class ApiModuleController {
|
|||
return apiModuleService.getTrashNodeTreeByProtocolAndProjectId(projectId, protocol, null);
|
||||
}
|
||||
|
||||
@PostMapping("/trash/list/{projectId}/{protocol}")
|
||||
public List<ApiModuleDTO> searchTrashNodeByProtocolAndProjectId(@PathVariable String projectId, @PathVariable String protocol, @RequestBody ApiDefinitionRequest request) {
|
||||
return apiModuleService.getTrashNodeTreeByProtocolAndProjectId(projectId, protocol, null, request);
|
||||
}
|
||||
|
||||
@GetMapping("/trash-count/{projectId}/{protocol}")
|
||||
public long trashCount(@PathVariable String projectId, @PathVariable String protocol) {
|
||||
String userId = SessionUtils.getUserId();
|
||||
|
|
|
@ -79,7 +79,7 @@ public class ApiHomeController {
|
|||
long effectiveApiCount = apiDefinitionService.countEffectiveByProjectId(projectId, versionId);
|
||||
long apiHasCase = apiDefinitionService.countApiByProjectIdAndHasCase(projectId, versionId);
|
||||
List<ApiDefinition> apiNoCaseList = apiDefinitionService.selectEffectiveIdByProjectIdAndHaveNotCase(projectId, versionId);
|
||||
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, versionId);
|
||||
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, null);
|
||||
int apiInScenario = apiAutomationService.getApiIdInScenario(projectId, scenarioUrlList, apiNoCaseList).size();
|
||||
|
||||
if (effectiveApiCount == 0) {
|
||||
|
@ -185,7 +185,6 @@ public class ApiHomeController {
|
|||
apiCountResult.setCoveredCount(coveredDTO.covered);
|
||||
apiCountResult.setNotCoveredCount(coveredDTO.notCovered);
|
||||
apiCountResult.setApiCoveredRate(coveredDTO.rateOfCovered);
|
||||
|
||||
return apiCountResult;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.controller.scenario;
|
||||
|
||||
import io.metersphere.api.dto.automation.ApiScenarioModuleDTO;
|
||||
import io.metersphere.api.dto.automation.ApiScenarioRequest;
|
||||
import io.metersphere.api.dto.automation.DragApiScenarioModuleRequest;
|
||||
import io.metersphere.service.scenario.ApiScenarioModuleService;
|
||||
import io.metersphere.base.domain.ApiScenarioModule;
|
||||
|
@ -24,11 +25,21 @@ public class ApiScenarioModuleController {
|
|||
return apiScenarioModuleService.getNodeTreeByProjectId(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/list/{projectId}")
|
||||
public List<ApiScenarioModuleDTO> searchNodeByProjectId(@PathVariable String projectId, @RequestBody ApiScenarioRequest request) {
|
||||
return apiScenarioModuleService.getNodeTreeByProjectId(projectId, request);
|
||||
}
|
||||
|
||||
@GetMapping("/trash/list/{projectId}")
|
||||
public List<ApiScenarioModuleDTO> getTrashNodeByProjectId(@PathVariable String projectId) {
|
||||
return apiScenarioModuleService.getTrashNodeTreeByProjectId(projectId);
|
||||
}
|
||||
|
||||
@PostMapping("/trash/list/{projectId}")
|
||||
public List<ApiScenarioModuleDTO> searchTrashNodeByProjectId(@PathVariable String projectId, @RequestBody ApiScenarioRequest request) {
|
||||
return apiScenarioModuleService.getTrashNodeTreeByProjectId(projectId, request);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@MsAuditLog(module = OperLogModule.API_AUTOMATION, type = OperLogConstants.CREATE, title = "#node.name", content = "#msClass.getLogDetails(#node)", msClass = ApiScenarioModuleService.class)
|
||||
public String addNode(@RequestBody ApiScenarioModule node) {
|
||||
|
|
|
@ -137,6 +137,8 @@ public class MockConfigService {
|
|||
}
|
||||
if (request.getApiId() != null) {
|
||||
criteria.andApiIdEqualTo(request.getApiId());
|
||||
} else {
|
||||
return new MockConfigResponse(null, new ArrayList<>());
|
||||
}
|
||||
if (request.getProjectId() != null) {
|
||||
criteria.andProjectIdEqualTo(request.getProjectId());
|
||||
|
|
|
@ -320,9 +320,8 @@ public class ApiDefinitionImportUtilService {
|
|||
String chooseModulePath = getChooseModulePath(idPathMap, chooseModule, chooseModuleParentId);
|
||||
//这样的过滤规则下可能存在重复接口,如果是覆盖模块,需要按照去重规则再次去重,否则就加上接口原有的模块
|
||||
if (fullCoverage) {
|
||||
List<ApiDefinitionWithBLOBs> singleOptionData = new ArrayList<>();
|
||||
removeHttpChooseModuleRepeat(optionData, singleOptionData, chooseModulePath);
|
||||
optionData = singleOptionData;
|
||||
removeHttpChooseModuleRepeat(optionData, chooseModulePath);
|
||||
// optionData = singleOptionData;
|
||||
optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(chooseModulePath), api -> api));
|
||||
} else {
|
||||
getChooseModuleUrlRepeatOptionMap(optionData, optionMap, chooseModulePath);
|
||||
|
@ -359,19 +358,6 @@ public class ApiDefinitionImportUtilService {
|
|||
removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases);
|
||||
}
|
||||
}
|
||||
//最后在整个体统内检查一遍(防止在有选择的模块时,未找到重复,直接创建的情况)
|
||||
if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) {
|
||||
repeatDataMap = repeatApiDefinitionWithBLOBs.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath())));
|
||||
optionMap = optionData.stream().collect(Collectors.toMap(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(t.getModulePath()), api -> api));
|
||||
if (fullCoverage) {
|
||||
startCover(toUpdateList, optionData, optionMap, repeatDataMap, updateVersionId, optionDataCases, oldCaseMap,null);
|
||||
} else {
|
||||
//不覆盖,同一接口不做更新
|
||||
if (CollectionUtils.isNotEmpty(repeatApiDefinitionWithBLOBs)) {
|
||||
removeSameData(repeatDataMap, optionMap, optionData, moduleMap, optionDataCases);
|
||||
}
|
||||
}
|
||||
}
|
||||
//将原来的case和更改的case组合在一起,为了同步的设置
|
||||
List<String> caseIds = optionDataCases.stream().map(ApiTestCase::getId).filter(StringUtils::isNotBlank).collect(Collectors.toList());
|
||||
buildCases(optionDataCases, oldCaseMap, caseIds);
|
||||
|
@ -462,18 +448,20 @@ public class ApiDefinitionImportUtilService {
|
|||
return s;
|
||||
}
|
||||
|
||||
private static void removeHttpChooseModuleRepeat(List<ApiDefinitionWithBLOBs> optionData, List<ApiDefinitionWithBLOBs> singleOptionData, String chooseModulePath) {
|
||||
private static void removeHttpChooseModuleRepeat(List<ApiDefinitionWithBLOBs> optionData, String chooseModulePath) {
|
||||
LinkedHashMap<String, List<ApiDefinitionWithBLOBs>> methodPathMap = optionData.stream().collect(Collectors.groupingBy(t -> t.getName().concat(t.getMethod()).concat(t.getPath()).concat(chooseModulePath), LinkedHashMap::new, Collectors.toList()));
|
||||
methodPathMap.forEach((k, v) -> singleOptionData.add(v.get(v.size() - 1)));
|
||||
methodPathMap.forEach((k, v) -> {
|
||||
if (v.size() > 1) {
|
||||
for (int i = 0; i < v.size() - 1; i++) {
|
||||
optionData.remove(v.get(i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void getChooseModuleUrlRepeatOptionMap(List<ApiDefinitionWithBLOBs> optionData, Map<String, ApiDefinitionWithBLOBs> optionMap, String chooseModulePath) {
|
||||
for (ApiDefinitionWithBLOBs optionDatum : optionData) {
|
||||
if (optionDatum.getModulePath() == null) {
|
||||
optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath), optionDatum);
|
||||
} else {
|
||||
optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath).concat(optionDatum.getModulePath()), optionDatum);
|
||||
}
|
||||
optionMap.put(optionDatum.getName().concat(optionDatum.getMethod()).concat(optionDatum.getPath()).concat(chooseModulePath), optionDatum);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import io.metersphere.base.domain.*;
|
|||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.*;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.enums.StorageEnums;
|
||||
import io.metersphere.commons.enums.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.*;
|
||||
|
@ -170,7 +171,6 @@ public class ApiDefinitionService {
|
|||
private static final String SCHEDULE = "schedule";
|
||||
|
||||
|
||||
|
||||
public List<ApiDefinitionResult> list(ApiDefinitionRequest request) {
|
||||
request = this.initRequest(request, true, true);
|
||||
List<ApiDefinitionResult> resList = extApiDefinitionMapper.list(request);
|
||||
|
@ -354,8 +354,8 @@ public class ApiDefinitionService {
|
|||
definitionList = this.selectEffectiveIdByProjectId(request.getProjectId(), versionId);
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(definitionList)) {
|
||||
//如果查询条件中有未覆盖/已覆盖, 则需要解析出没有用例的接口中,有多少是符合场景覆盖规律的。然后将这些接口的id作为查询参数
|
||||
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(request.getProjectId(), versionId);
|
||||
//如果查询条件中有未覆盖/已覆盖, 则需要解析出没有用例的接口中,有多少是符合场景覆盖规律的。然后将这些接口的id作为查询参数. 这里不根据版本筛选覆盖的url。
|
||||
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(request.getProjectId(), null);
|
||||
List<String> apiIdInScenario = apiAutomationService.getApiIdInScenario(request.getProjectId(), scenarioUrlList, definitionList);
|
||||
if (CollectionUtils.isNotEmpty(apiIdInScenario)) {
|
||||
request.setCoverageIds(apiIdInScenario);
|
||||
|
@ -783,7 +783,10 @@ public class ApiDefinitionService {
|
|||
apiTestCaseService.updateByApiDefinitionId(ids, test, request.getTriggerUpdate());
|
||||
}
|
||||
ApiDefinitionWithBLOBs result = apiDefinitionMapper.selectByPrimaryKey(test.getId());
|
||||
//checkAndSetLatestVersion(result.getRefId());
|
||||
String defaultVersion = baseProjectVersionMapper.getDefaultVersion(request.getProjectId());
|
||||
if (StringUtils.equalsIgnoreCase(request.getVersionId(), defaultVersion)) {
|
||||
checkAndSetLatestVersion(result.getRefId());
|
||||
}
|
||||
|
||||
// 存储附件关系
|
||||
extFileAssociationService.saveApi(test.getId(), request.getRequest(), FileAssociationTypeEnums.API.name());
|
||||
|
@ -935,21 +938,6 @@ public class ApiDefinitionService {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取存储执行结果报告
|
||||
*
|
||||
|
@ -1010,7 +998,7 @@ public class ApiDefinitionService {
|
|||
apiReportEnvConfig.setResourcePoolName(resourcePool.getName());
|
||||
}
|
||||
} else {
|
||||
apiReportEnvConfig.setResourcePoolName("LOCAL");
|
||||
apiReportEnvConfig.setResourcePoolName(StorageEnums.LOCAL.name());
|
||||
}
|
||||
return apiReportEnvConfig;
|
||||
}
|
||||
|
@ -1685,9 +1673,11 @@ public class ApiDefinitionService {
|
|||
} else {
|
||||
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||
ApiDefinitionExample.Criteria criteria = example.createCriteria();
|
||||
criteria.andProjectIdEqualTo(projectId).andStatusNotEqualTo("Trash").andLatestEqualTo(true);
|
||||
criteria.andProjectIdEqualTo(projectId).andStatusNotEqualTo("Trash");
|
||||
if (StringUtils.isNotBlank(versionId)) {
|
||||
criteria.andVersionIdEqualTo(versionId);
|
||||
} else {
|
||||
criteria.andLatestEqualTo(true);
|
||||
}
|
||||
return apiDefinitionMapper.countByExample(example);
|
||||
}
|
||||
|
@ -1919,6 +1909,11 @@ public class ApiDefinitionService {
|
|||
return apiList.stream().collect(Collectors.groupingBy(ApiDefinition::getModuleId));
|
||||
}
|
||||
|
||||
public Map<String, List<ApiDefinition>> selectApiBaseInfoGroupByModuleId(String projectId, String protocol, String versionId, String status, ApiDefinitionRequest request) {
|
||||
List<ApiDefinition> apiList = extApiDefinitionMapper.selectApiBaseInfoByCondition(projectId, protocol, versionId, status, request);
|
||||
return apiList.stream().collect(Collectors.groupingBy(ApiDefinition::getModuleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将模块删除了的接口模块改为默认模块
|
||||
*
|
||||
|
|
|
@ -102,6 +102,19 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
|
|||
return getNodeTrees(trashModuleList);
|
||||
}
|
||||
|
||||
public List<ApiModuleDTO> getTrashNodeTreeByProtocolAndProjectId(String projectId, String protocol, String versionId, ApiDefinitionRequest request) {
|
||||
//回收站数据初始化:检查是否存在模块被删除的接口,则把接口挂再默认节点上
|
||||
initTrashDataModule(projectId, protocol, versionId);
|
||||
//通过回收站里的接口模块进行反显
|
||||
Map<String, List<ApiDefinition>> trashApiMap =
|
||||
apiDefinitionService.selectApiBaseInfoGroupByModuleId(projectId, protocol, versionId,
|
||||
ApiTestDataStatus.TRASH.getValue(), request);
|
||||
//查找回收站里的模块
|
||||
List<ApiModuleDTO> trashModuleList = this.selectTreeStructModuleById(trashApiMap.keySet());
|
||||
this.initApiCount(trashModuleList, trashApiMap);
|
||||
return getNodeTrees(trashModuleList);
|
||||
}
|
||||
|
||||
private void initApiCount(List<ApiModuleDTO> apiModules, Map<String, List<ApiDefinition>> trashApiMap) {
|
||||
if (CollectionUtils.isNotEmpty(apiModules) && MapUtils.isNotEmpty(trashApiMap)) {
|
||||
apiModules.forEach(node -> {
|
||||
|
@ -192,6 +205,53 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
|
|||
return getNodeTrees(apiModules);
|
||||
}
|
||||
|
||||
public List<ApiModuleDTO> getNodeTreeByCondition(String projectId, String protocol, String versionId, ApiDefinitionRequest request ) {
|
||||
// 判断当前项目下是否有默认模块,没有添加默认模块
|
||||
this.getDefaultNode(projectId, protocol);
|
||||
List<ApiModuleDTO> apiModules = getApiModulesByProjectAndPro(projectId, protocol);
|
||||
request.setProjectId(projectId);
|
||||
request.setProtocol(protocol);
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(ApiTestDataStatus.PREPARE.getValue());
|
||||
list.add(ApiTestDataStatus.UNDERWAY.getValue());
|
||||
list.add(ApiTestDataStatus.COMPLETED.getValue());
|
||||
Map<String, List<String>> filters = new LinkedHashMap<>();
|
||||
filters.put("status", list);
|
||||
request.setFilters(filters);
|
||||
|
||||
//优化: 所有统计SQL一次查询出来
|
||||
List<String> allModuleIdList = new ArrayList<>();
|
||||
for (ApiModuleDTO node : apiModules) {
|
||||
List<String> moduleIds = new ArrayList<>();
|
||||
moduleIds = this.nodeList(apiModules, node.getId(), moduleIds);
|
||||
moduleIds.add(node.getId());
|
||||
for (String moduleId : moduleIds) {
|
||||
if (!allModuleIdList.contains(moduleId)) {
|
||||
allModuleIdList.add(moduleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
request.setModuleIds(allModuleIdList);
|
||||
if (StringUtils.isNotBlank(versionId)) {
|
||||
request.setVersionId(versionId);
|
||||
}
|
||||
List<Map<String, Object>> moduleCountList = extApiDefinitionMapper.moduleCountByCollection(request);
|
||||
Map<String, Integer> moduleCountMap = this.parseModuleCountList(moduleCountList);
|
||||
apiModules.forEach(node -> {
|
||||
List<String> moduleIds = new ArrayList<>();
|
||||
moduleIds = this.nodeList(apiModules, node.getId(), moduleIds);
|
||||
moduleIds.add(node.getId());
|
||||
int countNum = 0;
|
||||
for (String moduleId : moduleIds) {
|
||||
if (moduleCountMap.containsKey(moduleId)) {
|
||||
countNum += moduleCountMap.get(moduleId).intValue();
|
||||
}
|
||||
}
|
||||
node.setCaseNum(countNum);
|
||||
});
|
||||
return getNodeTrees(apiModules);
|
||||
}
|
||||
|
||||
private Map<String, Integer> parseModuleCountList(List<Map<String, Object>> moduleCountList) {
|
||||
Map<String, Integer> returnMap = new HashMap<>();
|
||||
for (Map<String, Object> map : moduleCountList) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
|
|||
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
|
||||
import io.metersphere.commons.constants.ElementConstants;
|
||||
import io.metersphere.commons.enums.ApiReportStatus;
|
||||
import io.metersphere.commons.enums.StorageEnums;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
|
@ -203,7 +204,7 @@ public class ExtApiTaskService extends TaskService {
|
|||
if (StringUtils.isEmpty(reportId)) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(actuator) && !StringUtils.equals(actuator, "LOCAL")) {
|
||||
if (StringUtils.isNotEmpty(actuator) && !StringUtils.equals(actuator, StorageEnums.LOCAL.name())) {
|
||||
if (poolMap.containsKey(actuator)) {
|
||||
poolMap.get(actuator).add(reportId);
|
||||
} else {
|
||||
|
|
|
@ -100,6 +100,47 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
|
|||
return getNodeTrees(nodes);
|
||||
}
|
||||
|
||||
public List<ApiScenarioModuleDTO> getNodeTreeByProjectId(String projectId, ApiScenarioRequest request) {
|
||||
// 判断当前项目下是否有默认模块,没有添加默认模块
|
||||
this.getDefaultNode(projectId);
|
||||
List<ApiScenarioModuleDTO> nodes = extApiScenarioModuleMapper.getNodeTreeByProjectId(projectId);
|
||||
request.setProjectId(projectId);
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add(ApiTestDataStatus.PREPARE.getValue());
|
||||
list.add(ApiTestDataStatus.UNDERWAY.getValue());
|
||||
list.add(ApiTestDataStatus.COMPLETED.getValue());
|
||||
Map<String, List<String>> filters = new LinkedHashMap<>();
|
||||
filters.put("status", list);
|
||||
request.setFilters(filters);
|
||||
List<String> allModuleIdList = new ArrayList<>();
|
||||
for (ApiScenarioModuleDTO node : nodes) {
|
||||
List<String> moduleIds = new ArrayList<>();
|
||||
moduleIds = this.nodeList(nodes, node.getId(), moduleIds);
|
||||
moduleIds.add(node.getId());
|
||||
for (String moduleId : moduleIds) {
|
||||
if (!allModuleIdList.contains(moduleId)) {
|
||||
allModuleIdList.add(moduleId);
|
||||
}
|
||||
}
|
||||
}
|
||||
request.setModuleIds(allModuleIdList);
|
||||
List<Map<String, Object>> moduleCountList = extApiScenarioMapper.listModuleByCollection(request);
|
||||
Map<String, Integer> moduleCountMap = this.parseModuleCountList(moduleCountList);
|
||||
nodes.forEach(node -> {
|
||||
List<String> moduleIds = new ArrayList<>();
|
||||
moduleIds = this.nodeList(nodes, node.getId(), moduleIds);
|
||||
moduleIds.add(node.getId());
|
||||
int countNum = 0;
|
||||
for (String moduleId : moduleIds) {
|
||||
if (moduleCountMap.containsKey(moduleId)) {
|
||||
countNum += moduleCountMap.get(moduleId).intValue();
|
||||
}
|
||||
}
|
||||
node.setCaseNum(countNum);
|
||||
});
|
||||
return getNodeTrees(nodes);
|
||||
}
|
||||
|
||||
public List<ApiScenarioModuleDTO> getTrashNodeTreeByProjectId(String projectId) {
|
||||
//回收站数据初始化:被删除了的数据挂在默认模块上
|
||||
initTrashDataModule(projectId);
|
||||
|
@ -112,6 +153,23 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
|
|||
return getNodeTrees(trashModuleList);
|
||||
}
|
||||
|
||||
public List<ApiScenarioModuleDTO> getTrashNodeTreeByProjectId(String projectId, ApiScenarioRequest request) {
|
||||
//回收站数据初始化:被删除了的数据挂在默认模块上
|
||||
initTrashDataModule(projectId);
|
||||
//通过回收站里的接口模块进行反显
|
||||
if(request.getFilters() != null && request.getFilters().get("status") != null){
|
||||
List<String> statusList = new ArrayList<>();
|
||||
statusList.add(ApiTestDataStatus.TRASH.getValue());
|
||||
request.getFilters().put("status", statusList);
|
||||
}
|
||||
Map<String, List<ApiScenario>> trashApiMap = apiAutomationService.selectApiBaseInfoGroupByModuleId(projectId,
|
||||
ApiTestDataStatus.TRASH.getValue(), request);
|
||||
//查找回收站里的模块
|
||||
List<ApiScenarioModuleDTO> trashModuleList = this.selectTreeStructModuleById(trashApiMap.keySet());
|
||||
this.initApiCount(trashModuleList, trashApiMap);
|
||||
return getNodeTrees(trashModuleList);
|
||||
}
|
||||
|
||||
private void initApiCount(List<ApiScenarioModuleDTO> moduleDTOList, Map<String, List<ApiScenario>> scenarioMap) {
|
||||
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(moduleDTOList) && MapUtils.isNotEmpty(scenarioMap)) {
|
||||
moduleDTOList.forEach(node -> {
|
||||
|
|
|
@ -790,7 +790,7 @@ public class ApiScenarioReportService {
|
|||
if (initModel.getConfig() != null && StringUtils.isNotBlank(initModel.getConfig().getResourcePoolId())) {
|
||||
report.setActuator(initModel.getConfig().getResourcePoolId());
|
||||
} else {
|
||||
report.setActuator("LOCAL");
|
||||
report.setActuator(StorageEnums.LOCAL.name());
|
||||
}
|
||||
report.setTriggerMode(initModel.getTriggerMode());
|
||||
report.setReportVersion(2);
|
||||
|
|
|
@ -10,6 +10,7 @@ import io.metersphere.commons.constants.MsTestElementConstants;
|
|||
import io.metersphere.commons.constants.PropertyConstant;
|
||||
import io.metersphere.commons.constants.ReportTypeConstants;
|
||||
import io.metersphere.commons.enums.ApiReportStatus;
|
||||
import io.metersphere.commons.enums.StorageEnums;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.constants.RunModeConstants;
|
||||
import io.metersphere.dto.RequestResult;
|
||||
|
@ -714,7 +715,7 @@ public class ApiScenarioReportStructureService {
|
|||
dto.setPoolName(resourcePool.getName());
|
||||
}
|
||||
} else {
|
||||
dto.setPoolName("LOCAL");
|
||||
dto.setPoolName(StorageEnums.LOCAL.name());
|
||||
}
|
||||
if (runModeConfigDTO != null && StringUtils.isNotBlank(runModeConfigDTO.getMode())) {
|
||||
dto.setMode(runModeConfigDTO.getMode());
|
||||
|
|
|
@ -19,6 +19,7 @@ import io.metersphere.commons.constants.ApiRunMode;
|
|||
import io.metersphere.commons.constants.ElementConstants;
|
||||
import io.metersphere.commons.constants.ReportTypeConstants;
|
||||
import io.metersphere.commons.enums.ApiReportStatus;
|
||||
import io.metersphere.commons.enums.StorageEnums;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.constants.RunModeConstants;
|
||||
|
@ -111,7 +112,7 @@ public class ApiScenarioRerunService {
|
|||
config.setMode(RunModeConstants.PARALLEL.toString());
|
||||
}
|
||||
config.setReportType(RunModeConstants.SET_REPORT.toString());
|
||||
if (!StringUtils.equalsAnyIgnoreCase(reportResults.get(0).getActuator(), "LOCAL")) {
|
||||
if (!StringUtils.equalsAnyIgnoreCase(reportResults.get(0).getActuator(), StorageEnums.LOCAL.name())) {
|
||||
config.setResourcePoolId(reportResults.get(0).getActuator());
|
||||
}
|
||||
request.setConfig(config);
|
||||
|
@ -165,7 +166,7 @@ public class ApiScenarioRerunService {
|
|||
config.setMode(RunModeConstants.PARALLEL.toString());
|
||||
}
|
||||
config.setReportName(reportDTO.getName());
|
||||
if (!StringUtils.equalsAnyIgnoreCase(reportDTO.getActuator(), "LOCAL")) {
|
||||
if (!StringUtils.equalsAnyIgnoreCase(reportDTO.getActuator(), StorageEnums.LOCAL.name())) {
|
||||
config.setResourcePoolId(reportDTO.getActuator());
|
||||
}
|
||||
config.setReportType(RunModeConstants.SET_REPORT.toString());
|
||||
|
@ -241,7 +242,7 @@ public class ApiScenarioRerunService {
|
|||
continue;
|
||||
}
|
||||
runModeConfig.setReportType(report.getReportType());
|
||||
if (!StringUtils.equalsAnyIgnoreCase(report.getActuator(), "LOCAL")) {
|
||||
if (!StringUtils.equalsAnyIgnoreCase(report.getActuator(), StorageEnums.LOCAL.name())) {
|
||||
runModeConfig.setResourcePoolId(report.getActuator());
|
||||
}
|
||||
executeQueue.put(testPlanApiCase.getId(), report);
|
||||
|
@ -293,7 +294,7 @@ public class ApiScenarioRerunService {
|
|||
// 执行配置
|
||||
RunModeConfigDTO config = new RunModeConfigDTO();
|
||||
config.setMode(RunModeConstants.PARALLEL.toString());
|
||||
if (!StringUtils.equalsAnyIgnoreCase(reports.get(0).getActuator(), "LOCAL")) {
|
||||
if (!StringUtils.equalsAnyIgnoreCase(reports.get(0).getActuator(), StorageEnums.LOCAL.name())) {
|
||||
config.setResourcePoolId(reports.get(0).getActuator());
|
||||
}
|
||||
|
||||
|
|
|
@ -409,7 +409,10 @@ public class ApiScenarioService {
|
|||
if (relationshipEdgeService != null) {
|
||||
relationshipEdgeService.initRelationshipEdge(beforeScenario, scenario);
|
||||
}
|
||||
//checkAndSetLatestVersion(beforeScenario.getRefId());
|
||||
String defaultVersion = baseProjectVersionMapper.getDefaultVersion(request.getProjectId());
|
||||
if (StringUtils.equalsIgnoreCase(request.getVersionId(), defaultVersion)) {
|
||||
checkAndSetLatestVersion(beforeScenario.getRefId());
|
||||
}
|
||||
// 存储附件关系
|
||||
extFileAssociationService.saveScenario(scenario.getId(), request.getScenarioDefinition());
|
||||
return scenario;
|
||||
|
@ -2204,6 +2207,11 @@ public class ApiScenarioService {
|
|||
return apiScenarioList.stream().collect(Collectors.groupingBy(ApiScenario::getApiScenarioModuleId));
|
||||
}
|
||||
|
||||
public Map<String, List<ApiScenario>> selectApiBaseInfoGroupByModuleId(String projectId, String status, ApiScenarioRequest request) {
|
||||
List<ApiScenario> apiScenarioList = extApiScenarioMapper.selectBaseInfoByCondition(projectId, status, request);
|
||||
return apiScenarioList.stream().collect(Collectors.groupingBy(ApiScenario::getApiScenarioModuleId));
|
||||
}
|
||||
|
||||
public List<ApiCountChartResult> countByRequest(ApiCountRequest request) {
|
||||
return extApiScenarioMapper.countByRequest(request);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,11 @@ export function getApiModules(projectId, protocol, currentVersion) {
|
|||
return get(url);
|
||||
}
|
||||
|
||||
export function postApiModules(projectId, protocol, currentVersion, param) {
|
||||
let url = '/api/module/list/' + projectId + '/' + protocol + (currentVersion ? '/' + currentVersion : '');
|
||||
return post(url, param);
|
||||
}
|
||||
|
||||
export function getApiModuleByProjectIdAndProtocol(projectId, protocol) {
|
||||
let url = '/api/module/list/' + projectId + '/' + protocol;
|
||||
return get(url);
|
||||
|
@ -15,6 +20,11 @@ export function getApiModuleByTrash(projectId, protocol, currentVersion) {
|
|||
return get(url);
|
||||
}
|
||||
|
||||
export function postApiModuleByTrash(projectId, protocol, currentVersion, param) {
|
||||
let url = '/api/module/trash/list/' + projectId + '/' + protocol + '/' + (currentVersion ? '/' + currentVersion : '');
|
||||
return post(url, param);
|
||||
}
|
||||
|
||||
export function getUserDefaultApiType() {
|
||||
let url = '/api/module/default-type';
|
||||
return get(url);
|
||||
|
|
|
@ -5,6 +5,11 @@ export function getModuleByProjectId(projectId) {
|
|||
return get(url);
|
||||
}
|
||||
|
||||
export function postModuleByProjectId(projectId, param) {
|
||||
let url = '/api/automation/module/list/' + projectId;
|
||||
return post(url, param);
|
||||
}
|
||||
|
||||
export function getModuleByRelevanceProjectId(relevanceProjectId) {
|
||||
let url = '/api/automation/module/list/' + relevanceProjectId;
|
||||
return get(url);
|
||||
|
@ -15,6 +20,11 @@ export function getModuleByTrash(projectId) {
|
|||
return get(url);
|
||||
}
|
||||
|
||||
export function postModuleByTrash(projectId, param) {
|
||||
let url = '/api/automation/module/trash/list/' + projectId;
|
||||
return post(url, param);
|
||||
}
|
||||
|
||||
export function editScenarioModule(params) {
|
||||
return post('/api/automation/module/edit', params);
|
||||
}
|
||||
|
|
|
@ -830,6 +830,7 @@ export default {
|
|||
}
|
||||
},
|
||||
search(projectId) {
|
||||
this.$EventBus.$emit("scenarioConditionBus", this.condition)
|
||||
if (this.needRefreshModule()) {
|
||||
this.$emit('refreshTree');
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ import {
|
|||
getModuleByProjectId,
|
||||
getModuleByRelevanceProjectId,
|
||||
getModuleByTrash,
|
||||
posScenarioModule,
|
||||
posScenarioModule, postModuleByProjectId, postModuleByTrash,
|
||||
} from '@/api/scenario-module';
|
||||
|
||||
export default {
|
||||
|
@ -112,6 +112,7 @@ export default {
|
|||
filterText: '',
|
||||
trashEnable: false,
|
||||
},
|
||||
param: {},
|
||||
data: [],
|
||||
currentModule: undefined,
|
||||
operators: [
|
||||
|
@ -155,6 +156,7 @@ export default {
|
|||
this.filter();
|
||||
},
|
||||
'condition.trashEnable'() {
|
||||
this.param = {};
|
||||
this.$emit('enableTrash', this.condition.trashEnable);
|
||||
},
|
||||
relevanceProjectId() {
|
||||
|
@ -162,9 +164,21 @@ export default {
|
|||
},
|
||||
isTrashData() {
|
||||
this.condition.trashEnable = this.isTrashData;
|
||||
this.list();
|
||||
this.param = {};
|
||||
},
|
||||
},
|
||||
created(){
|
||||
this.$EventBus.$on("scenarioConditionBus", (param)=>{
|
||||
this.param = param;
|
||||
this.list();
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$EventBus.$off("scenarioConditionBus", (param)=>{
|
||||
this.param = param;
|
||||
this.list();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
handleImport() {
|
||||
if (this.projectId) {
|
||||
|
@ -188,11 +202,11 @@ export default {
|
|||
this.setData(response);
|
||||
});
|
||||
} else if (this.isTrashData) {
|
||||
this.result = getModuleByTrash(projectId ? projectId : this.projectId).then((response) => {
|
||||
this.result = postModuleByTrash(projectId ? projectId : this.projectId, this.param).then((response) => {
|
||||
this.setData(response);
|
||||
});
|
||||
} else {
|
||||
this.result = getModuleByProjectId(projectId ? projectId : this.projectId).then((response) => {
|
||||
this.result = postModuleByProjectId(projectId ? projectId : this.projectId, this.param).then((response) => {
|
||||
this.setData(response);
|
||||
});
|
||||
}
|
||||
|
@ -287,14 +301,14 @@ export default {
|
|||
if (!this.projectId) {
|
||||
return;
|
||||
}
|
||||
getModuleByTrash(this.projectId).then((response) => {
|
||||
postModuleByTrash(this.projectId, this.param).then((response) => {
|
||||
this.setModuleList(response, selectNodeId);
|
||||
});
|
||||
} else {
|
||||
if (!this.projectId) {
|
||||
return;
|
||||
}
|
||||
getModuleByProjectId(this.projectId).then((response) => {
|
||||
postModuleByProjectId(this.projectId, this.param).then((response) => {
|
||||
this.setModuleList(response, selectNodeId);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<el-form :model="formData" :rules="rules" label-width="105px" v-loading="result" ref="form">
|
||||
<el-form :model="formData" :rules="rules" label-width="110px" v-loading="result" ref="form">
|
||||
<el-row>
|
||||
<el-col :span="11">
|
||||
<el-form-item :label="$t('commons.import_module')">
|
||||
|
@ -37,8 +37,8 @@
|
|||
checkStrictly />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.import_mode')" prop="modeId">
|
||||
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
<el-select size="small" v-model="formData.modeId" class="project-select" clearable style="width: 100%">
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
<el-checkbox size="mini" v-if="formData.modeId === 'fullCoverage'" v-model="formData.coverModule">
|
||||
{{ this.$t('commons.cover_scenario') }}
|
||||
|
@ -56,7 +56,7 @@
|
|||
<el-form-item
|
||||
v-xpack
|
||||
v-if="projectVersionEnable && formData.modeId === 'fullCoverage'"
|
||||
:label="$t('api_test.api_import.data_update_version')"
|
||||
:label="$t('api_test.scenario_import.data_update_version')"
|
||||
prop="versionId">
|
||||
<el-select size="small" v-model="formData.updateVersionId" clearable style="width: 100%">
|
||||
<el-option v-for="item in versionOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
|
@ -65,7 +65,7 @@
|
|||
<el-form-item
|
||||
v-xpack
|
||||
v-if="projectVersionEnable && formData.modeId === 'fullCoverage'"
|
||||
:label="$t('api_test.api_import.data_new_version')"
|
||||
:label="$t('api_test.scenario_import.data_new_version')"
|
||||
prop="versionId">
|
||||
<el-select size="small" v-model="formData.versionId" clearable style="width: 100%">
|
||||
<el-option v-for="item in versionOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||
|
@ -426,9 +426,6 @@ export default {
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.el-form {
|
||||
padding: 30px 10px;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
float: right;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="json-schema-editor" style="padding: 0 10px">
|
||||
<el-row class="row" :gutter="10">
|
||||
<el-row class="row" :gutter="10" v-if="reloadSelfOver">
|
||||
<el-col :style="{ minWidth: `${200 - 10 * deep}px` }" class="ms-col-name">
|
||||
<div :style="{ marginLeft: `${10 * deep}px` }" class="ms-col-name-c" />
|
||||
<span
|
||||
|
@ -401,6 +401,7 @@ export default {
|
|||
countAdd: 1,
|
||||
modalVisible: false,
|
||||
reloadItemOver: true,
|
||||
reloadSelfOver: true,
|
||||
advancedValue: {},
|
||||
addProp: {}, // 自定义属性
|
||||
customProps: [],
|
||||
|
@ -484,15 +485,19 @@ export default {
|
|||
changeAllItemsType(changeType) {
|
||||
if (this.isArray(this.pickValue) && this.pickValue.items && this.pickValue.items.length > 0) {
|
||||
this.pickValue.items.forEach((item) => {
|
||||
item.type = changeType;
|
||||
delete item['properties'];
|
||||
delete item['items'];
|
||||
delete item['required'];
|
||||
delete item['mock'];
|
||||
this.$set(item, 'type', changeType);
|
||||
if (changeType === 'array') {
|
||||
this.$set(item, 'items', [{ type: 'string', mock: { mock: '' } }]);
|
||||
}
|
||||
});
|
||||
this.$nextTick(() => {
|
||||
this.reloadSelf();
|
||||
this.reloadItems();
|
||||
});
|
||||
}
|
||||
},
|
||||
onCheck(e) {
|
||||
|
@ -609,6 +614,13 @@ export default {
|
|||
this.reloadItemOver = true;
|
||||
});
|
||||
},
|
||||
|
||||
reloadSelf() {
|
||||
this.reloadSelfOver = false;
|
||||
this.$nextTick(() => {
|
||||
this.reloadSelfOver = true;
|
||||
});
|
||||
},
|
||||
editScenarioAdvance(data) {
|
||||
this.$emit('editScenarioAdvance', data);
|
||||
},
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
const value = {
|
||||
description: null,
|
||||
};
|
||||
const attr = {
|
||||
description: {
|
||||
name: '描述',
|
||||
type: 'string',
|
||||
},
|
||||
};
|
||||
const wrapper = { value, attr };
|
||||
export default wrapper;
|
|
@ -4,6 +4,7 @@ import _array from './array';
|
|||
import _boolean from './boolean';
|
||||
import _integer from './integer';
|
||||
import _number from './number';
|
||||
import _null from './null';
|
||||
|
||||
const TYPE_NAME = ['string', 'number', 'integer', 'object', 'array', 'boolean', 'null'];
|
||||
|
||||
|
@ -14,7 +15,7 @@ const TYPE = {
|
|||
boolean: _boolean,
|
||||
integer: _integer,
|
||||
number: _number,
|
||||
null: { description: null },
|
||||
null: _null,
|
||||
};
|
||||
export { TYPE, TYPE_NAME };
|
||||
|
||||
|
|
|
@ -212,6 +212,14 @@ export default {
|
|||
this.assignKey(to, from, key);
|
||||
}
|
||||
}
|
||||
//判断null
|
||||
if (to.type && from.type) {
|
||||
to.type = from.type;
|
||||
if (from.type === 'null') {
|
||||
this.assign(Object(to.mock), { mock: '' });
|
||||
}
|
||||
}
|
||||
|
||||
let property = ['description', 'maxLength', 'minLength', 'pattern', 'format', 'enum', 'default'];
|
||||
// 清除多出部分属性
|
||||
for (let key in to) {
|
||||
|
|
|
@ -849,6 +849,7 @@ export default {
|
|||
this.search();
|
||||
},
|
||||
search() {
|
||||
this.$EventBus.$emit("apiConditionBus", this.condition)
|
||||
this.changeSelectDataRangeAll();
|
||||
this.initTable();
|
||||
},
|
||||
|
|
|
@ -52,7 +52,7 @@ import {
|
|||
getApiModuleByTrash,
|
||||
getApiModules,
|
||||
getUserDefaultApiType,
|
||||
posModule,
|
||||
posModule, postApiModuleByTrash, postApiModules,
|
||||
} from '@/api/definition-module';
|
||||
import MsAddBasisApi from '../basis/AddBasisApi';
|
||||
import SelectMenu from '@/business/commons/SelectMenu';
|
||||
|
@ -83,6 +83,7 @@ export default {
|
|||
},
|
||||
data: [],
|
||||
currentModule: {},
|
||||
param: {},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -155,6 +156,7 @@ export default {
|
|||
this.list();
|
||||
},
|
||||
'condition.trashEnable'() {
|
||||
this.param = {};
|
||||
this.$emit('enableTrash', this.condition.trashEnable);
|
||||
},
|
||||
relevanceProjectId() {
|
||||
|
@ -164,6 +166,7 @@ export default {
|
|||
this.list();
|
||||
},
|
||||
isTrashData() {
|
||||
this.param = {};
|
||||
this.condition.trashEnable = this.isTrashData;
|
||||
this.list();
|
||||
},
|
||||
|
@ -173,6 +176,18 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
created(){
|
||||
this.$EventBus.$on("apiConditionBus", (param)=>{
|
||||
this.param = param;
|
||||
this.list();
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$EventBus.$off("apiConditionBus", (param)=>{
|
||||
this.param = param;
|
||||
this.list();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
initProtocol() {
|
||||
//不是跳转来的页面不查询上次请求类型
|
||||
|
@ -220,11 +235,11 @@ export default {
|
|||
}
|
||||
);
|
||||
} else if (this.isTrashData) {
|
||||
this.result = getApiModuleByTrash(projectId, this.condition.protocol, this.currentVersion).then((response) => {
|
||||
this.result = postApiModuleByTrash(projectId, this.condition.protocol, this.currentVersion, this.param).then((response) => {
|
||||
this.setData(response);
|
||||
});
|
||||
} else {
|
||||
this.result = getApiModules(projectId, this.condition.protocol, this.currentVersion).then((response) => {
|
||||
this.result = postApiModules(projectId, this.condition.protocol, this.currentVersion, this.param).then((response) => {
|
||||
this.setData(response);
|
||||
});
|
||||
}
|
||||
|
@ -329,16 +344,20 @@ export default {
|
|||
this.setNohupData(response, selectNodeId);
|
||||
});
|
||||
} else if (this.isTrashData) {
|
||||
getApiModuleByTrash(
|
||||
postApiModuleByTrash(
|
||||
this.projectId,
|
||||
this.condition.protocol + (this.currentVersion ? '/' + this.currentVersion : '')
|
||||
this.condition.protocol,
|
||||
this.currentVersion,
|
||||
this.param
|
||||
).then((response) => {
|
||||
this.setNohupData(response, selectNodeId);
|
||||
});
|
||||
} else {
|
||||
getApiModules(
|
||||
postApiModules(
|
||||
this.projectId,
|
||||
this.condition.protocol + (this.currentVersion ? '/' + this.currentVersion : '')
|
||||
this.condition.protocol,
|
||||
this.currentVersion,
|
||||
this.param
|
||||
).then((response) => {
|
||||
this.setNohupData(response, selectNodeId);
|
||||
});
|
||||
|
|
|
@ -25,17 +25,17 @@
|
|||
<el-row style="margin: 20px">
|
||||
<span style="margin-right: 10px"> {{ $t('api_test.request.cert_alias') }}: </span>
|
||||
<span style="margin-right: 10px">
|
||||
<el-input size="small" style="width: 350px" v-model="request.alias" />
|
||||
<el-input size="small" style="width: 350px" v-model="request.alias" :disabled="isReadOnly"/>
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row style="margin: 20px">
|
||||
<span style="margin-right: 10px">
|
||||
<el-checkbox class="follow-redirects-item" v-model="request.followRedirects" @change="changeFollow">{{
|
||||
<el-checkbox class="follow-redirects-item" v-model="request.followRedirects" @change="changeFollow" :disabled="isReadOnly">{{
|
||||
$t('api_test.request.follow_redirects')
|
||||
}}</el-checkbox>
|
||||
</span>
|
||||
<span style="margin-left: 10px; margin-right: 10px">
|
||||
<el-checkbox class="follow-redirects-item" v-model="request.autoRedirects" @change="changeAuto">{{
|
||||
<el-checkbox class="follow-redirects-item" v-model="request.autoRedirects" @change="changeAuto" :disabled="isReadOnly">{{
|
||||
$t('api_definition.request.auto_redirects')
|
||||
}}</el-checkbox>
|
||||
</span>
|
||||
|
|
|
@ -65,6 +65,9 @@ export default {
|
|||
} else {
|
||||
this.$EventBus.$emit('API_TEST_ERROR', this.reportId);
|
||||
}
|
||||
} else {
|
||||
this.loading = true;
|
||||
this.socketSync();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
:placeholder="$t('home.dashboard.public.default_version')"
|
||||
size="small"
|
||||
style="height: 100%">
|
||||
<el-option v-for="item in versions" :key="item.id" :label="item.name" :value="item.id"> </el-option>
|
||||
<el-option v-for="item in versions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
|
@ -233,6 +233,7 @@ export default {
|
|||
.api-home-layout :deep(.dashboard-title) {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #1f2329;
|
||||
}
|
||||
|
||||
.api-home-layout :deep(.common-amount) {
|
||||
|
|
|
@ -123,15 +123,13 @@ export default {
|
|||
tableData: [],
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
versionId: '',
|
||||
total: 0,
|
||||
condition: {
|
||||
filters: {},
|
||||
},
|
||||
};
|
||||
},
|
||||
activated() {
|
||||
this.search();
|
||||
},
|
||||
methods: {
|
||||
clickRow(row, column) {
|
||||
if (column.property !== 'status') {
|
||||
|
@ -143,10 +141,13 @@ export default {
|
|||
}
|
||||
},
|
||||
search(versionId) {
|
||||
if (versionId) {
|
||||
this.versionId = versionId;
|
||||
}
|
||||
let projectId = getCurrentProjectID();
|
||||
this.loading = true;
|
||||
this.loadError = false;
|
||||
this.result = getRunningTask(projectId, versionId, this.currentPage, this.pageSize)
|
||||
this.result = getRunningTask(projectId, this.versionId, this.currentPage, this.pageSize)
|
||||
.then((response) => {
|
||||
this.total = response.data.itemCount;
|
||||
this.tableData = response.data.listObject;
|
||||
|
|
|
@ -156,12 +156,14 @@ export default {
|
|||
}
|
||||
},
|
||||
search(versionId) {
|
||||
this.versionId = versionId;
|
||||
if (versionId) {
|
||||
this.versionId = versionId;
|
||||
}
|
||||
let projectId = getCurrentProjectID();
|
||||
this.loading = true;
|
||||
this.loadError = false;
|
||||
|
||||
this.result = definitionWeekList(projectId, versionId, this.currentPage, this.pageSize)
|
||||
this.result = definitionWeekList(projectId, this.versionId, this.currentPage, this.pageSize)
|
||||
.then((response) => {
|
||||
this.total = response.data.itemCount;
|
||||
this.tableData = response.data.listObject;
|
||||
|
|
|
@ -118,9 +118,9 @@
|
|||
<template slot-scope="scope">
|
||||
<el-select
|
||||
v-model="scope.row.type"
|
||||
v-if="!scope.row.scope || scope.row.scope == 'api'"
|
||||
:placeholder="$t('commons.please_select')"
|
||||
size="mini"
|
||||
@change="changeType(scope.row)"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in typeSelectOptions"
|
||||
|
@ -129,6 +129,20 @@
|
|||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
|
||||
<el-select
|
||||
v-else
|
||||
v-model="scope.row.type"
|
||||
:placeholder="$t('commons.please_select')"
|
||||
size="mini"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in uiTypeSelectOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
|
||||
|
@ -271,6 +285,12 @@ export default {
|
|||
{value: "COUNTER", label: this.$t("api_test.automation.counter")},
|
||||
{value: "RANDOM", label: this.$t("api_test.automation.random")},
|
||||
],
|
||||
uiTypeSelectOptions: [
|
||||
{value: "STRING", label: this.$t("api_test.automation.string")},
|
||||
{value: "ARRAY", label: this.$t("api_test.automation.array")},
|
||||
{value: "JSON", label: this.$t("api_test.automation.json")},
|
||||
{value: "NUMBER", label: this.$t("api_test.automation.number")},
|
||||
],
|
||||
variables: {},
|
||||
selectVariable: "",
|
||||
editData: {},
|
||||
|
@ -343,6 +363,10 @@ export default {
|
|||
data.files = [];
|
||||
data.quotedData = "false";
|
||||
}
|
||||
|
||||
if (!data.scope || data.scope == "ui") {
|
||||
data.type = 'STRING';
|
||||
}
|
||||
},
|
||||
valueText(data) {
|
||||
switch (data.type) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<el-col :span="16">
|
||||
<el-select v-model="selfQuantity" placeholder=" " size="mini" filterable default-first-option
|
||||
allow-create
|
||||
class="timing_select" :disabled="selfChoose">
|
||||
class="timing_select" :disabled="selfChoose" @change="chooseChange(true)">
|
||||
<el-option
|
||||
v-for="item in quantityOptions"
|
||||
:key="item"
|
||||
|
@ -12,7 +12,7 @@
|
|||
</el-option>
|
||||
</el-select>
|
||||
<el-select v-model="selfUnit" placeholder=" " size="mini"
|
||||
class="timing_select" :disabled="selfChoose">
|
||||
class="timing_select" :disabled="selfChoose" @change="chooseChange(true)">
|
||||
<el-option
|
||||
v-for="item in unitOptions"
|
||||
:key="item.value"
|
||||
|
@ -58,6 +58,7 @@ export default {
|
|||
type: Array,
|
||||
default() {
|
||||
return [
|
||||
{value: "H", label: this.$t('commons.date_unit.hour')},
|
||||
{value: "D", label: this.$t('commons.date_unit.day')},
|
||||
{value: "M", label: this.$t('commons.date_unit.month')},
|
||||
{value: "Y", label: this.$t('commons.date_unit.year')},
|
||||
|
@ -66,8 +67,11 @@ export default {
|
|||
}
|
||||
},
|
||||
watch: {
|
||||
expr(val) {
|
||||
this.parseExpr(val);
|
||||
expr: {
|
||||
handler(val) {
|
||||
this.parseExpr(val);
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
choose(val) {
|
||||
this.selfChoose = val;
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
:class="{ 'el-icon-arrow-left pointer' : !active, 'el-icon-arrow-down pointer' : active}"
|
||||
@click="active=!active"></span>
|
||||
</el-tooltip>
|
||||
<template v-if="active">
|
||||
<div v-show="active">
|
||||
<slot></slot>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -156,10 +156,16 @@ export default {
|
|||
if (!hasLicense()) {
|
||||
return;
|
||||
}
|
||||
ModuleEvent.$on(MODULE_CHANGE, (key, status) => {
|
||||
getModuleList().then(() => {
|
||||
this.menuKey++;
|
||||
getModuleList()
|
||||
.then(response => {
|
||||
response.data.forEach(m => {
|
||||
this.modules[m.key] = m.status;
|
||||
});
|
||||
localStorage.setItem('modules', JSON.stringify(this.modules));
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
ModuleEvent.$on(MODULE_CHANGE, (key, status) => {
|
||||
this.$set(this.modules, key, status);
|
||||
this.menuKey++;
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<font-awesome-icon class="icon global focusing" :icon="['fas', 'tasks']"/>
|
||||
</el-badge>
|
||||
</div>
|
||||
<font-awesome-icon @click="showTaskCenter" class="icon global focusing" :icon="['fas', 'tasks']" v-else/>
|
||||
<font-awesome-icon @click="open('API')" class="icon global focusing" :icon="['fas', 'tasks']" v-else/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<el-drawer
|
||||
|
@ -299,7 +299,9 @@ export default {
|
|||
if (activeName) {
|
||||
this.activeName = activeName;
|
||||
}
|
||||
this.showTaskCenter();
|
||||
this.init(true);
|
||||
this.taskVisible = true;
|
||||
setTimeout(this.showTaskCenter, 2000);
|
||||
},
|
||||
getPercentage(status) {
|
||||
if (status) {
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<font-awesome-icon v-if="scope.row.isCurrent"
|
||||
class="icon global focusing" :icon="['fas', 'tag']"/>
|
||||
{{ scope.row.name }}
|
||||
<el-tag v-if="scope.row.id === dataLatestId" size="mini" type="primary">
|
||||
{{ $t('api_test.api_import.latest_version') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" column-key="status"
|
||||
|
@ -52,11 +55,11 @@
|
|||
placement="bottom"
|
||||
width="100"
|
||||
trigger="hover"
|
||||
v-if="!scope.row.latest"
|
||||
v-if="scope.row.isCheckout || scope.row.status !== 'open'"
|
||||
>
|
||||
<div style="text-align: left;">
|
||||
<el-link @click="setLatest(scope.row)" v-if="hasLatest && !scope.row.isCurrent && scope.row.isCheckout"
|
||||
:disabled="scope.row.isCurrent || isRead">
|
||||
<el-link @click="setLatest(scope.row)" v-if="hasLatest && scope.row.isCheckout"
|
||||
:disabled="isRead || scope.row.id === dataLatestId">
|
||||
{{ $t('project.version.set_new') }}
|
||||
</el-link>
|
||||
<br/>
|
||||
|
@ -66,8 +69,6 @@
|
|||
</div>
|
||||
<span slot="reference">...</span>
|
||||
</el-popover>
|
||||
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -109,7 +110,7 @@ export default {
|
|||
hasLatest: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -118,6 +119,7 @@ export default {
|
|||
versionOptions: [],
|
||||
userData: {},
|
||||
currentVersion: {},
|
||||
dataLatestId: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -176,6 +178,10 @@ export default {
|
|||
this.loading = false;
|
||||
return;
|
||||
}
|
||||
let latestData = versionData.filter((v) => v.latest === true);
|
||||
if (latestData) {
|
||||
this.dataLatestId = latestData[0].versionId;
|
||||
}
|
||||
this.versionOptions.forEach(version => {
|
||||
let vs = versionData.filter(v => v.versionId === version.id);
|
||||
version.isCheckout = vs.length > 0; // 已存在可以切换,不存在则创建
|
||||
|
@ -210,7 +216,7 @@ export default {
|
|||
testUsers() {
|
||||
this.updateUserDataByExternal();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1915,10 +1915,14 @@ const message = {
|
|||
no_cover_tip_scenario_1: "1. The same Scenario that already exists in the system will not be changed",
|
||||
no_cover_tip_scenario_2: "2. Add Scenario that do not exist in the system",
|
||||
import_version: 'Import version',
|
||||
data_update_version: 'New API created to',
|
||||
data_new_version: 'The same API is updated to',
|
||||
data_update_version: 'The same API is updated to',
|
||||
data_new_version: 'New API created to',
|
||||
latest_version: 'Latest version',
|
||||
},
|
||||
scenario_import: {
|
||||
data_update_version: 'The same Scenario is updated to',
|
||||
data_new_version: 'New Scenario created to',
|
||||
},
|
||||
home_page: {
|
||||
unit_of_measurement: "",
|
||||
unit_of_count: "",
|
||||
|
@ -3185,7 +3189,7 @@ const message = {
|
|||
store_window_handle: "Window Handle(storeWindowHandle)",
|
||||
store_title: "Web page title (storeTitle)",
|
||||
wait_time: "Wait time",
|
||||
per_tip: "After enabling the performance mode, the memory and cpu usage will be reduced, and the running results will not show step screenshots",
|
||||
per_tip: "After enabling the performance mode, the memory and cpu usage will be reduced",
|
||||
fail_over: "Failed to terminate",
|
||||
validate_tip: "Check means a hard assertion (assert), if the assertion fails, the program will terminate. Unchecked means a soft assertion (verify), if the assertion fails, the program will not terminate.",
|
||||
scenario: "Scenario",
|
||||
|
|
|
@ -1928,6 +1928,10 @@ const message = {
|
|||
data_new_version: '新增API创建到',
|
||||
latest_version: '最新版本',
|
||||
},
|
||||
scenario_import: {
|
||||
data_update_version: '同一场景更新到',
|
||||
data_new_version: '新增场景创建到',
|
||||
},
|
||||
home_page: {
|
||||
unit_of_measurement: "个",
|
||||
unit_of_count: "个",
|
||||
|
@ -3200,7 +3204,7 @@ const message = {
|
|||
store_window_handle: "窗口 Handle(storeWindowHandle)",
|
||||
store_title: "网页标题(storeTitle)",
|
||||
wait_time: "等待时间",
|
||||
per_tip: "启用性能模式后,将减少内存和cpu的占用,运行结果不展示步骤截图",
|
||||
per_tip: "启用性能模式后,将减少内存和cpu的占用",
|
||||
fail_over: "失败终止",
|
||||
validate_tip: "勾选表示为硬断言(assert),如果断言失败,程序会终止。不勾选表示为软断言(verify),如果断言失败,程序不会终止。",
|
||||
scenario: "场景",
|
||||
|
|
|
@ -1924,6 +1924,10 @@ const message = {
|
|||
data_new_version: '新增API創建到',
|
||||
latest_version: '最新版本',
|
||||
},
|
||||
scenario_import: {
|
||||
data_update_version: '同一場景更新到',
|
||||
data_new_version: '新增場景創建到',
|
||||
},
|
||||
home_page: {
|
||||
unit_of_measurement: "個",
|
||||
unit_of_count: "個",
|
||||
|
@ -2916,28 +2920,24 @@ const message = {
|
|||
ui_automation: "UI 自動化",
|
||||
ui_element: "元素庫",
|
||||
report: "測試報告",
|
||||
ui_debug_mode: 'UI自動化調試管道',
|
||||
ui_local_debug: '本地調試',
|
||||
ui_server_debug: '後端調試',
|
||||
all_element: "全部元素",
|
||||
element_name: "元素名稱",
|
||||
element_locator: "元素定位",
|
||||
element_locator_type: "定位類型",
|
||||
screenshot: "截圖",
|
||||
update_user: "更新人",
|
||||
create_user: "創建人",
|
||||
all_scenario: "全部場景",
|
||||
scenario_list: "場景列表",
|
||||
log: "日誌",
|
||||
performance_mode: "性能模式",
|
||||
error_handling: "錯誤處理",
|
||||
other_settings: "其他設置",
|
||||
step_results: "步骤结果",
|
||||
treatment_method: "處理方式",
|
||||
scenario_steps: "場景步驟",
|
||||
basic_information: "基礎信息",
|
||||
check_element: "請勾選元素",
|
||||
check_subitem: '請選擇子分類',
|
||||
selenium_tip: "支持 Selenium-IDE 插件格式導入",
|
||||
selenium_export_tip: "通過 MeterSphere 導出 side 文件",
|
||||
elementObject: "元素對象",
|
||||
elementLocator: "元素定位",
|
||||
elementType: "所屬分類",
|
||||
not_selected: "(未選擇元素)",
|
||||
not_selected_location: "(未選擇元素定位)",
|
||||
location: "定位",
|
||||
run: "運行",
|
||||
locate_type: "定位方式",
|
||||
coord: "坐標",
|
||||
enable_or_not: "啟用/禁用",
|
||||
enable: "啟用",
|
||||
disable: "禁用",
|
||||
resolution: "分辨率",
|
||||
ignore_fail: "忽略異常並繼續執行",
|
||||
not_ignore_fail: "終止流程",
|
||||
cmdValidation: "斷言",
|
||||
cmdValidateValue: "斷言值",
|
||||
cmdValidateText: "彈窗文本",
|
||||
|
@ -2964,10 +2964,138 @@ const message = {
|
|||
cmdElse: "Else",
|
||||
cmdElseIf: "ElseIf",
|
||||
close: "關閉網頁",
|
||||
cmdExtraction: "提取參數",
|
||||
cmdExtraction: "數據提取",
|
||||
cmdExtractWindow: "提取窗口信息",
|
||||
cmdExtractElement: "提取元素信息",
|
||||
valiate_fail: "校驗失敗,請檢查必填項",
|
||||
ui_debug_mode: 'UI自動化調試方式',
|
||||
ui_local_debug: '本地調試',
|
||||
ui_server_debug: '後端調試',
|
||||
all_element: "全部元素",
|
||||
element_name: "元素名稱",
|
||||
element_locator: "元素定位",
|
||||
element_locator_type: "定位類型",
|
||||
screenshot: "截圖",
|
||||
update_user: "更新人",
|
||||
create_user: "創建人",
|
||||
all_scenario: "全部場景",
|
||||
log: "日誌",
|
||||
performance_mode: "性能模式",
|
||||
error_handling: "錯誤處理",
|
||||
other_settings: "其他設置",
|
||||
step_results: "步驟結果",
|
||||
treatment_method: "處理方式",
|
||||
scenario_steps: "場景步驟",
|
||||
basic_information: "基礎信息",
|
||||
step_type: "步驟類型",
|
||||
input_or_not: "是否輸入",
|
||||
input_content: "輸入內容",
|
||||
insert_content: "鍵入內容",
|
||||
append_content: "追加輸入",
|
||||
append_tip: "勾選,在現有內容後面追加輸入;<br/>不勾選,清空現有內容後再進行輸入",
|
||||
pls_input: "請輸入內容",
|
||||
opt_type: "操作方式:",
|
||||
yes: "是",
|
||||
no: "否",
|
||||
confirm: "確定",
|
||||
cancel: "取消",
|
||||
press_button: "點擊彈窗確定按鈕或取消按鈕",
|
||||
param_null: "參數不能為空",
|
||||
operation_object: "操作對象",
|
||||
sub_item: "子選項",
|
||||
value: "值",
|
||||
select: "選擇",
|
||||
option: "選項( Option )",
|
||||
index: "索引( Index )",
|
||||
s_value: "值( Value )",
|
||||
text: "文本( Text )",
|
||||
set_itera: "設置遍歷",
|
||||
foreach_tip: "設置循環叠代,支持數組行數據,例如: [1,2,3];也可輸入變量",
|
||||
intervals: "間隔時間",
|
||||
condition_type: "條件類型",
|
||||
please_select: "請選擇",
|
||||
condition_list: "條件列表:通過列表的方式設置多個條件",
|
||||
condition_list_: "條件列表",
|
||||
condition_exp: "條件表達式:表達式判斷為真,則執行裏面的步驟",
|
||||
condition_exp_: "條件表達式",
|
||||
expression: "表達式",
|
||||
if_tip: "變量請使用${var},字符串請加單引號,如:${name} === '張三'",
|
||||
input_c_tip: "'可編輯段落的元素 contenteditable 屬性必須為 true, 才可實現輸入;例:<p contenteditable="true">這是一段可編輯的段落。請試著編輯該文本。</p>'",
|
||||
input: "輸入框",
|
||||
editable_p: "可編輯段落",
|
||||
click_type: "點擊方式",
|
||||
set_click_point: "設置鼠標點擊位置",
|
||||
click_tip_1: "勾選,可控製鼠標在元素上的點擊位置",
|
||||
element_location: "元素位置",
|
||||
click_point: "點擊位置",
|
||||
x: "橫坐標",
|
||||
y: "縱坐標",
|
||||
click_tip_2: "默認元素的左上角為0,0;通過設置相對位置,控製鼠標在元素上的點擊位置",
|
||||
click: "單擊",
|
||||
dclick: "雙擊",
|
||||
press: "按下",
|
||||
standup: "彈起",
|
||||
mouse_start: "鼠標起始位置",
|
||||
drag_start: "被拖拽的元素起點的位置",
|
||||
mouse_end: "鼠標終點位置",
|
||||
drag_end: "被拖拽的元素最終的位置",
|
||||
move_type: "移動方式",
|
||||
mouse_location: "鼠標位置",
|
||||
relative_location: "相對坐標位置",
|
||||
move_tip: "相對位置,元素當前的位置坐標為0,0",
|
||||
mouse_out_e: "鼠標移出元素",
|
||||
mouse_in_e: "鼠標移入元素",
|
||||
mouse_e_to_c: "鼠標從元素移到坐標位置",
|
||||
url: "網頁地址",
|
||||
sf_tip: "如果是切換 frame,需要傳入索引或者元素定位後再切換",
|
||||
sf_index: "frame 索引號",
|
||||
select_index: "選擇當前頁面的第幾個 frame;",
|
||||
select_f_tip: "例:比如索引值輸入 1,那麽效果會切換到當前頁面的第 2 個 frame(索引值從 0 開始計算)",
|
||||
exit_frame: "退出當前 frame(回到主頁面)",
|
||||
select_frame_index: "根據 frame 索引號切換到指定 frame",
|
||||
select_by_location: "根據定位方式切換 frame",
|
||||
sw_tip1: "如果是切換到指定窗口,需要傳入句柄",
|
||||
handle_id: "句柄 ID",
|
||||
window_handle: "窗口句柄 ID",
|
||||
frame_index: "網頁索引號",
|
||||
window_index: "窗口網頁索引號",
|
||||
select_open_window: "選擇打開過的第幾個網頁;",
|
||||
s_w_t1: "例:比如索引值輸入 3,那麽效果會切換到已經打開過的第 3 個窗口(索引值從 1 開始計算)",
|
||||
switch_by_id: "根據句柄 ID 切換到指定窗口",
|
||||
switch_by_index: "根據網頁索引號切換到指定窗口",
|
||||
swicth_to_default: "切換到初始窗口",
|
||||
ws_tip1: "指定尺寸,根據輸入的寬度和高度,設置窗口的大小",
|
||||
size: "尺寸:",
|
||||
by_pixel: "以像素為單位",
|
||||
width: "寬度",
|
||||
height: "高度",
|
||||
times: "循環次數",
|
||||
set_times: "設置循環的次數,可輸入變量",
|
||||
wait_text: "等待文本",
|
||||
wait_timeout: "等待超時",
|
||||
wait_for_text: "等待元素等於給定的定值(Text)",
|
||||
wait_for_ele_pre: "等待元素存在",
|
||||
wait_for_ele_visible: "等待元素顯示",
|
||||
wait_for_ele_not_visible: "等待元素不顯示",
|
||||
wait_for_ele_not_pre: "等待元素不存在",
|
||||
wait_for_ele_edi: "等待元素可編輯",
|
||||
wait_for_ele_not_edi: "等待元素不可編輯",
|
||||
wait_tip: "針對元素的Text屬性,指頁面展示出來的文本內容,等待超時時間為15000ms",
|
||||
exe_first: "先執行後判斷",
|
||||
while_t_1: "先執行後判斷類似 doWhile ,先執行一次循環體再判斷條件",
|
||||
while_t_2: "變量請使用${var},字符串請加單引號,如:${name} === '張三'",
|
||||
loop_time_out: "循環超時時間",
|
||||
operation: '操作',
|
||||
use_pixel: '指定尺寸(像素為單位)',
|
||||
fullscreen: '窗口最大化',
|
||||
program_controller: '流程控製',
|
||||
input_operation: '輸入操作',
|
||||
mouse_operation: '鼠標操作',
|
||||
element_operation: '元素操作',
|
||||
dialog_operation: '彈窗操作',
|
||||
browser_operation: '瀏覽器操作',
|
||||
check_element: "請勾選元素",
|
||||
check_subitem: '請選擇子分類',
|
||||
pause: '等待時間',
|
||||
browser: "瀏覽器",
|
||||
inner_drag: "元素內部拖拽",
|
||||
|
@ -2979,14 +3107,14 @@ const message = {
|
|||
not_screentshot: "不截圖",
|
||||
error_step_screenshot: "出現異常截圖",
|
||||
downloadScreenshot: "下載截圖文件",
|
||||
description: "備注",
|
||||
description: "描述",
|
||||
scenario_title: "場景",
|
||||
custom_command_title: "指令",
|
||||
custom_command_label: "自定義指令",
|
||||
automation_list: "自動化列錶",
|
||||
automation_list: "自動化列表",
|
||||
create_custom_command: "創建指令",
|
||||
create_custom_command_label: "創建自定義指令",
|
||||
import_by_list_label: "UI列錶導入",
|
||||
import_by_list_label: "UI列表導入",
|
||||
open_custom_command_label: "打開指令",
|
||||
debug_result_label: "調試結果",
|
||||
scenario_ref_label: "場景引用",
|
||||
|
@ -2997,13 +3125,162 @@ const message = {
|
|||
command_name_label: "指令名稱",
|
||||
command_steps_label: "指令步驟",
|
||||
command_step_info: "在右側添加指令步驟",
|
||||
default_module: "默認模塊",
|
||||
executing: "正在執行...",
|
||||
unexecute: "未執行",
|
||||
check_command: "請勾選指令",
|
||||
ip: "ip地址",
|
||||
cword: "詞語",
|
||||
csentence: "句子",
|
||||
cparagraph: "段落",
|
||||
loading: "加載中...",
|
||||
close_dialog: "關閉",
|
||||
unknown_scenario: "創建場景",
|
||||
unknown_instruction: "創建指令",
|
||||
unknown_element: "創建元素",
|
||||
scenario_ref_add_warning: "引用的場景/指令步驟及子步驟都無法添加其他步驟!",
|
||||
batch_editing_steps: "批量編輯步驟",
|
||||
wait_time_config: "超時時間設置",
|
||||
wait_element_timeout: "等待元素超時時間",
|
||||
more_config_options: "更多高級設置選項",
|
||||
updated_config_info: "更新後選項為",
|
||||
config_success: "配置成功",
|
||||
cmdFileUpload: "文件上傳",
|
||||
relevant_file: "關聯需要上傳的文件",
|
||||
validate_tips: "判斷實際的結果是否與期望的一致,可添加多條斷言",
|
||||
instruction: "指令",
|
||||
screen_tip: "場景步驟如果觸發原生彈窗(alert或prompt),或不存在頁面時,截圖不生效;",
|
||||
ele_css_attr: "元素CSS屬性",
|
||||
ele_css_tip1: "如元素的 CSS 屬性,color 屬性,font-size 屬性等",
|
||||
store_css_attr: "CSS屬性(storeCssAttribute)",
|
||||
validate_type: "請選擇斷言方式",
|
||||
expect_value: "期望值",
|
||||
expect_title: "請輸入期望的網頁標題",
|
||||
title_tip: "斷言當前窗口的標題是否跟期望值一致,完全匹配則斷言成功,否則失敗",
|
||||
input_var: "請輸入變量",
|
||||
input_expect: "請輸入期望值",
|
||||
var_tip: "斷言變量與期望值是否匹配",
|
||||
confirm_after_success: "成功後是否點擊確認按鈕",
|
||||
input_expect_text: "請輸入期望的彈窗文本",
|
||||
input_window_tip: "僅支持彈窗文本的斷言,選擇是,則斷言成功後會點擊彈窗上的確認按鈕,選擇否,則斷言成功後不點擊任何按鈕",
|
||||
select_value: "所選元素的值等於期望(SelectedValue)",
|
||||
select_label: "下拉框選項顯示的文本等於期望(SelectedLabel) ",
|
||||
not_select_value: "所選元素的值不等於期望(NotSelectedValue) ",
|
||||
assert_check: "元素被選中(Checked)",
|
||||
assert_editable: "元素可編輯(Editable)",
|
||||
assert_element_present: "元素存在(ElementPresent)",
|
||||
assert_element_not_present: "元素不存在(ElementNotPresent)",
|
||||
assert_element_not_checked: "元素未被選中(NotChecked)",
|
||||
assert_element_not_editable: "元素不可編輯(NotEditable)",
|
||||
assert_element_not_text: "元素文本不等於期望(NotText)",
|
||||
assert_text: "元素文本等於期望(Text)",
|
||||
assert_value: "元素值等於期望(Value)",
|
||||
script_tip: "僅支持js腳本,設置的腳本將在瀏覽器執行",
|
||||
script_type: "腳本類型",
|
||||
set_var: "設置變量",
|
||||
async: "異步",
|
||||
sync: "同步",
|
||||
return: "有返回值",
|
||||
no_return: "無返回值",
|
||||
sample_obj: "普通對象",
|
||||
is_string: "是否為字符串類型",
|
||||
like_string_tip: "如字符串、數字、json對象、數組等;",
|
||||
like_string_tip2: "註意:作為對象類型存儲時如果不是一個合法的 js 對象類型(如非法特殊字符、空格影響),可能會生成報告失敗。",
|
||||
ele_pro: "元素屬性",
|
||||
like_ele_tip: "如元素的 name 屬性,id 屬性,value 屬性等",
|
||||
xpath_locator: "xpath 路徑",
|
||||
xpath_tip: "只支持 xpath 方式的元素定位,返回的是一個數值",
|
||||
store: "普通對象(store)",
|
||||
store_text: "元素文本(storeText)",
|
||||
store_value: "元素值(storeValue)",
|
||||
store_attr: "元素屬性(storeAttribute)",
|
||||
store_xpath_count: "匹配 xpath 的元素數量(storeXpathCount)",
|
||||
store_window_handle: "窗口 Handle(storeWindowHandle)",
|
||||
store_title: "網頁標題(storeTitle)",
|
||||
wait_time: "等待時間",
|
||||
per_tip: "啟用性能模式後,將減少內存和cpu的占用",
|
||||
fail_over: "失敗終止",
|
||||
validate_tip: "勾選表示為硬斷言(assert),如果斷言失敗,程序會終止。不勾選表示為軟斷言(verify),如果斷言失敗,程序不會終止。",
|
||||
scenario: "場景",
|
||||
extract_type: "請選擇提取信息類型",
|
||||
input_handle_name: "請輸入存儲窗口 Handle 變量名",
|
||||
extract_tip: "將提取的內容保存到變量中",
|
||||
input_window_title: "請輸入存儲網頁標題變量名",
|
||||
revoke: "撤回",
|
||||
is_required: "是否必填",
|
||||
remark: "備註",
|
||||
result: "執行結果",
|
||||
var_step: "變量產生步驟",
|
||||
param_required_tip: "調試保存後,自動校驗變量使用情況, 必填為自定義步驟內使用到的參數;非必填為自定義步驟內未使用到的冗余參數",
|
||||
name: "名稱",
|
||||
parameter_configuration: "參數配置",
|
||||
enter_parameters: "入參",
|
||||
out_parameters: "出參",
|
||||
opt_ele: "操作元素",
|
||||
dst_ele: "目標元素",
|
||||
drag_type: "拖拽方式",
|
||||
drag_end_point: "被拖拽的元素最終的位置",
|
||||
add_file: "添加文件",
|
||||
file: "文件: 【",
|
||||
been_deleted: "】 已被刪除!請重新選擇文件!",
|
||||
click_force: "強製點擊",
|
||||
click_tip_3: "勾選,元素被遮擋,可強製點擊",
|
||||
pls_sel_loctype: "請選擇定位類型",
|
||||
pls_input_locator: "請填寫定位",
|
||||
import_template: "導入模板",
|
||||
download_template: "下載模板",
|
||||
import_desc: "導入說明",
|
||||
el_import_tip_1: "1.如果導入的ID已存在,則更新元素;",
|
||||
el_import_tip_2: "2.如果導入的ID為空或ID不存在,則新增元素;",
|
||||
el_import: "元素導入",
|
||||
empty_text: "暫無數據",
|
||||
confirm_del_el: "確認刪除元素 ",
|
||||
confirm_del: "確認刪除 ",
|
||||
confirm_del_in : "確認刪除指令 ",
|
||||
deng: "等 ",
|
||||
ge_instruction: "個指令 ?",
|
||||
ge_el: "個元素 ?",
|
||||
ge_scenario: " 個場景 ?",
|
||||
view_ref: "查看引用",
|
||||
unplanned_element: "未規劃元素",
|
||||
scenario_instruction: "場景/指令",
|
||||
element_beening: "模塊下的元素被",
|
||||
element_beening_desc: "元素被場景",
|
||||
reference: "引用",
|
||||
continue_or_not: "是否繼續",
|
||||
continue_or_not_delete: "是否繼續刪除",
|
||||
unplanned_scenario: "未規劃場景",
|
||||
unplanned_module: "未規劃模塊",
|
||||
confrim_del_node: "確定刪除節點 ",
|
||||
and_all_sub_node: " 及其子節點下所有資源?",
|
||||
instruction_is_referenced: "指令被引用:",
|
||||
scenario_is_referenced: "場景被引用:",
|
||||
confirm_del_ins: "確認刪除指令",
|
||||
confirm_del_scen: "確認刪除場景",
|
||||
check_grid: "連接失敗,請檢查 selenium-grid 服務狀態",
|
||||
check_grid_ip: "連接失敗,請檢查 selenium-grid 地址信息",
|
||||
view_config: "查看配置信息",
|
||||
config_ip: "沒有檢測到本地ip和端口信息,請在",
|
||||
personal_info: "個人信息",
|
||||
in_config: "中設置",
|
||||
assert_in_text: "元素文本包含期望(InText)",
|
||||
assert_not_in_text: "元素文本不包含期望(NotInText)",
|
||||
equal: "等於",
|
||||
not_equal: "不等於",
|
||||
contain: "包含",
|
||||
not_contain: "不包含",
|
||||
greater: "大於",
|
||||
greater_equal: "大於等於",
|
||||
lower: "小於",
|
||||
lower_equal: "小於等於",
|
||||
null: "空",
|
||||
not_null: "非空",
|
||||
assertion_configuration: "斷言配置",
|
||||
smart_variable_enable: "優先使用當前場景變量,沒有則使用原場景變量",
|
||||
use_origin_variable_scene: "使用原場景變量",
|
||||
use_origin_env_run: "使用原場景環境執行",
|
||||
open_new: "追加頁面",
|
||||
open_new_tip: "追加頁面,在新的頁面打開url,不勾選覆蓋當前url",
|
||||
},
|
||||
project_application: {
|
||||
workstation: {
|
||||
|
|
|
@ -84,6 +84,7 @@ export class HttpConfig extends BaseConfig {
|
|||
this.protocol = 'https';
|
||||
this.port = undefined;
|
||||
this.conditions = [];
|
||||
this.cookie = options.cookie ? options.cookie : [new Cookie()];
|
||||
this.isMock = false;
|
||||
this.description = "";
|
||||
this.set(options);
|
||||
|
@ -93,6 +94,7 @@ export class HttpConfig extends BaseConfig {
|
|||
|
||||
initOptions(options = {}) {
|
||||
options.headers = options.headers || [new KeyValue()];
|
||||
options.cookie = options.cookie || [new Cookie()];
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
@ -111,6 +113,20 @@ export class Host extends BaseConfig {
|
|||
}
|
||||
}
|
||||
|
||||
export class Cookie extends BaseConfig {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
this.cookie = undefined;
|
||||
this.expireTime = "1D";
|
||||
this.updateTime = undefined;
|
||||
this.relevanceId = undefined;
|
||||
this.enable = false;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------- Functions ------- */
|
||||
|
||||
|
|
|
@ -411,7 +411,7 @@ export function downloadPDF(ele, pdfName) {
|
|||
let imgWidth = 595.28;
|
||||
let imgHeight = (595.28 / contentWidth) * contentHeight;
|
||||
|
||||
let pageData = canvas.toDataURL("image/jpeg", 0.1);
|
||||
let pageData = canvas.toDataURL("image/jpeg", 1.0);
|
||||
let pdf = new JsPDF("", "pt", "a4");
|
||||
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
|
||||
//当内容未超过pdf一页显示的范围,无需分页
|
||||
|
|
|
@ -541,9 +541,11 @@ export function getCustomFieldValue(row, field, members) {
|
|||
return val;
|
||||
} else if (field.type === 'multipleInput') {
|
||||
let val = '';
|
||||
item.value.forEach(i => {
|
||||
val += i + ' ';
|
||||
});
|
||||
if (item.value instanceof Array) {
|
||||
item.value.forEach(i => {
|
||||
val += i + ' ';
|
||||
});
|
||||
}
|
||||
return val;
|
||||
} else if (field.type === 'datetime' || field.type === 'date') {
|
||||
return datetimeFormat(item.value);
|
||||
|
|
|
@ -18,6 +18,10 @@ export function getCurrentProjectID() {
|
|||
return getCurrentUser().lastProjectId;
|
||||
}
|
||||
|
||||
export function setCurrentProjectID(projectId) {
|
||||
sessionStorage.setItem(PROJECT_ID, projectId);
|
||||
}
|
||||
|
||||
export function getCurrentUser() {
|
||||
try {
|
||||
const store = useUserStore();
|
||||
|
|
|
@ -421,7 +421,7 @@ public class FileUtils {
|
|||
public static byte[] fileToByte(File tradeFile) {
|
||||
byte[] buffer = null;
|
||||
try (FileInputStream fis = new FileInputStream(tradeFile);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
|
||||
byte[] b = new byte[1024];
|
||||
int n;
|
||||
while ((n = fis.read(b)) != -1) {
|
||||
|
@ -429,7 +429,7 @@ public class FileUtils {
|
|||
}
|
||||
buffer = bos.toByteArray();
|
||||
} catch (Exception e) {
|
||||
LoggerUtil.error(e);
|
||||
LogUtil.error(e);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -321,11 +321,17 @@ public class PerformanceTestService {
|
|||
loadTest.setCreateUser(SessionUtils.getUserId());
|
||||
loadTest.setOrder(oldLoadTest.getOrder());
|
||||
loadTest.setRefId(oldLoadTest.getRefId());
|
||||
if (oldLoadTest.getLatest()) {
|
||||
loadTest.setLatest(false);
|
||||
}
|
||||
//插入文件
|
||||
copyLoadTestFiles(testId, loadTest.getId());
|
||||
loadTestMapper.insertSelective(loadTest);
|
||||
}
|
||||
//checkAndSetLatestVersion(loadTest.getRefId());
|
||||
String defaultVersion = baseProjectVersionMapper.getDefaultVersion(request.getProjectId());
|
||||
if (StringUtils.equalsIgnoreCase(request.getVersionId(), defaultVersion)) {
|
||||
checkAndSetLatestVersion(loadTest.getRefId());
|
||||
}
|
||||
return loadTest;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,8 @@ public class GroupService {
|
|||
@Resource
|
||||
private BaseUserService baseUserService;
|
||||
private static final String GLOBAL = "global";
|
||||
private static final String SUPER_GROUP = "super_group";
|
||||
|
||||
|
||||
// 服务权限拼装顺序
|
||||
private static final String[] servicePermissionLoadOrder = {MicroServiceName.PROJECT_MANAGEMENT,
|
||||
|
@ -223,6 +225,9 @@ public class GroupService {
|
|||
}
|
||||
|
||||
public void editGroupPermission(EditGroupRequest request) {
|
||||
if (StringUtils.equals(request.getUserGroupId(), SUPER_GROUP)) {
|
||||
return;
|
||||
}
|
||||
List<GroupPermission> permissions = request.getPermissions();
|
||||
if (CollectionUtils.isEmpty(permissions)) {
|
||||
return;
|
||||
|
|
|
@ -283,14 +283,6 @@ public class ProjectService {
|
|||
return project;
|
||||
}
|
||||
|
||||
|
||||
public boolean isThirdPartTemplate(Project project) {
|
||||
if (project.getThirdPartTemplate() != null && project.getThirdPartTemplate() && project.getPlatform().equals(IssuesManagePlatform.Jira.name())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Project> getByCaseTemplateId(String templateId) {
|
||||
ProjectExample example = new ProjectExample();
|
||||
example.createCriteria().andCaseTemplateIdEqualTo(templateId);
|
||||
|
|
|
@ -136,7 +136,7 @@ import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator
|
|||
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
|
||||
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
|
||||
import ApiEnvironmentConfig from "metersphere-frontend/src/components/environment/ApiEnvironmentConfig";
|
||||
import {Environment, parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
|
||||
import {Environment, parseEnvironment, HttpConfig} from "metersphere-frontend/src/model/EnvironmentModel";
|
||||
import EnvironmentEdit from "./components/EnvironmentEdit";
|
||||
import MsAsideItem from "metersphere-frontend/src/components/MsAsideItem";
|
||||
import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContainer";
|
||||
|
@ -174,7 +174,7 @@ export default {
|
|||
projectList: [],
|
||||
condition: {}, //封装传递给后端的查询条件
|
||||
environments: [],
|
||||
currentEnvironment: new Environment(),
|
||||
currentEnvironment: new Environment({httpConfig: new HttpConfig()}),
|
||||
result: {},
|
||||
loading: false,
|
||||
dialogVisible: false,
|
||||
|
@ -288,7 +288,7 @@ export default {
|
|||
createEnv() {
|
||||
this.dialogTitle = this.$t('api_test.environment.create');
|
||||
this.dialogVisible = true;
|
||||
this.currentEnvironment = new Environment();
|
||||
this.currentEnvironment = new Environment({httpConfig: new HttpConfig()});
|
||||
this.currentEnvironment.projectId = this.currentProjectId;
|
||||
this.currentEnvironment.currentProjectId = this.currentProjectId;
|
||||
this.ifCreate = true;
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 接口测试配置 -->
|
||||
<!-- 接口测试配置 -->
|
||||
<form-section :title="$t('commons.api')" :init-active=true>
|
||||
<p>{{ $t('api_test.request.headers') }}</p>
|
||||
<el-row>
|
||||
|
@ -55,28 +55,14 @@
|
|||
</el-link>
|
||||
</el-row>
|
||||
<ms-api-key-value :items="condition.headers" :isShowEnable="true" :suggestions="headerSuggestions"/>
|
||||
<div style="margin-top: 20px">
|
||||
<el-button v-if="!condition.id" type="primary" style="float: right" size="mini" @click="add">
|
||||
{{ $t('commons.add') }}
|
||||
</el-button>
|
||||
<div v-else>
|
||||
<el-button type="primary" style="float: right;margin-left: 10px" size="mini" @click="clear">
|
||||
{{ $t('commons.clear') }}
|
||||
</el-button>
|
||||
<el-button type="primary" style="float: right" size="mini" @click="update(condition)">{{
|
||||
$t('commons.update')
|
||||
}}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</form-section>
|
||||
|
||||
<!-- UI 配置 -->
|
||||
<form-section :title="$t('commons.ui_test')" :init-active="false">
|
||||
<!-- UI 配置 -->
|
||||
<form-section :title="$t('commons.ui_test')" :init-active=false v-if="condition.type !== 'MODULE'">
|
||||
<el-row :gutter="10" style="padding-top: 10px;">
|
||||
<el-col :span="6">
|
||||
<!-- 浏览器驱动 -->
|
||||
<span style="margin-right: 10px;">{{$t("ui.browser")}}</span>
|
||||
<span style="margin-right: 10px;">{{ $t("ui.browser") }}</span>
|
||||
<el-select
|
||||
size="mini"
|
||||
v-model="httpConfig.browser"
|
||||
|
@ -102,13 +88,29 @@
|
|||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="24">
|
||||
<ms-ui-scenario-cookie-table :items="httpConfig.cookie"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 当前版本实现免登录是基于 cookie 的但是现在由于安全性问题绝大多数网站都不支持 cookie登录所以先屏蔽了-->
|
||||
<!-- <el-row :gutter="10">-->
|
||||
<!-- <el-col :span="24">-->
|
||||
<!-- <ms-ui-scenario-cookie-table :items="httpConfig.cookie" ref="cookieTable"/>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
</form-section>
|
||||
|
||||
<div style="margin-top: 20px">
|
||||
<el-button v-if="!condition.id" type="primary" style="float: right" size="mini" @click="add">
|
||||
{{ $t('commons.add') }}
|
||||
</el-button>
|
||||
<div v-else>
|
||||
<el-button type="primary" style="float: right;margin-left: 10px" size="mini" @click="clear">
|
||||
{{ $t('commons.clear') }}
|
||||
</el-button>
|
||||
<el-button type="primary" style="float: right" size="mini" @click="update(condition)">{{
|
||||
$t('commons.update')
|
||||
}}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="ms-border">
|
||||
|
@ -174,9 +176,10 @@ export default {
|
|||
name: "MsEnvironmentHttpConfig",
|
||||
components: {
|
||||
MsUiScenarioCookieTable,
|
||||
FormSection, MsApiKeyValue, MsSelectTree, MsTableOperatorButton, BatchAddParameter, MsInstructionsIcon},
|
||||
FormSection, MsApiKeyValue, MsSelectTree, MsTableOperatorButton, BatchAddParameter, MsInstructionsIcon
|
||||
},
|
||||
props: {
|
||||
httpConfig: new HttpConfig({cookie: []}),
|
||||
httpConfig: new HttpConfig(),
|
||||
projectId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
|
@ -185,9 +188,6 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.list();
|
||||
if (this.httpConfig && !this.httpConfig.cookie) {
|
||||
this.$set(this.httpConfig, "cookie", []);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
let socketValidator = (rule, value, callback) => {
|
||||
|
@ -220,7 +220,7 @@ export default {
|
|||
port: 0,
|
||||
headers: [new KeyValue()],
|
||||
headlessEnabled: true,
|
||||
browser : 'CHROME'
|
||||
browser: 'CHROME'
|
||||
},
|
||||
beforeCondition: {},
|
||||
browsers: [
|
||||
|
@ -354,7 +354,7 @@ export default {
|
|||
list() {
|
||||
if (this.projectId) {
|
||||
this.result = getApiModuleByProjectIdAndProtocol(this.projectId, "HTTP").then((response) => {
|
||||
if (response.data && response.data !== null) {
|
||||
if (response.data && response.data !== null) {
|
||||
this.moduleOptions = response.data;
|
||||
}
|
||||
});
|
||||
|
@ -507,6 +507,9 @@ export default {
|
|||
this.$refs["httpConfig"].validate((valid) => {
|
||||
isValidate = valid;
|
||||
});
|
||||
// if (this.$refs.cookieTable && !this.$refs.cookieTable.validate()) {
|
||||
// return false;
|
||||
// }
|
||||
return isValidate;
|
||||
},
|
||||
batchAdd() {
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
:data="variables"
|
||||
:total="items.length"
|
||||
:screen-height="'100px'"
|
||||
:batch-operators="batchButtons"
|
||||
:remember-order="true"
|
||||
:highlightCurrentRow="true"
|
||||
@refresh="onChange"
|
||||
:enable-selection="false"
|
||||
ref="variableTable"
|
||||
>
|
||||
<ms-table-column prop="cookie" label="cookie" min-width="160">
|
||||
|
@ -28,44 +28,44 @@
|
|||
v-model="scope.row.cookie"
|
||||
size="mini"
|
||||
:placeholder="$t('cookie')"
|
||||
@change="change"
|
||||
@change="change(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
|
||||
<ms-table-column
|
||||
prop="userName"
|
||||
:label="$t('api_test.request.sql.username')"
|
||||
min-width="200"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.userName"
|
||||
size="mini"
|
||||
maxlength="200"
|
||||
:placeholder="$t('api_test.request.sql.username')"
|
||||
show-word-limit
|
||||
@change="change"
|
||||
/>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
<!-- <ms-table-column-->
|
||||
<!-- prop="userName"-->
|
||||
<!-- :label="$t('api_test.request.sql.username')"-->
|
||||
<!-- min-width="200"-->
|
||||
<!-- >-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="scope.row.userName"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- maxlength="200"-->
|
||||
<!-- :placeholder="$t('api_test.request.sql.username')"-->
|
||||
<!-- show-word-limit-->
|
||||
<!-- @change="change"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<!-- </ms-table-column>-->
|
||||
|
||||
<ms-table-column
|
||||
prop="password"
|
||||
:label="$t('api_test.request.tcp.password')"
|
||||
min-width="140"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.password"
|
||||
size="mini"
|
||||
maxlength="200"
|
||||
show-password
|
||||
:placeholder="$t('api_test.request.tcp.password')"
|
||||
@change="change"
|
||||
/>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
<!-- <ms-table-column-->
|
||||
<!-- prop="password"-->
|
||||
<!-- :label="$t('api_test.request.tcp.password')"-->
|
||||
<!-- min-width="140"-->
|
||||
<!-- >-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="scope.row.password"-->
|
||||
<!-- size="mini"-->
|
||||
<!-- maxlength="200"-->
|
||||
<!-- show-password-->
|
||||
<!-- :placeholder="$t('api_test.request.tcp.password')"-->
|
||||
<!-- @change="change"-->
|
||||
<!-- />-->
|
||||
<!-- </template>-->
|
||||
<!-- </ms-table-column>-->
|
||||
|
||||
<ms-table-column
|
||||
prop="description"
|
||||
|
@ -74,7 +74,7 @@
|
|||
:editContent="'aaa'"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<mini-timing-item :expr="scope.row.expireTime"></mini-timing-item>
|
||||
<mini-timing-item :expr.sync="scope.row.expireTime" @chooseChange="change(scope.row)"></mini-timing-item>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
|
||||
|
@ -95,7 +95,8 @@
|
|||
<el-switch v-model="scope.row.enable" size="mini"></el-switch>
|
||||
<el-tooltip
|
||||
effect="dark"
|
||||
:content="$t('关联登录场景/指令')"
|
||||
:content="$t('environment.relevance_ui')"
|
||||
v-if="!existCookieConfig"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button
|
||||
|
@ -103,26 +104,26 @@
|
|||
circle
|
||||
size="mini"
|
||||
@click="openRelevance"
|
||||
v-if="!existCookieConfig"
|
||||
style="margin-left: 10px"
|
||||
/>
|
||||
</el-tooltip>
|
||||
|
||||
<el-dropdown @command="handleCommand" v-if="existCookieConfig">
|
||||
<el-button
|
||||
icon="el-icon-paperclip"
|
||||
circle
|
||||
size="mini"
|
||||
style="margin-left: 10px"
|
||||
/>
|
||||
|
||||
<el-dropdown @command="handleCommand" v-if="existCookieConfig">
|
||||
<el-button
|
||||
icon="el-icon-paperclip"
|
||||
circle
|
||||
size="mini"
|
||||
style="margin-left: 10px"
|
||||
/>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="view">{{ $t("environment.view_ui_relevane") }}</el-dropdown-item>
|
||||
<el-dropdown-item command="cancelRelevance">{{ $t("environment.cancel_ui_relevane") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="relevance">{{ $t("environment.re_ui_relevane") }}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="view">查看关联</el-dropdown-item>
|
||||
<el-dropdown-item command="cancelRelevance">取消关联</el-dropdown-item>
|
||||
<el-dropdown-item command="relevance">重新关联</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</ms-table-column>
|
||||
</ms-table>
|
||||
|
@ -158,6 +159,7 @@ import VariableImport from "metersphere-frontend/src/components/environment/Vari
|
|||
import _ from "lodash";
|
||||
import MiniTimingItem from "metersphere-frontend/src/components/environment/commons/MiniTimingItem";
|
||||
import UiScenarioEditRelevance from "@/business/menu/environment/components/ui-related/UiScenarioEditRelevance";
|
||||
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
|
||||
|
||||
export default {
|
||||
name: "MsUiScenarioCookieTable",
|
||||
|
@ -218,8 +220,8 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
items: {
|
||||
handler(v) {
|
||||
this.variables = v;
|
||||
handler(val) {
|
||||
this.variables = val;
|
||||
this.sortParameters();
|
||||
},
|
||||
immediate: true,
|
||||
|
@ -235,7 +237,7 @@ export default {
|
|||
},
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
remove: function (index) {
|
||||
|
@ -271,6 +273,7 @@ export default {
|
|||
this.$t("load_test.param_is_duplicate")
|
||||
);
|
||||
}
|
||||
this.variables[0].updateTime = new Date().getTime();
|
||||
this.$emit("change", this.variables);
|
||||
},
|
||||
changeType(data) {
|
||||
|
@ -314,23 +317,22 @@ export default {
|
|||
},
|
||||
sortParameters() {
|
||||
let index = 1;
|
||||
this.variables.forEach((item) => {
|
||||
item.num = index;
|
||||
if (!item.type || item.type === "text") {
|
||||
item.type = "CONSTANT";
|
||||
}
|
||||
if (!item.id) {
|
||||
item.id = getUUID();
|
||||
}
|
||||
if (item.remark) {
|
||||
this.$set(item, "description", item.remark);
|
||||
item.remark = undefined;
|
||||
}
|
||||
if (!item.scope) {
|
||||
this.$set(item, "scope", "api");
|
||||
}
|
||||
index++;
|
||||
});
|
||||
if (this.variables) {
|
||||
this.variables.forEach((item) => {
|
||||
item.num = index;
|
||||
if (!item.type || item.type === "text") {
|
||||
item.type = "CONSTANT";
|
||||
}
|
||||
if (!item.id) {
|
||||
item.id = getUUID();
|
||||
}
|
||||
if (item.remark) {
|
||||
this.$set(item, "description", item.remark);
|
||||
item.remark = undefined;
|
||||
}
|
||||
index++;
|
||||
});
|
||||
}
|
||||
},
|
||||
handleDeleteBatch() {
|
||||
operationConfirm(
|
||||
|
@ -509,17 +511,36 @@ export default {
|
|||
handleCommand(c) {
|
||||
switch (c) {
|
||||
case "view":
|
||||
this.redirectPage(this.variables[0].relevanceId);
|
||||
break;
|
||||
case "cancelRelevance":
|
||||
this.variables[0].relevanceId = null;
|
||||
this.$success(this.$t("organization.integration.successful_operation"));
|
||||
break;
|
||||
case "relevance":
|
||||
this.openRelevance();
|
||||
this.openRelevance("scenario", "scenario",);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
redirectPage(resourceId) {
|
||||
let uuid = getUUID().substring(1, 5);
|
||||
let projectId = getCurrentProjectID();
|
||||
let workspaceId = getCurrentWorkspaceId();
|
||||
let prefix = '/#';
|
||||
if (
|
||||
this.$route &&
|
||||
this.$route.path.startsWith('/#')
|
||||
) {
|
||||
prefix = '';
|
||||
}
|
||||
let path = `/ui/automation/?redirectID=${uuid}&dataType=scenario&projectId=${projectId}&workspaceId=${workspaceId}&resourceId=${resourceId}`;
|
||||
let data = this.$router.resolve({
|
||||
path: path,
|
||||
});
|
||||
window.open(data.href, '_blank');
|
||||
},
|
||||
openRelevance() {
|
||||
this.$refs.relevanceUiDialog.open();
|
||||
},
|
||||
|
@ -527,19 +548,31 @@ export default {
|
|||
this.variables[0].relevanceId = id.keys().next().value.id;
|
||||
this.$refs.relevanceUiDialog.close();
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
},
|
||||
validate() {
|
||||
if (!this.variables[0].enable) {
|
||||
return true;
|
||||
}
|
||||
let cookieConfig = this.variables[0];
|
||||
if (!cookieConfig) {
|
||||
this.$warning(this.$t("配置错误"));
|
||||
return false;
|
||||
}
|
||||
if (!cookieConfig.expireTime || cookieConfig.expireTime === "") {
|
||||
this.$warning(this.$t("environment.need_expire_time"));
|
||||
return false;
|
||||
}
|
||||
if (!cookieConfig.relevanceId) {
|
||||
this.$warning(this.$t("environment.need_relevance_ui_scenario"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.items.length === 0) {
|
||||
this.items.push(new KeyValue({enable: true, expireTime: '1Y'}));
|
||||
} else {
|
||||
//历史数据默认是 api 应用场景
|
||||
_.forEach(this.items, item => {
|
||||
if (!item.scope) {
|
||||
this.$set(item, "scope", "api");
|
||||
}
|
||||
})
|
||||
this.variables = this.items;
|
||||
if (!this.items || this.items.length === 0) {
|
||||
this.items = [];
|
||||
this.items.push(new KeyValue({id: getUUID(), enable: true, expireTime: '1M'}));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
|
@ -35,6 +35,15 @@ const message = {
|
|||
},
|
||||
project_version: {
|
||||
version_time: 'Version cycle',
|
||||
},
|
||||
environment: {
|
||||
export_variable_tip : "Export interface test variables",
|
||||
need_expire_time : "Please enter an expiration time",
|
||||
need_relevance_ui_scenario : "Please associate the login scenario",
|
||||
view_ui_relevane : "View Relevane",
|
||||
cancel_ui_relevane : "Relevant",
|
||||
re_ui_relevane : "Relevane",
|
||||
relevance_ui : "Relevance login scene/command",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,13 @@ const message = {
|
|||
version_time: '版本周期',
|
||||
},
|
||||
environment: {
|
||||
export_variable_tip : "导出接口测试变量"
|
||||
export_variable_tip : "导出接口测试变量",
|
||||
need_expire_time : "请输入过期时间",
|
||||
need_relevance_ui_scenario : "请关联登录场景",
|
||||
view_ui_relevane : "查看关联",
|
||||
cancel_ui_relevane : "取消关联",
|
||||
re_ui_relevane : "重新关联",
|
||||
relevance_ui : "关联登录场景/指令",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,15 @@ const message = {
|
|||
},
|
||||
project_version: {
|
||||
version_time: '版本週期',
|
||||
},
|
||||
environment: {
|
||||
export_variable_tip : "導出接口測試變量",
|
||||
need_expire_time : "請輸入過期時間",
|
||||
need_relevance_ui_scenario : "請關聯登錄場景",
|
||||
view_ui_relevane : "查看關聯",
|
||||
cancel_ui_relevane : "取消關聯",
|
||||
re_ui_relevane : "重新關聯",
|
||||
relevance_ui : "關聯登錄場景/指令",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import io.metersphere.commons.utils.JSON;
|
|||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
@ -14,6 +13,7 @@ import org.springframework.stereotype.Component;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -24,8 +24,6 @@ public class CleanSessionJob {
|
|||
@Resource
|
||||
private RedisIndexedSessionRepository redisIndexedSessionRepository;
|
||||
|
||||
@Value("${spring.session.timeout:43200s}")
|
||||
private Duration timeout;
|
||||
|
||||
/**
|
||||
* 清理没有绑定user的session
|
||||
|
@ -55,7 +53,7 @@ public class CleanSessionJob {
|
|||
userCount.put(userId, count);
|
||||
LogUtil.info(key + " : " + userId + " 过期时间: " + expire);
|
||||
if (expire != null && expire.intValue() == -1) {
|
||||
redisIndexedSessionRepository.getSessionRedisOperations().expire(key, timeout);
|
||||
redisIndexedSessionRepository.getSessionRedisOperations().expire(key, Duration.of(30, ChronoUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -235,6 +235,10 @@ public class GroupService {
|
|||
}
|
||||
|
||||
public void editGroupPermission(EditGroupRequest request) {
|
||||
// 超级用户组禁止修改权限
|
||||
if (StringUtils.equals(request.getUserGroupId(), SUPER_GROUP)) {
|
||||
return;
|
||||
}
|
||||
List<GroupPermission> permissions = request.getPermissions();
|
||||
if (CollectionUtils.isEmpty(permissions)) {
|
||||
return;
|
||||
|
|
|
@ -18,8 +18,10 @@ import io.metersphere.platform.loader.PlatformPluginManager;
|
|||
import io.metersphere.request.IntegrationRequest;
|
||||
import io.metersphere.utils.PluginManagerUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
@ -46,6 +48,9 @@ public class PlatformPluginService {
|
|||
private BaseIntegrationService baseIntegrationService;
|
||||
@Resource
|
||||
private KafkaTemplate<String, String> kafkaTemplate;
|
||||
@Resource
|
||||
@Lazy
|
||||
private PluginService pluginService;
|
||||
|
||||
private static final String PLUGIN_DOWNLOAD_URL = "https://github.com/metersphere/metersphere-platform-plugin";
|
||||
|
||||
|
@ -58,6 +63,13 @@ public class PlatformPluginService {
|
|||
return pluginManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新开一个事务,保证发送 kafka 消息是在事务提交之后
|
||||
* 因为接受消息需要判断数据库是否有数据
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
|
||||
public PluginWithBLOBs addPlatformPlugin(MultipartFile file) {
|
||||
String id = UUID.randomUUID().toString();
|
||||
|
||||
|
@ -75,7 +87,6 @@ public class PlatformPluginService {
|
|||
plugin.setPluginId(pluginMetaInfo.getKey() + "-" + pluginMetaInfo.getVersion());
|
||||
plugin.setScriptId(pluginMetaInfo.getKey());
|
||||
plugin.setSourcePath("");
|
||||
// plugin.setFormOption(item.getFormOption());
|
||||
plugin.setFormScript(JSON.toJSONString(map));
|
||||
plugin.setClazzName("");
|
||||
plugin.setSourceName(file.getOriginalFilename());
|
||||
|
@ -84,11 +95,16 @@ public class PlatformPluginService {
|
|||
plugin.setCreateUserId(SessionUtils.getUserId());
|
||||
plugin.setXpack(pluginMetaInfo.isXpack());
|
||||
plugin.setScenario(PluginScenario.platform.name());
|
||||
// 初始化项目默认节点
|
||||
kafkaTemplate.send(KafkaTopicConstants.PLATFORM_PLUGIN_ADD, id);
|
||||
|
||||
pluginService.addPlugin(plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public void notifiedPlatformPluginAdd(String pluginId) {
|
||||
// 初始化项目默认节点
|
||||
kafkaTemplate.send(KafkaTopicConstants.PLATFORM_PLUGIN_ADD, pluginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有平台插件并加载
|
||||
*/
|
||||
|
|
|
@ -115,7 +115,7 @@ public class PluginService {
|
|||
checkPluginExist(file);
|
||||
if (StringUtils.equalsIgnoreCase(scenario, PluginScenario.platform.name())) {
|
||||
PluginWithBLOBs plugin = platformPluginService.addPlatformPlugin(file);
|
||||
addPlugin(plugin);
|
||||
platformPluginService.notifiedPlatformPluginAdd(plugin.getId());
|
||||
} else {
|
||||
List<PluginWithBLOBs> plugins = apiPluginService.addApiPlugin(file);
|
||||
plugins.forEach(this::addPlugin);
|
||||
|
|
|
@ -126,7 +126,7 @@ public class SystemProjectService {
|
|||
if (quotaService != null) {
|
||||
quotaService.projectUseDefaultQuota(pjId);
|
||||
}
|
||||
|
||||
|
||||
// 创建默认版本
|
||||
addProjectVersion(project);
|
||||
// 初始化项目应用管理
|
||||
|
@ -339,11 +339,6 @@ public class SystemProjectService {
|
|||
return project;
|
||||
}
|
||||
|
||||
|
||||
public boolean isThirdPartTemplate(Project project) {
|
||||
return project.getThirdPartTemplate() != null && project.getThirdPartTemplate() && project.getPlatform().equals(IssuesManagePlatform.Jira.name());
|
||||
}
|
||||
|
||||
public List<Project> getByCaseTemplateId(String templateId) {
|
||||
ProjectExample example = new ProjectExample();
|
||||
example.createCriteria().andCaseTemplateIdEqualTo(templateId);
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
-- v2_5_modify_workspace_name_length
|
||||
-- 创建人 liyuhao
|
||||
-- 创建时间 2022-11-29 13:19:51
|
||||
ALTER TABLE workspace
|
||||
MODIFY name varchar(100) NOT NULL COMMENT 'Workspace name';
|
||||
|
||||
|
||||
-- v2_5_init_super_group_permission
|
||||
-- 创建人 liyuhao
|
||||
-- 创建时间 2022-12-05 14:38:10
|
||||
DROP PROCEDURE IF EXISTS init_super_permission;
|
||||
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE init_super_permission()
|
||||
BEGIN
|
||||
SET @permission_str = 'SYSTEM_USER:READ, SYSTEM_USER:READ+CREATE, SYSTEM_USER:READ+IMPORT, SYSTEM_USER:READ+EDIT,
|
||||
SYSTEM_USER:READ+DELETE, SYSTEM_USER:READ+EDIT_PASSWORD, SYSTEM_WORKSPACE:READ, SYSTEM_WORKSPACE:READ+CREATE,
|
||||
SYSTEM_WORKSPACE:READ+EDIT, SYSTEM_WORKSPACE:READ+DELETE, SYSTEM_GROUP:READ, SYSTEM_GROUP:READ+CREATE,
|
||||
SYSTEM_GROUP:READ+EDIT, SYSTEM_GROUP:READ+SETTING_PERMISSION, SYSTEM_GROUP:READ+DELETE, SYSTEM_TEST_POOL:READ,
|
||||
SYSTEM_TEST_POOL:READ+CREATE, SYSTEM_TEST_POOL:READ+EDIT, SYSTEM_TEST_POOL:READ+DELETE,
|
||||
SYSTEM_SETTING:READ, SYSTEM_SETTING:READ+EDIT, SYSTEM_QUOTA:READ, SYSTEM_QUOTA:READ+EDIT,
|
||||
SYSTEM_AUTH:READ, SYSTEM_AUTH:READ+EDIT, SYSTEM_OPERATING_LOG:READ, WORKSPACE_SERVICE:READ,
|
||||
WORKSPACE_SERVICE:READ+EDIT, WORKSPACE_USER:READ, WORKSPACE_USER:READ+CREATE, WORKSPACE_USER:READ+EDIT,
|
||||
WORKSPACE_USER:READ+DELETE, WORKSPACE_PROJECT_MANAGER:READ, WORKSPACE_PROJECT_MANAGER:READ+CREATE,
|
||||
WORKSPACE_PROJECT_MANAGER:READ+EDIT, WORKSPACE_PROJECT_MANAGER:READ+DELETE,
|
||||
WORKSPACE_PROJECT_MANAGER:READ+ENVIRONMENT_CONFIG, WORKSPACE_PROJECT_MANAGER:READ+ADD_USER,
|
||||
WORKSPACE_PROJECT_MANAGER:READ+EDIT_USER, WORKSPACE_PROJECT_MANAGER:READ+DELETE_USER,
|
||||
WORKSPACE_PROJECT_ENVIRONMENT:READ, WORKSPACE_PROJECT_ENVIRONMENT:READ+CREATE,
|
||||
WORKSPACE_PROJECT_ENVIRONMENT:READ+EDIT, WORKSPACE_PROJECT_ENVIRONMENT:READ+DELETE,
|
||||
WORKSPACE_PROJECT_ENVIRONMENT:READ+COPY, WORKSPACE_PROJECT_ENVIRONMENT:READ+IMPORT,
|
||||
WORKSPACE_PROJECT_ENVIRONMENT:READ+EXPORT, WORKSPACE_PROJECT_ENVIRONMENT:READ+CREATE_GROUP,
|
||||
WORKSPACE_PROJECT_ENVIRONMENT:READ+EDIT_GROUP, WORKSPACE_PROJECT_ENVIRONMENT:READ+COPY_GROUP,
|
||||
WORKSPACE_PROJECT_ENVIRONMENT:READ+DELETE_GROUP, WORKSPACE_QUOTA:READ, WORKSPACE_QUOTA:READ+EDIT,
|
||||
WORKSPACE_OPERATING_LOG:READ, SYSTEM_PLUGIN:UPLOAD, SYSTEM_PLUGIN:DEL, SYSTEM_PLUGIN:READ,
|
||||
PERSONAL_INFORMATION:READ+EDIT, PERSONAL_INFORMATION:READ+API_KEYS, PERSONAL_INFORMATION:READ+EDIT_PASSWORD,
|
||||
PERSONAL_INFORMATION:READ+THIRD_ACCOUNT, PERSONAL_INFORMATION:READ+UI_SETTING,
|
||||
PROJECT_USER:READ, PROJECT_USER:READ+CREATE, PROJECT_USER:READ+EDIT, PROJECT_USER:READ+DELETE,
|
||||
PROJECT_GROUP:READ, PROJECT_GROUP:READ+CREATE, PROJECT_GROUP:READ+EDIT, PROJECT_GROUP:READ+DELETE,
|
||||
PROJECT_GROUP:READ+SETTING_PERMISSION, PROJECT_MANAGER:READ, PROJECT_MANAGER:READ+EDIT,
|
||||
PROJECT_APP_MANAGER:READ+EDIT, PROJECT_ENVIRONMENT:READ, PROJECT_ENVIRONMENT:READ+CREATE,
|
||||
PROJECT_ENVIRONMENT:READ+EDIT, PROJECT_ENVIRONMENT:READ+DELETE, PROJECT_ENVIRONMENT:READ+COPY,
|
||||
PROJECT_ENVIRONMENT:READ+IMPORT, PROJECT_ENVIRONMENT:READ+EXPORT, PROJECT_OPERATING_LOG:READ,
|
||||
PROJECT_FILE:READ, PROJECT_FILE:READ+UPLOAD+JAR, PROJECT_FILE:READ+DOWNLOAD+JAR, PROJECT_FILE:READ+DELETE+JAR,
|
||||
PROJECT_FILE:READ+BATCH+DELETE, PROJECT_FILE:READ+BATCH+DOWNLOAD, PROJECT_FILE:READ+BATCH+MOVE,
|
||||
PROJECT_TEMPLATE:READ, PROJECT_TEMPLATE:READ+CASE_TEMPLATE, PROJECT_TEMPLATE:READ+ISSUE_TEMPLATE,
|
||||
PROJECT_TEMPLATE:READ+API_TEMPLATE, PROJECT_TEMPLATE:READ+CUSTOM, PROJECT_MESSAGE:READ, PROJECT_MESSAGE:READ+EDIT,
|
||||
PROJECT_MESSAGE:READ+DELETE, PROJECT_CUSTOM_CODE:READ, PROJECT_CUSTOM_CODE:READ+CREATE, PROJECT_CUSTOM_CODE:READ+EDIT,
|
||||
PROJECT_CUSTOM_CODE:READ+DELETE, PROJECT_CUSTOM_CODE:READ+COPY, PROJECT_VERSION:READ, PROJECT_VERSION:READ+CREATE,
|
||||
PROJECT_VERSION:READ+EDIT, PROJECT_VERSION:READ+DELETE, PROJECT_VERSION:READ+ENABLE, PROJECT_ERROR_REPORT_LIBRARY:READ,
|
||||
PROJECT_ERROR_REPORT_LIBRARY:READ+CREATE, PROJECT_ERROR_REPORT_LIBRARY:READ+EDIT, PROJECT_ERROR_REPORT_LIBRARY:READ+DELETE,
|
||||
PROJECT_ERROR_REPORT_LIBRARY:READ+BATCH_DELETE, PROJECT_TRACK_HOME:READ, PROJECT_TRACK_CASE:READ,
|
||||
PROJECT_TRACK_CASE:READ+CREATE, PROJECT_TRACK_CASE:READ+EDIT, PROJECT_TRACK_CASE:READ+DELETE, PROJECT_TRACK_CASE:READ+COPY,
|
||||
PROJECT_TRACK_CASE:READ+IMPORT, PROJECT_TRACK_CASE:READ+EXPORT, PROJECT_TRACK_CASE:READ+RECOVER,
|
||||
PROJECT_TRACK_CASE:READ+BATCH_EDIT, PROJECT_TRACK_CASE:READ+BATCH_MOVE, PROJECT_TRACK_CASE:READ+BATCH_COPY,
|
||||
PROJECT_TRACK_CASE:READ+BATCH_DELETE, PROJECT_TRACK_CASE:READ+BATCH_REDUCTION, PROJECT_TRACK_CASE:READ+BATCH_LINK_DEMAND,
|
||||
PROJECT_TRACK_CASE:READ+GENERATE_DEPENDENCIES, PROJECT_TRACK_CASE:READ+BATCH_ADD_PUBLIC, PROJECT_TRACK_REVIEW:READ,
|
||||
PROJECT_TRACK_REVIEW:READ+CREATE, PROJECT_TRACK_REVIEW:READ+EDIT, PROJECT_TRACK_REVIEW:READ+DELETE,
|
||||
PROJECT_TRACK_REVIEW:READ+REVIEW, PROJECT_TRACK_REVIEW:READ+COMMENT, PROJECT_TRACK_REVIEW:READ+RELEVANCE_OR_CANCEL,
|
||||
PROJECT_TRACK_PLAN:READ, PROJECT_TRACK_PLAN:READ+CREATE, PROJECT_TRACK_PLAN:READ+EDIT, PROJECT_TRACK_PLAN:READ+DELETE,
|
||||
PROJECT_TRACK_PLAN:READ+COPY, PROJECT_TRACK_PLAN:READ+RUN, PROJECT_TRACK_PLAN:READ+CASE_BATCH_RUN,
|
||||
PROJECT_TRACK_PLAN:READ+CASE_BATCH_EDIT, PROJECT_TRACK_PLAN:READ+BATCH_DELETE, PROJECT_TRACK_PLAN:READ+SCHEDULE,
|
||||
PROJECT_TRACK_PLAN:READ+RELEVANCE_OR_CANCEL, PROJECT_TRACK_PLAN:READ+CASE_BATCH_DELETE, PROJECT_TRACK_ISSUE:READ,
|
||||
PROJECT_TRACK_ISSUE:READ+CREATE, PROJECT_TRACK_ISSUE:READ+EDIT, PROJECT_TRACK_ISSUE:READ+DELETE, PROJECT_TRACK_REPORT:READ,
|
||||
PROJECT_TRACK_REPORT:READ+DELETE, PROJECT_TRACK_REPORT:READ+EXPORT, PROJECT_API_HOME:READ, PROJECT_API_DEFINITION:READ,
|
||||
PROJECT_API_DEFINITION:READ+CREATE_API, PROJECT_API_DEFINITION:READ+EDIT_API, PROJECT_API_DEFINITION:READ+DELETE_API,
|
||||
PROJECT_API_DEFINITION:READ+COPY_API, PROJECT_API_DEFINITION:READ+CREATE_CASE, PROJECT_API_DEFINITION:READ+EDIT_CASE,
|
||||
PROJECT_API_DEFINITION:READ+DELETE_CASE, PROJECT_API_DEFINITION:READ+COPY_CASE, PROJECT_API_DEFINITION:READ+IMPORT_API,
|
||||
PROJECT_API_DEFINITION:READ+EXPORT_API, PROJECT_API_DEFINITION:READ+TIMING_SYNC, PROJECT_API_DEFINITION:READ+CREATE_PERFORMANCE,
|
||||
PROJECT_API_DEFINITION:READ+RUN, PROJECT_API_DEFINITION:READ+DEBUG, PROJECT_API_DEFINITION:READ+MOCK, PROJECT_API_SCENARIO:READ,
|
||||
PROJECT_API_SCENARIO:READ+CREATE, PROJECT_API_SCENARIO:READ+EDIT, PROJECT_API_SCENARIO:READ+DELETE, PROJECT_API_SCENARIO:READ+COPY,
|
||||
PROJECT_API_SCENARIO:READ+RUN, PROJECT_API_SCENARIO:READ+DEBUG, PROJECT_API_SCENARIO:READ+SCHEDULE, PROJECT_API_SCENARIO:READ+IMPORT_SCENARIO,
|
||||
PROJECT_API_SCENARIO:READ+EXPORT_SCENARIO, PROJECT_API_SCENARIO:READ+MOVE_BATCH, PROJECT_API_SCENARIO:READ+CREATE_PERFORMANCE,
|
||||
PROJECT_API_SCENARIO:READ+CREATE_PERFORMANCE_BATCH, PROJECT_API_SCENARIO:READ+BATCH_COPY, PROJECT_API_REPORT:READ,
|
||||
PROJECT_API_REPORT:READ+DELETE, PROJECT_API_REPORT:READ+EXPORT, PROJECT_UI_ELEMENT:READ, PROJECT_UI_ELEMENT:READ+CREATE,
|
||||
PROJECT_UI_ELEMENT:READ+EDIT, PROJECT_UI_ELEMENT:READ+DELETE, PROJECT_UI_ELEMENT:READ+COPY, PROJECT_UI_SCENARIO:READ,
|
||||
PROJECT_UI_SCENARIO:READ+CREATE, PROJECT_UI_SCENARIO:READ+EDIT, PROJECT_UI_SCENARIO:READ+DELETE, PROJECT_UI_SCENARIO:READ+COPY,
|
||||
PROJECT_UI_SCENARIO:READ+RUN, PROJECT_UI_SCENARIO:READ+DEBUG, PROJECT_UI_SCENARIO:READ+IMPORT_SCENARIO,
|
||||
PROJECT_UI_SCENARIO:READ+EXPORT_SCENARIO, PROJECT_UI_SCENARIO:READ+MOVE_BATCH, PROJECT_UI_SCENARIO:READ+BATCH_COPY, PROJECT_UI_REPORT:READ,
|
||||
PROJECT_UI_REPORT:READ+DELETE, PROJECT_PERFORMANCE_HOME:READ, PROJECT_PERFORMANCE_TEST:READ, PROJECT_PERFORMANCE_TEST:READ+CREATE,
|
||||
PROJECT_PERFORMANCE_TEST:READ+EDIT, PROJECT_PERFORMANCE_TEST:READ+DELETE, PROJECT_PERFORMANCE_TEST:READ+COPY,
|
||||
PROJECT_PERFORMANCE_TEST:READ+RUN, PROJECT_PERFORMANCE_TEST:READ+SCHEDULE, PROJECT_PERFORMANCE_REPORT:READ,
|
||||
PROJECT_PERFORMANCE_REPORT:READ+DELETE, PROJECT_PERFORMANCE_REPORT:READ+EXPORT, PROJECT_PERFORMANCE_REPORT:READ+COMPARE,
|
||||
PROJECT_REPORT_ANALYSIS:READ, PROJECT_REPORT_ANALYSIS:READ+EXPORT, PROJECT_REPORT_ANALYSIS:READ+UPDATE,
|
||||
PROJECT_REPORT_ANALYSIS:READ+CREATE, PROJECT_ENTERPRISE_REPORT:READ+EXPORT, PROJECT_ENTERPRISE_REPORT:READ+CREATE,
|
||||
PROJECT_ENTERPRISE_REPORT:READ+DELETE,PROJECT_ENTERPRISE_REPORT:READ+COPY,
|
||||
PROJECT_ENTERPRISE_REPORT:READ+SCHEDULE, PROJECT_ENTERPRISE_REPORT:READ+EDIT';
|
||||
SET @i = 1;
|
||||
SET @count = CHAR_LENGTH(@permission_str) - CHAR_LENGTH(REPLACE(@permission_str, ',', '')) + 1;
|
||||
WHILE @i <= @count
|
||||
DO
|
||||
SET @original_str = SUBSTRING_INDEX(SUBSTRING_INDEX(@permission_str, ',', @i), ',', -1);
|
||||
SET @permission = TRIM(REPLACE(@original_str, CHAR(10), ''));
|
||||
SET @module = SUBSTRING_INDEX(@permission, ':', 1);
|
||||
INSERT INTO user_group_permission (id, group_id, permission_id, module_id)
|
||||
VALUES (UUID(), 'super_group', @permission, @module);
|
||||
SET @i = @i + 1;
|
||||
END WHILE;
|
||||
END
|
||||
//
|
||||
DELIMITER ;
|
||||
|
||||
CALL init_super_permission();
|
||||
DROP PROCEDURE IF EXISTS init_super_permission;
|
||||
|
||||
|
||||
-- v2_5_insert_super_group
|
||||
-- 创建人 liyuhao
|
||||
INSERT INTO `group` (id, name, description, `system`, type, create_time, update_time, creator, scope_id)
|
||||
VALUES ('super_group', '超级管理员(系统)', '拥有系统全部工作空间以及项目的操作权限', 1, 'SYSTEM', UNIX_TIMESTAMP() * 1000,
|
||||
UNIX_TIMESTAMP() * 1000, 'system', 'system');
|
|
@ -99,7 +99,10 @@ export default {
|
|||
},
|
||||
isReadOnly() {
|
||||
return function (data) {
|
||||
const isDefaultSystemGroup = (this.group.id === 'admin' || this.group.id === 'super_group') && data.resource.id === 'SYSTEM_GROUP';
|
||||
if (this.group.id === 'super_group') {
|
||||
return true;
|
||||
}
|
||||
const isDefaultSystemGroup = this.group.id === 'admin' && data.resource.id === 'SYSTEM_GROUP';
|
||||
return this.readOnly || isDefaultSystemGroup;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,13 @@ export default {
|
|||
computed: {
|
||||
isReadOnly() {
|
||||
return function (permission) {
|
||||
// 禁止取消系统管理员用户组权限
|
||||
if (this.group.id === 'super_group') {
|
||||
return true;
|
||||
}
|
||||
// 禁止取消系统管理员用户组和超级管理员用户组的读取和设置权限
|
||||
const isSystemGroupPermission = permission.id === 'SYSTEM_GROUP:READ' || permission.id === 'SYSTEM_GROUP:READ+SETTING_PERMISSION';
|
||||
const isDefaultSystemGroup = (this.group.id === 'admin' || this.group.id === 'super_group') && isSystemGroupPermission;
|
||||
const isDefaultSystemGroup = this.group.id === 'admin' && isSystemGroupPermission;
|
||||
return this.readOnly || isDefaultSystemGroup;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,8 +63,9 @@
|
|||
|
||||
<script>
|
||||
import MsValidLicense from "./ValidLicense";
|
||||
import {hasPermission} from "metersphere-frontend/src/utils/permission";
|
||||
import {getLicense, saveLicense} from "../../../api/license";
|
||||
import {hasPermission, saveLicense, hasLicense} from "metersphere-frontend/src/utils/permission";
|
||||
import {getLicense} from "../../../api/license";
|
||||
import {getModuleList} from "metersphere-frontend/src/api/module";
|
||||
|
||||
export default {
|
||||
name: "MxLicense",
|
||||
|
@ -108,7 +109,17 @@ export default {
|
|||
this.license.licenseCount = value.license.licenseCount;
|
||||
this.license.status = value.status;
|
||||
saveLicense(value.status);
|
||||
window.location.reload();
|
||||
if (hasLicense()) {
|
||||
getModuleList()
|
||||
.then(response => {
|
||||
let modules = {};
|
||||
response.data.forEach(m => {
|
||||
modules[m.key] = m.status;
|
||||
});
|
||||
localStorage.setItem('modules', JSON.stringify(modules));
|
||||
location.reload();
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@
|
|||
test_case.review_status, test_case.tags,
|
||||
test_case.demand_id, test_case.demand_name, test_case.`status`,
|
||||
test_case.custom_num, test_case.step_model, test_case.create_user,
|
||||
test_case.ref_id
|
||||
test_case.ref_id,test_case.latest
|
||||
</if>
|
||||
from test_case
|
||||
left join project_version on project_version.id = test_case.version_id
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.metersphere.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.plan.dto.TestPlanSimpleReportDTO;
|
||||
import io.metersphere.plan.service.TestPlanReportService;
|
||||
|
@ -44,7 +45,7 @@ public class ShareController {
|
|||
|
||||
@GetMapping("/report/export/{shareId}/{planId}/{lang}")
|
||||
public void exportHtmlReport(@PathVariable String shareId, @PathVariable String planId,
|
||||
@PathVariable(required = false) String lang, HttpServletResponse response) throws UnsupportedEncodingException {
|
||||
@PathVariable(required = false) String lang, HttpServletResponse response) throws UnsupportedEncodingException, JsonProcessingException {
|
||||
shareInfoService.validate(shareId, planId);
|
||||
testPlanService.exportPlanReport(planId, lang, response);
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ public class TestPlanController {
|
|||
}
|
||||
|
||||
@GetMapping("/report/export/{planId}/{lang}")
|
||||
public void exportHtmlReport(@PathVariable String planId, @PathVariable(required = false) String lang, HttpServletResponse response) throws UnsupportedEncodingException {
|
||||
public void exportHtmlReport(@PathVariable String planId, @PathVariable(required = false) String lang, HttpServletResponse response) throws UnsupportedEncodingException, JsonProcessingException {
|
||||
testPlanService.exportPlanReport(planId, lang, response);
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ public class TestPlanController {
|
|||
}
|
||||
|
||||
@GetMapping("/report/db/export/{reportId}/{lang}")
|
||||
public void exportHtmlDbReport(@PathVariable String reportId, @PathVariable(required = false) String lang, HttpServletResponse response) throws UnsupportedEncodingException {
|
||||
public void exportHtmlDbReport(@PathVariable String reportId, @PathVariable(required = false) String lang, HttpServletResponse response) throws UnsupportedEncodingException, JsonProcessingException {
|
||||
testPlanService.exportPlanDbReport(reportId, lang, response);
|
||||
}
|
||||
|
||||
|
@ -376,8 +376,13 @@ public class TestPlanController {
|
|||
testPlanService.resetStatus(planId);
|
||||
}
|
||||
|
||||
@GetMapping("/ext/report/{planId}")
|
||||
public TestPlanExtReportDTO getExtReport(@PathVariable String planId) throws JsonProcessingException {
|
||||
return testPlanService.getExtReport(planId);
|
||||
@GetMapping("/ext/report/{reportId}")
|
||||
public TestPlanExtReportDTO getExtReport(@PathVariable String reportId) throws JsonProcessingException {
|
||||
return testPlanService.getExtInfoByReportId(reportId);
|
||||
}
|
||||
|
||||
@GetMapping("/ext/plan/{planId}")
|
||||
public TestPlanExtReportDTO getExtPlan(@PathVariable String planId) throws JsonProcessingException {
|
||||
return testPlanService.getExtInfoByPlanId(planId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ public class TestPlanSimpleReportDTO extends TestPlanReportContent {
|
|||
* projectEnvMap: <项目,运行环境>
|
||||
*/
|
||||
private String runMode;
|
||||
private String resourcePool;
|
||||
private String envGroupName;
|
||||
private Map<String, List<String>> projectEnvMap;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.plan.service;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
||||
|
@ -559,6 +560,11 @@ public class TestPlanReportService {
|
|||
resourceId = planExecutionQueues.get(0).getResourceId();
|
||||
testPlanExecutionQueueMapper.deleteByExample(testPlanExecutionQueueExample);
|
||||
}
|
||||
|
||||
testPlanReportMapper.updateByPrimaryKey(testPlanReport);
|
||||
//发送通知
|
||||
testPlanMessageService.checkTestPlanStatusAndSendMessage(testPlanReport, content, isSendMessage);
|
||||
|
||||
if (runMode != null && StringUtils.equalsIgnoreCase(runMode, RunModeConstants.SERIAL.name()) && resourceId != null) {
|
||||
TestPlanExecutionQueueExample queueExample = new TestPlanExecutionQueueExample();
|
||||
queueExample.createCriteria().andReportIdIsNotNull().andResourceIdEqualTo(resourceId);
|
||||
|
@ -573,12 +579,20 @@ public class TestPlanReportService {
|
|||
TestPlanRequestUtil.changeStringToBoolean(jsonObject);
|
||||
TestPlanRunRequest runRequest = JSON.parseObject(JSON.toJSONString(jsonObject), TestPlanRunRequest.class);
|
||||
runRequest.setReportId(testPlanExecutionQueue.getReportId());
|
||||
testPlanService.runPlan(runRequest);
|
||||
runRequest.setTestPlanId(testPlan.getId());
|
||||
try {
|
||||
HttpHeaderUtils.runAsUser("admin");
|
||||
//如果运行测试计划的过程中出现异常,则整个事务会回滚。 删除队列的事务也不会提交,也不会执行后面的测试计划
|
||||
testPlanService.runPlan(runRequest);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("执行队列中的下一个测试计划失败! ", e);
|
||||
this.finishedTestPlanReport(runRequest.getReportId(), TestPlanReportStatus.FAILED.name());
|
||||
} finally {
|
||||
HttpHeaderUtils.clearUser();
|
||||
}
|
||||
}
|
||||
testPlanReportMapper.updateByPrimaryKey(testPlanReport);
|
||||
|
||||
}
|
||||
//发送通知
|
||||
testPlanMessageService.checkTestPlanStatusAndSendMessage(testPlanReport, content, isSendMessage);
|
||||
return testPlanReport;
|
||||
}
|
||||
|
||||
|
@ -1044,6 +1058,16 @@ public class TestPlanReportService {
|
|||
testPlanReportDTO.setId(reportId);
|
||||
TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(testPlanReportContent.getTestPlanReportId());
|
||||
testPlanReportDTO.setName(testPlanReport.getName());
|
||||
TestPlanService testPlanService = CommonBeanFactory.getBean(TestPlanService.class);
|
||||
TestPlanExtReportDTO extReport = null;
|
||||
try {
|
||||
extReport = testPlanService.getExtInfoByReportId(reportId);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (extReport != null) {
|
||||
BeanUtils.copyBean(testPlanReportDTO, extReport);
|
||||
}
|
||||
return testPlanReportDTO;
|
||||
}
|
||||
|
||||
|
@ -1363,7 +1387,7 @@ public class TestPlanReportService {
|
|||
example.setOrderByClause("create_time desc");
|
||||
example.createCriteria().andTestPlanIdEqualTo(planId);
|
||||
List<TestPlanReport> testPlanReports = testPlanReportMapper.selectByExample(example);
|
||||
if(CollectionUtils.isNotEmpty(testPlanReports)){
|
||||
if (CollectionUtils.isNotEmpty(testPlanReports)) {
|
||||
return testPlanReports.get(0).getId();
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -65,8 +65,6 @@ import org.springframework.kafka.core.KafkaTemplate;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
|
@ -1385,15 +1383,22 @@ public class TestPlanService {
|
|||
return report;
|
||||
}
|
||||
|
||||
public void exportPlanReport(String planId, String lang, HttpServletResponse response) throws UnsupportedEncodingException {
|
||||
public void exportPlanReport(String planId, String lang, HttpServletResponse response) throws UnsupportedEncodingException, JsonProcessingException {
|
||||
TestPlanSimpleReportDTO report = buildPlanReport(planId, true);
|
||||
report.setLang(lang);
|
||||
TestPlanExtReportDTO extReport = getExtInfoByPlanId(planId);
|
||||
if(extReport != null) {
|
||||
BeanUtils.copyBean(report, extReport);
|
||||
}
|
||||
render(report, response);
|
||||
}
|
||||
|
||||
public void exportPlanDbReport(String reportId, String lang, HttpServletResponse response) throws UnsupportedEncodingException {
|
||||
public void exportPlanDbReport(String reportId, String lang, HttpServletResponse response) throws UnsupportedEncodingException, JsonProcessingException {
|
||||
TestPlanSimpleReportDTO report = testPlanReportService.getReport(reportId);
|
||||
|
||||
TestPlanExtReportDTO extReport = getExtInfoByReportId(reportId);
|
||||
if(extReport != null) {
|
||||
BeanUtils.copyBean(report, extReport);
|
||||
}
|
||||
Set<String> serviceIdSet = DiscoveryUtil.getServiceIdSet();
|
||||
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
|
||||
report.setApiAllCases(planTestPlanApiCaseService.buildResponse(report.getApiAllCases()));
|
||||
|
@ -1543,6 +1548,10 @@ public class TestPlanService {
|
|||
envMap = planTestPlanApiCaseService.getApiCaseEnv(planId);
|
||||
Map<String, List<String>> scenarioEnv = planTestPlanScenarioCaseService.getApiScenarioEnv(planId);
|
||||
|
||||
if (DiscoveryUtil.hasService(MicroServiceName.UI_TEST)) {
|
||||
scenarioEnv = mergeUiScenarioEnv(planId, scenarioEnv);
|
||||
}
|
||||
|
||||
Set<String> projectIds = scenarioEnv.keySet();
|
||||
for (String projectId : projectIds) {
|
||||
if (envMap.containsKey(projectId)) {
|
||||
|
@ -1565,6 +1574,32 @@ public class TestPlanService {
|
|||
return envMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并ui场景的环境信息
|
||||
* @param planId
|
||||
* @param scenarioEnv
|
||||
* @return
|
||||
*/
|
||||
private Map<String, List<String>> mergeUiScenarioEnv(String planId, Map<String, List<String>> scenarioEnv) {
|
||||
Map<String, List<String>> uiScenarioEnv = planTestPlanUiScenarioCaseService.getUiScenarioEnv(planId);
|
||||
if (MapUtils.isEmpty(scenarioEnv)) {
|
||||
return uiScenarioEnv;
|
||||
}
|
||||
if (MapUtils.isNotEmpty(uiScenarioEnv)) {
|
||||
uiScenarioEnv.entrySet().forEach(entry -> {
|
||||
if (scenarioEnv.containsKey(entry.getKey())) {
|
||||
List<String> environmentIds = scenarioEnv.get(entry.getKey());
|
||||
entry.getValue().forEach(eId -> {
|
||||
if (!environmentIds.contains(eId)) {
|
||||
environmentIds.add(eId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return scenarioEnv;
|
||||
}
|
||||
|
||||
public String runPlan(TestPlanRunRequest testplanRunRequest) {
|
||||
//检查测试计划下有没有可以执行的用例;
|
||||
if (!haveExecCase(testplanRunRequest.getTestPlanId(), false)) {
|
||||
|
@ -1960,7 +1995,53 @@ public class TestPlanService {
|
|||
this.deleteTestPlans(ids);
|
||||
}
|
||||
|
||||
public TestPlanExtReportDTO getExtReport(String planId) throws JsonProcessingException {
|
||||
public TestPlanExtReportDTO getExtInfoByReportId(String reportId) throws JsonProcessingException {
|
||||
TestPlanExtReportDTO testPlanExtReportDTO = new TestPlanExtReportDTO();
|
||||
Set<String> serviceIdSet = DiscoveryUtil.getServiceIdSet();
|
||||
if (serviceIdSet.contains(MicroServiceName.API_TEST)) {
|
||||
List<ApiDefinitionExecResultWithBLOBs> apiDefinitionLists = planTestPlanApiCaseService.selectExtForPlanReport(reportId);
|
||||
if(CollectionUtils.isNotEmpty(apiDefinitionLists)){
|
||||
ApiDefinitionExecResultWithBLOBs apiDefinition = apiDefinitionLists.get(0);
|
||||
convertEnvConfig(apiDefinition.getEnvConfig(), testPlanExtReportDTO);
|
||||
getResourcePool(apiDefinition.getActuator(), testPlanExtReportDTO);
|
||||
return testPlanExtReportDTO;
|
||||
}
|
||||
}
|
||||
if (serviceIdSet.contains(MicroServiceName.UI_TEST)) {
|
||||
List<UiScenarioReportWithBLOBs> apiDefinitionLists = planTestPlanUiScenarioCaseService.selectExtForPlanReport(reportId);
|
||||
if(CollectionUtils.isNotEmpty(apiDefinitionLists)){
|
||||
UiScenarioReportWithBLOBs apiDefinition = apiDefinitionLists.get(0);
|
||||
convertEnvConfig(apiDefinition.getEnvConfig(), testPlanExtReportDTO);
|
||||
getResourcePool(apiDefinition.getActuator(), testPlanExtReportDTO);
|
||||
return testPlanExtReportDTO;
|
||||
}
|
||||
}
|
||||
return testPlanExtReportDTO;
|
||||
}
|
||||
|
||||
private void convertEnvConfig(String envConfig, TestPlanExtReportDTO testPlanExtReportDTO) throws JsonProcessingException {
|
||||
if(StringUtils.isEmpty(envConfig)){
|
||||
return;
|
||||
}
|
||||
EnvConfig env = objectMapper.readValue(envConfig, EnvConfig.class);
|
||||
if(StringUtils.isNotEmpty(env.getMode())){
|
||||
if(RunMode.RUN_MODE_SERIAL.getCode().equals(env.getMode())){
|
||||
testPlanExtReportDTO.setRunMode(RunMode.RUN_MODE_SERIAL.getDesc());
|
||||
} else if (RunMode.RUN_MODE_PARALLEL.getCode().equals(env.getMode())) {
|
||||
testPlanExtReportDTO.setRunMode(RunMode.RUN_MODE_PARALLEL.getDesc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getResourcePool(String actuator, TestPlanExtReportDTO testPlanExtReportDTO){
|
||||
if(StringUtils.isEmpty(actuator)){
|
||||
return;
|
||||
}
|
||||
TestResourcePool testResourcePool = testResourcePoolMapper.selectByPrimaryKey(actuator);
|
||||
testPlanExtReportDTO.setResourcePool(testResourcePool == null ? null : testResourcePool.getName());
|
||||
}
|
||||
|
||||
public TestPlanExtReportDTO getExtInfoByPlanId(String planId) throws JsonProcessingException {
|
||||
String reportId = testPlanReportService.getLastReportByPlanId(planId);
|
||||
if(StringUtils.isEmpty(reportId)){
|
||||
return null;
|
||||
|
@ -1985,28 +2066,6 @@ public class TestPlanService {
|
|||
return testPlanExtReportDTO;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void convertEnvConfig(String envConfig, TestPlanExtReportDTO testPlanExtReportDTO) throws JsonProcessingException {
|
||||
if(StringUtils.isEmpty(envConfig)){
|
||||
return;
|
||||
}
|
||||
EnvConfig env = objectMapper.readValue(envConfig, EnvConfig.class);
|
||||
if(StringUtils.isNotEmpty(env.getMode())){
|
||||
if(RunMode.RUN_MODE_SERIAL.getCode().equals(env.getMode())){
|
||||
testPlanExtReportDTO.setRunMode(RunMode.RUN_MODE_SERIAL.getDesc());
|
||||
} else if (RunMode.RUN_MODE_PARALLEL.getCode().equals(env.getMode())) {
|
||||
testPlanExtReportDTO.setRunMode(RunMode.RUN_MODE_PARALLEL.getDesc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getResourcePool(String actuator, TestPlanExtReportDTO testPlanExtReportDTO){
|
||||
if(StringUtils.isEmpty(actuator)){
|
||||
return;
|
||||
}
|
||||
TestResourcePool testResourcePool = testResourcePoolMapper.selectByPrimaryKey(actuator);
|
||||
testPlanExtReportDTO.setResourcePool(testResourcePool == null ? null : testResourcePool.getName());
|
||||
return testPlanExtReportDTO;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,4 +153,8 @@ public class PlanTestPlanUiScenarioCaseService extends UiTestService {
|
|||
public List<UiScenarioReportWithBLOBs> selectExtForPlanReport(String planId) {
|
||||
return microService.getForDataArray(serviceName, BASE_URL + "/get/report/ext/" + planId, UiScenarioReportWithBLOBs.class);
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getUiScenarioEnv(String planId) {
|
||||
return microService.getForData(serviceName, BASE_URL + "/get/env/" + planId, Map.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,9 +170,10 @@ public class IssuesService {
|
|||
issues = platformPluginService.getPlatform(project.getPlatform())
|
||||
.addIssue(platformIssuesUpdateRequest);
|
||||
|
||||
issues.setPlatform(project.getPlatform());
|
||||
insertIssues(issues);
|
||||
issuesRequest.setId(issues.getId());
|
||||
issues.setPlatform(project.getPlatform());
|
||||
issuesRequest.setPlatformId(issues.getPlatformId());
|
||||
// 用例与第三方缺陷平台中的缺陷关联
|
||||
handleTestCaseIssues(issuesRequest);
|
||||
|
||||
|
@ -496,8 +497,6 @@ public class IssuesService {
|
|||
public List<String> getPlatforms(Project project) {
|
||||
String workspaceId = project.getWorkspaceId();
|
||||
boolean tapd = isIntegratedPlatform(workspaceId, IssuesManagePlatform.Tapd.toString());
|
||||
boolean jira = isIntegratedPlatform(workspaceId, IssuesManagePlatform.Jira.toString());
|
||||
boolean zentao = isIntegratedPlatform(workspaceId, IssuesManagePlatform.Zentao.toString());
|
||||
boolean azure = isIntegratedPlatform(workspaceId, IssuesManagePlatform.AzureDevops.toString());
|
||||
|
||||
List<String> platforms = new ArrayList<>();
|
||||
|
@ -510,20 +509,6 @@ public class IssuesService {
|
|||
|
||||
}
|
||||
|
||||
if (jira) {
|
||||
String jiraKey = project.getJiraKey();
|
||||
if (StringUtils.isNotBlank(jiraKey) && PlatformPluginService.isPluginPlatform(project.getPlatform())) {
|
||||
platforms.add(IssuesManagePlatform.Jira.name());
|
||||
}
|
||||
}
|
||||
|
||||
if (zentao) {
|
||||
String zentaoId = project.getZentaoId();
|
||||
if (StringUtils.isNotBlank(zentaoId) && StringUtils.equals(project.getPlatform(), IssuesManagePlatform.Zentao.toString())) {
|
||||
platforms.add(IssuesManagePlatform.Zentao.name());
|
||||
}
|
||||
}
|
||||
|
||||
if (azure) {
|
||||
String azureDevopsId = project.getAzureDevopsId();
|
||||
if (StringUtils.isNotBlank(azureDevopsId) && StringUtils.equals(project.getPlatform(), IssuesManagePlatform.AzureDevops.toString())) {
|
||||
|
@ -637,13 +622,13 @@ public class IssuesService {
|
|||
issuesRequest.setProjectId(SessionUtils.getCurrentProjectId());
|
||||
List<IssuesDao> issuesDaos = listByWorkspaceId(issuesRequest);
|
||||
if (CollectionUtils.isNotEmpty(issuesDaos)) {
|
||||
issuesDaos.parallelStream().forEach(issuesDao -> {
|
||||
issuesDaos.forEach(issuesDao -> {
|
||||
delete(issuesDao.getId());
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (CollectionUtils.isNotEmpty(request.getBatchDeleteIds())) {
|
||||
request.getBatchDeleteIds().parallelStream().forEach(id -> delete(id));
|
||||
request.getBatchDeleteIds().forEach(id -> delete(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -945,10 +930,10 @@ public class IssuesService {
|
|||
}
|
||||
|
||||
private String getDefaultCustomField(Project project) {
|
||||
if (!trackProjectService.isThirdPartTemplate(project)) {
|
||||
return getDefaultCustomFields(project.getId());
|
||||
if (isThirdPartTemplate(project)) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return getDefaultCustomFields(project.getId());
|
||||
}
|
||||
|
||||
public void syncPluginThirdPartyIssues(List<IssuesDao> issues, Project project, String defaultCustomFields) {
|
||||
|
@ -1057,10 +1042,6 @@ public class IssuesService {
|
|||
}
|
||||
|
||||
private void syncAllPluginIssueAttachment(Project project, IssueSyncRequest syncIssuesResult) {
|
||||
// todo 所有平台改造完之后删除
|
||||
if (!StringUtils.equals(project.getPlatform(), IssuesManagePlatform.Jira.name())) {
|
||||
return;
|
||||
}
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
try {
|
||||
AttachmentModuleRelationMapper batchAttachmentModuleRelationMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||
|
@ -1510,7 +1491,8 @@ public class IssuesService {
|
|||
public boolean isThirdPartTemplate(Project project) {
|
||||
return project.getThirdPartTemplate() != null
|
||||
&& project.getThirdPartTemplate()
|
||||
&& PlatformPluginService.isPluginPlatform(project.getPlatform());
|
||||
&& PlatformPluginService.isPluginPlatform(project.getPlatform())
|
||||
&& platformPluginService.isThirdPartTemplateSupport(project.getPlatform());
|
||||
}
|
||||
|
||||
public void checkThirdProjectExist(Project project) {
|
||||
|
@ -1709,6 +1691,7 @@ public class IssuesService {
|
|||
IssueTemplateDao issueTemplateDao;
|
||||
Project project = baseProjectService.getProjectById(projectId);
|
||||
if (PlatformPluginService.isPluginPlatform(project.getPlatform())
|
||||
&& platformPluginService.isThirdPartTemplateSupport(project.getPlatform())
|
||||
&& project.getThirdPartTemplate()) {
|
||||
// 第三方Jira平台
|
||||
issueTemplateDao = getThirdPartTemplate(project.getId());
|
||||
|
@ -1794,13 +1777,13 @@ public class IssuesService {
|
|||
}
|
||||
|
||||
public void saveImportData(List<IssuesUpdateRequest> issues) {
|
||||
issues.parallelStream().forEach(issue -> {
|
||||
issues.forEach(issue -> {
|
||||
addIssues(issue, null);
|
||||
});
|
||||
}
|
||||
|
||||
public void updateImportData(List<IssuesUpdateRequest> issues) {
|
||||
issues.parallelStream().forEach(issue -> {
|
||||
issues.forEach(issue -> {
|
||||
updateIssues(issue);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -463,7 +463,11 @@ public class TestCaseService {
|
|||
dealWithOtherInfoOfNewVersion(testCase, oldTestCase.getId());
|
||||
testCaseMapper.insertSelective(testCase);
|
||||
}
|
||||
//checkAndSetLatestVersion(testCase.getRefId());
|
||||
String defaultVersion = baseProjectVersionMapper.getDefaultVersion(testCase.getProjectId());
|
||||
if (StringUtils.equalsIgnoreCase(testCase.getVersionId(), defaultVersion)) {
|
||||
checkAndSetLatestVersion(testCase.getRefId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -704,14 +708,8 @@ public class TestCaseService {
|
|||
}
|
||||
|
||||
public int deleteToGcBatch(TestCaseBatchRequest request) {
|
||||
List<String> ids = new ArrayList<String>();
|
||||
if (request.getCondition() != null && request.getCondition().isSelectAll()) {
|
||||
List<TestCaseDTO> testCaseDTOS = listTestCase(request.getCondition());
|
||||
ids = testCaseDTOS.stream().map(TestCaseDTO::getId).collect(Collectors.toList());
|
||||
} else {
|
||||
ids = request.getIds();
|
||||
}
|
||||
return deleteToGcBatch(ids, null);
|
||||
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extTestCaseMapper.selectIds(query));
|
||||
return deleteToGcBatch(request.getIds(), null);
|
||||
}
|
||||
|
||||
public int deleteToGcBatch(List<String> ids, String projectId) {
|
||||
|
@ -2434,13 +2432,8 @@ public class TestCaseService {
|
|||
}
|
||||
|
||||
public void reduction(TestCaseBatchRequest request) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
if (request.getCondition() != null && request.getCondition().isSelectAll()) {
|
||||
List<TestCaseDTO> allReductionTestCases = listTestCase(request.getCondition());
|
||||
ids = allReductionTestCases.stream().map(TestCaseDTO::getId).collect(Collectors.toList());
|
||||
} else {
|
||||
ids = request.getIds();
|
||||
}
|
||||
ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extTestCaseMapper.selectIds(query));
|
||||
List<String> ids = request.getIds();
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
extTestCaseMapper.checkOriginalStatusByIds(ids);
|
||||
|
||||
|
|
|
@ -26,13 +26,6 @@ public class TrackProjectService {
|
|||
@Resource
|
||||
ProjectMapper projectMapper;
|
||||
|
||||
public boolean isThirdPartTemplate(Project project) {
|
||||
if (project.getThirdPartTemplate() != null && project.getThirdPartTemplate() && project.getPlatform().equals(IssuesManagePlatform.Jira.name())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean useCustomNum(String projectId) {
|
||||
return useCustomNum(baseProjectService.getProjectById(projectId));
|
||||
}
|
||||
|
|
|
@ -52,11 +52,21 @@ public abstract class AbstractCustomFieldValidator {
|
|||
try {
|
||||
// [a, b] => ["a","b"]
|
||||
if (!StringUtils.equals(value, "[]")) {
|
||||
value = value.replace("[", "[\"")
|
||||
.replace("]", "\"]")
|
||||
.replace(",", "\",\"")
|
||||
.replace(",", "\",\"")
|
||||
.replace(StringUtils.SPACE, StringUtils.EMPTY);
|
||||
if (!value.contains("[\"")) {
|
||||
value = value.replace("[", "[\"");
|
||||
}
|
||||
if (!value.contains("\"]")) {
|
||||
value = value.replace("]", "\"]");
|
||||
|
||||
}
|
||||
if (!value.contains("\",\"")) {
|
||||
value = value.replace(",", "\",\"");
|
||||
|
||||
}
|
||||
if (!value.contains("\",\"")) {
|
||||
value = value.replace(",", "\",\"");
|
||||
}
|
||||
value = value.replace(StringUtils.SPACE, StringUtils.EMPTY);
|
||||
}
|
||||
return JSON.parseArray(value, String.class);
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
package io.metersphere.validate;
|
||||
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.exception.CustomFieldValidateException;
|
||||
import io.metersphere.dto.CustomFieldDao;
|
||||
import io.metersphere.exception.CustomFieldValidateException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class CustomFieldMultipleTextValidator extends AbstractCustomFieldValidator {
|
||||
|
||||
public CustomFieldMultipleTextValidator() {
|
||||
this.isKVOption = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(CustomFieldDao customField, String value) throws CustomFieldValidateException {
|
||||
validateRequired(customField, value);
|
||||
if (StringUtils.isNotBlank(value)) {
|
||||
try {
|
||||
JSON.parseArray(value);
|
||||
parse2Array(customField.getName(), value);
|
||||
} catch (Exception e) {
|
||||
CustomFieldValidateException.throwException(String.format(Translator.get("custom_field_array_tip"), customField.getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object parse2Key(String keyOrValuesStr, CustomFieldDao customField) {
|
||||
if (StringUtils.isBlank(keyOrValuesStr)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return parse2Array(keyOrValuesStr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -309,9 +309,13 @@ export function testPlanLoadCaseEditStatus(planId) {
|
|||
return post(BASE_URL + `edit/status/${planId}`, new Promise(() => {}));
|
||||
}
|
||||
|
||||
export function getTestPlanExtReport(planId) {
|
||||
if (planId) {
|
||||
return get('/test/plan/ext/report/' + planId);
|
||||
export function getTestPlanExtReport(planId, reportId) {
|
||||
if (reportId) {
|
||||
return get('/test/plan/ext/report/' + reportId);
|
||||
} else if (planId) {
|
||||
return get('/test/plan/ext/plan/' + planId);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import {get, post} from "metersphere-frontend/src/plugins/request"
|
||||
|
||||
const BASE_URL = "/ui/automation/";
|
||||
|
||||
export function getUiScenarioEnvByProjectId(id) {
|
||||
return get(BASE_URL + `env-project-ids/${id}`);
|
||||
}
|
||||
|
||||
export function uiAutomationReduction(param) {
|
||||
return post(BASE_URL + 'reduction', param);
|
||||
}
|
||||
|
||||
export function uiScenarioEnvMap(params) {
|
||||
return post(BASE_URL + 'env/map', params);
|
||||
}
|
|
@ -465,7 +465,7 @@ export default {
|
|||
this.isXpack = false;
|
||||
}
|
||||
if (hasLicense()) {
|
||||
this.getVersionHistory();
|
||||
this.getDefaultVersion();
|
||||
}
|
||||
|
||||
//浏览器拉伸时窗口编辑窗口自适应
|
||||
|
|
|
@ -425,7 +425,8 @@ export default {
|
|||
let platform = this.issueTemplate.platform;
|
||||
|
||||
this.platformTransitions = null;
|
||||
if (this.form.platformId) {
|
||||
// 编辑的时候才展示
|
||||
if (this.form.platformId && this.form.id) {
|
||||
let data = {
|
||||
platformKey: this.form.platformId,
|
||||
projectId: getCurrentProjectID(),
|
||||
|
|
|
@ -201,7 +201,7 @@ export default {
|
|||
// 选中环境是否存在
|
||||
temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId;
|
||||
}
|
||||
if (this.projectEnvMap) {
|
||||
if (this.projectEnvMap && Object.keys(this.projectEnvMap).length > 0) {
|
||||
let projectEnvMapElement = this.projectEnvMap[d];
|
||||
if (projectEnvMapElement.length>0) {
|
||||
projectEnvMapElement.forEach(envId => {
|
||||
|
|
|
@ -76,13 +76,13 @@ import TestPlanFunctional from "./comonents/functional/TestPlanFunctional";
|
|||
import TestPlanApi from "./comonents/api/TestPlanApi";
|
||||
import TestPlanUi from "./comonents/ui/TestPlanUi";
|
||||
import TestPlanLoad from "@/business/plan/view/comonents/load/TestPlanLoad";
|
||||
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
|
||||
import {getCurrentProjectID, setCurrentProjectID} from "metersphere-frontend/src/utils/token";
|
||||
import {hasLicense} from "metersphere-frontend/src/utils/permission"
|
||||
import TestPlanReportContent from "@/business/plan/view/comonents/report/detail/TestPlanReportContent";
|
||||
import IsChangeConfirm from "metersphere-frontend/src/components/IsChangeConfirm";
|
||||
import {PROJECT_ID, WORKSPACE_ID} from "metersphere-frontend/src/utils/constants";
|
||||
import {useStore} from "@/store";
|
||||
import {testPlanListAll} from "@/api/remote/plan/test-plan";
|
||||
import {testPlanGet, testPlanListAll} from "@/api/remote/plan/test-plan";
|
||||
import {isProjectVersionEnable} from "@/business/utils/sdk-utils";
|
||||
|
||||
export default {
|
||||
|
@ -207,11 +207,23 @@ export default {
|
|||
testPlanListAll({projectId: getCurrentProjectID()})
|
||||
.then(response => {
|
||||
this.testPlans = response.data;
|
||||
let hasPlan = false;
|
||||
this.testPlans.forEach(plan => {
|
||||
if (this.planId && plan.id === this.planId) {
|
||||
hasPlan = true;
|
||||
this.currentPlan = plan;
|
||||
}
|
||||
});
|
||||
if (!hasPlan) {
|
||||
if (this.planId) {
|
||||
testPlanGet(this.planId)
|
||||
.then((r) => {
|
||||
let plan = r.data;
|
||||
setCurrentProjectID(plan.projectId);
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
changePlan(plan) {
|
||||
|
|
|
@ -511,13 +511,11 @@ export default {
|
|||
singleRun(row) {
|
||||
let reportId = getUUID().substring(0, 8);
|
||||
this.rowLoading = row.id;
|
||||
run(row.id, reportId)
|
||||
.then(() => {
|
||||
this.runningReport.add(reportId);
|
||||
this.$refs.apiCaseResult.open(reportId);
|
||||
},error =>{
|
||||
this.rowLoading = "";
|
||||
});
|
||||
this.runningReport.add(reportId);
|
||||
// 这里先打开报告,建立 websock
|
||||
// 否则可能执行完了才建立 websock,拿不到结果
|
||||
this.$refs.apiCaseResult.open(reportId);
|
||||
run(row.id, reportId);
|
||||
},
|
||||
handleTestEnd(reportId) {
|
||||
if (this.runningReport.has(reportId)) {
|
||||
|
|
|
@ -71,13 +71,13 @@ export default {
|
|||
isDb: Boolean,
|
||||
shareId: String,
|
||||
reportId: String,
|
||||
runMode: String,
|
||||
resourcePool: String,
|
||||
needMoveBar: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
report: {},
|
||||
runMode: '',
|
||||
resourcePool: '',
|
||||
loading: false,
|
||||
shareUrl: ''
|
||||
};
|
||||
|
@ -139,6 +139,8 @@ export default {
|
|||
getReport() {
|
||||
if (this.isTemplate) {
|
||||
this.report = "#report";
|
||||
this.runMode = this.report.runMode;
|
||||
this.resourcePool = this.report.resourcePool;
|
||||
if (this.report.lang) {
|
||||
this.$setLang(this.report.lang);
|
||||
}
|
||||
|
@ -151,6 +153,8 @@ export default {
|
|||
.then((r) => {
|
||||
this.loading = false;
|
||||
this.report = r.data;
|
||||
this.runMode = r.data.runMode;
|
||||
this.resourcePool = r.data.resourcePool;
|
||||
this.report.config = this.getDefaultConfig(this.report);
|
||||
});
|
||||
} else {
|
||||
|
@ -183,8 +187,7 @@ export default {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
getTestPlanExtReport(this.planId).then((response) => {
|
||||
getTestPlanExtReport(this.planId, this.reportId).then((response) => {
|
||||
this.runMode = response.data.runMode;
|
||||
this.resourcePool = response.data.resourcePool;
|
||||
})
|
||||
|
|
|
@ -239,7 +239,7 @@ export default {
|
|||
this.responseLoading = false;
|
||||
if (response.data) {
|
||||
let data = response.data;
|
||||
if (data && data.content) {
|
||||
if (data) {
|
||||
this.showResponse = true;
|
||||
try {
|
||||
this.response = JSON.parse(data.content);
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
<template>
|
||||
<div v-loading="loading">
|
||||
<env-group-popover
|
||||
:env-map="projectEnvMap"
|
||||
:project-ids="projectIds"
|
||||
:show-env-group="false"
|
||||
@setProjectEnvMap="setProjectEnvMap"
|
||||
:environment-type.sync="environmentType"
|
||||
:group-id="envGroupId"
|
||||
:is-scenario="false"
|
||||
@setEnvGroup="setEnvGroup"
|
||||
:show-config-button-with-out-permission="
|
||||
showConfigButtonWithOutPermission
|
||||
"
|
||||
:project-list="projectList"
|
||||
ref="envPopover"
|
||||
class="env-popover"
|
||||
/>
|
||||
|
||||
<ms-table-adv-search-bar :condition.sync="condition" class="adv-search-bar"
|
||||
v-if="condition.components !== undefined && condition.components.length > 0"
|
||||
|
@ -99,6 +115,9 @@ import {
|
|||
getCustomTableWidth
|
||||
} from "metersphere-frontend/src/utils/tableUtils";
|
||||
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
|
||||
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
||||
import {getApiScenarioEnvByProjectId} from "@/api/remote/api/api-automation";
|
||||
import {getUiScenarioEnvByProjectId} from "@/api/remote/ui/ui-automation";
|
||||
|
||||
export default {
|
||||
name: "RelevanceUiScenarioList",
|
||||
|
@ -112,6 +131,7 @@ export default {
|
|||
MsTag,
|
||||
MsTableAdvSearchBar,
|
||||
MsTableColumn,
|
||||
EnvGroupPopover,
|
||||
},
|
||||
props: {
|
||||
referenced: {
|
||||
|
@ -147,6 +167,7 @@ export default {
|
|||
envGroupId: "",
|
||||
versionFilters: [],
|
||||
fieldsWidth: getCustomTableWidth('TEST_PLAN_UI_SCENARIO_CASE'),
|
||||
projectIds: new Set()
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -244,10 +265,22 @@ export default {
|
|||
selectCountChange(data) {
|
||||
this.selectRows = this.$refs.scenarioTable.selectRows;
|
||||
this.$emit("selectCountChange", data);
|
||||
this.initProjectIds();
|
||||
},
|
||||
showReport() {
|
||||
|
||||
}
|
||||
},
|
||||
initProjectIds() {
|
||||
this.projectIds.clear();
|
||||
// this.map.clear();
|
||||
this.selectRows.forEach((row) => {
|
||||
getUiScenarioEnvByProjectId(row.id).then((res) => {
|
||||
let data = res.data;
|
||||
data.projectIds.forEach((d) => this.projectIds.add(d));
|
||||
// this.map.set(row.id, data.projectIds);
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -148,6 +148,10 @@ export default {
|
|||
let map = this.$refs.apiScenarioList.map;
|
||||
let envGroupId = this.$refs.apiScenarioList.envGroupId;
|
||||
|
||||
if (!envMap || envMap.size == 0) {
|
||||
this.$warning(this.$t('api_test.environment.select_environment'));
|
||||
return;
|
||||
}
|
||||
selectRows.forEach(row => {
|
||||
selectIds.push(row.id);
|
||||
})
|
||||
|
@ -179,8 +183,6 @@ export default {
|
|||
this.autoCheckStatus();
|
||||
this.$refs.baseRelevance.close();
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
autoCheckStatus() { // 检查执行结果,自动更新计划状态
|
||||
if (!this.planId) {
|
||||
|
|
|
@ -147,7 +147,8 @@
|
|||
:filters="apiscenariofilters.RESULT_FILTERS"
|
||||
:label="$t('api_test.automation.last_result')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-link @click="showReport(row)" :disabled="!row.lastResult || row.lastResult==='PENDING' || row.lastResult==='UnExecute'">
|
||||
<el-link @click="showReport(row)"
|
||||
:disabled="!row.lastResult || row.lastResult==='PENDING' || row.lastResult==='UnExecute'">
|
||||
<ms-test-plan-api-status :status="row.lastResult==='UnExecute' ? 'PENDING' : row.lastResult"/>
|
||||
</el-link>
|
||||
</template>
|
||||
|
@ -176,8 +177,8 @@
|
|||
:select-row="this.$refs.table ? this.$refs.table.selectRows : new Set()" ref="batchEdit"
|
||||
@batchEdit="batchEdit"/>
|
||||
|
||||
<ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true"
|
||||
:custom-serial-on-sample-error="true"/>
|
||||
<ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true"
|
||||
:custom-serial-on-sample-error="true" :request="conditionRequest"/>
|
||||
|
||||
<ms-task-center ref="taskCenter" :show-menu="false"/>
|
||||
</div>
|
||||
|
@ -327,6 +328,8 @@ export default {
|
|||
]
|
||||
},
|
||||
versionFilters: [],
|
||||
//
|
||||
conditionRequest: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -358,14 +361,14 @@ export default {
|
|||
},
|
||||
search() {
|
||||
initCondition(this.condition, this.condition.selectAll);
|
||||
if(this.condition && this.condition.filters && this.condition.filters.last_result){
|
||||
if(this.condition.filters.last_result.length > 0){
|
||||
if (this.condition && this.condition.filters && this.condition.filters.last_result) {
|
||||
if (this.condition.filters.last_result.length > 0) {
|
||||
//校验是否含有PENDING
|
||||
if(this.condition.filters.last_result.includes("PENDING")){
|
||||
if (this.condition.filters.last_result.includes("PENDING")) {
|
||||
this.condition.filters.last_result = [...this.condition.filters.last_result, "UnExecute"]
|
||||
}
|
||||
//校验是否含有ERROR
|
||||
if(this.condition.filters.last_result.includes("ERROR")){
|
||||
if (this.condition.filters.last_result.includes("ERROR")) {
|
||||
this.condition.filters.last_result = [...this.condition.filters.last_result, "FAIL"]
|
||||
}
|
||||
}
|
||||
|
@ -437,12 +440,19 @@ export default {
|
|||
let rows = this.orderBySelectRows(this.$refs.table.selectRows);
|
||||
this.planCaseIds = [];
|
||||
rows.forEach(row => {
|
||||
this.planCaseIds.push(row.id);
|
||||
this.planCaseIds.push(row.caseId);
|
||||
})
|
||||
this.conditionRequest.id = getUUID();
|
||||
this.conditionRequest.ids = this.planCaseIds;
|
||||
this.conditionRequest.projectId = this.projectId;
|
||||
this.conditionRequest.condition = this.condition;
|
||||
this.$refs.runMode.open();
|
||||
},
|
||||
orderBySelectRows(rows) {
|
||||
let selectIds = Array.from(rows).map(row => row.id);
|
||||
let selectIds = this.$refs.table.selectIds;
|
||||
if (rows) {
|
||||
selectIds = Array.from(rows).map(row => row.id);
|
||||
}
|
||||
let array = [];
|
||||
for (let i in this.tableData) {
|
||||
if (selectIds.indexOf(this.tableData[i].id) !== -1) {
|
||||
|
@ -499,8 +509,8 @@ export default {
|
|||
},
|
||||
},
|
||||
},
|
||||
this.$t("ui.view_config")
|
||||
),
|
||||
this.$t("ui.view_config")
|
||||
),
|
||||
])
|
||||
);
|
||||
validate = false;
|
||||
|
|
|
@ -7,6 +7,21 @@
|
|||
:visible.sync="runModeVisible"
|
||||
>
|
||||
<div class="mode-container">
|
||||
|
||||
<div>
|
||||
<div>{{ $t("commons.environment") }}:</div>
|
||||
<env-select-popover :project-ids="projectIds"
|
||||
:project-list="projectList"
|
||||
:project-env-map="projectEnvListMap"
|
||||
:environment-type="'JSON'"
|
||||
:has-option-group="false"
|
||||
:group-id="runConfig.environmentGroupId"
|
||||
@setProjectEnvMap="setProjectEnvMap"
|
||||
ref="envSelectPopover"
|
||||
class="mode-row"
|
||||
></env-select-popover>
|
||||
</div>
|
||||
|
||||
<!-- 浏览器 -->
|
||||
<div class="browser-row wrap">
|
||||
<div class="title">{{ $t("ui.browser") }}:</div>
|
||||
|
@ -175,11 +190,13 @@
|
|||
|
||||
<script>
|
||||
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter'
|
||||
import {getOwnerProjects} from "@/business/utils/sdk-utils";
|
||||
import {getCurrentProjectID, getOwnerProjects, strMapToObj} from "@/business/utils/sdk-utils";
|
||||
import {uiScenarioEnvMap} from "@/api/remote/ui/ui-automation";
|
||||
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
|
||||
|
||||
export default {
|
||||
name: "UiRunMode",
|
||||
components: {MsDialogFooter},
|
||||
components: {MsDialogFooter, EnvSelectPopover},
|
||||
data() {
|
||||
return {
|
||||
runModeVisible: false,
|
||||
|
@ -207,6 +224,8 @@ export default {
|
|||
},
|
||||
projectList: [],
|
||||
projectIds: new Set(),
|
||||
projectEnvListMap: {},
|
||||
caseIdEnvNameMap: {},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -262,6 +281,7 @@ export default {
|
|||
};
|
||||
this.runModeVisible = true;
|
||||
this.getWsProjects();
|
||||
this.showPopover();
|
||||
},
|
||||
changeMode() {
|
||||
this.runConfig.runWithinResourcePool = false;
|
||||
|
@ -296,6 +316,29 @@ export default {
|
|||
this.$emit("handleRunBatch", this.runConfig);
|
||||
this.close();
|
||||
},
|
||||
setProjectEnvMap(projectEnvMap) {
|
||||
this.runConfig.envMap = strMapToObj(projectEnvMap);
|
||||
},
|
||||
showPopover() {
|
||||
this.showScenarioPopover();
|
||||
},
|
||||
showScenarioPopover() {
|
||||
let currentProjectID = getCurrentProjectID();
|
||||
this.projectIds.clear();
|
||||
uiScenarioEnvMap(this.request).then((res) => {
|
||||
let data = res.data;
|
||||
this.projectEnvListMap = data;
|
||||
if (data) {
|
||||
for (let d in data) {
|
||||
this.projectIds.add(d);
|
||||
}
|
||||
}
|
||||
if (this.projectIds.size === 0) {
|
||||
this.projectIds.add(currentProjectID);
|
||||
}
|
||||
this.$refs.envSelectPopover.open();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue