Merge remote-tracking branch 'origin/master'

# Conflicts:
#	backend/src/main/java/io/metersphere/xpack
This commit is contained in:
q4speed 2020-08-18 18:10:02 +08:00
commit cadcf9eb7b
70 changed files with 2746 additions and 503 deletions

View File

@ -187,6 +187,12 @@ v1.1.0 是 v1.0.0 之后的功能版本。
- 基础设施: [Docker](https://www.docker.com/), [Kubernetes](https://kubernetes.io/)
- 测试引擎: [JMeter](https://jmeter.apache.org/)
## 致谢
- [BlazeMeter](https://www.blazemeter.com/):感谢 BlazeMeter 提供的设计思路
- [JMeter](https://jmeter.apache.org/)MeterSphere 使用了 JMeter 作为测试引擎
- [Element](https://element.eleme.cn/#/):感谢 Element 提供的优秀组件库
## 加入 MeterSphere 团队
我们正在招聘 MeterSphere 技术布道师,一起打造开源明星项目,请发简历到 metersphere@fit2cloud.com

3
backend/.gitignore vendored
View File

@ -31,4 +31,5 @@ target
.settings
.project
.classpath
.factorypath
.factorypath
*.jar

View File

@ -140,11 +140,6 @@
</dependency>
<!-- jmeter -->
<!-- <dependency>-->
<!-- <groupId>org.apache.jmeter</groupId>-->
<!-- <artifactId>ApacheJMeter_core</artifactId>-->
<!-- <version>${jmeter.version}</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.apache.jmeter</groupId>
@ -272,6 +267,24 @@
<scope>runtime</scope>
</dependency>
<!-- buji-pac4j -->
<dependency>
<groupId>org.pac4j</groupId>
<artifactId>pac4j-cas</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>io.buji</groupId>
<artifactId>buji-pac4j</artifactId>
<version>4.0.0</version>
<exclusions>
<exclusion>
<artifactId>shiro-web</artifactId>
<groupId>org.apache.shiro</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
@ -356,6 +369,16 @@
<version>2.6</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/jmeter/lib/**/*.jar</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
@ -383,6 +406,35 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>generate-resources</phase>
<goals>
<goal>copy</goal>
</goals>
</execution>
</executions>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_functions</artifactId>
<version>${jmeter.version}</version>
<type>jar</type>
<overWrite>true</overWrite>
<outputDirectory>src/main/resources/jmeter/lib/ext</outputDirectory>
<destFileName>ApacheJMeter_functions.jar</destFileName>
</artifactItem>
</artifactItems>
<outputDirectory>${project.build.directory}/wars</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>

View File

@ -0,0 +1,10 @@
package io.metersphere.api.dto.scenario.processor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class BeanShellPostProcessor extends BeanShellProcessor {
}

View File

@ -0,0 +1,10 @@
package io.metersphere.api.dto.scenario.processor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class BeanShellPreProcessor extends BeanShellProcessor {
}

View File

@ -0,0 +1,8 @@
package io.metersphere.api.dto.scenario.processor;
import lombok.Data;
@Data
public class BeanShellProcessor {
private String script;
}

View File

@ -6,6 +6,8 @@ import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.assertions.Assertions;
import io.metersphere.api.dto.scenario.extract.Extract;
import io.metersphere.api.dto.scenario.processor.BeanShellPostProcessor;
import io.metersphere.api.dto.scenario.processor.BeanShellPreProcessor;
import lombok.Data;
import java.util.List;
@ -35,4 +37,8 @@ public class HttpRequest implements Request {
private Assertions assertions;
@JSONField(ordinal = 10)
private Extract extract;
@JSONField(ordinal = 11)
private BeanShellPreProcessor beanShellPreProcessor;
@JSONField(ordinal = 12)
private BeanShellPostProcessor beanShellPostProcessor;
}

View File

@ -14,6 +14,7 @@ import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
@ -24,14 +25,15 @@ public class JMeterService {
private JmeterProperties jmeterProperties;
public void run(String testId, String debugReportId, InputStream is) {
String JMETER_HOME = jmeterProperties.getHome();
String JMETER_HOME = getJmeterHome();
String JMETER_PROPERTIES = JMETER_HOME + "/bin/jmeter.properties";
JMeterUtils.loadJMeterProperties(JMETER_PROPERTIES);
JMeterUtils.setJMeterHome(JMETER_HOME);
try {
Object scriptWrapper = SaveService.loadElement(is);
HashTree testPlan = getHashTree(scriptWrapper);
addBackendListener(testId, debugReportId, testPlan);
addBackendListener(testId, debugReportId, testPlan);
LocalRunner runner = new LocalRunner(testPlan);
runner.run();
@ -41,6 +43,20 @@ public class JMeterService {
}
}
private String getJmeterHome() {
String home = getClass().getResource("/").getPath() + "jmeter";
try {
File file = new File(home);
if (file.exists()) {
return home;
} else {
return jmeterProperties.getHome();
}
} catch (Exception e) {
return jmeterProperties.getHome();
}
}
private HashTree getHashTree(Object scriptWrapper) throws Exception {
Field field = scriptWrapper.getClass().getDeclaredField("testPlan");
field.setAccessible(true);

View File

@ -0,0 +1,222 @@
package io.metersphere.api.parse;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ScriptEngineUtils;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
public class JmeterDocumentParser {
private final static String HASH_TREE_ELEMENT = "hashTree";
private final static String STRING_PROP = "stringProp";
private final static String ARGUMENTS = "Arguments";
private final static String COLLECTION_PROP = "collectionProp";
private final static String HTTP_SAMPLER_PROXY = "HTTPSamplerProxy";
private final static String ELEMENT_PROP = "elementProp";
public static byte[] parse(byte[] source) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try (
ByteArrayInputStream byteStream = new ByteArrayInputStream(source)
) {
InputSource inputSource = new InputSource(byteStream);
DocumentBuilder docBuilder = factory.newDocumentBuilder();
final Document document = docBuilder.parse(inputSource);
final Element jmeterTestPlan = document.getDocumentElement();
NodeList childNodes = jmeterTestPlan.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
parseHashTree(ele);
}
}
return documentToBytes(document);
} catch (Exception e) {
LogUtil.error(e);
return source;
}
}
private static byte[] documentToBytes(Document document) throws TransformerException {
DOMSource domSource = new DOMSource(document);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
return writer.toString().getBytes();
}
private static void parseHashTree(Element hashTree) {
if (invalid(hashTree)) {
return;
}
if (hashTree.getChildNodes().getLength() > 0) {
final NodeList childNodes = hashTree.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (invalid(ele)) {
continue;
}
if (nodeNameEquals(ele, HASH_TREE_ELEMENT)) {
parseHashTree(ele);
} else if (nodeNameEquals(ele, ARGUMENTS)) {
processArguments(ele);
} else if (nodeNameEquals(ele, HTTP_SAMPLER_PROXY)) {
processHttpSamplerProxy(ele);
}
}
}
}
}
private static void processHttpSamplerProxy(Element ele) {
if (invalid(ele)) {
return;
}
NodeList childNodes = ele.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (!(item instanceof Element)) {
continue;
}
Element element = (Element) item;
if (nodeNameEquals(element, ELEMENT_PROP) && "HTTPsampler.Arguments".equals(element.getAttribute("name"))) {
processArguments(element);
} else if ("HTTPSampler.path".equals(element.getAttribute("name"))) {
processStringProp(element);
}
}
}
private static void processArguments(Element ele) {
if (invalid(ele)) {
return;
}
NodeList childNodes = ele.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node item = childNodes.item(i);
if (!(item instanceof Element)) {
continue;
}
Element element = (Element) item;
if (nodeNameEquals(item, COLLECTION_PROP) && "Arguments.arguments".equals(element.getAttribute("name"))) {
NodeList elementProps = item.getChildNodes();
for (int j = 0; j < elementProps.getLength(); j++) {
Node elementProp = elementProps.item(j);
if (!(elementProp instanceof Element)) {
continue;
}
NodeList stringProps = elementProp.getChildNodes();
for (int k = 0; k < stringProps.getLength(); k++) {
Node stringProp = stringProps.item(k);
if (!(stringProp instanceof Element)) {
continue;
}
processStringProp((Element) stringProp);
}
}
}
}
}
private static void processStringProp(Element ele) {
String name = ele.getAttribute("name");
switch (name) {
case "HTTPSampler.path":
String path = ele.getTextContent();
Map<String, String> parser = parserUrl(path);
String url = parser.get("URL");
String params = parser.keySet().stream().filter(k -> !"URL".equals(k)).reduce("", (u, k) -> {
String v = parser.get(k);
u += "&" + k + "=" + ScriptEngineUtils.calculate(v);
return u;
});
ele.setTextContent(url + params);
break;
case "Argument.value":
String textContent = ele.getTextContent();
ele.setTextContent(ScriptEngineUtils.calculate(textContent));
break;
default:
break;
}
}
private static boolean nodeNameEquals(Node node, String desiredName) {
return desiredName.equals(node.getNodeName()) || desiredName.equals(node.getLocalName());
}
private static boolean invalid(Element ele) {
return !StringUtils.isBlank(ele.getAttribute("enabled")) && !Boolean.parseBoolean(ele.getAttribute("enabled"));
}
private static Map<String, String> parserUrl(String url) {
// 传递的URL参数
Map<String, String> strUrlParas = new HashMap<>();
String strUrl;
String strUrlParams;
// 解析访问地址
if (url.contains("?")) {
String[] strUrlPatten = url.split("\\?");
strUrl = strUrlPatten[0];
strUrlParams = strUrlPatten[1];
} else {
strUrl = url;
strUrlParams = url;
}
strUrlParas.put("URL", strUrl);
// 解析参数
String[] params = null;
if (strUrlParams.contains("&")) {
params = strUrlParams.split("&");
} else {
params = new String[]{strUrlParams};
}
// 保存参数到参数容器
for (String p : params) {
if (p.contains("=")) {
String[] param = p.split("=");
if (param.length == 1) {
strUrlParas.put(param[0], "");
} else {
String key = param[0];
String value = param[1];
strUrlParas.put(key, value);
}
}
}
return strUrlParas;
}
}

View File

@ -8,11 +8,15 @@ import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.parse.ApiImportParser;
import io.metersphere.api.parse.ApiImportParserFactory;
import io.metersphere.api.parse.JmeterDocumentParser;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiTestFileMapper;
import io.metersphere.base.mapper.ApiTestMapper;
import io.metersphere.base.mapper.ext.ExtApiTestMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.FileType;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.constants.ScheduleType;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
@ -145,6 +149,8 @@ public class APITestService {
MSException.throwException(Translator.get("file_cannot_be_null"));
}
byte[] bytes = fileService.loadFileAsBytes(file.getFileId());
// 解析 xml 处理 mock 数据
bytes = JmeterDocumentParser.parse(bytes);
InputStream is = new ByteArrayInputStream(bytes);
APITestResult apiTest = get(request.getId());
@ -338,7 +344,10 @@ public class APITestService {
InputStream is = null;
try {
is = new ByteArrayInputStream(file.getBytes());
byte[] bytes = file.getBytes();
// 解析 xml 处理 mock 数据
bytes = JmeterDocumentParser.parse(bytes);
is = new ByteArrayInputStream(bytes);
} catch (IOException e) {
LogUtil.error(e);
}

View File

@ -0,0 +1,27 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
public class Issues implements Serializable {
private String id;
private String title;
private String status;
private Long createTime;
private Long updateTime;
private String reporter;
private String lastmodify;
private String platform;
private String description;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,740 @@
package io.metersphere.base.domain;
import java.util.ArrayList;
import java.util.List;
public class IssuesExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public IssuesExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andIdIsNull() {
addCriterion("id is null");
return (Criteria) this;
}
public Criteria andIdIsNotNull() {
addCriterion("id is not null");
return (Criteria) this;
}
public Criteria andIdEqualTo(String value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThan(String value) {
addCriterion("id >", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThanOrEqualTo(String value) {
addCriterion("id >=", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThan(String value) {
addCriterion("id <", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThanOrEqualTo(String value) {
addCriterion("id <=", value, "id");
return (Criteria) this;
}
public Criteria andIdLike(String value) {
addCriterion("id like", value, "id");
return (Criteria) this;
}
public Criteria andIdNotLike(String value) {
addCriterion("id not like", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<String> values) {
addCriterion("id in", values, "id");
return (Criteria) this;
}
public Criteria andIdNotIn(List<String> values) {
addCriterion("id not in", values, "id");
return (Criteria) this;
}
public Criteria andIdBetween(String value1, String value2) {
addCriterion("id between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andIdNotBetween(String value1, String value2) {
addCriterion("id not between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andTitleIsNull() {
addCriterion("title is null");
return (Criteria) this;
}
public Criteria andTitleIsNotNull() {
addCriterion("title is not null");
return (Criteria) this;
}
public Criteria andTitleEqualTo(String value) {
addCriterion("title =", value, "title");
return (Criteria) this;
}
public Criteria andTitleNotEqualTo(String value) {
addCriterion("title <>", value, "title");
return (Criteria) this;
}
public Criteria andTitleGreaterThan(String value) {
addCriterion("title >", value, "title");
return (Criteria) this;
}
public Criteria andTitleGreaterThanOrEqualTo(String value) {
addCriterion("title >=", value, "title");
return (Criteria) this;
}
public Criteria andTitleLessThan(String value) {
addCriterion("title <", value, "title");
return (Criteria) this;
}
public Criteria andTitleLessThanOrEqualTo(String value) {
addCriterion("title <=", value, "title");
return (Criteria) this;
}
public Criteria andTitleLike(String value) {
addCriterion("title like", value, "title");
return (Criteria) this;
}
public Criteria andTitleNotLike(String value) {
addCriterion("title not like", value, "title");
return (Criteria) this;
}
public Criteria andTitleIn(List<String> values) {
addCriterion("title in", values, "title");
return (Criteria) this;
}
public Criteria andTitleNotIn(List<String> values) {
addCriterion("title not in", values, "title");
return (Criteria) this;
}
public Criteria andTitleBetween(String value1, String value2) {
addCriterion("title between", value1, value2, "title");
return (Criteria) this;
}
public Criteria andTitleNotBetween(String value1, String value2) {
addCriterion("title not between", value1, value2, "title");
return (Criteria) this;
}
public Criteria andStatusIsNull() {
addCriterion("`status` is null");
return (Criteria) this;
}
public Criteria andStatusIsNotNull() {
addCriterion("`status` is not null");
return (Criteria) this;
}
public Criteria andStatusEqualTo(String value) {
addCriterion("`status` =", value, "status");
return (Criteria) this;
}
public Criteria andStatusNotEqualTo(String value) {
addCriterion("`status` <>", value, "status");
return (Criteria) this;
}
public Criteria andStatusGreaterThan(String value) {
addCriterion("`status` >", value, "status");
return (Criteria) this;
}
public Criteria andStatusGreaterThanOrEqualTo(String value) {
addCriterion("`status` >=", value, "status");
return (Criteria) this;
}
public Criteria andStatusLessThan(String value) {
addCriterion("`status` <", value, "status");
return (Criteria) this;
}
public Criteria andStatusLessThanOrEqualTo(String value) {
addCriterion("`status` <=", value, "status");
return (Criteria) this;
}
public Criteria andStatusLike(String value) {
addCriterion("`status` like", value, "status");
return (Criteria) this;
}
public Criteria andStatusNotLike(String value) {
addCriterion("`status` not like", value, "status");
return (Criteria) this;
}
public Criteria andStatusIn(List<String> values) {
addCriterion("`status` in", values, "status");
return (Criteria) this;
}
public Criteria andStatusNotIn(List<String> values) {
addCriterion("`status` not in", values, "status");
return (Criteria) this;
}
public Criteria andStatusBetween(String value1, String value2) {
addCriterion("`status` between", value1, value2, "status");
return (Criteria) this;
}
public Criteria andStatusNotBetween(String value1, String value2) {
addCriterion("`status` not between", value1, value2, "status");
return (Criteria) this;
}
public Criteria andCreateTimeIsNull() {
addCriterion("create_time is null");
return (Criteria) this;
}
public Criteria andCreateTimeIsNotNull() {
addCriterion("create_time is not null");
return (Criteria) this;
}
public Criteria andCreateTimeEqualTo(Long value) {
addCriterion("create_time =", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotEqualTo(Long value) {
addCriterion("create_time <>", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThan(Long value) {
addCriterion("create_time >", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("create_time >=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThan(Long value) {
addCriterion("create_time <", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThanOrEqualTo(Long value) {
addCriterion("create_time <=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeIn(List<Long> values) {
addCriterion("create_time in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotIn(List<Long> values) {
addCriterion("create_time not in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeBetween(Long value1, Long value2) {
addCriterion("create_time between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotBetween(Long value1, Long value2) {
addCriterion("create_time not between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andUpdateTimeIsNull() {
addCriterion("update_time is null");
return (Criteria) this;
}
public Criteria andUpdateTimeIsNotNull() {
addCriterion("update_time is not null");
return (Criteria) this;
}
public Criteria andUpdateTimeEqualTo(Long value) {
addCriterion("update_time =", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotEqualTo(Long value) {
addCriterion("update_time <>", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeGreaterThan(Long value) {
addCriterion("update_time >", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("update_time >=", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeLessThan(Long value) {
addCriterion("update_time <", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeLessThanOrEqualTo(Long value) {
addCriterion("update_time <=", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeIn(List<Long> values) {
addCriterion("update_time in", values, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotIn(List<Long> values) {
addCriterion("update_time not in", values, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeBetween(Long value1, Long value2) {
addCriterion("update_time between", value1, value2, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotBetween(Long value1, Long value2) {
addCriterion("update_time not between", value1, value2, "updateTime");
return (Criteria) this;
}
public Criteria andReporterIsNull() {
addCriterion("reporter is null");
return (Criteria) this;
}
public Criteria andReporterIsNotNull() {
addCriterion("reporter is not null");
return (Criteria) this;
}
public Criteria andReporterEqualTo(String value) {
addCriterion("reporter =", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterNotEqualTo(String value) {
addCriterion("reporter <>", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterGreaterThan(String value) {
addCriterion("reporter >", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterGreaterThanOrEqualTo(String value) {
addCriterion("reporter >=", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterLessThan(String value) {
addCriterion("reporter <", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterLessThanOrEqualTo(String value) {
addCriterion("reporter <=", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterLike(String value) {
addCriterion("reporter like", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterNotLike(String value) {
addCriterion("reporter not like", value, "reporter");
return (Criteria) this;
}
public Criteria andReporterIn(List<String> values) {
addCriterion("reporter in", values, "reporter");
return (Criteria) this;
}
public Criteria andReporterNotIn(List<String> values) {
addCriterion("reporter not in", values, "reporter");
return (Criteria) this;
}
public Criteria andReporterBetween(String value1, String value2) {
addCriterion("reporter between", value1, value2, "reporter");
return (Criteria) this;
}
public Criteria andReporterNotBetween(String value1, String value2) {
addCriterion("reporter not between", value1, value2, "reporter");
return (Criteria) this;
}
public Criteria andLastmodifyIsNull() {
addCriterion("lastmodify is null");
return (Criteria) this;
}
public Criteria andLastmodifyIsNotNull() {
addCriterion("lastmodify is not null");
return (Criteria) this;
}
public Criteria andLastmodifyEqualTo(String value) {
addCriterion("lastmodify =", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyNotEqualTo(String value) {
addCriterion("lastmodify <>", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyGreaterThan(String value) {
addCriterion("lastmodify >", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyGreaterThanOrEqualTo(String value) {
addCriterion("lastmodify >=", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyLessThan(String value) {
addCriterion("lastmodify <", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyLessThanOrEqualTo(String value) {
addCriterion("lastmodify <=", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyLike(String value) {
addCriterion("lastmodify like", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyNotLike(String value) {
addCriterion("lastmodify not like", value, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyIn(List<String> values) {
addCriterion("lastmodify in", values, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyNotIn(List<String> values) {
addCriterion("lastmodify not in", values, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyBetween(String value1, String value2) {
addCriterion("lastmodify between", value1, value2, "lastmodify");
return (Criteria) this;
}
public Criteria andLastmodifyNotBetween(String value1, String value2) {
addCriterion("lastmodify not between", value1, value2, "lastmodify");
return (Criteria) this;
}
public Criteria andPlatformIsNull() {
addCriterion("platform is null");
return (Criteria) this;
}
public Criteria andPlatformIsNotNull() {
addCriterion("platform is not null");
return (Criteria) this;
}
public Criteria andPlatformEqualTo(String value) {
addCriterion("platform =", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotEqualTo(String value) {
addCriterion("platform <>", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformGreaterThan(String value) {
addCriterion("platform >", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformGreaterThanOrEqualTo(String value) {
addCriterion("platform >=", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformLessThan(String value) {
addCriterion("platform <", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformLessThanOrEqualTo(String value) {
addCriterion("platform <=", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformLike(String value) {
addCriterion("platform like", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotLike(String value) {
addCriterion("platform not like", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformIn(List<String> values) {
addCriterion("platform in", values, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotIn(List<String> values) {
addCriterion("platform not in", values, "platform");
return (Criteria) this;
}
public Criteria andPlatformBetween(String value1, String value2) {
addCriterion("platform between", value1, value2, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotBetween(String value1, String value2) {
addCriterion("platform not between", value1, value2, "platform");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -11,7 +11,5 @@ public class TestCaseIssues implements Serializable {
private String issuesId;
private String platform;
private static final long serialVersionUID = 1L;
}

View File

@ -313,76 +313,6 @@ public class TestCaseIssuesExample {
addCriterion("issues_id not between", value1, value2, "issuesId");
return (Criteria) this;
}
public Criteria andPlatformIsNull() {
addCriterion("platform is null");
return (Criteria) this;
}
public Criteria andPlatformIsNotNull() {
addCriterion("platform is not null");
return (Criteria) this;
}
public Criteria andPlatformEqualTo(String value) {
addCriterion("platform =", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotEqualTo(String value) {
addCriterion("platform <>", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformGreaterThan(String value) {
addCriterion("platform >", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformGreaterThanOrEqualTo(String value) {
addCriterion("platform >=", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformLessThan(String value) {
addCriterion("platform <", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformLessThanOrEqualTo(String value) {
addCriterion("platform <=", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformLike(String value) {
addCriterion("platform like", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotLike(String value) {
addCriterion("platform not like", value, "platform");
return (Criteria) this;
}
public Criteria andPlatformIn(List<String> values) {
addCriterion("platform in", values, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotIn(List<String> values) {
addCriterion("platform not in", values, "platform");
return (Criteria) this;
}
public Criteria andPlatformBetween(String value1, String value2) {
addCriterion("platform between", value1, value2, "platform");
return (Criteria) this;
}
public Criteria andPlatformNotBetween(String value1, String value2) {
addCriterion("platform not between", value1, value2, "platform");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -0,0 +1,36 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.Issues;
import io.metersphere.base.domain.IssuesExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface IssuesMapper {
long countByExample(IssuesExample example);
int deleteByExample(IssuesExample example);
int deleteByPrimaryKey(String id);
int insert(Issues record);
int insertSelective(Issues record);
List<Issues> selectByExampleWithBLOBs(IssuesExample example);
List<Issues> selectByExample(IssuesExample example);
Issues selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") Issues record, @Param("example") IssuesExample example);
int updateByExampleWithBLOBs(@Param("record") Issues record, @Param("example") IssuesExample example);
int updateByExample(@Param("record") Issues record, @Param("example") IssuesExample example);
int updateByPrimaryKeySelective(Issues record);
int updateByPrimaryKeyWithBLOBs(Issues record);
int updateByPrimaryKey(Issues record);
}

View File

@ -0,0 +1,323 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.IssuesMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.Issues">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="title" jdbcType="VARCHAR" property="title" />
<result column="status" jdbcType="VARCHAR" property="status" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="reporter" jdbcType="VARCHAR" property="reporter" />
<result column="lastmodify" jdbcType="VARCHAR" property="lastmodify" />
<result column="platform" jdbcType="VARCHAR" property="platform" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.Issues">
<result column="description" jdbcType="LONGVARCHAR" property="description" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id, title, `status`, create_time, update_time, reporter, lastmodify, platform
</sql>
<sql id="Blob_Column_List">
description
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.IssuesExample" resultMap="ResultMapWithBLOBs">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from issues
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByExample" parameterType="io.metersphere.base.domain.IssuesExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from issues
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from issues
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from issues
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.IssuesExample">
delete from issues
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.Issues">
insert into issues (id, title, `status`,
create_time, update_time, reporter,
lastmodify, platform, description
)
values (#{id,jdbcType=VARCHAR}, #{title,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{reporter,jdbcType=VARCHAR},
#{lastmodify,jdbcType=VARCHAR}, #{platform,jdbcType=VARCHAR}, #{description,jdbcType=LONGVARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Issues">
insert into issues
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="title != null">
title,
</if>
<if test="status != null">
`status`,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateTime != null">
update_time,
</if>
<if test="reporter != null">
reporter,
</if>
<if test="lastmodify != null">
lastmodify,
</if>
<if test="platform != null">
platform,
</if>
<if test="description != null">
description,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="title != null">
#{title,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=BIGINT},
</if>
<if test="reporter != null">
#{reporter,jdbcType=VARCHAR},
</if>
<if test="lastmodify != null">
#{lastmodify,jdbcType=VARCHAR},
</if>
<if test="platform != null">
#{platform,jdbcType=VARCHAR},
</if>
<if test="description != null">
#{description,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.IssuesExample" resultType="java.lang.Long">
select count(*) from issues
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update issues
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.title != null">
title = #{record.title,jdbcType=VARCHAR},
</if>
<if test="record.status != null">
`status` = #{record.status,jdbcType=VARCHAR},
</if>
<if test="record.createTime != null">
create_time = #{record.createTime,jdbcType=BIGINT},
</if>
<if test="record.updateTime != null">
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
<if test="record.reporter != null">
reporter = #{record.reporter,jdbcType=VARCHAR},
</if>
<if test="record.lastmodify != null">
lastmodify = #{record.lastmodify,jdbcType=VARCHAR},
</if>
<if test="record.platform != null">
platform = #{record.platform,jdbcType=VARCHAR},
</if>
<if test="record.description != null">
description = #{record.description,jdbcType=LONGVARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update issues
set id = #{record.id,jdbcType=VARCHAR},
title = #{record.title,jdbcType=VARCHAR},
`status` = #{record.status,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT},
reporter = #{record.reporter,jdbcType=VARCHAR},
lastmodify = #{record.lastmodify,jdbcType=VARCHAR},
platform = #{record.platform,jdbcType=VARCHAR},
description = #{record.description,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update issues
set id = #{record.id,jdbcType=VARCHAR},
title = #{record.title,jdbcType=VARCHAR},
`status` = #{record.status,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT},
reporter = #{record.reporter,jdbcType=VARCHAR},
lastmodify = #{record.lastmodify,jdbcType=VARCHAR},
platform = #{record.platform,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.Issues">
update issues
<set>
<if test="title != null">
title = #{title,jdbcType=VARCHAR},
</if>
<if test="status != null">
`status` = #{status,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=BIGINT},
</if>
<if test="reporter != null">
reporter = #{reporter,jdbcType=VARCHAR},
</if>
<if test="lastmodify != null">
lastmodify = #{lastmodify,jdbcType=VARCHAR},
</if>
<if test="platform != null">
platform = #{platform,jdbcType=VARCHAR},
</if>
<if test="description != null">
description = #{description,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.Issues">
update issues
set title = #{title,jdbcType=VARCHAR},
`status` = #{status,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT},
reporter = #{reporter,jdbcType=VARCHAR},
lastmodify = #{lastmodify,jdbcType=VARCHAR},
platform = #{platform,jdbcType=VARCHAR},
description = #{description,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.Issues">
update issues
set title = #{title,jdbcType=VARCHAR},
`status` = #{status,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT},
reporter = #{reporter,jdbcType=VARCHAR},
lastmodify = #{lastmodify,jdbcType=VARCHAR},
platform = #{platform,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -5,7 +5,6 @@
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="test_case_id" jdbcType="VARCHAR" property="testCaseId" />
<result column="issues_id" jdbcType="VARCHAR" property="issuesId" />
<result column="platform" jdbcType="VARCHAR" property="platform" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -66,7 +65,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, test_case_id, issues_id, platform
id, test_case_id, issues_id
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.TestCaseIssuesExample" resultMap="BaseResultMap">
select
@ -99,10 +98,10 @@
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.TestCaseIssues">
insert into test_case_issues (id, test_case_id, issues_id,
platform)
values (#{id,jdbcType=VARCHAR}, #{testCaseId,jdbcType=VARCHAR}, #{issuesId,jdbcType=VARCHAR},
#{platform,jdbcType=VARCHAR})
insert into test_case_issues (id, test_case_id, issues_id
)
values (#{id,jdbcType=VARCHAR}, #{testCaseId,jdbcType=VARCHAR}, #{issuesId,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestCaseIssues">
insert into test_case_issues
@ -116,9 +115,6 @@
<if test="issuesId != null">
issues_id,
</if>
<if test="platform != null">
platform,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -130,9 +126,6 @@
<if test="issuesId != null">
#{issuesId,jdbcType=VARCHAR},
</if>
<if test="platform != null">
#{platform,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.TestCaseIssuesExample" resultType="java.lang.Long">
@ -153,9 +146,6 @@
<if test="record.issuesId != null">
issues_id = #{record.issuesId,jdbcType=VARCHAR},
</if>
<if test="record.platform != null">
platform = #{record.platform,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -165,8 +155,7 @@
update test_case_issues
set id = #{record.id,jdbcType=VARCHAR},
test_case_id = #{record.testCaseId,jdbcType=VARCHAR},
issues_id = #{record.issuesId,jdbcType=VARCHAR},
platform = #{record.platform,jdbcType=VARCHAR}
issues_id = #{record.issuesId,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -180,17 +169,13 @@
<if test="issuesId != null">
issues_id = #{issuesId,jdbcType=VARCHAR},
</if>
<if test="platform != null">
platform = #{platform,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.TestCaseIssues">
update test_case_issues
set test_case_id = #{testCaseId,jdbcType=VARCHAR},
issues_id = #{issuesId,jdbcType=VARCHAR},
platform = #{platform,jdbcType=VARCHAR}
issues_id = #{issuesId,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -132,7 +132,6 @@
LEFT JOIN user ON user.id = r.user_id
<where>
r.id = #{id}
AND r.status != 'Debug'
</where>
ORDER BY r.update_time DESC
</select>

View File

@ -0,0 +1,11 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.Issues;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtIssuesMapper {
List<Issues> getIssues(@Param("caseId") String caseId, @Param("platform") String platform);
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="io.metersphere.base.mapper.ext.ExtIssuesMapper">
<select id="getIssues" resultType="io.metersphere.base.domain.Issues">
select issues.*
from test_case_issues, issues
where test_case_issues.issues_id = issues.id
and test_case_issues.test_case_id = #{caseId}
and issues.platform = #{platform};
</select>
</mapper>

View File

@ -11,4 +11,6 @@ public interface ExtProjectMapper {
List<ProjectDTO> getProjectWithWorkspace(@Param("proRequest") ProjectRequest request);
List<String> getProjectIdByWorkspaceId(String workspaceId);
int removeIssuePlatform(@Param("platform") String platform, @Param("orgId") String orgId);
}

View File

@ -4,9 +4,9 @@
<select id="getProjectWithWorkspace" resultType="io.metersphere.dto.ProjectDTO">
select p.id, p.workspace_id, p.name, p.description, p.update_time,
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key
from project p
join workspace w on p.workspace_id = w.id
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key
from project p
join workspace w on p.workspace_id = w.id
<where>
<if test="proRequest.name != null and proRequest.name != ''">
and p.name like #{proRequest.name, jdbcType=VARCHAR}
@ -28,4 +28,22 @@
where workspace_id = #{workspaceId}
</select>
<update id="removeIssuePlatform">
update project
<set>
<if test="platform == 'Jira'">
jira_key = null
</if>
<if test="platform == 'Tapd'">
tapd_id = null
</if>
</set>
where project.id in (select id from (select id
from project
where workspace_id in
(select workspace.id
from workspace
where organization_id = #{orgId})) as a)
</update>
</mapper>

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum IssuesManagePlatform {
Tapd, Jira
Tapd, Jira, Local
}

View File

@ -0,0 +1,5 @@
package io.metersphere.commons.constants;
public enum SsoMode {
CAS,LOCAL
}

View File

@ -1,5 +1,6 @@
package io.metersphere.commons.utils;
import io.metersphere.commons.exception.MSException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@ -22,7 +23,9 @@ public class RestTemplateUtils {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
return responseEntity.getBody();
} catch (Exception e) {
throw new RuntimeException("调用接口失败", e);
LogUtil.error(e.getMessage(), e);
MSException.throwException("Tapd接口调用失败" + e.getMessage());
return null;
}
}
@ -34,7 +37,9 @@ public class RestTemplateUtils {
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
return responseEntity.getBody();
} catch (Exception e) {
throw new RuntimeException("调用接口失败", e);
LogUtil.error(e.getMessage(), e);
MSException.throwException("Tapd接口调用失败" + e.getMessage());
return null;
}
}

View File

@ -0,0 +1,33 @@
package io.metersphere.commons.utils;
import org.apache.commons.io.IOUtils;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.nio.charset.StandardCharsets;
public class ScriptEngineUtils {
private static final String ENGINE_NAME = "graal.js";
private static ScriptEngine engine;
static {
final ScriptEngineManager engineManager = new ScriptEngineManager();
engine = engineManager.getEngineByName(ENGINE_NAME);
try {
String script = IOUtils.toString(ScriptEngineUtils.class.getResource("/javascript/func.js"), StandardCharsets.UTF_8);
engine.eval(script);
} catch (Exception e) {
LogUtil.error(e);
}
}
public static String calculate(String input) {
try {
return engine.eval("calculate('" + input + "')").toString();
} catch (ScriptException e) {
LogUtil.error(e);
return input;
}
}
}

View File

@ -0,0 +1,53 @@
package io.metersphere.commons.utils;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import java.util.Map;
public class ShiroUtils {
public static void loadBaseFilterChain(Map<String, String> filterChainDefinitionMap){
filterChainDefinitionMap.put("/resource/**", "anon");
filterChainDefinitionMap.put("/signin", "anon");
filterChainDefinitionMap.put("/ldap/signin", "anon");
filterChainDefinitionMap.put("/ldap/open", "anon");
filterChainDefinitionMap.put("/isLogin", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
// for swagger
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-ui/**", "anon");
filterChainDefinitionMap.put("/v3/api-docs/**", "anon");
filterChainDefinitionMap.put("/403", "anon");
filterChainDefinitionMap.put("/anonymous/**", "anon");
}
public static Cookie getSessionIdCookie(){
SimpleCookie sessionIdCookie = new SimpleCookie();
sessionIdCookie.setPath("/");
sessionIdCookie.setName("MS_SESSION_ID");
return sessionIdCookie;
}
public static SessionManager getSessionManager(Long sessionTimeout, CacheManager cacheManager){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionIdCookie(ShiroUtils.getSessionIdCookie());
sessionManager.setGlobalSessionTimeout(sessionTimeout * 1000);// 超时时间ms
sessionManager.setCacheManager(cacheManager);
//sessionManager.setSessionIdCookieEnabled(true);
return sessionManager;
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.config;
import io.metersphere.commons.utils.ShiroUtils;
import io.metersphere.security.ApiKeyFilter;
import io.metersphere.security.LoginFilter;
import io.metersphere.security.ShiroDBRealm;
@ -9,9 +10,8 @@ import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
@ -29,6 +29,7 @@ import java.util.Map;
import java.util.Objects;
@Configuration
@ConditionalOnProperty(prefix="sso",name = "mode", havingValue = "local", matchIfMissing = true)
public class ShiroConfig implements EnvironmentAware {
private Environment env;
@ -42,26 +43,8 @@ public class ShiroConfig implements EnvironmentAware {
shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
filterChainDefinitionMap.put("/resource/**", "anon");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/signin", "anon");
filterChainDefinitionMap.put("/ldap/signin", "anon");
filterChainDefinitionMap.put("/ldap/open", "anon");
filterChainDefinitionMap.put("/isLogin", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/fonts/**", "anon");
// for swagger
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-ui/**", "anon");
filterChainDefinitionMap.put("/v3/api-docs/**", "anon");
filterChainDefinitionMap.put("/403", "anon");
filterChainDefinitionMap.put("/anonymous/**", "anon");
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
filterChainDefinitionMap.put("/**", "apikey, authc");
return shiroFilterFactoryBean;
}
@ -120,18 +103,7 @@ public class ShiroConfig implements EnvironmentAware {
@Bean
public SessionManager sessionManager(MemoryConstrainedCacheManager memoryConstrainedCacheManager) {
Long sessionTimeout = env.getProperty("session.timeout", Long.class, 1800L); // 默认1800s, 半个小时
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setGlobalSessionTimeout(sessionTimeout * 1000);// 超时时间ms
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
SimpleCookie sessionIdCookie = new SimpleCookie();
sessionManager.setSessionIdCookie(sessionIdCookie);
sessionIdCookie.setPath("/");
sessionIdCookie.setName("MS_SESSION_ID");
sessionManager.setCacheManager(memoryConstrainedCacheManager);
return sessionManager;
return ShiroUtils.getSessionManager(sessionTimeout, memoryConstrainedCacheManager);
}
/**

View File

@ -1,6 +1,7 @@
package io.metersphere.controller;
import io.metersphere.commons.utils.SessionUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@ -22,4 +23,15 @@ public class IndexController {
return "redirect:/";
}
}
@GetMapping(value = "/sso/login")
public String ossLogin() {
return "redirect:/";
}
@GetMapping(value = "/sso/logout")
public void ossLogout() {
SecurityUtils.getSubject().logout();
}
}

View File

@ -1,10 +1,15 @@
package io.metersphere.controller;
import io.metersphere.commons.constants.SsoMode;
import io.metersphere.commons.constants.UserSource;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.LoginRequest;
import io.metersphere.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -15,11 +20,21 @@ public class LoginController {
@Resource
private UserService userService;
@Resource
private Environment env;
@GetMapping(value = "/isLogin")
public ResultHolder isLogin() {
if (SecurityUtils.getSubject().isAuthenticated()) {
return ResultHolder.success(LocaleContextHolder.getLocale());
SessionUser user = SessionUtils.getUser();
if (StringUtils.isBlank(user.getLanguage())) {
user.setLanguage(LocaleContextHolder.getLocale().toString());
}
return ResultHolder.success(user);
}
String ssoMode = env.getProperty("sso.mode");
if (ssoMode != null && StringUtils.equalsIgnoreCase(SsoMode.CAS.name(), ssoMode)) {
return ResultHolder.error("sso");
}
return ResultHolder.error("");
}
@ -30,9 +45,19 @@ public class LoginController {
return userService.login(request);
}
@GetMapping(value = "/currentUser")
public ResultHolder currentUser() {
return ResultHolder.success(SecurityUtils.getSubject().getSession().getAttribute("user"));
}
@GetMapping(value = "/signout")
public ResultHolder logout() {
SecurityUtils.getSubject().logout();
String ssoMode = env.getProperty("sso.mode");
if (ssoMode != null && StringUtils.equalsIgnoreCase(SsoMode.CAS.name(), ssoMode)) {
return ResultHolder.error("sso");
} else {
SecurityUtils.getSubject().logout();
}
return ResultHolder.success("");
}
@ -42,5 +67,4 @@ public class LoginController {
return userService.getDefaultLanguage();
}
}

View File

@ -1,6 +1,7 @@
package io.metersphere.dto;
import io.metersphere.base.domain.Role;
import io.metersphere.base.domain.User;
import io.metersphere.base.domain.UserRole;
import lombok.Getter;
import lombok.Setter;
@ -10,28 +11,7 @@ import java.util.List;
@Getter
@Setter
public class UserDTO {
private String id;
private String name;
private String email;
private String phone;
private String status;
private String source;
private Long createTime;
private Long updateTime;
private String language;
private String lastWorkspaceId;
private String lastOrganizationId;
public class UserDTO extends User {
private List<Role> roles = new ArrayList<>();

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.ScriptEngineUtils;
import io.metersphere.config.KafkaProperties;
import io.metersphere.i18n.Translator;
import io.metersphere.performance.engine.EngineContext;
@ -377,7 +378,9 @@ public class JmeterDocumentParser implements DocumentParser {
elementProp.setAttribute("name", jsonObject.getString("name"));
elementProp.setAttribute("elementType", "Argument");
elementProp.appendChild(createStringProp(document, "Argument.name", jsonObject.getString("name")));
elementProp.appendChild(createStringProp(document, "Argument.value", jsonObject.getString("value")));
// 处理 mock data
String value = jsonObject.getString("value");
elementProp.appendChild(createStringProp(document, "Argument.value", ScriptEngineUtils.calculate(value)));
elementProp.appendChild(createStringProp(document, "Argument.metadata", "="));
item.appendChild(elementProp);
}

View File

@ -49,15 +49,16 @@ public class ShiroDBRealm extends AuthorizingRealm {
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userId = (String) principals.getPrimaryPrincipal();
return getAuthorizationInfo(userId, userService);
}
String userName = (String) principals.getPrimaryPrincipal();
public static AuthorizationInfo getAuthorizationInfo(String userId, UserService userService) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// roles 内容填充
UserDTO userDTO = userService.getUserDTO(userName);
UserDTO userDTO = userService.getUserDTO(userId);
Set<String> roles = userDTO.getRoles().stream().map(Role::getId).collect(Collectors.toSet());
authorizationInfo.setRoles(roles);
return authorizationInfo;
}
@ -148,7 +149,6 @@ public class ShiroDBRealm extends AuthorizingRealm {
if (!userService.checkUserPassword(userId, password)) {
throw new IncorrectCredentialsException(Translator.get("password_is_incorrect"));
}
//
SessionUser sessionUser = SessionUser.fromUser(user);
SessionUtils.putUser(sessionUser);
return new SimpleAuthenticationInfo(userId, password, getName());

View File

@ -3,6 +3,7 @@ package io.metersphere.service;
import io.metersphere.base.domain.ServiceIntegration;
import io.metersphere.base.domain.ServiceIntegrationExample;
import io.metersphere.base.mapper.ServiceIntegrationMapper;
import io.metersphere.base.mapper.ext.ExtProjectMapper;
import io.metersphere.controller.request.IntegrationRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -20,6 +21,8 @@ public class IntegrationService {
@Resource
private ServiceIntegrationMapper serviceIntegrationMapper;
@Resource
private ExtProjectMapper extProjectMapper;
public ServiceIntegration save(ServiceIntegration service) {
ServiceIntegrationExample example = new ServiceIntegrationExample();
@ -63,6 +66,8 @@ public class IntegrationService {
.andOrganizationIdEqualTo(orgId)
.andPlatformEqualTo(platform);
serviceIntegrationMapper.deleteByExample(example);
// 删除项目关联的id/key
extProjectMapper.removeIssuePlatform(platform, orgId);
}
public List<ServiceIntegration> getAll(String orgId) {

View File

@ -1,177 +0,0 @@
package io.metersphere.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.TestCaseIssuesMapper;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.RestTemplateUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.ResultHolder;
import io.metersphere.controller.request.IntegrationRequest;
import io.metersphere.track.dto.IssuesDTO;
import io.metersphere.track.request.testcase.IssuesRequest;
import io.metersphere.track.service.TestCaseService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class IssuesService {
@Resource
private IntegrationService integrationService;
@Resource
private TestCaseIssuesMapper testCaseIssuesMapper;
@Resource
private ProjectService projectService;
@Resource
private TestCaseService testCaseService;
public void testAuth() {
String url = "https://api.tapd.cn/quickstart/testauth";
ResultHolder call = call(url);
System.out.println(call.getData());
}
public void addIssues(IssuesRequest issuesRequest) {
String url = "https://api.tapd.cn/bugs";
String testCaseId = issuesRequest.getTestCaseId();
String tapdId = getTapdProjectId(testCaseId);
if (StringUtils.isBlank(tapdId)) {
MSException.throwException("未关联Tapd 项目ID");
}
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("title", issuesRequest.getTitle());
paramMap.add("workspace_id", tapdId);
paramMap.add("description", issuesRequest.getContent());
ResultHolder result = call(url, HttpMethod.POST, paramMap);
String listJson = JSON.toJSONString(result.getData());
JSONObject jsonObject = JSONObject.parseObject(listJson);
String issuesId = jsonObject.getObject("Bug", IssuesDTO.class).getId();
// 用例与第三方缺陷平台中的缺陷关联
TestCaseIssues issues = new TestCaseIssues();
issues.setId(UUID.randomUUID().toString());
issues.setIssuesId(issuesId);
issues.setTestCaseId(testCaseId);
testCaseIssuesMapper.insert(issues);
}
private ResultHolder call(String url) {
return call(url, HttpMethod.GET, null);
}
private ResultHolder call(String url, HttpMethod httpMethod, Object params) {
String responseJson;
String config = tapdConfig();
JSONObject object = JSON.parseObject(config);
if (object == null) {
MSException.throwException("tapd config is null");
}
String account = object.getString("account");
String password = object.getString("password");
HttpHeaders header = tapdAuth(account, password);
if (httpMethod.equals(HttpMethod.GET)) {
responseJson = RestTemplateUtils.get(url, header);
} else {
responseJson = RestTemplateUtils.post(url, params, header);
}
ResultHolder result = JSON.parseObject(responseJson, ResultHolder.class);
if (!result.isSuccess()) {
MSException.throwException(result.getMessage());
}
return JSON.parseObject(responseJson, ResultHolder.class);
}
private String tapdConfig() {
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
IntegrationRequest request = new IntegrationRequest();
if (StringUtils.isBlank(orgId)) {
MSException.throwException("organization id is null");
}
request.setOrgId(orgId);
request.setPlatform(IssuesManagePlatform.Tapd.toString());
ServiceIntegration integration = integrationService.get(request);
return integration.getConfiguration();
}
private HttpHeaders tapdAuth(String apiUser, String password) {
String authKey = EncryptUtils.base64Encoding(apiUser + ":" + password);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + authKey);
return headers;
}
public IssuesDTO getTapdIssues(String projectId, String issuesId) {
String url = "https://api.tapd.cn/bugs?workspace_id=" + projectId + "&id=" + issuesId;
ResultHolder call = call(url);
String listJson = JSON.toJSONString(call.getData());
if (StringUtils.equals(Boolean.FALSE.toString(), listJson)) {
return new IssuesDTO();
}
JSONObject jsonObject = JSONObject.parseObject(listJson);
return jsonObject.getObject("Bug", IssuesDTO.class);
}
public List<IssuesDTO> getIssues(String caseId) {
List<IssuesDTO> list = new ArrayList<>();
String tapdId = getTapdProjectId(caseId);
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(caseId);
List<TestCaseIssues> issues = testCaseIssuesMapper.selectByExample(example);
List<String> issuesIds = issues.stream().map(issue -> issue.getIssuesId()).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
IssuesDTO dto = getTapdIssues(tapdId, issuesId);
if (StringUtils.isBlank(dto.getId())) {
// 缺陷不存在解除用例和缺陷的关联
TestCaseIssuesExample issuesExample = new TestCaseIssuesExample();
issuesExample.createCriteria()
.andTestCaseIdEqualTo(caseId)
.andIssuesIdEqualTo(issuesId);
testCaseIssuesMapper.deleteByExample(issuesExample);
} else {
list.add(dto);
}
});
return list;
}
public String getTapdProjectId(String testCaseId) {
TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId);
Project project = projectService.getProjectById(testCase.getProjectId());
return project.getTapdId();
}
}

View File

@ -5,7 +5,6 @@ import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.LoadTestMapper;
import io.metersphere.base.mapper.TestResourceMapper;
import io.metersphere.base.mapper.TestResourcePoolMapper;
import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.constants.ResourceStatusEnum;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil;
@ -25,7 +24,6 @@ import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@ -70,11 +68,9 @@ public class TestResourcePoolService {
}
public void checkTestStatus(String testResourcePoolId) {
List list = Arrays.asList(PerformanceTestStatus.Running, PerformanceTestStatus.Starting, PerformanceTestStatus.Error);
LoadTestExample example = new LoadTestExample();
example.createCriteria()
.andTestResourcePoolIdEqualTo(testResourcePoolId)
.andStatusIn(list);
.andTestResourcePoolIdEqualTo(testResourcePoolId);
if (loadTestMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("test_resource_pool_is_use"));
}

View File

@ -163,6 +163,17 @@ public class UserService {
userMapper.insertSelective(user);
}
public void createOssUser(User user) {
user.setCreateTime(System.currentTimeMillis());
user.setUpdateTime(System.currentTimeMillis());
user.setStatus(UserStatus.NORMAL);
if (StringUtils.isBlank(user.getEmail())) {
user.setEmail(user.getId() + "@metershpere.io");
}
userMapper.insertSelective(user);
}
private void checkEmailIsExist(String email) {
UserExample userExample = new UserExample();
UserExample.Criteria criteria = userExample.createCriteria();

View File

@ -1,7 +1,7 @@
package io.metersphere.track.controller;
import io.metersphere.service.IssuesService;
import io.metersphere.track.dto.IssuesDTO;
import io.metersphere.base.domain.Issues;
import io.metersphere.track.service.IssuesService;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.springframework.web.bind.annotation.*;
@ -21,7 +21,18 @@ public class TestCaseIssuesController {
}
@GetMapping("/get/{id}")
public List<IssuesDTO> getIssues(@PathVariable String id) {
public List<Issues> getIssues(@PathVariable String id) {
return issuesService.getIssues(id);
}
@GetMapping("/auth/{platform}")
public void testAuth(@PathVariable String platform) {
issuesService.testAuth(platform);
}
@GetMapping("/close/{id}")
public void closeLocalIssue(@PathVariable String id) {
issuesService.closeLocalIssue(id);
}
}

View File

@ -86,12 +86,32 @@ public class ReportResultComponent extends ReportComponent {
moduleResult.setCaseCount(0);
moduleResult.setPassCount(0);
moduleResult.setIssuesCount(0);
moduleResult.setFailureCount(0);
moduleResult.setBlockingCount(0);
moduleResult.setPrepareCount(0);
moduleResult.setSkipCount(0);
moduleResult.setUnderwayCount(0);
moduleResult.setModuleId(rootNodeId);
}
moduleResult.setCaseCount(moduleResult.getCaseCount() + 1);
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Pass.name())) {
moduleResult.setPassCount(moduleResult.getPassCount() + 1);
}
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Prepare.name())) {
moduleResult.setPrepareCount(moduleResult.getPrepareCount() + 1);
}
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Underway.name())) {
moduleResult.setUnderwayCount(moduleResult.getUnderwayCount() + 1);
}
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Failure.name())) {
moduleResult.setFailureCount(moduleResult.getFailureCount() + 1);
}
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Skip.name())) {
moduleResult.setSkipCount(moduleResult.getSkipCount() + 1);
}
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Blocking.name())) {
moduleResult.setBlockingCount(moduleResult.getBlockingCount() + 1);
}
if (StringUtils.isNotBlank(testCase.getIssues())) {
if (JSON.parseObject(testCase.getIssues()).getBoolean("hasIssues")) {
moduleResult.setIssuesCount(moduleResult.getIssuesCount() + 1);

View File

@ -12,4 +12,9 @@ public class TestCaseReportModuleResultDTO {
private Integer passCount;
private Double passRate;
private Integer issuesCount;
private Integer prepareCount;
private Integer skipCount;
private Integer failureCount;
private Integer blockingCount;
private Integer underwayCount;
}

View File

@ -0,0 +1,494 @@
package io.metersphere.track.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.IssuesMapper;
import io.metersphere.base.mapper.TestCaseIssuesMapper;
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.RestTemplateUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.ResultHolder;
import io.metersphere.controller.request.IntegrationRequest;
import io.metersphere.service.IntegrationService;
import io.metersphere.service.ProjectService;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class IssuesService {
@Resource
private IntegrationService integrationService;
@Resource
private TestCaseIssuesMapper testCaseIssuesMapper;
@Resource
private ProjectService projectService;
@Resource
private TestCaseService testCaseService;
@Resource
private IssuesMapper issuesMapper;
@Resource
private ExtIssuesMapper extIssuesMapper;
public void testAuth(String platform) {
if (StringUtils.equals(platform, IssuesManagePlatform.Tapd.toString())) {
try {
String tapdConfig = platformConfig(IssuesManagePlatform.Tapd.toString());
JSONObject object = JSON.parseObject(tapdConfig);
String account = object.getString("account");
String password = object.getString("password");
HttpHeaders headers = auth(account, password);
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange("https://api.tapd.cn/quickstart/testauth", HttpMethod.GET, requestEntity, String.class);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("验证失败!");
}
} else {
try {
String config = platformConfig(IssuesManagePlatform.Jira.toString());
JSONObject object = JSON.parseObject(config);
String account = object.getString("account");
String password = object.getString("password");
String url = object.getString("url");
HttpHeaders headers = auth(account, password);
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange(url + "rest/api/2/issue/createmeta", HttpMethod.GET, requestEntity, String.class);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("验证失败!");
}
}
}
private ResultHolder call(String url) {
return call(url, HttpMethod.GET, null);
}
private ResultHolder call(String url, HttpMethod httpMethod, Object params) {
String responseJson;
String config = platformConfig(IssuesManagePlatform.Tapd.toString());
JSONObject object = JSON.parseObject(config);
if (object == null) {
MSException.throwException("tapd config is null");
}
String account = object.getString("account");
String password = object.getString("password");
HttpHeaders header = auth(account, password);
if (httpMethod.equals(HttpMethod.GET)) {
responseJson = RestTemplateUtils.get(url, header);
} else {
responseJson = RestTemplateUtils.post(url, params, header);
}
ResultHolder result = JSON.parseObject(responseJson, ResultHolder.class);
if (!result.isSuccess()) {
MSException.throwException(result.getMessage());
}
return JSON.parseObject(responseJson, ResultHolder.class);
}
private String platformConfig(String platform) {
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
IntegrationRequest request = new IntegrationRequest();
if (StringUtils.isBlank(orgId)) {
MSException.throwException("organization id is null");
}
request.setOrgId(orgId);
request.setPlatform(platform);
ServiceIntegration integration = integrationService.get(request);
return integration.getConfiguration();
}
private HttpHeaders auth(String apiUser, String password) {
String authKey = EncryptUtils.base64Encoding(apiUser + ":" + password);
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + authKey);
return headers;
}
public void addIssues(IssuesRequest issuesRequest) {
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
boolean tapd = isIntegratedPlatform(orgId, IssuesManagePlatform.Tapd.toString());
boolean jira = isIntegratedPlatform(orgId, IssuesManagePlatform.Jira.toString());
String tapdId = getTapdProjectId(issuesRequest.getTestCaseId());
String jiraKey = getJiraProjectKey(issuesRequest.getTestCaseId());
if (tapd) {
// 是否关联了项目
if (StringUtils.isNotBlank(tapdId)) {
addTapdIssues(issuesRequest);
}
}
if (jira) {
if (StringUtils.isNotBlank(jiraKey)) {
addJiraIssues(issuesRequest);
}
}
if (StringUtils.isBlank(tapdId) && StringUtils.isBlank(jiraKey)) {
addLocalIssues(issuesRequest);
}
}
public void addTapdIssues(IssuesRequest issuesRequest) {
String url = "https://api.tapd.cn/bugs";
String testCaseId = issuesRequest.getTestCaseId();
String tapdId = getTapdProjectId(testCaseId);
if (StringUtils.isBlank(tapdId)) {
MSException.throwException("未关联Tapd 项目ID");
}
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("title", issuesRequest.getTitle());
paramMap.add("workspace_id", tapdId);
paramMap.add("description", issuesRequest.getContent());
ResultHolder result = call(url, HttpMethod.POST, paramMap);
String listJson = JSON.toJSONString(result.getData());
JSONObject jsonObject = JSONObject.parseObject(listJson);
String issuesId = jsonObject.getObject("Bug", Issues.class).getId();
// 用例与第三方缺陷平台中的缺陷关联
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(issuesId);
testCaseIssues.setTestCaseId(testCaseId);
testCaseIssuesMapper.insert(testCaseIssues);
// 插入缺陷表
Issues issues = new Issues();
issues.setId(issuesId);
issues.setPlatform(IssuesManagePlatform.Tapd.toString());
issuesMapper.insert(issues);
}
public void addJiraIssues(IssuesRequest issuesRequest) {
String config = platformConfig(IssuesManagePlatform.Jira.toString());
JSONObject object = JSON.parseObject(config);
if (object == null) {
MSException.throwException("jira config is null");
}
String account = object.getString("account");
String password = object.getString("password");
String url = object.getString("url");
String auth = EncryptUtils.base64Encoding(account + ":" + password);
String testCaseId = issuesRequest.getTestCaseId();
String jiraKey = getJiraProjectKey(testCaseId);
if (StringUtils.isBlank(jiraKey)) {
MSException.throwException("未关联Jira 项目Key");
}
String json = "{\n" +
" \"fields\":{\n" +
" \"project\":{\n" +
" \"key\":\"" + jiraKey + "\"\n" +
" },\n" +
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\":\"" + issuesRequest.getContent() + "\",\n" +
" \"issuetype\":{\n" +
" \"id\":\"10009\",\n" +
" \"name\":\"Defect\"\n" +
" }\n" +
" }\n" +
"}";
String result = addJiraIssue(url, auth, json);
JSONObject jsonObject = JSON.parseObject(result);
String id = jsonObject.getString("id");
// 用例与第三方缺陷平台中的缺陷关联
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(id);
testCaseIssues.setTestCaseId(testCaseId);
testCaseIssuesMapper.insert(testCaseIssues);
// 插入缺陷表
Issues issues = new Issues();
issues.setId(id);
issues.setPlatform(IssuesManagePlatform.Jira.toString());
issuesMapper.insert(issues);
}
private String addJiraIssue(String url, String auth, String json) {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Authorization", "Basic " + auth);
requestHeaders.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);
//HttpEntity
HttpEntity<String> requestEntity = new HttpEntity<>(json, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
//post
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(url + "/rest/api/2/issue", HttpMethod.POST, requestEntity, String.class);
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("调用Jira接口创建缺陷失败");
}
return responseEntity.getBody();
}
public void addLocalIssues(IssuesRequest request) {
SessionUser user = SessionUtils.getUser();
String id = UUID.randomUUID().toString();
Issues issues = new Issues();
issues.setId(id);
issues.setStatus("new");
issues.setReporter(user.getId());
issues.setTitle(request.getTitle());
issues.setDescription(request.getContent());
issues.setCreateTime(System.currentTimeMillis());
issues.setUpdateTime(System.currentTimeMillis());
issues.setPlatform(IssuesManagePlatform.Local.toString());
issuesMapper.insert(issues);
TestCaseIssues testCaseIssues = new TestCaseIssues();
testCaseIssues.setId(UUID.randomUUID().toString());
testCaseIssues.setIssuesId(id);
testCaseIssues.setTestCaseId(request.getTestCaseId());
testCaseIssuesMapper.insert(testCaseIssues);
}
public Issues getTapdIssues(String projectId, String issuesId) {
String url = "https://api.tapd.cn/bugs?workspace_id=" + projectId + "&id=" + issuesId;
ResultHolder call = call(url);
String listJson = JSON.toJSONString(call.getData());
if (StringUtils.equals(Boolean.FALSE.toString(), listJson)) {
return new Issues();
}
JSONObject jsonObject = JSONObject.parseObject(listJson);
return jsonObject.getObject("Bug", Issues.class);
}
public Issues getJiraIssues(HttpHeaders headers, String url, String issuesId) {
HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
//post
ResponseEntity<String> responseEntity;
Issues issues = new Issues();
try {
responseEntity = restTemplate.exchange(url + "/rest/api/2/issue/" + issuesId, HttpMethod.GET, requestEntity, String.class);
String body = responseEntity.getBody();
JSONObject obj = JSONObject.parseObject(body);
JSONObject fields = (JSONObject) obj.get("fields");
JSONObject statusObj = (JSONObject) fields.get("status");
JSONObject statusCategory = (JSONObject) statusObj.get("statusCategory");
String id = obj.getString("id");
String title = fields.getString("summary");
String description = fields.getString("description");
String status = statusCategory.getString("key");
issues.setId(id);
issues.setTitle(title);
issues.setDescription(description);
issues.setStatus(status);
issues.setPlatform(IssuesManagePlatform.Jira.toString());
} catch (HttpClientErrorException.NotFound e) {
LogUtil.error(e.getStackTrace(), e);
return new Issues();
} catch (HttpClientErrorException.Unauthorized e) {
LogUtil.error(e.getStackTrace(), e);
MSException.throwException("获取Jira缺陷失败检查Jira配置信息");
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
MSException.throwException("调用Jira接口获取缺陷失败");
}
return issues;
}
public List<Issues> getIssues(String caseId) {
List<Issues> list = new ArrayList<>();
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
boolean tapd = isIntegratedPlatform(orgId, IssuesManagePlatform.Tapd.toString());
boolean jira = isIntegratedPlatform(orgId, IssuesManagePlatform.Jira.toString());
if (tapd) {
// 是否关联了项目
String tapdId = getTapdProjectId(caseId);
if (StringUtils.isNotBlank(tapdId)) {
list.addAll(getTapdIssues(caseId));
}
}
if (jira) {
String jiraKey = getJiraProjectKey(caseId);
if (StringUtils.isNotBlank(jiraKey)) {
list.addAll(getJiraIssues(caseId));
}
}
list.addAll(getLocalIssues(caseId));
return list;
}
public List<Issues> getTapdIssues(String caseId) {
List<Issues> list = new ArrayList<>();
String tapdId = getTapdProjectId(caseId);
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(caseId);
List<Issues> issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Tapd.toString());
List<String> issuesIds = issues.stream().map(Issues::getId).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
Issues dto = getTapdIssues(tapdId, issuesId);
if (StringUtils.isBlank(dto.getId())) {
// 缺陷不存在解除用例和缺陷的关联
TestCaseIssuesExample issuesExample = new TestCaseIssuesExample();
issuesExample.createCriteria()
.andTestCaseIdEqualTo(caseId)
.andIssuesIdEqualTo(issuesId);
testCaseIssuesMapper.deleteByExample(issuesExample);
issuesMapper.deleteByPrimaryKey(issuesId);
} else {
dto.setPlatform(IssuesManagePlatform.Tapd.toString());
// 缺陷状态为 关闭则不显示
if (!StringUtils.equals("closed", dto.getStatus())) {
list.add(dto);
}
}
});
return list;
}
public List<Issues> getJiraIssues(String caseId) {
List<Issues> list = new ArrayList<>();
String config = platformConfig(IssuesManagePlatform.Jira.toString());
JSONObject object = JSON.parseObject(config);
if (object == null) {
MSException.throwException("tapd config is null");
}
String account = object.getString("account");
String password = object.getString("password");
String url = object.getString("url");
HttpHeaders headers = auth(account, password);
TestCaseIssuesExample example = new TestCaseIssuesExample();
example.createCriteria().andTestCaseIdEqualTo(caseId);
List<Issues> issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Jira.toString());
List<String> issuesIds = issues.stream().map(Issues::getId).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
Issues dto = getJiraIssues(headers, url, issuesId);
if (StringUtils.isBlank(dto.getId())) {
// 缺陷不存在解除用例和缺陷的关联
TestCaseIssuesExample issuesExample = new TestCaseIssuesExample();
issuesExample.createCriteria()
.andTestCaseIdEqualTo(caseId)
.andIssuesIdEqualTo(issuesId);
testCaseIssuesMapper.deleteByExample(issuesExample);
issuesMapper.deleteByPrimaryKey(issuesId);
} else {
// 缺陷状态为 完成则不显示
if (!StringUtils.equals("done", dto.getStatus())) {
list.add(dto);
}
}
});
return list;
}
public List<Issues> getLocalIssues(String caseId) {
List<Issues> list = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Local.toString());
List<Issues> issues = list.stream()
.filter(l -> !StringUtils.equals(l.getStatus(), "closed"))
.collect(Collectors.toList());
return issues;
}
public String getTapdProjectId(String testCaseId) {
TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId);
Project project = projectService.getProjectById(testCase.getProjectId());
return project.getTapdId();
}
public String getJiraProjectKey(String testCaseId) {
TestCaseWithBLOBs testCase = testCaseService.getTestCase(testCaseId);
Project project = projectService.getProjectById(testCase.getProjectId());
return project.getJiraKey();
}
/**
* 是否关联平台
*/
public boolean isIntegratedPlatform(String orgId, String platform) {
IntegrationRequest request = new IntegrationRequest();
request.setPlatform(platform);
request.setOrgId(orgId);
ServiceIntegration integration = integrationService.get(request);
return StringUtils.isNotBlank(integration.getId());
}
public void closeLocalIssue(String issueId) {
Issues issues = new Issues();
issues.setId(issueId);
issues.setStatus("closed");
issuesMapper.updateByPrimaryKeySelective(issues);
}
}

@ -1 +1 @@
Subproject commit 9b9208204ce138e2cb49d9549eac140019293160
Subproject commit 8eff343619df1572e1cded52f173257ef4b518a1

View File

@ -13,6 +13,19 @@ create table if not exists test_case_issues
id varchar(50) not null
primary key,
test_case_id varchar(50) not null,
issues_id varchar(100) not null,
platform varchar(50) not null
issues_id varchar(100) not null
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
create table if not exists issues
(
id varchar(50) not null
primary key,
title varchar(50) null,
description text null,
status varchar(50) null,
create_time bigint(13) null,
update_time bigint(13) null,
reporter varchar(50) null comment 'case issues creator',
lastmodify varchar(50) null,
platform varchar(50) null
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -1,4 +1,4 @@
export HEAP=$(cat /proc/meminfo | grep MemTotal | awk '{ mem=int($2/1024/1024 * 3/4 + 0.5); metasize=int(mem/4+0.5)"g"; if(mem<1) mem=1; if (metasize == "0g") metasize="256m"; HEAP="-Xms"mem"g -Xmx"mem"g -XX:MaxMetaspaceSize="metasize; print HEAP }')
for file in ${TESTS_DIR}/*.jmx; do
echo "one shot run."
jmeter -n -t ${file} -Jserver.rmi.ssl.disable=${SSL_DISABLED}
done

View File

@ -25,6 +25,7 @@
import MsUser from "./components/common/head/HeaderUser";
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
import {saveLocalStorage} from "../common/js/utils";
export default {
name: 'app',
@ -36,7 +37,8 @@
beforeCreate() {
this.$get("/isLogin").then(response => {
if (response.data.success) {
this.$setLang(response.data.data);
this.$setLang(response.data.data.language);
saveLocalStorage(response.data);
this.auth = true;
} else {
window.location.href = "/login"

View File

@ -2,17 +2,22 @@
<div>
<el-radio-group v-model="body.type" size="mini">
<el-radio-button :disabled="isReadOnly" :label="type.KV">
{{$t('api_test.request.body_kv')}}
{{ $t('api_test.request.body_kv') }}
</el-radio-button>
<el-radio-button :disabled="isReadOnly" :label="type.RAW">
{{$t('api_test.request.body_text')}}
{{ $t('api_test.request.body_text') }}
</el-radio-button>
</el-radio-group>
<ms-dropdown :default-command="body.format" v-if="body.type == 'Raw'" :commands="modes" @command="modeChange"/>
<ms-api-variable :is-read-only="isReadOnly" :items="body.kvs" v-if="body.isKV()"/>
<ms-api-variable :is-read-only="isReadOnly"
:parameters="body.kvs"
:environment="environment"
:scenario="scenario"
:extract="extract"
:description="$t('api_test.request.parameters_desc')"
v-if="body.isKV()"/>
<div class="body-raw" v-if="body.type == 'Raw'">
<ms-code-edit :mode="body.format" :read-only="isReadOnly" :data.sync="body.raw" :modes="modes" ref="codeEdit"/>
</div>
@ -22,7 +27,7 @@
<script>
import MsApiKeyValue from "./ApiKeyValue";
import {Body, BODY_FORMAT, BODY_TYPE} from "../model/ScenarioModel";
import {Body, BODY_FORMAT, BODY_TYPE, Scenario} from "../model/ScenarioModel";
import MsCodeEdit from "../../../common/components/MsCodeEdit";
import MsDropdown from "../../../common/components/MsDropdown";
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
@ -32,6 +37,9 @@ export default {
components: {MsApiVariable, MsDropdown, MsCodeEdit, MsApiKeyValue},
props: {
body: Body,
scenario: Scenario,
environment: Object,
extract: Object,
isReadOnly: {
type: Boolean,
default: false
@ -40,45 +48,45 @@ export default {
data() {
return {
type: BODY_TYPE,
modes: ['text', 'json', 'xml', 'html']
};
},
type: BODY_TYPE,
modes: ['text', 'json', 'xml', 'html']
};
},
methods: {
modeChange(mode) {
this.body.format = mode;
}
},
methods: {
modeChange(mode) {
this.body.format = mode;
}
},
created() {
if (!this.body.type) {
this.body.type = BODY_TYPE.KV;
}
if (!this.body.format) {
this.body.format = BODY_FORMAT.TEXT;
}
created() {
if (!this.body.type) {
this.body.type = BODY_TYPE.KV;
}
if (!this.body.format) {
this.body.format = BODY_FORMAT.TEXT;
}
}
}
</script>
<style scoped>
.textarea {
margin-top: 10px;
}
.textarea {
margin-top: 10px;
}
.body-raw {
padding: 15px 0;
height: 300px;
}
.body-raw {
padding: 15px 0;
height: 300px;
}
.el-dropdown {
margin-left: 20px;
line-height: 30px;
}
.el-dropdown {
margin-left: 20px;
line-height: 30px;
}
.ace_editor {
border-radius: 5px;
}
.ace_editor {
border-radius: 5px;
}
</style>

View File

@ -34,13 +34,14 @@
</el-col>
</el-row>
</div>
<ms-api-variable-advance ref="variableAdvance" :environment="environment" :scenario="scenario" :request="request"
<ms-api-variable-advance ref="variableAdvance" :environment="environment" :scenario="scenario"
:parameters="parameters"
:current-item="currentItem"/>
</div>
</template>
<script>
import {HttpRequest, KeyValue, Scenario} from "../model/ScenarioModel";
import {KeyValue, Scenario} from "../model/ScenarioModel";
import {MOCKJS_FUNC} from "@/common/js/constants";
import MsApiVariableAdvance from "@/business/components/api/test/components/ApiVariableAdvance";
@ -51,7 +52,7 @@ export default {
keyPlaceholder: String,
valuePlaceholder: String,
description: String,
request: HttpRequest,
parameters: Array,
environment: Object,
scenario: Scenario,
isReadOnly: {
@ -63,7 +64,6 @@ export default {
data() {
return {
currentItem: null,
parameters: [],
}
},
computed: {

View File

@ -33,7 +33,7 @@
</el-col>
</el-row>
</el-tab-pane>
<el-tab-pane label="变量">
<el-tab-pane :label="$t('api_test.variable')">
<el-row>
<el-col :span="6" class="col-height">
<div v-if="environment">
@ -49,15 +49,14 @@
<el-tree :data="preRequestParams" :props="treeProps" @node-click="selectVariable"></el-tree>
</div>
</el-col>
<el-col :span="6" v-for="(itemFunc, itemIndex) in jmeterVariableFuncs" :key="itemIndex" class="col-height">
<el-col :span="18" class="col-height">
<div>
<div v-for="(func, funcIndex) in jmeterFuncs"
:key="`${itemIndex}-${funcIndex}`">
<el-row>
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
@change="methodChange(itemFunc, func)"/>
</el-row>
</div>
<h1>{{ $t('api_test.request.jmeter_func') }}</h1>
<el-table border :data="jmeterFuncs" class="adjust-table table-content" height="400">
<el-table-column prop="type" label="Type" width="150"/>
<el-table-column prop="name" label="Functions" width="250"/>
<el-table-column prop="description" label="Description"/>
</el-table>
</div>
</el-col>
</el-row>
@ -91,13 +90,13 @@
</template>
<script>
import {calculate, HttpRequest, Scenario} from "@/business/components/api/test/model/ScenarioModel";
import {calculate, Scenario} from "@/business/components/api/test/model/ScenarioModel";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
export default {
name: "MsApiVariableAdvance",
props: {
request: HttpRequest,
parameters: Array,
environment: Object,
scenario: Scenario,
currentItem: Object,
@ -163,9 +162,6 @@ export default {
this.itemValueVisible = true;
},
prepareData() {
if (this.request) {
this.parameters = this.request.parameters;
}
if (this.scenario) {
let variables = this.scenario.variables;
this.scenarioParams = [

View File

@ -0,0 +1,42 @@
<template>
<div class="script-content">
<ms-code-edit mode="java" :read-only="isReadOnly" :data.sync="beanShellProcessor.script" theme="eclipse" :modes="['java']" ref="codeEdit"/>
</div>
</template>
<script>
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
export default {
name: "MsBeanShellProcessor",
components: {MsCodeEdit},
data() {
return {
}
},
props: {
type: {
type: String,
},
isReadOnly: {
type: Boolean,
default: false
},
beanShellProcessor: {
type: Object,
}
},
}
</script>
<style scoped>
.ace_editor {
border-radius: 5px;
}
.script-content {
padding: 15px 0;
height: 300px;
}
</style>

View File

@ -46,7 +46,7 @@
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
<ms-api-variable :is-read-only="isReadOnly"
:request="request"
:parameters="request.parameters"
:environment="request.environment"
:scenario="scenario"
:extract="request.extract"
@ -56,7 +56,11 @@
<ms-api-key-value :is-read-only="isReadOnly" :suggestions="headerSuggestions" :items="request.headers"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.body')" name="body" v-if="isNotGet">
<ms-api-body :is-read-only="isReadOnly" :body="request.body"/>
<ms-api-body :is-read-only="isReadOnly"
:body="request.body"
:scenario="scenario"
:extract="request.extract"
:environment="request.environment"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.assertions.label')" name="assertions">
<ms-api-assertions :is-read-only="isReadOnly" :assertions="request.assertions"/>
@ -64,6 +68,12 @@
<el-tab-pane :label="$t('api_test.request.extract.label')" name="extract">
<ms-api-extract :is-read-only="isReadOnly" :extract="request.extract"/>
</el-tab-pane>
<el-tab-pane :label="'预执行脚本'" name="beanShellPreProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPreProcessor"/>
</el-tab-pane>
<el-tab-pane :label="'后执行脚本'" name="beanShellPostProcessor">
<ms-bean-shell-processor :is-read-only="isReadOnly" :bean-shell-processor="request.beanShellPostProcessor"/>
</el-tab-pane>
</el-tabs>
</el-form>
</template>
@ -77,10 +87,13 @@ import MsApiExtract from "../extract/ApiExtract";
import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
import {REQUEST_HEADERS} from "@/common/js/constants";
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
import MsBeanShellProcessor from "../processor/BeanShellProcessor";
export default {
name: "MsApiHttpRequestForm",
components: {MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
components: {
MsBeanShellProcessor,
MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue},
props: {
request: HttpRequest,
scenario: Scenario,

View File

@ -1,7 +1,7 @@
<template>
<div class="request-form">
<component @runDebug="runDebug" :is="component" :is-read-only="isReadOnly" :request="request" :scenario="scenario"/>
<ms-scenario-results v-loading="debugReportLoading" v-if="isCompleted" :scenarios="isCompleted ? request.debugReport.scenarios : []"/>
<ms-request-result-tail v-loading="debugReportLoading" v-if="isCompleted" :request="request.debugRequestResult ? request.debugRequestResult : {responseResult: {}, subRequestResults: []}" :scenario-name="request.debugScenario ? request.debugScenario.name : ''"/>
</div>
</template>
@ -10,10 +10,11 @@ import {Request, RequestFactory, Scenario} from "../../model/ScenarioModel";
import MsApiHttpRequestForm from "./ApiHttpRequestForm";
import MsApiDubboRequestForm from "./ApiDubboRequestForm";
import MsScenarioResults from "../../../report/components/ScenarioResults";
import MsRequestResultTail from "../../../report/components/RequestResultTail";
export default {
name: "MsApiRequestForm",
components: {MsScenarioResults, MsApiDubboRequestForm, MsApiHttpRequestForm},
components: {MsRequestResultTail, MsScenarioResults, MsApiDubboRequestForm, MsApiHttpRequestForm},
props: {
scenario: Scenario,
request: Request,
@ -66,12 +67,13 @@ export default {
try {
res = JSON.parse(report.content);
} catch (e) {
console.log(report.content)
throw e;
}
if (res) {
this.debugReportLoading = false;
this.request.debugReport = res;
this.request.debugScenario = res.scenarios[0];
this.request.debugRequestResult = this.request.debugScenario.requestResults[0];
this.deleteReport(this.debugReportId)
} else {
setTimeout(this.getReport, 2000)

View File

@ -405,12 +405,40 @@ export class ResponseHeadersAssertion extends ResponseAssertion {
}
}
export class BeanShellProcessor extends DefaultTestElement {
constructor(tag, guiclass, testclass, testname, processor) {
super(tag, guiclass, testclass, testname);
this.processor = processor || {};
this.boolProp('resetInterpreter', false);
this.stringProp('parameters');
this.stringProp('filename');
this.stringProp('script', processor.script);
}
}
export class BeanShellPreProcessor extends BeanShellProcessor {
constructor(testName, processor) {
super('BeanShellPreProcessor', 'TestBeanGUI', 'BeanShellPreProcessor', testName, processor)
}
}
export class BeanShellPostProcessor extends BeanShellProcessor {
constructor(testName, script) {
let processor = {
script: script,
};
super('BeanShellPostProcessor', 'TestBeanGUI', 'BeanShellPostProcessor', testName, processor)
}
}
export class HeaderManager extends DefaultTestElement {
constructor(testName, headers) {
super('HeaderManager', 'HeaderPanel', 'HeaderManager', testName);
this.headers = headers || [];
let collectionProp = this.collectionProp('HeaderManager.headers');
this.headers.forEach(header => {
let elementProp = collectionProp.elementProp('', 'Header');
elementProp.stringProp('Header.name', header.name);

View File

@ -1,5 +1,5 @@
import {
Arguments,
Arguments, BeanShellPreProcessor,
CookieManager,
DubboSample,
DurationAssertion,
@ -302,6 +302,8 @@ export class HttpRequest extends Request {
this.environment = undefined;
this.useEnvironment = undefined;
this.debugReport = undefined;
this.beanShellPreProcessor = undefined;
this.beanShellPostProcessor = undefined;
this.set(options);
this.sets({parameters: KeyValue, headers: KeyValue}, options);
@ -313,6 +315,8 @@ export class HttpRequest extends Request {
options.body = new Body(options.body);
options.assertions = new Assertions(options.assertions);
options.extract = new Extract(options.extract);
options.beanShellPreProcessor = new BeanShellProcessor(options.beanShellPreProcessor);
options.beanShellPostProcessor = new BeanShellProcessor(options.beanShellPostProcessor);
return options;
}
@ -352,6 +356,7 @@ export class HttpRequest extends Request {
showMethod() {
return this.method.toUpperCase();
}
}
export class DubboRequest extends Request {
@ -564,6 +569,14 @@ export class AssertionType extends BaseConfig {
}
}
export class BeanShellProcessor extends BaseConfig {
constructor(options) {
super();
this.script = undefined;
this.set(options);
}
}
export class Text extends AssertionType {
constructor(options) {
super(ASSERTION_TYPE.TEXT);
@ -722,8 +735,6 @@ class JMXHttpRequest {
});
for (let i = 0; i < parameters.length; i++) {
let parameter = parameters[i];
// 非 GET 请求中出现了 url 参数
parameter.value = calculate(parameter.value);
path += (parameter.name + '=' + parameter.value);
if (i !== parameters.length - 1) {
path += '&';
@ -820,6 +831,7 @@ class JMXGenerator {
} else {
this.addRequestBody(sampler, request);
}
this.addBeanShellProcessor(sampler, request);
}
this.addRequestAssertion(sampler, request);
@ -888,6 +900,16 @@ class JMXGenerator {
}
}
addBeanShellProcessor(httpSamplerProxy, request) {
let name = request.name;
if (request.beanShellPreProcessor && request.beanShellPreProcessor.script) {
httpSamplerProxy.put(new BeanShellPreProcessor(name, request.beanShellPreProcessor));
}
if (request.beanShellPostProcessor && request.beanShellPostProcessor.script) {
httpSamplerProxy.put(new BeanShellPreProcessor(name, request.beanShellPostProcessor));
}
}
addBodyFormat(request) {
let bodyFormat = request.body.format;
if (bodyFormat) {
@ -921,9 +943,6 @@ class JMXGenerator {
addRequestArguments(httpSamplerProxy, request) {
let args = this.filterKV(request.parameters);
args.forEach(arg => {
arg.value = calculate(arg.value);
});
if (args.length > 0) {
httpSamplerProxy.add(new HTTPSamplerArguments(args));
}
@ -933,9 +952,6 @@ class JMXGenerator {
let body = [];
if (request.body.isKV()) {
body = this.filterKV(request.body.kvs);
body.forEach(arg => {
arg.value = calculate(arg.value);
});
} else {
httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true);
body.push({name: '', value: request.body.raw, encode: false});

View File

@ -1,5 +1,5 @@
<template>
<editor v-model="formatData" :lang="mode" @init="editorInit" theme="chrome"/>
<editor v-model="formatData" :lang="mode" @init="editorInit" :theme="theme"/>
</template>
<script>
@ -15,6 +15,12 @@
data: {
type: String
},
theme: {
type: String,
default() {
return 'chrome'
}
},
init: {
type: Function
},
@ -54,7 +60,7 @@
this.modes.forEach(mode => {
require('brace/mode/' + mode); //language
});
require('brace/theme/chrome')
require('brace/theme/' + this.theme)
require('brace/snippets/javascript') //snippet
if (this.readOnly) {
editor.setReadOnly(true);

View File

@ -18,6 +18,7 @@
<script>
import {getCurrentUser} from "../../../../common/js/utils";
import AboutUs from "./AboutUs";
import axios from "axios";
export default {
name: "MsUser",
@ -35,7 +36,17 @@
this.$router.push('/setting/personsetting').catch(error => error);
break;
case "logout":
this.$get("/signout", function () {
axios.get("/signout").then(response => {
if (response.data.success) {
localStorage.clear();
window.location.href = "/login";
} else {
if (response.data.message === 'sso') {
localStorage.clear();
window.location.href = "/sso/logout"
}
}
}).catch(error => {
localStorage.clear();
window.location.href = "/login";
});

View File

@ -1,6 +1,6 @@
<template>
<el-card class="header-title">
<div v-loading="result.loading">
<el-card class="header-title" v-loading="result.loading">
<div>
<div>{{$t('organization.select_defect_platform')}}</div>
<el-radio-group v-model="platform" style="margin-top: 10px" @change="change">
<el-radio v-for="(item, index) in platforms" :key="index" :label="item.value" size="small">
@ -19,14 +19,19 @@
<el-input v-model="form.password" auto-complete="new-password"
:placeholder="$t('organization.input_api_password')" show-password/>
</el-form-item>
<el-form-item label="JIRA 地址" prop="url" v-if="platform === 'Jira'">
<el-input v-model="form.url" placeholder="请输入Jira地址"/>
</el-form-item>
</el-form>
</div>
<div style="margin-left: 100px">
<el-button v-if="showEdit" size="small" @click="edit">{{$t('commons.edit')}}</el-button>
<el-button type="primary" v-if="showSave" size="small" @click="save('form')">{{$t('commons.save')}}</el-button>
<el-button v-if="showCancel" size="small" @click="cancelEdit">取消编辑</el-button>
<el-button type="info" size="small" @click="cancelIntegration('form')" :disabled="!show">
<el-button type="primary" size="mini" :disabled="!show" @click="testConnection">{{$t('ldap.test_connect')}}
</el-button>
<el-button v-if="showEdit" size="mini" @click="edit">{{$t('commons.edit')}}</el-button>
<el-button type="primary" v-if="showSave" size="mini" @click="save('form')">{{$t('commons.save')}}</el-button>
<el-button v-if="showCancel" size="mini" @click="cancelEdit">取消编辑</el-button>
<el-button type="info" size="mini" @click="cancelIntegration('form')" :disabled="!show">
取消集成
</el-button>
</div>
@ -49,7 +54,7 @@
import {getCurrentUser} from "../../../../common/js/utils";
export default {
name: "DefectManagement",
name: "IssuesManagement",
data() {
return {
form: {},
@ -72,7 +77,8 @@
],
rules: {
account: {required: true, message: this.$t('organization.input_api_account'), trigger: ['change', 'blur']},
password: {required: true, message: this.$t('organization.input_api_password'), trigger: ['change', 'blur']}
password: {required: true, message: this.$t('organization.input_api_password'), trigger: ['change', 'blur']},
url: {required: true, message: '请输入url', trigger: ['change', 'blur']}
},
}
},
@ -91,6 +97,7 @@
let config = JSON.parse(data.configuration);
this.$set(this.form, 'account', config.account);
this.$set(this.form, 'password', config.password);
this.$set(this.form, 'url', config.url);
} else {
this.clear();
}
@ -138,7 +145,8 @@
let param = {};
let auth = {
account: this.form.account,
password: this.form.password
password: this.form.password,
url: this.form.url
};
param.organizationId = getCurrentUser().lastOrganizationId;
param.platform = this.platform;
@ -172,6 +180,7 @@
let config = JSON.parse(data.configuration);
this.$set(this.form, 'account', config.account);
this.$set(this.form, 'password', config.password);
this.$set(this.form, 'url', config.url);
} else {
this.clear();
}
@ -180,9 +189,15 @@
clear() {
this.$set(this.form, 'account', '');
this.$set(this.form, 'password', '');
this.$set(this.form, 'url', '');
this.$nextTick(() => {
this.$refs.form.clearValidate();
});
},
testConnection() {
this.result = this.$get("issues/auth/" + this.platform, () => {
this.$success("验证通过!");
});
}
}
}

View File

@ -10,7 +10,7 @@
<script>
import DefectManagement from "./DefectManagement";
import DefectManagement from "./IssuesManagement";
export default {
name: "ServiceIntegration",

View File

@ -61,6 +61,7 @@
</template>
</el-table-column>
</el-table>
<div style="text-align: center"> {{testCases.length}} </div>
</el-main>
</el-container>
</el-container>
@ -161,6 +162,8 @@
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
// param.nodeIds = this.selectNodeIds;
this.condition.nodeIds = this.selectNodeIds;
} else {
this.condition.nodeIds = [];
}
this.result = this.$post('/test/case/name', this.condition, response => {
this.testCases = response.data;

View File

@ -207,10 +207,22 @@
<el-row>
<el-col :span="20" :offset="1" class="issues-edit">
<el-table border class="adjust-table" :data="issues" style="width: 100%">
<el-table-column prop="id" label="缺陷ID"/>
<el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>
<el-table-column prop="title" label="缺陷标题"/>
<el-table-column prop="status" label="缺陷状态"/>
<el-table-column prop="description" label="缺陷描述" show-overflow-tooltip/>
<el-table-column prop="status" label="缺陷状态"/>
<el-table-column prop="platform" label="平台"/>
<el-table-column label="操作">
<template v-slot:default="scope">
<el-tooltip content="关闭缺陷"
placement="right">
<el-button type="danger" icon="el-icon-circle-close" size="mini"
circle v-if="scope.row.platform === 'Local'"
@click="closeIssue(scope.row)"
/>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
@ -474,11 +486,16 @@
})
},
getIssues(caseId) {
this.result = this.$get("/issues/get/"+caseId,response => {
this.result = this.$get("/issues/get/" + caseId, response => {
let data = response.data;
this.issues = data;
console.log(data);
})
},
closeIssue(row) {
this.result = this.$get("/issues/close/" + row.id, () => {
this.getIssues(this.testCase.caseId);
this.$success("关闭成功");
});
}
}
}

View File

@ -102,6 +102,28 @@
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="nodePath"
label="缺陷"
show-overflow-tooltip>
<template v-slot:default="scope">
<el-popover
placement="right"
width="400"
trigger="hover">
<el-table border class="adjust-table" :data="scope.row.issuesContent" style="width: 100%">
<!-- <el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>-->
<el-table-column prop="title" label="缺陷标题"/>
<el-table-column prop="description" label="缺陷描述" show-overflow-tooltip/>
<!-- <el-table-column prop="status" label="缺陷状态"/>-->
<el-table-column prop="platform" label="平台"/>
</el-table>
<el-button slot="reference" type="text">{{scope.row.issuesSize}}</el-button>
</el-popover>
</template>
</el-table-column>
<el-table-column
prop="executorName"
:label="$t('test_track.plan_view.executor')">
@ -317,6 +339,14 @@
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
for (let i = 0; i < this.tableData.length; i++) {
this.$set(this.tableData[i], "issuesSize", 0);
this.$get("/issues/get/" + this.tableData[i].caseId, response => {
let issues = response.data;
this.$set(this.tableData[i], "issuesSize", issues.length);
this.$set(this.tableData[i], "issuesContent", issues);
})
}
// this.selectIds.clear();
this.selectRows.clear();
});

View File

@ -0,0 +1,82 @@
<template>
<common-component :title="$t('test_track.plan_view.defect_list')">
<template>
<el-table
row-key="id"
:data="defectList"
>
<el-table-column
prop="id"
:label="$t('commons.id')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="module"
:label="$t('test_track.module.module')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="title"
:label="$t('test_track.module.title')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="description"
:label="$t('test_track.module.describe')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="status"
:label="$t('test_track.module.status')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="currentOwner"
:label="$t('test_track.module.current_owner')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="created"
:label="$t('test_track.module.creation_time')">
</el-table-column>
</el-table>
</template>
</common-component>
</template>
<script>
import CommonComponent from "./CommonComponent";
import PriorityTableItem from "../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../common/tableItems/planview/StatusTableItem";
export default {
name: "DefectListComponent",
components: {StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem, CommonComponent},
props: {
defectList: {
type: Array,
default() {
return [
{
id: "1023",
module: "模块e",
title: 'testCase1',
description: "第一个模块测试",
status: "接受/处理",
currentOwner: "Andy",
created: "2010.3.3",
},
]
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -7,6 +7,7 @@
<test-result-component v-if="preview.id == 2"/>
<test-result-chart-component v-if="preview.id == 3"/>
<failure-result-component v-if="preview.id == 4"/>
<defect-list-component v-if="preview.id == 5"/>
<rich-text-component :preview="preview" v-if="preview.type != 'system'"/>
</div>
@ -16,6 +17,7 @@
<test-result-component :test-results="metric.moduleExecuteResult" v-if="preview.id == 2"/>
<test-result-chart-component :execute-result="metric.executeResult" v-if="preview.id == 3"/>
<failure-result-component :failure-test-cases="metric.failureTestCases" v-if="preview.id == 4"/>
<defect-list-component :defect-list="metric.defectList" v-if="preview.id == 5"/>
<rich-text-component :is-report-view="isReportView" :preview="preview" v-if="preview.type != 'system'"/>
</div>
@ -28,10 +30,11 @@
import TestResultChartComponent from "./TestResultChartComponent";
import RichTextComponent from "./RichTextComponent";
import FailureResultComponent from "./FailureResultComponent";
import DefectListComponent from "./DefectListComponent";
export default {
name: "TemplateComponent",
components: {
FailureResultComponent,
FailureResultComponent,DefectListComponent,
RichTextComponent, TestResultChartComponent, TestResultComponent, BaseInfoComponent},
props: {
preview: {

View File

@ -18,6 +18,36 @@
:label="$t('test_track.plan_view.case_count')"
width="180">
</el-table-column>
<el-table-column
prop="passCount"
:label="$t('test_track.plan_view.pass')"
width="180">
</el-table-column>
<el-table-column
prop="failureCount"
:label="$t('test_track.plan_view.failure')"
width="180">
</el-table-column>
<el-table-column
prop="blockingCount"
:label="$t('test_track.plan_view.blocking')"
width="180">
</el-table-column>
<el-table-column
prop="skipCount"
:label="$t('test_track.plan_view.skip')"
width="180">
</el-table-column>
<el-table-column
prop="underwayCount"
:label="$t('test_track.plan.plan_status_running')"
width="180">
</el-table-column>
<el-table-column
prop="prepareCount"
:label="$t('test_track.plan.plan_status_prepare')"
width="180">
</el-table-column>
<el-table-column
prop="passRate"
:label="$t('test_track.pass_rate')">
@ -50,19 +80,37 @@
moduleName: this.$t('test_track.module.module') + '1',
caseCount: '14',
passRate: 10.8,
issuesCount: 3
issuesCount: 3,
passCount:0,
failureCount:0,
blockingCount:0,
skipCount:0,
underwayCount:0,
prepareCount:0
},
{
moduleName: this.$t('test_track.module.module') + '2',
caseCount: '24',
passRate: 40,
issuesCount: 6
moduleName: this.$t('test_track.module.module') + '1',
caseCount: '14',
passRate: 10.8,
issuesCount: 3,
passCount:0,
failureCount:0,
blockingCount:0,
skipCount:0,
underwayCount:0,
prepareCount:0
},
{
moduleName: this.$t('test_track.module.module') + '3',
caseCount: '50',
passRate: 76.9,
issuesCount: 8
moduleName: this.$t('test_track.module.module') + '1',
caseCount: '14',
passRate: 10.8,
issuesCount: 3,
passCount:0,
failureCount:0,
blockingCount:0,
skipCount:0,
underwayCount:0,
prepareCount:0
}
]
}

View File

@ -77,10 +77,11 @@
[2, { name: this.$t('test_track.plan_view.test_result'), id: 2 , type: 'system'}],
[3, { name: this.$t('test_track.plan_view.result_distribution'), id: 3 ,type: 'system'}],
[4, { name: this.$t('test_track.plan_view.failure_case'), id: 4 ,type: 'system'}],
[5, { name: this.$t('test_track.plan_view.custom_component'), id: 5 ,type: 'custom'}]
[5, { name: this.$t('test_track.plan_view.defect_list'), id: 5 ,type: 'system'}],
[6, { name: this.$t('test_track.plan_view.custom_component'), id:6,type: 'custom'}]
]
),
components: [5],
components: [6],
previews: [],
template: {},
isReport: false
@ -109,12 +110,12 @@
this.template = {
name: '',
content: {
components: [1,2,3,4,5],
components: [1,2,3,4,5,6],
customComponent: new Map()
}
};
this.previews = [];
this.components = [5];
this.components = [6];
if (id) {
this.type = 'edit';
this.getTemplateById(id);
@ -144,6 +145,7 @@
}
}
});
},
handleClose() {
window.removeEventListener('popstate', this.goBack, false);

View File

@ -69,7 +69,8 @@
[2, { name: this.$t('test_track.plan_view.test_result'), id: 2 , type: 'system'}],
[3, { name: this.$t('test_track.plan_view.result_distribution'), id: 3 ,type: 'system'}],
[4, { name: this.$t('test_track.plan_view.failure_case'), id: 4 ,type: 'system'}],
[5, { name: this.$t('test_track.plan_view.custom_component'), id: 5 ,type: 'custom'}]
[5, { name: this.$t('test_track.plan_view.defect_list'), id: 5 ,type: 'system'}],
[6, { name: this.$t('test_track.plan_view.custom_component'), id: 6 ,type: 'custom'}]
]
),
isTestManagerOrTestUser: false
@ -175,6 +176,9 @@
if (!this.metric.moduleExecuteResult) {
this.metric.moduleExecuteResult = [];
}
/*缺陷列表*/
this.metric.defectList = [];
if (this.report.startTime) {
this.metric.startTime = new Date(this.report.startTime);
}

View File

@ -113,39 +113,39 @@ export const MOCKJS_FUNC = [
]
export const JMETER_FUNC = [
{name: "${__threadNum}"},
{name: "${__samplerName}"},
{name: "${__machineIP}"},
{name: "${__machineName}"},
{name: "${__time}"},
{name: "${__log}"},
{name: "${__logn}"},
{name: "${__StringFromFile}"},
{name: "${__FileToString}"},
{name: "${__CSVRead}"},
{name: "${__XPath}"},
{name: "${__counter}"},
{name: "${__intSum}"},
{name: "${__longSum}"},
{name: "${__Random}"},
{name: "${__RandomString}"},
{name: "${__UUID}"},
{name: "${__BeanShell}"},
{name: "${__javaScript}"},
{name: "${__jexl}"},
{name: "${__jexl2}"},
{name: "${__property}"},
{name: "${__P}"},
{name: "${__setProperty}"},
{name: "${__split}"},
{name: "${__V}"},
{name: "${__eval}"},
{name: "${__evalVar}"},
{name: "${__regexFunction}"},
{name: "${__escapeOroRegexpChars}"},
{name: "${__char}"},
{name: "${__unescape}"},
{name: "${__unescapeHtml}"},
{name: "${__escapeHtml}"},
{name: "${__TestPlanName}"},
{type: "Information", name: "${__threadNum}", description: "get thread number"},
{type: "Information", name: "${__samplerName}", description: "get the sampler name (label)"},
{type: "Information", name: "${__machineIP}", description: "get the local machine IP address"},
{type: "Information", name: "${__machineName}", description: "get the local machine name"},
{type: "Information", name: "${__time}", description: "return current time in various formats"},
{type: "Information", name: "${__log}", description: "log (or display) a message (and return the value)"},
{type: "Information", name: "${__logn}", description: "log (or display) a message (empty return value)"},
{type: "Input", name: "${__StringFromFile}", description: "read a line from a file"},
{type: "Input", name: "${__FileToString}", description: "read an entire file"},
{type: "Input", name: "${__CSVRead}", description: "read from CSV delimited file"},
{type: "Input", name: "${__XPath}", description: "Use an XPath expression to read from a file"},
{type: "Calculation", name: "${__counter}", description: "generate an incrementing number"},
{type: "Calculation", name: "${__intSum}", description: "add int numbers"},
{type: "Calculation", name: "${__longSum}", description: "add long numbers"},
{type: "Calculation", name: "${__Random}", description: "generate a random number"},
{type: "Calculation", name: "${__RandomString}", description: "generate a random string"},
{type: "Calculation", name: "${__UUID}", description: "generate a random type 4 UUID"},
{type: "Scripting", name: "${__BeanShell}", description: "run a BeanShell script"},
{type: "Scripting", name: "${__javaScript}", description: "process JavaScript (Mozilla Rhino)"},
{type: "Scripting", name: "${__jexl}", description: "evaluate a Commons Jexl expression"},
{type: "Scripting", name: "${__jexl2}", description: "evaluate a Commons Jexl expression"},
{type: "Properties", name: "${__property}", description: "read a property"},
{type: "Properties", name: "${__P}", description: "read a property (shorthand method)"},
{type: "Properties", name: "${__setProperty}", description: "set a JMeter property"},
{type: "Variables", name: "${__split}", description: "Split a string into variables"},
{type: "Variables", name: "${__V}", description: "evaluate a variable name"},
{type: "Variables", name: "${__eval}", description: "evaluate a variable expression"},
{type: "Variables", name: "${__evalVar}", description: "evaluate an expression stored in a variable"},
{type: "String", name: "${__regexFunction}", description: "parse previous response using a regular expression"},
{type: "String", name: "${__escapeOroRegexpChars}", description: "quote meta chars used by ORO regular expression"},
{type: "String", name: "${__char}", description: "generate Unicode char values from a list of numbers"},
{type: "String", name: "${__unescape}", description: "Process strings containing Java escapes (e.g. & )"},
{type: "String", name: "${__unescapeHtml}", description: "Decode HTML-encoded strings"},
{type: "String", name: "${__escapeHtml}", description: "Encode strings using HTML encoding"},
{type: "String", name: "${__TestPlanName}", description: "Return name of current test plan"},
]

View File

@ -353,6 +353,7 @@ export default {
input_name: "Please enter the test name",
select_project: "Please select project",
variable_name: "Variable name",
variable: "Variable",
copied: "copied",
key: "Key",
value: "Value",
@ -401,6 +402,7 @@ export default {
url_description: "etc: https://fit2cloud.com",
path_description: "etc/login",
parameters: "Query parameters",
jmeter_func: "Jmeter Functions",
parameters_filter_example: "Example",
parameters_filter_tips: "Only support MockJs function result preview",
parameters_advance: "Advanced parameter settings",
@ -610,6 +612,11 @@ export default {
delete_confirm: "Confirm delete module:",
delete_all_resource: "and all submodules and test cases under the module",
module: "Module",
title: "Title",
describe: "Describe",
status: "Status",
current_owner: "Current Owner",
creation_time: "Creation time"
},
home: {
recent_test: "Recent test",
@ -649,6 +656,7 @@ export default {
result_distribution: "Result distribution",
custom_component: "Custom",
create_report: "Create report",
defect_list:"Defect list",
view_report: "View report",
component_library: "Component library",
component_library_tip: "Drag and drop the component from the component library, add to the right, preview the report effect, only one can be added per system component.",

View File

@ -354,6 +354,7 @@ export default {
input_name: "请输入测试名称",
select_project: "请选择项目",
variable_name: "变量名",
variable: "变量",
copied: "已拷贝",
key: "键",
value: "值",
@ -402,8 +403,7 @@ export default {
path_description: "例如:/login",
url_invalid: "URL无效",
parameters: "请求参数",
parameters_filter: "内置函数",
parameters_filter_desc: "使用方法",
jmeter_func: "Jmeter 方法",
parameters_filter_example: "示例",
parameters_filter_tips: "只支持 MockJs 函数结果预览",
parameters_advance: "高级参数设置",
@ -615,6 +615,11 @@ export default {
delete_confirm: "确认删除模块: ",
delete_all_resource: "以及模块下所有子模块和测试用例",
module: "模块",
title: "标题",
status: "状态",
describe: "描述",
current_owner: "处理人",
creation_time: "创建时间"
},
home: {
recent_test: "最近测试",
@ -653,6 +658,7 @@ export default {
test_result: "测试结果",
result_distribution: "测试结果分布",
custom_component: "自定义模块",
defect_list:"缺陷列表",
create_report: "创建测试报告",
view_report: "查看测试报告",
component_library: "组件库",

View File

@ -352,6 +352,7 @@ export default {
input_name: "請輸入測試名稱",
select_project: "請選擇項目",
variable_name: "變數名",
variable: "變數",
copied: "已拷貝",
key: "鍵",
value: "值",
@ -401,6 +402,7 @@ export default {
path_description: "例如:/login",
url_invalid: "URL無效",
parameters: "請求參數",
jmeter_func: "Jmeter 方法",
parameters_filter_example: "示例",
parameters_filter_tips: "只支持MockJs函數結果預覽",
parameters_advance: "高級參數設置",
@ -610,6 +612,11 @@ export default {
delete_confirm: "確認刪除模塊: ",
delete_all_resource: "以及模塊下所有子模塊和測試用例",
module: "模塊",
title: "標題",
status: "狀態",
describe: "描述",
current_owner: "處理人",
creation_time: "創建時間"
},
home: {
recent_test: "最近測試",
@ -649,6 +656,7 @@ export default {
result_distribution: "測試結果分布",
custom_component: "自定義模塊",
create_report: "創建測試報告",
defect_list:"缺陷清單",
view_report: "查看測試報告",
component_library: "組件庫",
component_library_tip: "拖拽組件庫中組件,添加至右側,預覽報告效果,每個系統組件只能添加壹個。",

View File

@ -88,8 +88,15 @@
beforeCreate() {
this.$get("/isLogin").then(response => {
if (!response.data.success) {
this.ready = true;
if (response.data.message === 'sso') {
window.location.href = "/sso/login"
} else {
this.ready = true;
}
} else {
let user = response.data.data;
saveLocalStorage(user);
this.getLanguage(user.language);
window.location.href = "/"
}
});