This commit is contained in:
liqiang-fit2cloud 2023-01-03 16:59:09 +08:00
commit 981088f066
52 changed files with 1209 additions and 726 deletions

View File

@ -158,7 +158,7 @@ curl -sSL https://github.com/metersphere/metersphere/releases/latest/download/qu
## License & Copyright
Copyright (c) 2014-2022 飞致云 FIT2CLOUD, All rights reserved.
Copyright (c) 2014-2023 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

View File

@ -97,7 +97,7 @@ MeterSphere 的最新 LTS 版本为 v1.20 LTS。MeterSphere 下一个 LTS 版本
### License & Copyright
Copyright (c) 2014-2022 飞致云 FIT2CLOUD, All rights reserved.
Copyright (c) 2014-2023 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

View File

@ -555,8 +555,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
String value = keyValue.getValue() != null && keyValue.getValue().startsWith("@") ?
ScriptEngineUtils.buildFunctionCallString(keyValue.getValue()) : keyValue.getValue();
value = keyValue.isUrlEncode() ? StringUtils.join(StringUtils.join("${__urlencode(", value), ")}") : value;
String key = keyValue.isUrlEncode() ? StringUtils.join(StringUtils.join("${__urlencode(", keyValue.getName()), ")}") : keyValue.getName();
keyValueMap.put(key, value);
keyValueMap.put(keyValue.getName(), value);
} catch (Exception e) {
LogUtil.error(e);
}

View File

@ -330,7 +330,7 @@ public class Swagger3Parser extends SwaggerAbstractParser {
Set<String> refSet = new HashSet<>();
Map<String, Schema> infoMap = new HashMap();
Schema schema = getSchema(mediaType.getSchema());
if (content.get(contentType).getExample() != null && schema.getExample() == null) {
if (content.get(contentType) != null && content.get(contentType).getExample() != null && schema.getExample() == null) {
schema.setExample(content.get(contentType).getExample());
}
Object bodyData = null;
@ -979,7 +979,7 @@ public class Swagger3Parser extends SwaggerAbstractParser {
return new JSONObject();
}
JSONObject responseBody = new JSONObject();
JSONObject statusCodeInfo = new JSONObject();
// build 请求头
JSONObject headers = new JSONObject();
JSONArray headValueList = response.optJSONArray("headers");
@ -995,17 +995,21 @@ public class Swagger3Parser extends SwaggerAbstractParser {
}
}
}
statusCodeInfo.put("headers", headers);
statusCodeInfo.put("content", buildContent(response));
statusCodeInfo.put("description", StringUtils.EMPTY);
// 返回code
JSONArray statusCode = response.optJSONArray("statusCode");
for (int i = 0; i < statusCode.length(); i++) {
JSONObject statusCodeInfo = new JSONObject();
statusCodeInfo.put("headers", headers);
statusCodeInfo.put("content", buildContent(response));
statusCodeInfo.put("description", StringUtils.EMPTY);
JSONObject jsonObject = statusCode.getJSONObject(i);
jsonObject.get("name");
statusCodeInfo.put("description", jsonObject.get("value"));
responseBody.put(jsonObject.get("name").toString(), statusCodeInfo);
if (jsonObject.optString("value") != null) {
statusCodeInfo.put("description", jsonObject.optString("value"));
}
if (jsonObject.optString("name") != null) {
responseBody.put(jsonObject.optString("name"), statusCodeInfo);
}
}
return responseBody;
}

View File

@ -81,6 +81,7 @@ import org.apache.jorphan.collections.HashTree;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
@ -823,6 +824,14 @@ public class JMeterParser extends ApiImportAbstractParser<ScenarioImport> {
elementNode.setName(transactionController.getName());
((MsTransactionController) elementNode).setGenerateParentSample(transactionController.isGenerateParentSample());
((MsTransactionController) elementNode).setIncludeTimers(transactionController.isIncludeTimers());
} else if (StringUtils.equals(key.getClass().getName(), "net.xmeter.samplers.ConnectSampler")) {
elementNode = getMqttElement(key, "io.metersphere.plugin.mqtt.sampler.MqttConnectSampler");
} else if (StringUtils.equals(key.getClass().getName(), "net.xmeter.samplers.DisConnectSampler")) {
elementNode = getMqttElement(key, "io.metersphere.plugin.mqtt.sampler.MqttDisConnectSampler");
} else if (StringUtils.equals(key.getClass().getName(), "net.xmeter.samplers.PubSampler")) {
elementNode = getMqttElement(key, "io.metersphere.plugin.mqtt.sampler.MqttPubSampler");
} else if (StringUtils.equals(key.getClass().getName(), "net.xmeter.samplers.SubSampler")) {
elementNode = getMqttElement(key, "io.metersphere.plugin.mqtt.sampler.MqttSubSampler");
}
// 平台不能识别的Jmeter步骤
else {
@ -866,6 +875,21 @@ public class JMeterParser extends ApiImportAbstractParser<ScenarioImport> {
}
}
private static MsTestElement getMqttElement(Object key, String className) {
MsTestElement elementNode;
try {
Class<?> clazz = null;
clazz = Class.forName(className);
Object instance = clazz.getConstructor().newInstance();
Method methods2 = clazz.getMethod("importJmx", Object.class);
Object invoke = methods2.invoke(instance, key);
elementNode = (MsTestElement) invoke;
} catch (Exception e) {
throw new RuntimeException(e);
}
return elementNode;
}
public static MsTestElement getMsTestElement(BeanShellPreProcessor key) {
MsTestElement elementNode;
BeanShellPreProcessor beanShellPreProcessor = key;

View File

@ -94,10 +94,11 @@ public class GenerateHashTreeUtil {
String environmentType = apiScenarioWithBLOBs.getEnvironmentType();
String environmentJson = apiScenarioWithBLOBs.getEnvironmentJson();
String environmentGroupId = apiScenarioWithBLOBs.getEnvironmentGroupId();
if (StringUtils.isBlank(environmentType)) {
environmentType = EnvironmentType.JSON.toString();
}
if (StringUtils.equals(environmentType, EnvironmentType.JSON.toString())) {
if (StringUtils.equals(environmentType, EnvironmentType.JSON.toString()) && StringUtils.isNotBlank(environmentJson)) {
scenario.setEnvironmentMap(JSON.parseObject(environmentJson, Map.class));
} else if (StringUtils.equals(environmentType, EnvironmentType.GROUP.toString())) {
Map<String, String> map = CommonBeanFactory.getBean(BaseEnvGroupProjectService.class).getEnvMap(environmentGroupId);
@ -133,7 +134,7 @@ public class GenerateHashTreeUtil {
String definition = element.toString();
MsScenario scenario = JSON.parseObject(definition, MsScenario.class);
group.setOnSampleError(scenario.getOnSampleError());
if (planEnvMap != null && planEnvMap.size() > 0) {
if (MapUtils.isNotEmpty(planEnvMap)) {
scenario.setEnvironmentMap(planEnvMap);
} else {
setScenarioEnv(scenario, item);

View File

@ -3,15 +3,13 @@ package io.metersphere.listener;
import com.mchange.lang.IntegerUtils;
import io.metersphere.api.exec.queue.ExecThreadPoolExecutor;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.service.ApiExecutionQueueService;
import io.metersphere.service.MockConfigService;
import io.metersphere.service.PluginService;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.service.BaseScheduleService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.service.*;
import io.metersphere.service.definition.ApiModuleService;
import io.metersphere.service.scenario.ApiScenarioModuleService;
import org.apache.commons.lang3.StringUtils;
import org.python.core.Options;
import org.python.util.PythonInterpreter;
@ -36,6 +34,10 @@ public class ApiAppStartListener implements ApplicationRunner {
private PluginService pluginService;
@Resource
private ApiExecutionQueueService apiExecutionQueueService;
@Resource
private ApiModuleService apiModuleService;
@Resource
private ApiScenarioModuleService apiScenarioModuleService;
@Value("${jmeter.home}")
private String jmeterHome;
@ -59,6 +61,12 @@ public class ApiAppStartListener implements ApplicationRunner {
LogUtil.info("处理重启导致未执行完成的报告");
apiExecutionQueueService.exceptionHandling();
LogUtil.info("初始化默认项目接口模块");
apiModuleService.initDefaultNode();
LogUtil.info("初始化默认项目场景模块");
apiScenarioModuleService.initDefaultModule();
BaseSystemConfigDTO dto = systemParameterService.getBaseInfo();
LogUtil.info("设置并发队列核心数", dto.getConcurrency());
if (StringUtils.isNotEmpty(dto.getConcurrency())) {

View File

@ -6,11 +6,13 @@ import io.metersphere.api.parse.api.ApiDefinitionImport;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionMapper;
import io.metersphere.base.mapper.ApiModuleMapper;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionMapper;
import io.metersphere.base.mapper.ext.ExtApiModuleMapper;
import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper;
import io.metersphere.commons.constants.ProjectModuleDefaultNodeEnum;
import io.metersphere.commons.constants.PropertyConstant;
import io.metersphere.commons.constants.RequestTypeConstants;
import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.enums.ApiTestDataStatus;
import io.metersphere.commons.exception.MSException;
@ -31,7 +33,6 @@ import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.jetbrains.annotations.NotNull;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -54,6 +55,8 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
private ApiDefinitionService apiDefinitionService;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
SqlSessionFactory sqlSessionFactory;
@ -141,8 +144,6 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
}
public List<ApiModuleDTO> getNodeTreeByProjectId(String projectId, String protocol, String versionId) {
// 判断当前项目下是否有默认模块没有添加默认模块
this.getDefaultNode(projectId, protocol);
ApiDefinitionRequest request = new ApiDefinitionRequest();
List<ApiModuleDTO> apiModules = getApiModulesByProjectAndPro(projectId, protocol);
request.setProjectId(projectId);
@ -189,8 +190,6 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
}
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);
@ -570,19 +569,29 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
return apiModuleMapper.countByExample(example);
}
public void initDefaultNode() {
ProjectExample projectExample = new ProjectExample();
projectExample.createCriteria().andNameEqualTo("默认项目");
List<Project> projects = projectMapper.selectByExample(projectExample);
if (CollectionUtils.isNotEmpty(projects)) {
String[] protocols = {RequestTypeConstants.HTTP, RequestTypeConstants.DUBBO, RequestTypeConstants.SQL, RequestTypeConstants.TCP};
for (String protocol : protocols) {
saveDefault(projects.get(0).getId(), protocol);
}
}
}
public ApiModule getDefaultNode(String projectId, String protocol) {
ApiModuleExample example = new ApiModuleExample();
example.createCriteria().andProjectIdEqualTo(projectId).andProtocolEqualTo(protocol).andNameEqualTo(ProjectModuleDefaultNodeEnum.API_MODULE_DEFAULT_NODE.getNodeName()).andParentIdIsNull();
List<ApiModule> list = apiModuleMapper.selectByExample(example);
if (CollectionUtils.isEmpty(list)) {
return saveDefault(projectId, protocol);
} else {
if (CollectionUtils.isNotEmpty(list)) {
return list.get(0);
}
return null;
}
@Async
public synchronized ApiModule saveDefault(String projectId, String protocol) {
public ApiModule saveDefault(String projectId, String protocol) {
ApiModuleExample example = new ApiModuleExample();
example.createCriteria().andProjectIdEqualTo(projectId).andProtocolEqualTo(protocol).andNameEqualTo(ProjectModuleDefaultNodeEnum.API_MODULE_DEFAULT_NODE.getNodeName()).andParentIdIsNull();
List<ApiModule> list = apiModuleMapper.selectByExample(example);

View File

@ -3,12 +3,10 @@ package io.metersphere.service.scenario;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.*;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioModule;
import io.metersphere.base.domain.ApiScenarioModuleExample;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiScenarioModuleMapper;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioModuleMapper;
import io.metersphere.commons.constants.ProjectModuleDefaultNodeEnum;
@ -30,10 +28,9 @@ import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.apache.commons.collections.CollectionUtils;
import javax.annotation.Resource;
import java.util.*;
@ -55,14 +52,14 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
SqlSessionFactory sqlSessionFactory;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
@Resource
private ProjectMapper projectMapper;
public ApiScenarioModuleService() {
super(ApiScenarioModuleDTO.class);
}
public List<ApiScenarioModuleDTO> getNodeTreeByProjectId(String projectId) {
// 判断当前项目下是否有默认模块没有添加默认模块
this.getDefaultNode(projectId);
List<ApiScenarioModuleDTO> nodes = extApiScenarioModuleMapper.getNodeTreeByProjectId(projectId);
ApiScenarioRequest request = new ApiScenarioRequest();
request.setProjectId(projectId);
@ -103,8 +100,6 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
}
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<>();
@ -174,7 +169,7 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
}
private void initApiCount(List<ApiScenarioModuleDTO> moduleDTOList, Map<String, List<ApiScenario>> scenarioMap) {
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(moduleDTOList) && MapUtils.isNotEmpty(scenarioMap)) {
if (CollectionUtils.isNotEmpty(moduleDTOList) && MapUtils.isNotEmpty(scenarioMap)) {
moduleDTOList.forEach(node -> {
List<String> moduleIds = new ArrayList<>();
moduleIds = this.nodeList(moduleDTOList, node.getId(), moduleIds);
@ -191,7 +186,7 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
}
private List<ApiScenarioModuleDTO> selectTreeStructModuleById(Collection<String> ids) {
if (org.apache.commons.collections.CollectionUtils.isEmpty(ids)) {
if (CollectionUtils.isEmpty(ids)) {
return new ArrayList<>(0);
} else {
List<String> parentIdList = new ArrayList<>();
@ -246,7 +241,7 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
public double getNextLevelPos(String projectId, int level, String parentId) {
List<ApiScenarioModule> list = getPos(projectId, level, parentId, "pos desc");
if (!CollectionUtils.isEmpty(list) && list.get(0) != null && list.get(0).getPos() != null) {
if (CollectionUtils.isNotEmpty(list) && list.get(0) != null && list.get(0).getPos() != null) {
return list.get(0).getPos() + DEFAULT_POS;
} else {
return DEFAULT_POS;
@ -463,7 +458,7 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
ApiScenarioModuleExample example = new ApiScenarioModuleExample();
example.createCriteria().andIdIn(ids);
List<ApiScenarioModule> nodes = apiScenarioModuleMapper.selectByExample(example);
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(nodes)) {
if (CollectionUtils.isNotEmpty(nodes)) {
List<String> names = nodes.stream().map(ApiScenarioModule::getName).collect(Collectors.toList());
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(ids), nodes.get(0).getProjectId(), String.join(",", names), nodes.get(0).getCreateUser(), new LinkedList<>());
return JSON.toJSONString(details);
@ -489,7 +484,7 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
criteria.andIdNotEqualTo(node.getId());
}
List<ApiScenarioModule> list = apiScenarioModuleMapper.selectByExample(example);
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(list)) {
if (CollectionUtils.isNotEmpty(list)) {
module = list.get(0);
}
}
@ -509,34 +504,40 @@ public class ApiScenarioModuleService extends NodeTreeService<ApiScenarioModuleD
public ApiScenarioModule getDefaultNode(String projectId) {
ApiScenarioModuleExample example = new ApiScenarioModuleExample();
example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(ProjectModuleDefaultNodeEnum.API_SCENARIO_DEFAULT_NODE.getNodeName()).andParentIdIsNull();
example.createCriteria()
.andProjectIdEqualTo(projectId)
.andNameEqualTo(ProjectModuleDefaultNodeEnum.API_SCENARIO_DEFAULT_NODE.getNodeName())
.andParentIdIsNull();
List<ApiScenarioModule> list = apiScenarioModuleMapper.selectByExample(example);
if (CollectionUtils.isEmpty(list)) {
return saveDefault(projectId);
} else {
if (!CollectionUtils.isEmpty(list)) {
return list.get(0);
}
return null;
}
@Async
public synchronized ApiScenarioModule saveDefault(String projectId) {
ApiScenarioModuleExample example = new ApiScenarioModuleExample();
example.createCriteria().andProjectIdEqualTo(projectId).andNameEqualTo(ProjectModuleDefaultNodeEnum.API_SCENARIO_DEFAULT_NODE.getNodeName()).andParentIdIsNull();
List<ApiScenarioModule> list = apiScenarioModuleMapper.selectByExample(example);
if (CollectionUtils.isEmpty(list)) {
ApiScenarioModule module = new ApiScenarioModule();
module.setId(UUID.randomUUID().toString());
module.setName(ProjectModuleDefaultNodeEnum.API_SCENARIO_DEFAULT_NODE.getNodeName());
module.setPos(1.0);
module.setLevel(1);
module.setCreateTime(System.currentTimeMillis());
module.setUpdateTime(System.currentTimeMillis());
module.setProjectId(projectId);
module.setCreateUser(SessionUtils.getUserId());
apiScenarioModuleMapper.insert(module);
return module;
} else {
return list.get(0);
public void initDefaultModule() {
ProjectExample projectExample = new ProjectExample();
projectExample.createCriteria().andNameEqualTo("默认项目");
List<Project> projects = projectMapper.selectByExample(projectExample);
if (!CollectionUtils.isEmpty(projects)) {
ApiScenarioModuleExample example = new ApiScenarioModuleExample();
example.createCriteria()
.andProjectIdEqualTo(projects.get(0).getId())
.andNameEqualTo(ProjectModuleDefaultNodeEnum.API_SCENARIO_DEFAULT_NODE.getNodeName())
.andParentIdIsNull();
List<ApiScenarioModule> list = apiScenarioModuleMapper.selectByExample(example);
if (CollectionUtils.isEmpty(list)) {
ApiScenarioModule module = new ApiScenarioModule();
module.setId(UUID.randomUUID().toString());
module.setName(ProjectModuleDefaultNodeEnum.API_SCENARIO_DEFAULT_NODE.getNodeName());
module.setPos(1.0);
module.setLevel(1);
module.setCreateTime(System.currentTimeMillis());
module.setUpdateTime(System.currentTimeMillis());
module.setProjectId(projects.get(0).getId());
module.setCreateUser(SessionUtils.getUserId());
apiScenarioModuleMapper.insert(module);
}
}
}

View File

@ -327,7 +327,7 @@ export default {
this.activeName = name;
let currentScenario = {
status: 'Underway',
principal: getCurrentUser().id,
principal: this._getCurrentUserId(),
apiScenarioModuleId: 'default-module',
id: getUUID(),
modulePath: '/' + this.$t('commons.module_title'),
@ -371,6 +371,17 @@ export default {
this.addListener();
}
},
_getCurrentUserId() {
const {id, userGroups} = getCurrentUser();
if (userGroups) {
//
let index = userGroups.findIndex(ug => ug.sourceId === getCurrentProjectID());
if (index !== -1) {
return id;
}
}
return '';
},
addListener() {
let index = this.tabs.findIndex((item) => item.name === this.activeName); // tabindex
if (index != -1) {

View File

@ -14,7 +14,14 @@
:closable="!readOnly"
:disable-transitions="false"
@close="remove(idx)">
{{ tag && tag.length > 10 ? tag.substring(0, 10) + '...' : tag }}
<span v-if="tag && tag.length > 10">
<el-tooltip class="item" effect="light" :content="tag" placement="top" :enterable="false">
<span>{{ tag && tag.length > 10 ? tag.substring(0, 10) + "..." : tag }}</span>
</el-tooltip>
</span>
<span v-else>
{{ tag }}
</span>
</el-tag>
<input
:disabled="readOnly"
@ -23,7 +30,7 @@
:placeholder="defaultPlaceHolder"
@keydown.delete.stop="removeLastTag"
@keydown="addNew"
@blur="addNew" />
@blur="addNew"/>
</div>
</template>
@ -44,7 +51,7 @@ export default {
type: Boolean,
default: false,
},
size: { type: String, default: 'small' },
size: {type: String, default: 'small'},
prop: {
type: String,
default: 'tags',

View File

@ -267,14 +267,14 @@ export default {
return (
!this.innerStep ||
(this.showBtn &&
(!this.data.disabled || this.data.root || this.data.isCopy) &&
(!this.data.disabled || this.data.root || this.data.isCopy || this.data.showExtend) &&
this.showVersion &&
this.stepFilter.get('ALlSamplerStep').indexOf(this.data.type) === -1)
);
}
return (
this.showBtn &&
(!this.data.disabled || this.data.root || this.isDeleted || this.data.isCopy) &&
(!this.data.disabled || this.data.root || this.isDeleted || this.data.isCopy || this.data.showExtend) &&
this.showVersion &&
this.stepFilter.get('ALlSamplerStep').indexOf(this.data.type) === -1
);

View File

@ -281,6 +281,8 @@ export default {
if (this.request.id && this.request.referenced === 'REF') {
this.request.disabled = true;
this.request.root = this.node.parent.parent ? false : true;
this.request.showExtend =
this.node.parent && this.node.parent.data && this.node.parent.data.disabled ? false : true;
}
this.isShowNum = this.request.num ? true : false;
if (this.request.protocol === 'HTTP') {
@ -332,15 +334,7 @@ export default {
return {};
},
isCompReadOnly() {
if (this.request) {
if (this.request.disabled) {
return this.request.disabled;
} else {
return false;
}
} else {
return false;
}
return this.request.disabled;
},
displayTitle() {
if (this.isApiImport) {

View File

@ -156,6 +156,8 @@ export default {
if (this.scenario.id && this.scenario.referenced === 'REF' && !this.scenario.loaded && this.scenario.hashTree) {
this.scenario.root = this.node.parent.parent ? false : true;
this.scenario.disabled = true;
this.scenario.showExtend =
this.node.parent && this.node.parent.data && this.node.parent.data.disabled ? false : true;
this.recursive(this.scenario.hashTree, this.scenario.projectId, true);
}
if (this.scenario.id && this.scenario.referenced === 'Copy' && !this.scenario.isCopy && !this.scenario.disabled) {

View File

@ -178,7 +178,7 @@
</span>
<span
class="ms-step-debug-code"
:class="node.data.code === 'ERROR' ? 'ms-req-error' : 'ms-req-success'"
:class="node.data.code && node.data.code.toUpperCase() === 'ERROR' ? 'ms-req-error' : 'ms-req-success'"
v-if="!loading && !node.data.testing && node.data.debug">
{{ getCode() }}
</span>

View File

@ -659,7 +659,7 @@ export default {
name: '',
status: 'Underway',
method: 'GET',
userId: getCurrentUser().id,
userId: this._getCurrentUserId(),
url: '',
protocol: this.currentProtocol,
environmentId: '',
@ -681,6 +681,17 @@ export default {
}
this.handleTabsEdit(this.$t('api_test.definition.request.title'), e, api);
},
_getCurrentUserId() {
const {id, userGroups} = getCurrentUser();
if (userGroups) {
//
let index = userGroups.findIndex(ug => ug.sourceId === getCurrentProjectID());
if (index !== -1) {
return id;
}
}
return '';
},
handleTabClose() {
let tabs = this.apiTabs[0];
let message = '';

View File

@ -240,7 +240,19 @@ export default {
}
});
if (isNeedCreate) {
this.items.push(new KeyValue({ enable: true }));
this.items.push(new KeyValue({
enable: true,
contentType: null,
description: null,
file: false,
files: null,
max: null,
min: null,
required: true,
type: null,
urlEncode: false,
value: null
}));
}
this.$emit('change', this.items);
// TODO key

View File

@ -424,13 +424,20 @@ export default {
if (isNeedCreate) {
this.parameters.push(
new KeyValue({
type: 'text',
contentType: 'text/plain',
description: null,
enable: true,
file: false,
files: null,
isEdit: false,
max: null,
min: null,
required: false,
type: 'text',
urlEncode: this.urlEncode,
uuid: this.uuid(),
required: false,
isEdit: false,
contentType: 'text/plain',
valid: false,
value: null
})
);
}
@ -520,13 +527,20 @@ export default {
if (this.parameters.length === 0 || this.parameters[this.parameters.length - 1].name) {
this.parameters.push(
new KeyValue({
type: 'text',
contentType: 'text/plain',
description: null,
enable: true,
file: false,
files: null,
isEdit: false,
max: null,
min: null,
required: false,
type: 'text',
urlEncode: this.urlEncode,
uuid: this.uuid(),
isEdit: false,
contentType: 'text/plain',
valid: false,
value: null
})
);
}

View File

@ -55,7 +55,7 @@
:api-template="apiTemplate"
@validateBasic="validateBasic" />
</div>
<div v-else-if="showTest">
<div v-show="showTest">
<ms-run-test-http-page
:syncTabs="syncTabs"
:currentProtocol="currentProtocol"

View File

@ -8,6 +8,7 @@
:placeholder="$t('api_test.definition.request.run_env')"
clearable
filterable
@click.native="refreshEnv"
@clear="clear">
<el-option
v-for="(environment, key) in environments"
@ -66,6 +67,9 @@ export default {
},
},
methods: {
refreshEnv() {
this.getEnvironments();
},
refreshEnvironment() {
this.$emit('setEnvironment', this.environment);
},

View File

@ -149,91 +149,15 @@
</ms-dialog-footer>
</template>
</el-dialog>
<el-dialog
:visible.sync="batchSyncApiVisible"
:title="$t('commons.save') + '&' + $t('workstation.sync') + $t('commons.setting')"
v-if="isXpack">
<el-row style="margin-bottom: 10px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)">
<div class="timeClass">
<span style="font-size: 16px; font-weight: bold; padding-left: 10px">{{
$t('api_test.definition.one_click_sync') + 'case'
}}</span>
<el-switch v-model="apiSyncRuleRelation.syncCase" style="padding-right: 10px"></el-switch>
</div>
<br />
<span style="font-size: 12px; padding-left: 10px">{{ $t('workstation.batch_sync_api_tips') }}</span
><br /><br />
<span v-if="apiSyncRuleRelation.syncCase" style="font-size: 16px; font-weight: bold; padding-left: 10px">
{{ $t('workstation.sync') + $t('commons.setting') }}
<i class="el-icon-arrow-down" v-if="showApiSyncConfig" @click="showApiSyncConfig = false" />
<i class="el-icon-arrow-right" v-if="!showApiSyncConfig" @click="showApiSyncConfig = true" /> </span
><br /><br />
<div v-if="showApiSyncConfig">
<sync-setting
style="padding-left: 20px"
v-if="apiSyncRuleRelation.syncCase"
v-bind:sync-data="apiSyncRuleRelation.apiSyncConfig"
ref="synSetting"
@updateSyncData="updateSyncData"></sync-setting>
</div>
</el-row>
<el-row style="margin-bottom: 10px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)">
<div class="timeClass">
<span style="font-size: 16px; font-weight: bold; padding-left: 10px">
{{ $t('api_test.definition.change_notification') }}
<el-tooltip
class="ms-num"
effect="dark"
:content="$t('project_application.workstation.api_receiver_tip')"
placement="top">
<i class="el-icon-warning" />
</el-tooltip>
</span>
<el-switch v-model="apiSyncRuleRelation.sendNotice" style="padding-right: 10px"></el-switch>
</div>
<span style="font-size: 12px; padding-left: 10px"> {{ $t('api_test.definition.recipient_tips') }} </span><br />
<p
style="
font-size: 12px;
color: var(--primary_color);
margin-bottom: 20px;
text-decoration: underline;
cursor: pointer;
padding-left: 10px;
"
@click="gotoApiMessage">
{{ $t('project_application.workstation.go_to_api_message') }}
</p>
<el-row v-if="apiSyncRuleRelation.sendNotice" style="margin-bottom: 5px; margin-top: 5px">
<el-col :span="4"
><span style="font-weight: bold; padding-left: 10px">{{ $t('api_test.definition.recipient') + ':' }}</span>
</el-col>
<el-col :span="20" style="color: var(--primary_color)">
<el-checkbox v-model="apiSyncRuleRelation.caseCreator">{{ 'CASE' + $t('api_test.creator') }}</el-checkbox>
<el-checkbox v-model="apiSyncRuleRelation.scenarioCreator">
{{ $t('commons.scenario') + $t('api_test.creator') }}
</el-checkbox>
</el-col>
</el-row>
</el-row>
<el-row>
<el-checkbox v-model="apiSyncRuleRelation.showUpdateRule" style="padding-left: 10px"
>{{ $t('project_application.workstation.no_show_setting') }}
</el-checkbox>
<el-tooltip
class="ms-num"
effect="dark"
:content="$t('project_application.workstation.no_show_setting_tip')"
placement="top">
<i class="el-icon-warning" />
</el-tooltip>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="batchSyncApiVisible = false">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" @click="batchSync()">{{ $t('commons.confirm') }}</el-button>
</span>
</el-dialog>
<api-sync-case-config
:is-xpack="isXpack"
:api-sync-rule-relation="apiSyncRuleRelation"
:batch-sync-api-visible="batchSyncApiVisible"
:show-api-sync-config="true"
@batchSync="batchSync"
ref="syncCaseConfig"
>
</api-sync-case-config>
</div>
</template>
@ -269,11 +193,10 @@ import {createComponent} from '.././jmeter/components';
import {TYPE_TO_C} from '@/business/automation/scenario/Setting';
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter';
import {getProjectMemberOption} from '@/api/project';
import {deepClone} from 'metersphere-frontend/src/utils/tableUtils';
import {getDefaultVersion, setLatestVersionById} from 'metersphere-frontend/src/api/version';
import SyncSetting from '@/business/definition/util/SyncSetting';
import {useApiStore} from '@/store';
import ApiSyncCaseConfig from "@/business/definition/components/sync/ApiSyncCaseConfig";
const {Body} = require('@/business/definition/model/ApiTestModel');
const Sampler = require('@/business/definition/components/jmeter/components/sampler/sampler');
@ -294,7 +217,7 @@ export default {
MsSelectTree,
MsChangeHistory,
HttpApiVersionDiff,
SyncSetting,
ApiSyncCaseConfig
},
data() {
let validateURL = (rule, value, callback) => {
@ -377,7 +300,6 @@ export default {
createNewVersionVisible: false,
batchSyncApiVisible: false,
isXpack: false,
showApiSyncConfig: true,
apiSyncRuleRelation: {
caseCreator: true,
scenarioCreator: true,
@ -555,7 +477,6 @@ export default {
this.$refs['httpForm'].validate((valid) => {
if (valid) {
this.setParameter();
if (!this.httpForm.versionId) {
if (this.$refs.versionHistory && this.$refs.versionHistory.currentVersion) {
this.httpForm.versionId = this.$refs.versionHistory.currentVersion.id;
@ -564,76 +485,214 @@ export default {
if (hasLicense() && (this.httpForm.caseTotal > 0 || this.citedScenarioCount > 0) && !this.httpForm.isCopy) {
if (this.httpForm.method !== this.beforeHttpForm.method && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
if (this.httpForm.path !== this.beforeHttpForm.path && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
if (this.httpForm.request.headers && this.beforeRequest.headers) {
for (let i = 0; i < this.httpForm.request.headers.length; i++) {
if (this.httpForm.request.headers[i].isEdit !== undefined) {
this.beforeRequest.headers[i].isEdit = this.httpForm.request.headers[i].isEdit
if (this.request.headers && this.beforeRequest.headers) {
if (this.request.headers.length === this.beforeRequest.headers.length) {
let requestHeaders = [];
let beforeHeaders = [];
for (let i = 0; i < this.request.headers.length; i++) {
this.beforeRequest.headers[i].valid = this.request.headers[i].valid
if (this.request.headers[i].isEdit !== undefined) {
this.beforeRequest.headers[i].isEdit = this.request.headers[i].isEdit
}
if (this.request.headers[i].uuid) {
this.beforeRequest.headers[i].uuid = this.request.headers[i].uuid
}
if (this.request.headers[i].time) {
this.beforeRequest.headers[i].time = this.request.headers[i].time
}
if (this.request.headers[i].name === undefined) {
this.beforeRequest.headers[i].name = undefined
}
let newRequest = this.request.headers[i];
const ordered = {};
Object.keys(newRequest).sort().forEach(function (key) {
ordered[key] = newRequest[key];
});
requestHeaders.push(ordered);
let beforeRequest = this.beforeRequest.headers[i];
const beforeOrdered = {};
Object.keys(beforeRequest).sort().forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeHeaders.push(beforeOrdered)
}
let submitRequestHeaders = JSON.stringify(requestHeaders);
let beforeRequestHeaders = JSON.stringify(beforeHeaders);
if (submitRequestHeaders !== beforeRequestHeaders && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
} else {
let submitRequestHeaders = JSON.stringify(this.request.headers);
let beforeRequestHeaders = JSON.stringify(this.beforeRequest.headers);
if (submitRequestHeaders !== beforeRequestHeaders && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
let submitRequestHeaders = JSON.stringify(this.httpForm.request.headers);
let beforeRequestHeaders = JSON.stringify(this.beforeRequest.headers);
if (submitRequestHeaders !== beforeRequestHeaders && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
}
}
if (this.httpForm.request.arguments && this.beforeRequest.arguments) {
for (let i = 0; i < this.httpForm.request.arguments.length; i++) {
if (this.httpForm.request.arguments[i].isEdit !== undefined) {
this.beforeRequest.arguments[i].isEdit = this.httpForm.request.arguments[i].isEdit
if (this.request.arguments && this.beforeRequest.arguments) {
if (this.request.arguments.length === this.beforeRequest.arguments.length) {
let requestArguments = [];
let beforeArguments = [];
for (let i = 0; i < this.request.arguments.length; i++) {
if (this.request.arguments[i].isEdit !== undefined) {
this.beforeRequest.arguments[i].isEdit = this.request.arguments[i].isEdit
}
if (this.request.arguments[i].uuid) {
this.beforeRequest.arguments[i].uuid = this.request.arguments[i].uuid
}
if (this.request.arguments[i].time) {
this.beforeRequest.arguments[i].time = this.request.arguments[i].time
}
if (this.request.arguments[i].name === undefined) {
this.beforeRequest.arguments[i].name = undefined
}
this.beforeRequest.arguments[i].valid = this.request.arguments[i].valid
let newRequest = this.request.arguments[i];
const ordered = {};
Object.keys(newRequest).sort().forEach(function (key) {
ordered[key] = newRequest[key];
});
requestArguments.push(ordered);
let beforeRequest = this.beforeRequest.arguments[i];
const beforeOrdered = {};
Object.keys(beforeRequest).sort().forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeArguments.push(beforeOrdered)
}
let submitRequestQuery = JSON.stringify(requestArguments);
let beforeRequestQuery = JSON.stringify(beforeArguments);
if (submitRequestQuery !== beforeRequestQuery && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
} else {
let submitRequestQuery = JSON.stringify(this.request.arguments);
let beforeRequestQuery = JSON.stringify(this.beforeRequest.arguments);
if (submitRequestQuery !== beforeRequestQuery && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
let submitRequestQuery = JSON.stringify(this.httpForm.request.arguments);
let beforeRequestQuery = JSON.stringify(this.beforeRequest.arguments);
if (submitRequestQuery !== beforeRequestQuery && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
}
}
if (this.httpForm.request.rest && this.beforeRequest.rest) {
for (let i = 0; i < this.httpForm.request.rest.length; i++) {
if (this.httpForm.request.rest[i].isEdit !== undefined) {
this.beforeRequest.rest[i].isEdit = this.httpForm.request.rest[i].isEdit
if (this.request.rest && this.beforeRequest.rest) {
if (this.request.rest.length === this.beforeRequest.rest.length) {
let requestRest = [];
let beforeRest = [];
for (let i = 0; i < this.request.rest.length; i++) {
if (this.request.rest[i].isEdit !== undefined) {
this.beforeRequest.rest[i].isEdit = this.request.rest[i].isEdit
}
if (this.request.rest[i].uuid) {
this.beforeRequest.rest[i].uuid = this.request.rest[i].uuid
}
if (this.request.rest[i].time) {
this.beforeRequest.rest[i].time = this.request.rest[i].time
}
if (this.request.rest[i].name === undefined) {
this.beforeRequest.rest[i].name = undefined
}
this.beforeRequest.rest[i].valid = this.request.rest[i].valid
let newRequest = this.request.rest[i];
const ordered = {};
Object.keys(newRequest).sort().forEach(function (key) {
ordered[key] = newRequest[key];
});
requestRest.push(ordered);
let beforeRequest = this.beforeRequest.rest[i];
const beforeOrdered = {};
Object.keys(beforeRequest).sort().forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeRest.push(beforeOrdered)
}
let submitRequestRest = JSON.stringify(requestRest);
let beforeRequestRest = JSON.stringify(beforeRest);
if (submitRequestRest !== beforeRequestRest && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
} else {
let submitRequestRest = JSON.stringify(this.request.rest);
let beforeRequestRest = JSON.stringify(this.beforeRequest.rest);
if (submitRequestRest !== beforeRequestRest && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
let submitRequestRest = JSON.stringify(this.httpForm.request.rest);
let beforeRequestRest = JSON.stringify(this.beforeRequest.rest);
if (submitRequestRest !== beforeRequestRest && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
}
}
if (this.httpForm.request.body && this.beforeRequest.body) {
let submitRequestBody = JSON.stringify(this.httpForm.request.body);
if (this.request.body && this.beforeRequest.body) {
if (this.request.body.valid) {
this.beforeRequest.body.valid = this.request.body.valid
}
if (this.request.body.kvs.length === this.beforeRequest.body.kvs.length) {
let requestKvs = [];
let beforeKvs = [];
for (let i = 0; i < this.request.body.kvs.length; i++) {
if (this.request.body.kvs[i].isEdit !== undefined) {
this.beforeRequest.body.kvs[i].isEdit = this.request.body.kvs[i].isEdit
}
if (this.request.body.kvs[i].files !== null && this.request.body.kvs[i].files.length === 0) {
this.beforeRequest.body.kvs[i].files = this.request.body.kvs[i].files
}
if (this.request.body.kvs[i].uuid) {
this.beforeRequest.body.kvs[i].uuid = this.request.body.kvs[i].uuid
}
if (this.request.body.kvs[i].time) {
this.beforeRequest.body.kvs[i].time = this.request.body.kvs[i].time
}
if (this.request.body.kvs[i].name === undefined) {
this.beforeRequest.body.kvs[i].name = undefined
}
this.beforeRequest.body.kvs[i].valid = this.request.body.kvs[i].valid
let newRequest = this.request.body.kvs[i];
const ordered = {};
Object.keys(newRequest).sort().forEach(function (key) {
ordered[key] = newRequest[key];
});
requestKvs.push(ordered);
let beforeRequest = this.request.body.kvs[i];
const beforeOrdered = {};
Object.keys(beforeRequest).sort().forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeKvs.push(beforeOrdered)
}
this.request.body.kvs = requestKvs;
this.beforeRequest.body.kvs = beforeKvs
}
let submitRequestBody = JSON.stringify(this.request.body);
let beforeRequestBody = JSON.stringify(this.beforeRequest.body);
for (let i = 0; i < this.httpForm.request.body.kvs.length; i++) {
if (this.httpForm.request.body.kvs[i].isEdit !== undefined) {
this.beforeRequest.body.kvs[i].isEdit = this.httpForm.request.body.kvs[i].isEdit
}
if (this.httpForm.request.body.kvs[i].files !== null && this.httpForm.request.body.kvs[i].files.length === 0) {
this.beforeRequest.body.kvs[i].files = this.httpForm.request.body.kvs[i].files
}
if (this.httpForm.request.body.kvs[i].uuid) {
this.beforeRequest.body.kvs[i].uuid = this.httpForm.request.body.kvs[i].uuid
}
}
if (submitRequestBody !== beforeRequestBody && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
if (this.httpForm.request.authManager && this.beforeRequest.authManager) {
let submitRequestAuthManager = JSON.stringify(this.httpForm.request.authManager);
if (this.request.authManager && this.beforeRequest.authManager) {
let submitRequestAuthManager = JSON.stringify(this.request.authManager);
let beforeRequestAuthManager = JSON.stringify(this.beforeRequest.authManager);
if (submitRequestAuthManager !== beforeRequestAuthManager && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
if (this.httpForm.request.hashTree && this.beforeRequest.hashTree) {
let submitRequestHashTree = JSON.stringify(this.httpForm.request.hashTree);
if (this.request.hashTree && this.beforeRequest.hashTree) {
let submitRequestHashTree = JSON.stringify(this.request.hashTree);
let beforeRequestHashTree = JSON.stringify(this.beforeRequest.hashTree);
if (submitRequestHashTree !== beforeRequestHashTree && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
if (
@ -645,10 +704,12 @@ export default {
!this.noShowSyncRuleRelation
) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
if (this.batchSyncApiVisible !== true) {
this.$emit('saveApi', this.httpForm);
}
} else {
this.$emit('saveApi', this.httpForm);
}
@ -660,16 +721,10 @@ export default {
}
});
},
batchSync() {
batchSync(fromData) {
if (hasLicense() && (this.httpForm.caseTotal > 0 || this.citedScenarioCount > 0) && !this.httpForm.isCopy) {
if (this.$refs.synSetting && this.$refs.synSetting.fromData) {
let fromData = this.$refs.synSetting.fromData;
fromData.method = true;
fromData.path = true;
fromData.protocol = true;
this.httpForm.triggerUpdate = JSON.stringify(fromData);
this.apiSyncRuleRelation.apiSyncCaseRequest = JSON.stringify(fromData);
}
this.httpForm.triggerUpdate = JSON.stringify(fromData);
this.apiSyncRuleRelation.apiSyncCaseRequest = JSON.stringify(fromData);
if (this.apiSyncRuleRelation.sendNotice && this.apiSyncRuleRelation.sendNotice === true) {
this.httpForm.sendSpecialMessage = this.apiSyncRuleRelation.sendNotice;
} else {
@ -694,7 +749,7 @@ export default {
saveApiSyncRuleRelation(apiSyncRuleRelation) {
updateRuleRelation(apiSyncRuleRelation.resourceId, apiSyncRuleRelation).then(() => {
this.$emit('saveApi', this.httpForm);
this.batchSyncApiVisible = false;
this.$refs.syncCaseConfig.close();
});
},
createModules() {
@ -1019,12 +1074,6 @@ export default {
this.checkout(row);
});
},
gotoApiMessage() {
let apiResolve = this.$router.resolve({
path: '/project/messagesettings',
});
window.open(apiResolve.href, '_blank');
},
getSyncRule() {
relationGet(this.httpForm.id, 'API').then((response) => {
if (response.data) {
@ -1051,9 +1100,6 @@ export default {
}
});
},
updateSyncData(value) {
this.apiSyncRuleRelation.apiSyncConfig = value;
},
handleCommand(command) {
if (command === 'openSyncRule') {
this.noShowSyncRuleRelation = false;

View File

@ -10,7 +10,7 @@
:destroy-on-close="true">
<div class="header-bar">
<div>{{ $t('api_test.api_import.data_format') }}</div>
<el-radio-group v-model="selectedPlatformValue">
<el-radio-group v-model="selectedPlatformValue" @input="clearUrParameter">
<span v-for="(item, index) in platforms" :key="index">
<el-radio v-if="!isScenarioModel || item.name != 'Swagger'" :label="item.value">{{ item.name }}</el-radio>
</span>
@ -106,7 +106,7 @@
:show-desc="true"
:isShowEnable="isShowEnable"
:suggestions="headerSuggestions"
:items="headers" />
:items="headers"/>
<!--query 参数-->
<div style="margin-top: 10px">
<span>{{ $t('api_test.definition.request.query_param') }}{{ $t('api_test.api_import.optional') }}</span>
@ -466,9 +466,9 @@ export default {
clearAuthInfo() {
this.headers = [];
this.queryArguments = [];
this.headers.push(new KeyValue({ enable: true }));
this.queryArguments.push(new KeyValue({ enable: true }));
this.authConfig = { hashTree: [], authManager: {} };
this.headers.push(new KeyValue({enable: true}));
this.queryArguments.push(new KeyValue({enable: true}));
this.authConfig = {hashTree: [], authManager: {}};
this.$refs.importAuth.initData();
},
changeAuthEnable() {
@ -476,6 +476,12 @@ export default {
this.clearAuthInfo();
}
},
clearUrParameter(value) {
if (value !== 'Swagger2') {
this.clearAuthInfo();
this.authEnable = false;
}
},
buildParam() {
let param = {};
Object.assign(param, this.formData);

View File

@ -513,6 +513,7 @@ export default {
if (item) {
let line = item.split(/|:/);
let values = item.split(line[0] + ':');
let required = false;
keyValues.push(
new KeyValue({
@ -525,6 +526,7 @@ export default {
encode: true,
enable: true,
contentType: 'text/plain',
})
);
}

View File

@ -335,7 +335,7 @@ export default {
this.currentRequest = this.api.request;
this.runLoading = false;
this.getEnvironments();
getLastResultDetail(this.api.id, this);
//getLastResultDetail(this.api.id, this);
this.checkVersionEnable();
},
};

View File

@ -81,7 +81,14 @@
<ms-request-result-tail :response="responseData" ref="runResult" />
</div>
</el-card>
<api-sync-case-config
:is-xpack="isXpack"
:api-sync-rule-relation="apiSyncRuleRelation"
:batch-sync-api-visible="batchSyncApiVisible"
:show-api-sync-config="true"
@batchSync="batchSync"
ref="syncCaseConfig">
</api-sync-case-config>
<!-- 加载用例 -->
<ms-api-case-list
@selectTestCase="selectTestCase"
@ -106,9 +113,9 @@
</template>
<script>
import { getMockEnvironment, updateDefinition } from '@/api/definition';
import { citedApiScenarioCount, getDefinitionVersions, getMockEnvironment, updateDefinition } from '@/api/definition';
import { getLastResultDetail } from '@/api/definition-report';
import { versionEnableByProjectId } from '@/api/xpack';
import { relationGet, updateRuleRelation, versionEnableByProjectId } from '@/api/xpack';
import MsApiRequestForm from '../request/http/ApiHttpRequestForm';
import { hasLicense, hasPermission } from 'metersphere-frontend/src/utils/permission';
import { getUUID } from 'metersphere-frontend/src/utils';
@ -122,6 +129,9 @@ import { TYPE_TO_C } from '@/business/automation/scenario/Setting';
import { mergeRequestDocumentData } from '@/business/definition/api-definition';
import { execStop } from '@/api/scenario';
import { useApiStore } from '@/store';
import { apiTestCaseCount } from '@/api/api-test-case';
import ApiSyncCaseConfig from '@/business/definition/components/sync/ApiSyncCaseConfig';
import { deepClone } from 'metersphere-frontend/src/utils/tableUtils';
const store = useApiStore();
export default {
@ -133,6 +143,7 @@ export default {
MsContainer,
MsRequestResultTail,
MsRun,
ApiSyncCaseConfig,
},
data() {
return {
@ -168,6 +179,22 @@ export default {
envMap: new Map(),
runLoading: false,
versionEnable: false,
beforeHttpForm: { environmentId: '', path: '', tags: [] },
beforeRequest: { arguments: [] },
beforeResponse: {},
citedScenarioCount: 0,
apiSyncRuleRelation: {
caseCreator: true,
scenarioCreator: true,
showUpdateRule: false,
apiSyncCaseRequest: '',
apiSyncConfig: {},
syncCase: true,
sendNotice: true,
},
noShowSyncRuleRelation: false,
batchSyncApiVisible: false,
isXpack: false,
};
},
props: {
@ -188,6 +215,11 @@ export default {
storeUseEnvironment: function () {
this.api.environmentId = store.useEnvironment;
},
batchSyncApiVisible() {
if (!this.batchSyncApiVisible && this.apiSyncRuleRelation.showUpdateRule) {
this.noShowSyncRuleRelation = true;
}
},
},
methods: {
hasPermission,
@ -390,16 +422,321 @@ export default {
if (this.api.tags instanceof Array) {
this.api.tags = JSON.stringify(this.api.tags);
}
if (this.beforeHttpForm) {
if (this.beforeHttpForm.tags instanceof Array) {
this.beforeHttpForm.tags = JSON.stringify(this.beforeHttpForm.tags);
}
}
//
if (this.api.request) {
this.api.request.clazzName = TYPE_TO_C.get(this.api.request.type);
this.compatibleHistory(this.api.request.hashTree);
}
updateDefinition(null, null, bodyFiles, this.api).then(() => {
this.$success(this.$t('commons.save_success'));
if (this.syncTabs.indexOf(this.api.id) === -1) {
this.syncTabs.push(this.api.id);
this.$emit('syncApi', this.api);
if (hasLicense() && (this.api.caseTotal > 0 || this.citedScenarioCount > 0)) {
if (this.api.method !== this.beforeHttpForm.method && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
if (this.api.path !== this.beforeHttpForm.path && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
if (this.api.request.headers && this.beforeRequest.headers) {
if (this.api.request.headers.length === this.beforeRequest.headers.length) {
let requestHeaders = [];
let beforeHeaders = [];
for (let i = 0; i < this.api.request.headers.length; i++) {
this.beforeRequest.headers[i].valid = this.api.request.headers[i].valid;
if (this.api.request.headers[i].isEdit !== undefined) {
this.beforeRequest.headers[i].isEdit = this.api.request.headers[i].isEdit;
}
if (this.api.request.headers[i].uuid) {
this.beforeRequest.headers[i].uuid = this.api.request.headers[i].uuid;
}
if (this.api.request.headers[i].time) {
this.beforeRequest.headers[i].time = this.api.request.headers[i].time;
}
if (this.api.request.headers[i].name === undefined) {
this.beforeRequest.headers[i].name = undefined;
}
let newRequest = this.api.request.headers[i];
const ordered = {};
Object.keys(newRequest)
.sort()
.forEach(function (key) {
ordered[key] = newRequest[key];
});
requestHeaders.push(ordered);
let beforeRequest = this.beforeRequest.headers[i];
const beforeOrdered = {};
Object.keys(beforeRequest)
.sort()
.forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeHeaders.push(beforeOrdered);
}
let submitRequestHeaders = JSON.stringify(requestHeaders);
let beforeRequestHeaders = JSON.stringify(beforeHeaders);
if (submitRequestHeaders !== beforeRequestHeaders && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
} else {
let submitRequestHeaders = JSON.stringify(this.api.request.headers);
let beforeRequestHeaders = JSON.stringify(this.beforeRequest.headers);
if (submitRequestHeaders !== beforeRequestHeaders && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
}
if (this.api.request.arguments && this.beforeRequest.arguments) {
if (this.api.request.arguments.length === this.beforeRequest.arguments.length) {
let requestArguments = [];
let beforeArguments = [];
for (let i = 0; i < this.api.request.arguments.length; i++) {
if (this.api.request.arguments[i].isEdit !== undefined) {
this.beforeRequest.arguments[i].isEdit = this.api.request.arguments[i].isEdit;
}
if (this.api.request.arguments[i].uuid) {
this.beforeRequest.arguments[i].uuid = this.api.request.arguments[i].uuid;
}
if (this.api.request.arguments[i].time) {
this.beforeRequest.arguments[i].time = this.api.request.arguments[i].time;
}
if (this.api.request.arguments[i].name === undefined) {
this.beforeRequest.arguments[i].name = undefined;
}
this.beforeRequest.arguments[i].valid = this.api.request.arguments[i].valid;
let newRequest = this.api.request.arguments[i];
const ordered = {};
Object.keys(newRequest)
.sort()
.forEach(function (key) {
ordered[key] = newRequest[key];
});
requestArguments.push(ordered);
let beforeRequest = this.beforeRequest.arguments[i];
const beforeOrdered = {};
Object.keys(beforeRequest)
.sort()
.forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeArguments.push(beforeOrdered);
}
let submitRequestQuery = JSON.stringify(requestArguments);
let beforeRequestQuery = JSON.stringify(beforeArguments);
if (submitRequestQuery !== beforeRequestQuery && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
} else {
let submitRequestQuery = JSON.stringify(this.api.request.arguments);
let beforeRequestQuery = JSON.stringify(this.beforeRequest.arguments);
if (submitRequestQuery !== beforeRequestQuery && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
}
if (this.api.request.rest && this.beforeRequest.rest) {
if (this.api.request.rest.length === this.beforeRequest.rest.length) {
let requestRest = [];
let beforeRest = [];
for (let i = 0; i < this.api.request.rest.length; i++) {
if (this.api.request.rest[i].isEdit !== undefined) {
this.beforeRequest.rest[i].isEdit = this.api.request.rest[i].isEdit;
}
if (this.api.request.rest[i].uuid) {
this.beforeRequest.rest[i].uuid = this.api.request.rest[i].uuid;
}
if (this.api.request.rest[i].time) {
this.beforeRequest.rest[i].time = this.api.request.rest[i].time;
}
if (this.api.request.rest[i].name === undefined) {
this.beforeRequest.rest[i].name = undefined;
}
this.beforeRequest.rest[i].valid = this.api.request.rest[i].valid;
let newRequest = this.api.request.rest[i];
const ordered = {};
Object.keys(newRequest)
.sort()
.forEach(function (key) {
ordered[key] = newRequest[key];
});
requestRest.push(ordered);
let beforeRequest = this.beforeRequest.rest[i];
const beforeOrdered = {};
Object.keys(beforeRequest)
.sort()
.forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeRest.push(beforeOrdered);
}
let submitRequestRest = JSON.stringify(requestRest);
let beforeRequestRest = JSON.stringify(beforeRest);
if (submitRequestRest !== beforeRequestRest && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
} else {
let submitRequestRest = JSON.stringify(this.api.request.rest);
let beforeRequestRest = JSON.stringify(this.beforeRequest.rest);
if (submitRequestRest !== beforeRequestRest && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
}
if (this.api.request.body && this.beforeRequest.body) {
if (this.api.request.body.valid) {
this.beforeRequest.body.valid = this.api.request.body.valid;
}
if (this.api.request.body.kvs.length === this.beforeRequest.body.kvs.length) {
let requestKvs = [];
let beforeKvs = [];
for (let i = 0; i < this.api.request.body.kvs.length; i++) {
if (this.api.request.body.kvs[i].isEdit !== undefined) {
this.beforeRequest.body.kvs[i].isEdit = this.api.request.body.kvs[i].isEdit;
}
if (this.api.request.body.kvs[i].files !== null && this.api.request.body.kvs[i].files.length === 0) {
this.beforeRequest.body.kvs[i].files = this.api.request.body.kvs[i].files;
}
if (this.api.request.body.kvs[i].uuid) {
this.beforeRequest.body.kvs[i].uuid = this.api.request.body.kvs[i].uuid;
}
if (this.api.request.body.kvs[i].time) {
this.beforeRequest.body.kvs[i].time = this.api.request.body.kvs[i].time;
}
if (this.api.request.body.kvs[i].name === undefined) {
this.beforeRequest.body.kvs[i].name = undefined;
}
this.beforeRequest.body.kvs[i].valid = this.api.request.body.kvs[i].valid;
let newRequest = this.api.request.body.kvs[i];
const ordered = {};
Object.keys(newRequest)
.sort()
.forEach(function (key) {
ordered[key] = newRequest[key];
});
requestKvs.push(ordered);
let beforeRequest = this.api.request.body.kvs[i];
const beforeOrdered = {};
Object.keys(beforeRequest)
.sort()
.forEach(function (key) {
beforeOrdered[key] = beforeRequest[key];
});
beforeKvs.push(beforeOrdered);
}
this.api.request.body.kvs = requestKvs;
this.beforeRequest.body.kvs = beforeKvs;
}
let submitRequestBody = JSON.stringify(this.api.request.body);
let beforeRequestBody = JSON.stringify(this.beforeRequest.body);
if (submitRequestBody !== beforeRequestBody && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
if (this.api.request.authManager && this.beforeRequest.authManager) {
let submitRequestAuthManager = JSON.stringify(this.api.request.authManager);
let beforeRequestAuthManager = JSON.stringify(this.beforeRequest.authManager);
if (submitRequestAuthManager !== beforeRequestAuthManager && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
if (this.api.request.hashTree && this.beforeRequest.hashTree) {
let submitRequestHashTree = JSON.stringify(this.api.request.hashTree);
let beforeRequestHashTree = JSON.stringify(this.beforeRequest.hashTree);
if (submitRequestHashTree !== beforeRequestHashTree && !this.noShowSyncRuleRelation) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
}
if (
(this.api.request.connectTimeout !== this.beforeRequest.connectTimeout ||
this.api.request.responseTimeout !== this.beforeRequest.responseTimeout ||
this.api.request.followRedirects !== this.beforeRequest.followRedirects ||
this.api.request.alias !== this.beforeRequest.alias ||
this.apiSyncRuleRelation.showUpdateRule === true) &&
!this.noShowSyncRuleRelation
) {
this.batchSyncApiVisible = true;
this.$refs.syncCaseConfig.show();
}
if (this.batchSyncApiVisible !== true) {
updateDefinition(null, null, bodyFiles, this.api).then(() => {
this.$success(this.$t('commons.save_success'));
if (this.syncTabs.indexOf(this.api.id) === -1) {
this.syncTabs.push(this.api.id);
this.$emit('syncApi', this.api);
}
});
}
} else {
updateDefinition(null, null, bodyFiles, this.api).then(() => {
this.$success(this.$t('commons.save_success'));
if (this.syncTabs.indexOf(this.api.id) === -1) {
this.syncTabs.push(this.api.id);
this.$emit('syncApi', this.api);
}
});
}
},
batchSync(fromData) {
this.beforeHttpForm = deepClone(this.api);
this.beforeRequest = deepClone(this.api.request);
this.beforeResponse = deepClone(this.api.response);
this.batchSyncApiVisible = false;
if (hasLicense() && (this.api.caseTotal > 0 || this.citedScenarioCount > 0)) {
this.api.triggerUpdate = JSON.stringify(fromData);
this.apiSyncRuleRelation.apiSyncCaseRequest = JSON.stringify(fromData);
if (this.apiSyncRuleRelation.sendNotice && this.apiSyncRuleRelation.sendNotice === true) {
this.api.sendSpecialMessage = this.apiSyncRuleRelation.sendNotice;
} else {
this.api.sendSpecialMessage = false;
}
if (this.apiSyncRuleRelation.caseCreator && this.apiSyncRuleRelation.caseCreator === true) {
this.api.caseCreator = this.apiSyncRuleRelation.caseCreator;
} else {
this.api.caseCreator = false;
}
if (this.apiSyncRuleRelation.scenarioCreator && this.apiSyncRuleRelation.scenarioCreator === true) {
this.api.scenarioCreator = this.apiSyncRuleRelation.scenarioCreator;
} else {
this.api.scenarioCreator = false;
}
this.apiSyncRuleRelation.resourceId = this.api.id;
this.apiSyncRuleRelation.resourceType = 'API';
this.saveApiSyncRuleRelation(this.apiSyncRuleRelation);
}
},
saveApiSyncRuleRelation(apiSyncRuleRelation) {
let bodyFiles = this.getBodyUploadFiles(this.api);
updateRuleRelation(apiSyncRuleRelation.resourceId, apiSyncRuleRelation).then(() => {
updateDefinition(null, null, bodyFiles, this.api).then(() => {
this.$success(this.$t('commons.save_success'));
if (this.syncTabs.indexOf(this.api.id) === -1) {
this.syncTabs.push(this.api.id);
this.$emit('syncApi', this.api);
}
});
this.$refs.syncCaseConfig.close();
});
},
getCitedScenarioCount() {
citedApiScenarioCount(this.api.id).then((response) => {
if (response.data) {
this.citedScenarioCount = response.data;
}
});
},
@ -440,9 +777,58 @@ export default {
if (!this.api.environmentId && store.useEnvironment) {
this.api.environmentId = store.useEnvironment;
}
getLastResultDetail(this.api.id, this);
// getLastResultDetail(this.api.id, this);
this.runLoading = false;
this.checkVersionEnable();
this.getCaseCount();
if (hasLicense()) {
this.isXpack = true;
this.getOldVersionData();
this.getSyncRule();
this.getCitedScenarioCount();
}
},
getSyncRule() {
relationGet(this.api.id, 'API').then((response) => {
if (response.data) {
this.apiSyncRuleRelation = response.data;
if (this.apiSyncRuleRelation.apiSyncCaseRequest) {
this.apiSyncRuleRelation.apiSyncConfig = JSON.parse(this.apiSyncRuleRelation.apiSyncCaseRequest);
}
if (this.apiSyncRuleRelation.caseCreator === null || this.apiSyncRuleRelation.caseCreator === undefined) {
this.apiSyncRuleRelation.caseCreator = true;
}
if (
this.apiSyncRuleRelation.scenarioCreator === null ||
this.apiSyncRuleRelation.scenarioCreator === undefined
) {
this.apiSyncRuleRelation.scenarioCreator = true;
}
if (this.apiSyncRuleRelation.syncCase === null || this.apiSyncRuleRelation.syncCase === undefined) {
this.apiSyncRuleRelation.syncCase = true;
}
if (this.apiSyncRuleRelation.sendNotice === null || this.apiSyncRuleRelation.sendNotice === undefined) {
this.apiSyncRuleRelation.sendNotice = true;
}
this.noShowSyncRuleRelation = this.apiSyncRuleRelation.showUpdateRule;
}
});
},
getCaseCount() {
apiTestCaseCount({ id: this.api.id }).then((response) => {
if (response.data > 0) {
this.api.caseTotal = response.data;
}
});
},
getOldVersionData() {
getDefinitionVersions(this.api.id).then((response) => {
if (response.data[0]) {
this.beforeHttpForm = response.data[0];
this.beforeRequest = JSON.parse(response.data[0].request);
this.beforeResponse = JSON.parse(response.data[0].response);
}
});
},
margeFiles(targetFiles, sourceFiles) {
targetFiles.forEach((target) => {

View File

@ -341,7 +341,7 @@ export default {
}
this.runLoading = false;
this.getEnvironments();
getLastResultDetail(this.api.id, this);
// getLastResultDetail(this.api.id, this);
this.checkVersionEnable();
},
};

View File

@ -342,7 +342,7 @@ export default {
}
this.currentRequest = this.api.request;
this.runLoading = false;
getLastResultDetail(this.api.id, this);
//getLastResultDetail(this.api.id, this);
if (this.api.environmentId) {
this.api.request.useEnvironment = this.api.environmentId;
}

View File

@ -0,0 +1,144 @@
<template>
<el-dialog
:visible.sync="batchSyncApiVisible" :close-on-click-modal="false"
:title="$t('commons.save') + '&' + $t('workstation.sync') + $t('commons.setting')"
v-if="isXpack">
<el-row style="margin-bottom: 10px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)">
<div class="timeClass">
<span style="font-size: 16px; font-weight: bold; padding-left: 10px">{{
$t('api_test.definition.one_click_sync') + 'case'
}}</span>
<el-switch v-model="apiSyncRuleRelation.syncCase" style="float:right; padding-right: 10px"></el-switch>
</div>
<br/>
<span style="font-size: 12px; padding-left: 10px">{{ $t('workstation.batch_sync_api_tips') }}</span
><br/><br/>
<span v-if="apiSyncRuleRelation.syncCase" style="font-size: 16px; font-weight: bold; padding-left: 10px">
{{ $t('workstation.sync') + $t('commons.setting') }}
<i class="el-icon-arrow-down" v-if="showApiSyncConfig" @click="showApiSyncConfig = false"/>
<i class="el-icon-arrow-right" v-if="!showApiSyncConfig" @click="showApiSyncConfig = true"/> </span
><br/><br/>
<div v-if="showApiSyncConfig">
<sync-setting
style="padding-left: 20px"
v-if="apiSyncRuleRelation.syncCase"
v-bind:sync-data="apiSyncRuleRelation.apiSyncConfig"
ref="synSetting"
@updateSyncData="updateSyncData"></sync-setting>
</div>
</el-row>
<el-row style="margin-bottom: 10px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)">
<div class="timeClass">
<span style="font-size: 16px; font-weight: bold; padding-left: 10px">
{{ $t('api_test.definition.change_notification') }}
<el-tooltip
class="ms-num"
effect="dark"
:content="$t('project_application.workstation.api_receiver_tip')"
placement="top">
<i class="el-icon-warning"/>
</el-tooltip>
</span>
<el-switch v-model="apiSyncRuleRelation.sendNotice" style="float:right;padding-right: 10px"></el-switch>
</div>
<span style="font-size: 12px; padding-left: 10px"> {{ $t('api_test.definition.recipient_tips') }} </span><br/>
<p
style="
font-size: 12px;
color: var(--primary_color);
margin-bottom: 20px;
text-decoration: underline;
cursor: pointer;
padding-left: 10px;
"
@click="gotoApiMessage">
{{ $t('project_application.workstation.go_to_api_message') }}
</p>
<el-row v-if="apiSyncRuleRelation.sendNotice" style="margin-bottom: 5px; margin-top: 5px">
<el-col :span="4"
><span style="font-weight: bold; padding-left: 10px">{{ $t('api_test.definition.recipient') + ':' }}</span>
</el-col>
<el-col :span="20" style="color: var(--primary_color)">
<el-checkbox v-model="apiSyncRuleRelation.caseCreator">{{ 'CASE' + $t('api_test.creator') }}</el-checkbox>
<el-checkbox v-model="apiSyncRuleRelation.scenarioCreator">
{{ $t('commons.scenario') + $t('api_test.creator') }}
</el-checkbox>
</el-col>
</el-row>
</el-row>
<el-row>
<el-checkbox v-model="apiSyncRuleRelation.showUpdateRule" style="padding-left: 10px"
>{{ $t('project_application.workstation.no_show_setting') }}
</el-checkbox>
<el-tooltip
class="ms-num"
effect="dark"
:content="$t('project_application.workstation.no_show_setting_tip')"
placement="top">
<i class="el-icon-warning"/>
</el-tooltip>
</el-row>
<span slot="footer" class="dialog-footer">
<el-button @click="batchSyncApiVisible = false">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" @click="batchSync()">{{ $t('commons.confirm') }}</el-button>
</span>
</el-dialog>
</template>
<script>
import SyncSetting from "@/business/definition/util/SyncSetting";
export default {
name: "ApiSyncCaseConfig",
components: {
SyncSetting,
},
props: {
apiSyncRuleRelation: {},
showApiSyncConfig: {
type: Boolean,
default: false,
},
isXpack: {
type: Boolean,
default: false,
}
},
data() {
return {
batchSyncApiVisible: false,
}
},
methods: {
updateSyncData(value) {
this.apiSyncRuleRelation.apiSyncConfig = value;
},
gotoApiMessage() {
let apiResolve = this.$router.resolve({
path: '/project/messagesettings',
});
window.open(apiResolve.href, '_blank');
},
batchSync() {
let fromData;
if (this.$refs.synSetting && this.$refs.synSetting.fromData) {
fromData = this.$refs.synSetting.fromData;
fromData.method = true;
fromData.path = true;
fromData.protocol = true;
}
this.$emit('batchSync', fromData)
},
show() {
this.batchSyncApiVisible = true
},
close() {
this.batchSyncApiVisible = false
}
},
}
</script>
<style scoped>
</style>

View File

@ -6,6 +6,7 @@
class="ms-htt-width"
:placeholder="$t('api_test.definition.request.run_env')"
filterable
@click.native="refreshEnv"
@change="environmentChange"
clearable>
<el-option
@ -54,6 +55,9 @@ export default {
this.getEnvironments();
},
methods: {
refreshEnv() {
this.getEnvironments();
},
getEnvironments() {
if (this.projectId) {
getEnvironmentByProjectId(this.projectId).then((response) => {

View File

@ -15,7 +15,14 @@
:closable="!readOnly"
:disable-transitions="false"
@close="remove(idx)">
{{ tag && tag.length > 10 ? tag.substring(0, 10) + "..." : tag }}
<span v-if="tag && tag.length > 10">
<el-tooltip class="item" effect="light" :content="tag" placement="top" :enterable="false">
<span>{{ tag && tag.length > 10 ? tag.substring(0, 10) + "..." : tag }}</span>
</el-tooltip>
</span>
<span v-else>
{{ tag }}
</span>
</el-tag>
<input
:disabled="readOnly"

View File

@ -57,45 +57,6 @@
<ms-api-key-value :items="condition.headers" :isShowEnable="true" :suggestions="headerSuggestions"/>
</form-section>
<!-- UI 配置 -->
<form-section :title="$t('commons.ui_test')" :init-active=false v-if="condition.type !== 'MODULE'" v-xpack>
<el-row :gutter="10" style="padding-top: 10px;">
<el-col :span="6">
<!-- 浏览器驱动 -->
<span style="margin-right: 10px;">{{ $t("ui.browser") }}</span>
<el-select
size="mini"
v-model="httpConfig.browser"
style="width: 100px"
>
<el-option
v-for="b in browsers"
:key="b.value"
:value="b.value"
:label="b.label"
></el-option>
</el-select>
</el-col>
<el-col :span="6">
<!-- 性能模式 -->
<el-checkbox
v-model="httpConfig.headlessEnabled"
>
<span> {{ $t("ui.performance_mode") }}</span>
</el-checkbox>
<ms-instructions-icon size="10" :content="$t('ui.per_tip')"/>
</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') }}

View File

@ -18,18 +18,12 @@
:content="$t('commons.import')"
@click="importJSON"
/>
<el-dropdown @command="handleExportCommand" class="scenario-ext-btn" trigger="hover"
v-permission="['PROJECT_ENVIRONMENT:READ+EXPORT']">
<ms-table-button
style="margin-left: 10px"
icon="el-icon-box"
:content="$t('commons.export')"
/>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="exportApi">{{ $t('envrionment.export_variable_tip') }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<ms-table-button
v-permission="['PROJECT_ENVIRONMENT:READ+EXPORT']"
icon="el-icon-box"
:content="$t('commons.export')"
@click="exportJSON"
/>
<el-link
style="margin-left: 10px"
@click="batchAdd"
@ -64,31 +58,6 @@
>
<ms-table-column prop="num" sortable label="ID" min-width="60">
</ms-table-column>
<ms-table-column
prop="scope"
sortable
:label="$t('commons.scope')"
:filters="scopeTypeFilters"
:filter-method="filterScope"
min-width="120">
<template slot-scope="scope">
<el-select
v-model="scope.row.scope"
:placeholder="$t('commons.please_select')"
size="mini"
@change="changeType(scope.row)"
>
<el-option
v-for="item in scopeTypeFilters"
:key="item.value"
:label="item.text"
:value="item.value"
/>
</el-select>
</template>
</ms-table-column>
<ms-table-column
prop="name"
:label="$t('api_test.variable_name')"
@ -115,9 +84,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"
@ -126,20 +95,6 @@
: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>
@ -173,14 +128,14 @@
sortable
>
<template slot-scope="scope">
<el-input v-model="scope.row.description" size="mini"/>
<el-input v-model="scope.row.description" size="mini" />
</template>
</ms-table-column>
<ms-table-column :label="$t('commons.operating')" width="150">
<template v-slot:default="scope">
<span>
<el-switch v-model="scope.row.enable" size="mini"/>
<el-switch v-model="scope.row.enable" size="mini" />
<el-tooltip
effect="dark"
:content="$t('commons.remove')"
@ -217,7 +172,7 @@
</ms-table-column>
</ms-table>
</div>
<batch-add-parameter @batchSave="batchSave" ref="batchAdd"/>
<batch-add-parameter @batchSave="batchSave" ref="batchAdd" />
<api-variable-setting ref="apiVariableSetting"></api-variable-setting>
<variable-import
ref="variableImport"
@ -227,7 +182,7 @@
</template>
<script>
import {KeyValue} from "../../../model/EnvTestModel";
import { KeyValue } from "../../../model/EnvTestModel";
import MsApiVariableInput from "./ApiVariableInput";
import BatchAddParameter from "./BatchAddParameter";
import MsTableButton from "../../MsTableButton";
@ -235,9 +190,8 @@ import MsTable from "../../table/MsTable";
import MsTableColumn from "../../table/MsTableColumn";
import ApiVariableSetting from "./ApiVariableSetting";
import CsvFileUpload from "./variable/CsvFileUpload";
import {downloadFile, getUUID, operationConfirm} from "../../../utils";
import { downloadFile, getUUID, operationConfirm } from "../../../utils";
import VariableImport from "./variable/VariableImport";
import _ from "lodash";
export default {
name: "MsApiScenarioVariables",
@ -277,25 +231,15 @@ export default {
},
],
typeSelectOptions: [
{value: "CONSTANT", label: this.$t("api_test.automation.constant")},
{value: "LIST", label: this.$t("test_track.case.list")},
{value: "CSV", label: "CSV"},
{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")},
{ value: "CONSTANT", label: this.$t("api_test.automation.constant") },
{ value: "LIST", label: this.$t("test_track.case.list") },
{ value: "CSV", label: "CSV" },
{ value: "COUNTER", label: this.$t("api_test.automation.counter") },
{ value: "RANDOM", label: this.$t("api_test.automation.random") },
],
variables: {},
selectVariable: "",
editData: {},
scopeTypeFilters: [
{text: this.$t("commons.api"), value: "api"},
{text: this.$t("commons.ui_test"), value: "ui"},
]
};
},
watch: {
@ -336,15 +280,15 @@ export default {
if (repeatKey !== "") {
this.$warning(
this.$t("api_test.environment.common_config") +
"【" +
repeatKey +
"】" +
this.$t("load_test.param_is_duplicate")
"【" +
repeatKey +
"】" +
this.$t("load_test.param_is_duplicate")
);
}
if (isNeedCreate) {
this.variables.push(
new KeyValue({enable: true, id: getUUID(), type: "CONSTANT", scope: "api"})
new KeyValue({ enable: true, id: getUUID(), type: "CONSTANT" })
);
}
this.$emit("change", this.variables);
@ -361,10 +305,6 @@ export default {
data.files = [];
data.quotedData = "false";
}
if (!data.scope || data.scope == "ui") {
data.type = 'STRING';
}
},
valueText(data) {
switch (data.type) {
@ -381,11 +321,11 @@ export default {
},
querySearch(queryString, cb) {
let restaurants = [
{value: "UTF-8"},
{value: "UTF-16"},
{value: "GB2312"},
{value: "ISO-8859-15"},
{value: "US-ASCll"},
{ value: "UTF-8" },
{ value: "UTF-16" },
{ value: "GB2312" },
{ value: "ISO-8859-15" },
{ value: "US-ASCll" },
];
let results = queryString
? restaurants.filter(this.createFilter(queryString))
@ -407,9 +347,6 @@ export default {
this.$set(item, "description", item.remark);
item.remark = undefined;
}
if (!item.scope) {
this.$set(item, "scope", "api");
}
index++;
});
},
@ -433,7 +370,7 @@ export default {
}
);
},
filter(scope) {
filter() {
let datas = [];
this.variables.forEach((item) => {
if (this.selectVariable && this.selectVariable != "" && item.name) {
@ -453,12 +390,6 @@ export default {
});
this.variables = datas;
},
filterScope(value, row) {
if (value == "ui") {
return row.scope == "ui";
}
return !row.scope || row.scope == "api";
},
openSetting(data) {
this.$refs.apiVariableSetting.open(data);
},
@ -519,15 +450,8 @@ export default {
this.sortParameters();
},
exportJSON() {
let apiVariable = [];
this.$refs.variableTable.selectRows.forEach((r) => {
if (!r.scope || r.scope != "ui") {
apiVariable.push(r);
}
});
if (apiVariable.length < 1) {
this.$warning(this.$t("api_test.environment.select_api_variable"));
if (this.$refs.variableTable.selectIds.length < 1) {
this.$warning(this.$t("api_test.environment.select_variable"));
return;
}
let variablesJson = [];
@ -537,7 +461,7 @@ export default {
if (row.type === "CSV") {
messages = this.$t("variables.csv_download");
}
if (row.name && (!row.scope || row.scope == "api")) {
if (row.name) {
variablesJson.push(row);
}
});
@ -570,21 +494,10 @@ export default {
}
});
},
handleExportCommand(command){
this.exportJSON();
}
},
created() {
if (this.items.length === 0) {
this.items.push(new KeyValue({enable: true, scope: "api"}));
} else {
// api
_.forEach(this.items, item => {
if (!item.scope) {
this.$set(item, "scope", "api");
}
})
this.variables = this.items;
this.items.push(new KeyValue({ enable: true }));
}
},
};

View File

@ -1,5 +1,5 @@
import i18n from "../i18n";
import { getCurrentUserId } from "../utils/token";
import {getCurrentProjectID, getCurrentUser} from "../utils/token";
import { SYSTEM_FIELD_NAME_MAP } from "../utils/table-constants";
function setDefaultValue(item, value) {
@ -39,7 +39,15 @@ export function parseCustomField(data, template, rules, oldFields) {
val &&
val === "CURRENT_USER"
) {
val = getCurrentUserId();
val = '';
const {id, userGroups} = getCurrentUser();
if (userGroups) {
// CURRENT_USER是否是当前项目下的成员
let index = userGroups.findIndex(ug => ug.sourceId === getCurrentProjectID());
if (index !== -1) {
val = id;
}
}
}
setDefaultValue(item, val);
}

View File

@ -42,9 +42,9 @@
<bcprov-jdk15on.version>1.70</bcprov-jdk15on.version>
<commons-io.version>2.11.0</commons-io.version>
<commons-text.version>1.10.0</commons-text.version>
<xstream.version>1.4.19</xstream.version>
<xstream.version>1.4.20</xstream.version>
<xmlbeans.version>3.1.0</xmlbeans.version>
<swagger-parser.version>2.1.5</swagger-parser.version>
<swagger-parser.version>2.1.8</swagger-parser.version>
<rhino.version>1.7.14</rhino.version>
<jsoup.version>1.15.3</jsoup.version>
<commonmark.version>0.19.0</commonmark.version>

View File

@ -136,8 +136,8 @@ 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, HttpConfig} from "metersphere-frontend/src/model/EnvironmentModel";
import EnvironmentEdit from "./components/EnvironmentEdit";
import {Environment, parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
import EnvironmentEdit from "metersphere-frontend/src/components/environment/EnvironmentEdit";
import MsAsideItem from "metersphere-frontend/src/components/MsAsideItem";
import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContainer";
import ProjectSwitch from "metersphere-frontend/src/components/head/ProjectSwitch";
@ -174,7 +174,7 @@ export default {
projectList: [],
condition: {}, //
environments: [],
currentEnvironment: new Environment({httpConfig: new HttpConfig()}),
currentEnvironment: new Environment(),
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({httpConfig: new HttpConfig()});
this.currentEnvironment = new Environment();
this.currentEnvironment.projectId = this.currentProjectId;
this.currentEnvironment.currentProjectId = this.currentProjectId;
this.ifCreate = true;

View File

@ -155,7 +155,7 @@ import {REQUEST_HEADERS} from "metersphere-frontend/src/utils/constants";
import {CommonConfig, Environment} from "metersphere-frontend/src/model/EnvironmentModel";
import MsApiHostTable from "metersphere-frontend/src/components/environment/commons/ApiHostTable";
import MsDatabaseConfig from "metersphere-frontend/src/components/environment/database/DatabaseConfig";
import MsEnvironmentHttpConfig from "./EnvironmentHttpConfig";
import MsEnvironmentHttpConfig from "metersphere-frontend/src/components/environment/EnvironmentHttpConfig";
import MsEnvironmentCommonConfig from "metersphere-frontend/src/components/environment/EnvironmentCommonConfig";
import MsEnvironmentSSLConfig from "metersphere-frontend/src/components/environment/EnvironmentSSLConfig";
import MsApiAuthConfig from "metersphere-frontend/src/components/environment/auth/ApiAuthConfig";

View File

@ -57,45 +57,6 @@
<ms-api-key-value :items="condition.headers" :isShowEnable="true" :suggestions="headerSuggestions"/>
</form-section>
<!-- 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>
<el-select
size="mini"
v-model="httpConfig.browser"
style="width: 100px"
>
<el-option
v-for="b in browsers"
:key="b.value"
:value="b.value"
:label="b.label"
></el-option>
</el-select>
</el-col>
<el-col :span="6">
<!-- 性能模式 -->
<el-checkbox
v-model="httpConfig.headlessEnabled"
>
<span> {{ $t("ui.performance_mode") }}</span>
</el-checkbox>
<ms-instructions-icon size="10" :content="$t('ui.per_tip')"/>
</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') }}

View File

@ -15,7 +15,14 @@
:closable="!readOnly"
:disable-transitions="false"
@close="remove(idx)">
{{ tag && tag.length > 10 ? tag.substring(0, 10) + "..." : tag }}
<span v-if="tag && tag.length > 10">
<el-tooltip class="item" effect="light" :content="tag" placement="top" :enterable="false">
<span>{{ tag && tag.length > 10 ? tag.substring(0, 10) + "..." : tag }}</span>
</el-tooltip>
</span>
<span v-else>
{{ tag }}
</span>
</el-tag>
<input
:disabled="readOnly"

View File

@ -0,0 +1,2 @@
-- 清理超级用户组权限信息,改为默认拥有全部权限
delete from user_group_permission where group_id = 'super_group';

View File

@ -8,6 +8,10 @@
<div class="license-content">
<div v-if="license.status !== 'Fail'">
<table>
<tr v-if="license.serialNo">
<th>{{ $t('license.serial_num') }}</th>
<td>{{ license.serialNo }}</td>
</tr>
<tr>
<th>{{ $t('license.corporation') }}</th>
<td>{{ license.corporation }}</td>
@ -37,14 +41,6 @@
<th>{{ $t('license.count') }}</th>
<td>{{ license.count }}</td>
</tr>
<tr v-if="license.serialNo">
<th>{{ $t('license.serial_num') }}</th>
<td>{{ license.serialNo }}</td>
</tr>
<tr v-if="license.remark">
<th>{{ $t('license.remark') }}</th>
<td>{{ license.remark }}</td>
</tr>
<tr>
<th>{{ $t('license.status') }}</th>
<td>
@ -57,6 +53,10 @@
<label class="ms-license-label" v-else>{{ $t('license.invalid') }}</label>
</td>
</tr>
<tr v-if="license.remark">
<th>{{ $t('license.remark') }}</th>
<td>{{ license.remark }}</td>
</tr>
</table>
</div>
<el-link type="primary" class="license-update" @click="create()" :disabled="disabled">
@ -148,7 +148,6 @@ export default {
.license-container {
margin: auto;
height: 400px;
position: relative;
}

View File

@ -86,18 +86,15 @@
<select id="getCountByStatus" resultType="io.metersphere.dto.IssuesStatusCountDao">
select count(1) as `count`, if(i.platform = 'Local', cfi.value, i.platform_status) as statusValue, i.platform
from issues i
inner join project p
on i.project_id = p.id and p.workspace_id = #{request.workspaceId}
left join custom_field_issues cfi
on i.id = cfi.resource_id and field_id in (
select cf.id
from custom_field cf
left join project pr
on cf.project_id = pr.id
inner join custom_field_template cft on cft.template_id = pr.issue_template_id
where cf.scene = 'ISSUE' and cf.name = '状态'
and (pr.workspace_id = #{request.workspaceId} or global = true)
)
inner join project p
on i.project_id = p.id and p.workspace_id = #{request.workspaceId}
left join custom_field_issues cfi
on i.id = cfi.resource_id and field_id in (select cf.id
from custom_field cf
inner join custom_field_template cft on cft.template_id = p.issue_template_id
where cf.scene = 'ISSUE'
and cf.name = '状态'
and (p.workspace_id = #{request.workspaceId} or cf.global = true))
where i.creator = #{request.creator}
group by statusValue;
</select>

View File

@ -65,6 +65,8 @@ 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;
@ -1554,10 +1556,6 @@ 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)) {
@ -1580,32 +1578,6 @@ 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)) {
@ -1669,6 +1641,10 @@ public class TestPlanService {
planTestPlanApiCaseService.setApiCaseEnv(planId, runModeConfig);
planTestPlanScenarioCaseService.setScenarioEnv(planId, runModeConfig);
}
if (DiscoveryUtil.hasService(MicroServiceName.UI_TEST)) {
planTestPlanUiScenarioCaseService.setScenarioEnv(planId, runModeConfig);
}
}
public void editReportConfig(TestPlanDTO testPlanDTO) {

View File

@ -161,4 +161,8 @@ public class PlanTestPlanUiScenarioCaseService extends UiTestService {
public List<String> getUiScenarioProjectIds(String planId) {
return microService.getForData(serviceName, BASE_URL + "/get/project/ids/" + planId, List.class);
}
public RunModeConfigDTO setScenarioEnv(String planId, RunModeConfigDTO runModeConfig) {
return microService.postForData(serviceName, BASE_URL + "/set/env/" + planId, runModeConfig, RunModeConfigDTO.class);
}
}

View File

@ -19,7 +19,11 @@ public class UserService {
UserMapper userMapper;
public UserDTO.PlatformInfo getCurrentPlatformInfo(String workspaceId) {
return JSON.parseObject(getCurrentPlatformInfoStr(workspaceId), UserDTO.PlatformInfo.class);
String currentPlatformInfoStr = getCurrentPlatformInfoStr(workspaceId);
if (StringUtils.isNotBlank(currentPlatformInfoStr)) {
JSON.parseObject(currentPlatformInfoStr, UserDTO.PlatformInfo.class);
}
return null;
}
public String getCurrentPlatformInfoStr(String workspaceId) {

View File

@ -0,0 +1,12 @@
-- init sql
-- 工单名称 v26_create_index
-- 创建人 guoyuqi
ALTER table issues ADD INDEX project_id_index(project_id);
ALTER table issues ADD INDEX creator_index(creator);
ALTER table custom_field ADD INDEX global_index(global);
ALTER table custom_field ADD INDEX scene_index(scene);
ALTER table custom_field ADD INDEX name_index(name);

View File

@ -1,21 +1,5 @@
<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"
@ -115,9 +99,6 @@ 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",
@ -131,7 +112,6 @@ export default {
MsTag,
MsTableAdvSearchBar,
MsTableColumn,
EnvGroupPopover,
},
props: {
referenced: {
@ -167,7 +147,6 @@ export default {
envGroupId: "",
versionFilters: [],
fieldsWidth: getCustomTableWidth('TEST_PLAN_UI_SCENARIO_CASE'),
projectIds: new Set()
};
},
computed: {
@ -265,24 +244,9 @@ 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);
});
});
},
closeEnv(){
this.$refs.envPopover.close();
}
}
};

View File

@ -150,10 +150,6 @@ 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);
})
@ -185,6 +181,8 @@ export default {
this.autoCheckStatus();
this.$refs.baseRelevance.close();
});
},
autoCheckStatus() { //
if (!this.planId) {

View File

@ -147,8 +147,7 @@
: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>
@ -177,8 +176,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" :request="conditionRequest"/>
<ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true"
:custom-serial-on-sample-error="true"/>
<ms-task-center ref="taskCenter" :show-menu="false"/>
</div>
@ -328,8 +327,6 @@ export default {
]
},
versionFilters: [],
//
conditionRequest: {}
}
},
computed: {
@ -361,14 +358,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"]
}
}
@ -440,19 +437,12 @@ export default {
let rows = this.orderBySelectRows(this.$refs.table.selectRows);
this.planCaseIds = [];
rows.forEach(row => {
this.planCaseIds.push(row.caseId);
this.planCaseIds.push(row.id);
})
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 = this.$refs.table.selectIds;
if (rows) {
selectIds = Array.from(rows).map(row => row.id);
}
let selectIds = Array.from(rows).map(row => row.id);
let array = [];
for (let i in this.tableData) {
if (selectIds.indexOf(this.tableData[i].id) !== -1) {
@ -509,8 +499,8 @@ export default {
},
},
},
this.$t("ui.view_config")
),
this.$t("ui.view_config")
),
])
);
validate = false;

View File

@ -8,21 +8,6 @@
>
<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"
:show-env-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>
@ -282,7 +267,6 @@ export default {
};
this.runModeVisible = true;
this.getWsProjects();
this.showPopover();
},
changeMode() {
this.runConfig.runWithinResourcePool = false;

View File

@ -88,7 +88,6 @@ const TRACK_HEADER = {
{id: 'name', key: '2', label: 'api_test.automation.scenario_name'},
{id: 'versionId', key: 'd', label: 'commons.version'},
{id: 'level', key: '3', label: 'api_test.automation.case_level'},
{id: 'envs', key: '8', label: 'commons.environment'},
{id: 'tagNames', key: '4', label: 'api_test.automation.tag'},
{id: 'stepTotal', key: '7', label: 'api_test.automation.step'},
{id: 'passRate', key: '9', label: 'api_test.automation.passing_rate'},

View File

@ -17,131 +17,77 @@
@refresh="getIssues"
ref="table"
>
<span v-for="(item) in fields" :key="item.key">
<ms-table-column
:label="$t('test_track.issue.id')"
prop="num"
:field="item"
sortable
min-width="100"
:fields-width="fieldsWidth">
</ms-table-column>
<ms-table-column
v-for="(item) in fields" :key="item.key"
:label="item.label"
:prop="item.id"
:field="item"
:sortable="item.sortable"
:min-width="item.minWidth"
:column-key="item.columnKey"
:fields-width="fieldsWidth"
:filters="item.filters"
>
<template v-slot="scope">
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
:label="$t('test_track.issue.title')"
min-width="100"
prop="title">
</ms-table-column>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
sortable
min-width="110"
:label="$t('test_track.issue.platform_status') "
prop="platformStatus">
<template v-slot="scope">
<span
v-if="scope.row.platform ==='Zentao'">{{ scope.row.platformStatus ? issueStatusMap[scope.row.platformStatus] : '--' }}</span>
<span v-else>{{ scope.row.platformStatus ? scope.row.platformStatus : '--' }}</span>
</template>
</ms-table-column>
<ms-table-column
:field="item"
:fields-width="fieldsWidth"
:filters="platformFilters"
:label="$t('test_track.issue.platform')"
min-width="100"
prop="platform">
</ms-table-column>
<ms-table-column
prop="createTime"
:field="item"
:fields-width="fieldsWidth"
:label="$t('commons.create_time')"
sortable
min-width="140px">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</ms-table-column>
<ms-table-column
prop="projectName"
:field="item"
:fields-width="fieldsWidth"
:label="$t('test_track.issue.issue_project')"
min-width="80">
<template v-slot="scope">
{{ scope.row.projectName ? scope.row.projectName : '--' }}
</template>
</ms-table-column>
<ms-table-column
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
column-key="creator"
min-width="100"
:label="$t('custom_field.issue_creator')"
prop="creatorName">
</ms-table-column>
<ms-table-column
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
:label="$t('test_track.issue.issue_resource')"
min-width="120"
prop="resourceName">
<template v-slot="scope">
<el-link v-if="scope.row.resourceName" @click="$router.push('/track/plan/view/' + scope.row.resourceId)">
{{ scope.row.resourceName }}
</el-link>
<span v-else>
--
</span>
</template>
</ms-table-column>
<issue-description-table-item :fields-width="fieldsWidth" :field="item" v-if="isShowAllColumn"/>
<ms-table-column
:field="item"
v-if="isShowAllColumn"
:fields-width="fieldsWidth"
:label="item.label"
prop="caseCount">
<template v-slot="scope">
<router-link
:to="scope.row.caseCount > 0 ? {name: 'testCase', params: { projectId: 'all', ids: scope.row.caseIds }} : {}">
{{ scope.row.caseCount }}
</router-link>
</template>
</ms-table-column>
<div v-if="isShowAllColumn">
<ms-table-column v-for="field in issueTemplate.customFields" :key="field.id"
:field="item"
min-width="120"
:fields-width="fieldsWidth"
:label="field.system ? $t(systemNameMap[field.name]) :field.name"
:prop="field.name">
<template v-slot="scope">
<span v-if="field.name === '状态'">
{{ getCustomFieldValue(scope.row, field) ? getCustomFieldValue(scope.row, field) : issueStatusMap[scope.row.status] }}
<span v-if="item.id === 'platformStatus'">
<span v-if="scope.row.platform === 'Tapd'">
{{ scope.row.platformStatus ? tapdIssueStatusMap[scope.row.platformStatus] : '--' }}
</span>
<span v-else-if="scope.row.platform ==='Local'">
{{ scope.row.platformStatus ? tapdIssueStatusMap[scope.row.platformStatus] : '--' }}
</span>
<span v-else-if="platformStatusMap && platformStatusMap.get(scope.row.platformStatus)">
{{ platformStatusMap.get(scope.row.platformStatus) }}
</span>
<span v-else>
{{ getCustomFieldValue(scope.row, field) }}
{{ scope.row.platformStatus ? scope.row.platformStatus : '--' }}
</span>
</template>
</ms-table-column>
</div>
</span>
</span>
<ms-review-table-item
v-else-if="item.id === 'description'"
:data="scope.row"
prop="description"/>
<span v-else-if="item.id === 'resourceName'">
<el-link v-if="scope.row.resourceName"
@click="$router.push('/track/plan/view/' + scope.row.resourceId)">
{{ scope.row.resourceName }}
</el-link>
<span v-else>
--
</span>
</span>
<span v-else-if="item.id === 'createTime'">
{{ scope.row.createTime | datetimeFormat }}
</span>
<span v-else-if="item.id === 'caseCount'">
<router-link
:to="scope.row.caseCount > 0 ? {name: 'testCase', params: { projectId: 'all', ids: scope.row.caseIds }} : {}">
{{ scope.row.caseCount }}
</router-link>
</span>
<!-- 自定义字段 -->
<span v-else-if="item.isCustom">
<span v-if="item.type === 'richText' && scope.row.displayValueMap[item.id]">
<ms-review-table-item
:data="scope.row.displayValueMap" :prop="item.id"/>
</span>
<span v-else>
{{ scope.row.displayValueMap[item.id] }}
</span>
</span>
<span v-else>
{{ scope.row[item.id] }}
</span>
</template>
</ms-table-column>
</ms-table>
<ms-table-pagination :change="getIssues" :current-page.sync="page.currentPage" :page-size.sync="page.pageSize"
@ -155,7 +101,12 @@ import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColu
import MsTableOperators from "metersphere-frontend/src/components/MsTableOperators";
import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import {ISSUE_PLATFORM_OPTION, ISSUE_STATUS_MAP, SYSTEM_FIELD_NAME_MAP} from "metersphere-frontend/src/utils/table-constants";
import {
ISSUE_PLATFORM_OPTION,
ISSUE_STATUS_MAP,
SYSTEM_FIELD_NAME_MAP,
TAPD_ISSUE_STATUS_MAP
} from "metersphere-frontend/src/utils/table-constants";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import {getDashboardIssues, getIssuePartTemplateWithProject, getIssues, getPlatformOption} from "@/api/issue";
import {
@ -171,15 +122,15 @@ import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/s
import {getProjectMember} from "@/api/user";
import {getTableHeaderWithCustomFieldsByXpack} from "@/business/component/js/table-head-util";
import {LOCAL} from "metersphere-frontend/src/utils/constants";
import IssueDescriptionTableItem from "@/business/component/IssueDescriptionTableItem";
import MsReviewTableItem from "@/business/component/MsReviewTableItem";
import {getUUID} from "metersphere-frontend/src/utils";
export default {
name: "IssueTableList",
components: {
MsMainContainer,
MsReviewTableItem,
MsContainer,
IssueDescriptionTableItem,
MsTableHeader,
MsTablePagination, MsTableButton, MsTableOperators, MsTableColumn, MsTable
},
@ -238,6 +189,9 @@ export default {
issueStatusMap() {
return ISSUE_STATUS_MAP;
},
tapdIssueStatusMap() {
return TAPD_ISSUE_STATUS_MAP;
},
systemNameMap() {
return SYSTEM_FIELD_NAME_MAP;
},
@ -324,18 +278,44 @@ export default {
this.page.total = data.itemCount;
this.page.data = data.listObject;
parseCustomFilesForList(this.page.data);
this.initCustomFieldValue();
});
} else {
this.page.result.loading = getIssues(this.page).then((response)=>{
this.page.result.loading = getIssues(this.page).then((response) => {
let data = response.data;
this.page.total = data.itemCount;
this.page.data = data.listObject;
parseCustomFilesForList(this.page.data);
this.initCustomFieldValue();
});
}
},
initCustomFieldValue() {
if (this.fields.length <= 0) {
return;
}
this.page.data.forEach(item => {
let displayValueMap = {};
let fieldIdSet = new Set(this.fields.map(i => i.id));
this.issueTemplate.customFields.forEach(field => {
let displayValue;
if (!fieldIdSet.has(field.name)) {
return;
}
if (field.name === '状态') {
displayValue = this.getCustomFieldValue(item, field, this.issueStatusMap[item.status]);
} else {
displayValue = this.getCustomFieldValue(item, field);
}
displayValueMap[field.name] = displayValue;
});
item.displayValueMap = displayValueMap;
});
this.loading = false;
},
handleEdit(resource) {
let issueData = this.$router.resolve({path:'/track/issue',query:{id:resource.id}});
let issueData = this.$router.resolve({path: '/track/issue', query: {id: resource.id}});
window.open(issueData.href, '_blank');
},
},

View File

@ -0,0 +1,28 @@
<template>
<el-popover
placement="right"
width="500"
trigger="hover"
popper-class="issues-popover">
<ms-mark-down-text :prop="prop" :data="data" :disabled="true"/>
<el-button slot="reference" type="text">{{ $t('test_track.issue.preview') }}</el-button>
</el-popover>
</template>
<script>
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import MsMarkDownText from "metersphere-frontend/src/components/MsMarkDownText";
export default {
name: "MsReviewTableItem",
components: {MsMarkDownText, MsTableColumn},
props: {
data: Object,
prop: String,
}
}
</script>
<style scoped>
</style>