diff --git a/README.md b/README.md index 6c3febbb0b..245a2a28ca 100755 --- a/README.md +++ b/README.md @@ -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 diff --git a/backend/.gitignore b/backend/.gitignore index 44cceecb40..b4e1c2fed7 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -31,4 +31,5 @@ target .settings .project .classpath -.factorypath \ No newline at end of file +.factorypath +*.jar \ No newline at end of file diff --git a/backend/pom.xml b/backend/pom.xml index bdd4e17897..50e2ee6ea5 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -140,11 +140,6 @@ - - - - - org.apache.jmeter @@ -272,6 +267,24 @@ runtime + + + org.pac4j + pac4j-cas + 3.0.2 + + + io.buji + buji-pac4j + 4.0.0 + + + shiro-web + org.apache.shiro + + + + @@ -356,6 +369,16 @@ 2.6 + + org.apache.maven.plugins + maven-jar-plugin + + + **/jmeter/lib/**/*.jar + + + + org.apache.maven.plugins maven-antrun-plugin @@ -383,6 +406,35 @@ + + org.apache.maven.plugins + maven-dependency-plugin + + + copy + generate-resources + + copy + + + + + + + org.apache.jmeter + ApacheJMeter_functions + ${jmeter.version} + jar + true + src/main/resources/jmeter/lib/ext + ApacheJMeter_functions.jar + + + ${project.build.directory}/wars + false + true + + org.mybatis.generator mybatis-generator-maven-plugin diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellPostProcessor.java b/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellPostProcessor.java new file mode 100644 index 0000000000..2dd6217c3f --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellPostProcessor.java @@ -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 { + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellPreProcessor.java b/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellPreProcessor.java new file mode 100644 index 0000000000..47b94107fb --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellPreProcessor.java @@ -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 { + +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellProcessor.java b/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellProcessor.java new file mode 100644 index 0000000000..4b87bf3861 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/processor/BeanShellProcessor.java @@ -0,0 +1,8 @@ +package io.metersphere.api.dto.scenario.processor; + +import lombok.Data; + +@Data +public class BeanShellProcessor { + private String script; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java b/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java index 56d3e98ede..efe616f29f 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/request/HttpRequest.java @@ -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; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index 868b6b60d5..48ba7fe65e 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -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); diff --git a/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java new file mode 100644 index 0000000000..d6aa1c845d --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/parse/JmeterDocumentParser.java @@ -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 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 parserUrl(String url) { +// 传递的URL参数 + Map 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; + } +} diff --git a/backend/src/main/java/io/metersphere/api/service/APITestService.java b/backend/src/main/java/io/metersphere/api/service/APITestService.java index 0c927b35ec..f31c9c84ce 100644 --- a/backend/src/main/java/io/metersphere/api/service/APITestService.java +++ b/backend/src/main/java/io/metersphere/api/service/APITestService.java @@ -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); } diff --git a/backend/src/main/java/io/metersphere/base/domain/Issues.java b/backend/src/main/java/io/metersphere/base/domain/Issues.java new file mode 100644 index 0000000000..1aac52464a --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/Issues.java @@ -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; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/IssuesExample.java b/backend/src/main/java/io/metersphere/base/domain/IssuesExample.java new file mode 100644 index 0000000000..c2f363f61d --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/IssuesExample.java @@ -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 oredCriteria; + + public IssuesExample() { + oredCriteria = new ArrayList(); + } + + 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 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 criteria; + + protected GeneratedCriteria() { + super(); + criteria = new ArrayList(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List getAllCriteria() { + return criteria; + } + + public List 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 values) { + addCriterion("id in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdNotIn(List 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 values) { + addCriterion("title in", values, "title"); + return (Criteria) this; + } + + public Criteria andTitleNotIn(List 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 values) { + addCriterion("`status` in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotIn(List 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 values) { + addCriterion("create_time in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotIn(List 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 values) { + addCriterion("update_time in", values, "updateTime"); + return (Criteria) this; + } + + public Criteria andUpdateTimeNotIn(List 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 values) { + addCriterion("reporter in", values, "reporter"); + return (Criteria) this; + } + + public Criteria andReporterNotIn(List 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 values) { + addCriterion("lastmodify in", values, "lastmodify"); + return (Criteria) this; + } + + public Criteria andLastmodifyNotIn(List 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 values) { + addCriterion("platform in", values, "platform"); + return (Criteria) this; + } + + public Criteria andPlatformNotIn(List 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); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java index c658c42f84..cff8104fd7 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java @@ -11,7 +11,5 @@ public class TestCaseIssues implements Serializable { private String issuesId; - private String platform; - private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/TestCaseIssuesExample.java b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssuesExample.java index 4870ff4ad4..02920aa81a 100644 --- a/backend/src/main/java/io/metersphere/base/domain/TestCaseIssuesExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssuesExample.java @@ -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 values) { - addCriterion("platform in", values, "platform"); - return (Criteria) this; - } - - public Criteria andPlatformNotIn(List 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 { diff --git a/backend/src/main/java/io/metersphere/base/mapper/IssuesMapper.java b/backend/src/main/java/io/metersphere/base/mapper/IssuesMapper.java new file mode 100644 index 0000000000..0dd8d77d7d --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/IssuesMapper.java @@ -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 selectByExampleWithBLOBs(IssuesExample example); + + List 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); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/IssuesMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/IssuesMapper.xml new file mode 100644 index 0000000000..5d5bac2cb6 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/IssuesMapper.xml @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + + + + + + + + and ${criterion.condition} + + + and ${criterion.condition} #{criterion.value} + + + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + + + and ${criterion.condition} + + #{listItem} + + + + + + + + + + + id, title, `status`, create_time, update_time, reporter, lastmodify, platform + + + description + + + + + + delete from issues + where id = #{id,jdbcType=VARCHAR} + + + delete from 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 into issues + + + id, + + + title, + + + `status`, + + + create_time, + + + update_time, + + + reporter, + + + lastmodify, + + + platform, + + + description, + + + + + #{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}, + + + + + + update issues + + + 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}, + + + + + + + + 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} + + + + + + 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} + + + + + + update issues + + + 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 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 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} + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.xml index 9a4bd3460f..10755b5265 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.xml @@ -5,7 +5,6 @@ - @@ -66,7 +65,7 @@ - id, test_case_id, issues_id, platform + id, test_case_id, issues_id @@ -153,9 +146,6 @@ issues_id = #{record.issuesId,jdbcType=VARCHAR}, - - platform = #{record.platform,jdbcType=VARCHAR}, - @@ -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} @@ -180,17 +169,13 @@ issues_id = #{issuesId,jdbcType=VARCHAR}, - - platform = #{platform,jdbcType=VARCHAR}, - where id = #{id,jdbcType=VARCHAR} 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} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml index 94c4b629ea..6079d8d5dd 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml @@ -132,7 +132,6 @@ LEFT JOIN user ON user.id = r.user_id r.id = #{id} - AND r.status != 'Debug' ORDER BY r.update_time DESC diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java new file mode 100644 index 0000000000..8bd29e8c0b --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.java @@ -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 getIssues(@Param("caseId") String caseId, @Param("platform") String platform); +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml new file mode 100644 index 0000000000..1b94ec96c5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtIssuesMapper.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java index b022159557..baafe6c422 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.java @@ -11,4 +11,6 @@ public interface ExtProjectMapper { List getProjectWithWorkspace(@Param("proRequest") ProjectRequest request); List getProjectIdByWorkspaceId(String workspaceId); + + int removeIssuePlatform(@Param("platform") String platform, @Param("orgId") String orgId); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml index 85589a901f..c4c9f8b821 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml @@ -4,9 +4,9 @@ + + update project + + + jira_key = null + + + tapd_id = null + + + 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) + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java b/backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java index e898914154..d1ace27122 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java +++ b/backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum IssuesManagePlatform { - Tapd, Jira + Tapd, Jira, Local } diff --git a/backend/src/main/java/io/metersphere/commons/constants/SsoMode.java b/backend/src/main/java/io/metersphere/commons/constants/SsoMode.java new file mode 100644 index 0000000000..beeed6ae3b --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/SsoMode.java @@ -0,0 +1,5 @@ +package io.metersphere.commons.constants; + +public enum SsoMode { + CAS,LOCAL +} diff --git a/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java b/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java index 20a1b9323e..0f66e13177 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java @@ -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 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 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; } } diff --git a/backend/src/main/java/io/metersphere/commons/utils/ScriptEngineUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ScriptEngineUtils.java new file mode 100644 index 0000000000..138d01c655 --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/utils/ScriptEngineUtils.java @@ -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; + } + } +} diff --git a/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java new file mode 100644 index 0000000000..2cf98d199a --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/utils/ShiroUtils.java @@ -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 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; + } +} diff --git a/backend/src/main/java/io/metersphere/config/ShiroConfig.java b/backend/src/main/java/io/metersphere/config/ShiroConfig.java index a5b65170cf..ab1e55c60c 100644 --- a/backend/src/main/java/io/metersphere/config/ShiroConfig.java +++ b/backend/src/main/java/io/metersphere/config/ShiroConfig.java @@ -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 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); } /** diff --git a/backend/src/main/java/io/metersphere/controller/IndexController.java b/backend/src/main/java/io/metersphere/controller/IndexController.java index 868b253dd3..29503b9c53 100644 --- a/backend/src/main/java/io/metersphere/controller/IndexController.java +++ b/backend/src/main/java/io/metersphere/controller/IndexController.java @@ -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(); + } + } diff --git a/backend/src/main/java/io/metersphere/controller/LoginController.java b/backend/src/main/java/io/metersphere/controller/LoginController.java index 997df80108..8452a51810 100644 --- a/backend/src/main/java/io/metersphere/controller/LoginController.java +++ b/backend/src/main/java/io/metersphere/controller/LoginController.java @@ -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(); } - } diff --git a/backend/src/main/java/io/metersphere/dto/UserDTO.java b/backend/src/main/java/io/metersphere/dto/UserDTO.java index 944c2a62a3..ca7753babc 100644 --- a/backend/src/main/java/io/metersphere/dto/UserDTO.java +++ b/backend/src/main/java/io/metersphere/dto/UserDTO.java @@ -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 roles = new ArrayList<>(); diff --git a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java index 60f1a40f46..5cdd5b3a54 100644 --- a/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java +++ b/backend/src/main/java/io/metersphere/performance/parse/xml/reader/jmx/JmeterDocumentParser.java @@ -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); } diff --git a/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java b/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java index 30927a2ef2..71a2bef3b8 100644 --- a/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java +++ b/backend/src/main/java/io/metersphere/security/ShiroDBRealm.java @@ -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 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()); diff --git a/backend/src/main/java/io/metersphere/service/IntegrationService.java b/backend/src/main/java/io/metersphere/service/IntegrationService.java index 0eb7d56df6..a3522cba37 100644 --- a/backend/src/main/java/io/metersphere/service/IntegrationService.java +++ b/backend/src/main/java/io/metersphere/service/IntegrationService.java @@ -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 getAll(String orgId) { diff --git a/backend/src/main/java/io/metersphere/service/IssuesService.java b/backend/src/main/java/io/metersphere/service/IssuesService.java deleted file mode 100644 index d8135f9222..0000000000 --- a/backend/src/main/java/io/metersphere/service/IssuesService.java +++ /dev/null @@ -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 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 getIssues(String caseId) { - List list = new ArrayList<>(); - String tapdId = getTapdProjectId(caseId); - - TestCaseIssuesExample example = new TestCaseIssuesExample(); - example.createCriteria().andTestCaseIdEqualTo(caseId); - - List issues = testCaseIssuesMapper.selectByExample(example); - List 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(); - } -} diff --git a/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java b/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java index 3158f4806e..d7919de2f7 100644 --- a/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java +++ b/backend/src/main/java/io/metersphere/service/TestResourcePoolService.java @@ -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")); } diff --git a/backend/src/main/java/io/metersphere/service/UserService.java b/backend/src/main/java/io/metersphere/service/UserService.java index 6895df41b1..ebeb4155b9 100644 --- a/backend/src/main/java/io/metersphere/service/UserService.java +++ b/backend/src/main/java/io/metersphere/service/UserService.java @@ -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(); diff --git a/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java index f832b05940..79a51257f5 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java @@ -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 getIssues(@PathVariable String id) { + public List 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); + } + } diff --git a/backend/src/main/java/io/metersphere/track/domain/ReportResultComponent.java b/backend/src/main/java/io/metersphere/track/domain/ReportResultComponent.java index 5f24e1a3b7..28c69361a3 100644 --- a/backend/src/main/java/io/metersphere/track/domain/ReportResultComponent.java +++ b/backend/src/main/java/io/metersphere/track/domain/ReportResultComponent.java @@ -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); diff --git a/backend/src/main/java/io/metersphere/track/dto/TestCaseReportModuleResultDTO.java b/backend/src/main/java/io/metersphere/track/dto/TestCaseReportModuleResultDTO.java index 59b82f0c50..00e05f9486 100644 --- a/backend/src/main/java/io/metersphere/track/dto/TestCaseReportModuleResultDTO.java +++ b/backend/src/main/java/io/metersphere/track/dto/TestCaseReportModuleResultDTO.java @@ -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; } diff --git a/backend/src/main/java/io/metersphere/track/service/IssuesService.java b/backend/src/main/java/io/metersphere/track/service/IssuesService.java new file mode 100644 index 0000000000..9afc632d83 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/service/IssuesService.java @@ -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 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 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 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 requestEntity = new HttpEntity<>(json, requestHeaders); + RestTemplate restTemplate = new RestTemplate(); + //post + ResponseEntity 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 requestEntity = new HttpEntity<>(headers); + RestTemplate restTemplate = new RestTemplate(); + //post + ResponseEntity 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 getIssues(String caseId) { + List 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 getTapdIssues(String caseId) { + List list = new ArrayList<>(); + String tapdId = getTapdProjectId(caseId); + + TestCaseIssuesExample example = new TestCaseIssuesExample(); + example.createCriteria().andTestCaseIdEqualTo(caseId); + + List issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Tapd.toString()); + + List 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 getJiraIssues(String caseId) { + List 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 = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Jira.toString()); + + List 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 getLocalIssues(String caseId) { + List list = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Local.toString()); + List 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); + } + +} diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 9b9208204c..8eff343619 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 9b9208204ce138e2cb49d9549eac140019293160 +Subproject commit 8eff343619df1572e1cded52f173257ef4b518a1 diff --git a/backend/src/main/resources/db/migration/V13__service_integration.sql b/backend/src/main/resources/db/migration/V13__service_integration.sql index c48d2d627c..299084bace 100644 --- a/backend/src/main/resources/db/migration/V13__service_integration.sql +++ b/backend/src/main/resources/db/migration/V13__service_integration.sql @@ -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; \ No newline at end of file diff --git a/docker/jmeter-master/run-test.sh b/docker/jmeter-master/run-test.sh index 1be0ccc6fe..54c94d91a9 100644 --- a/docker/jmeter-master/run-test.sh +++ b/docker/jmeter-master/run-test.sh @@ -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 diff --git a/frontend/src/business/App.vue b/frontend/src/business/App.vue index ccd7c71aba..6f75a42fa0 100644 --- a/frontend/src/business/App.vue +++ b/frontend/src/business/App.vue @@ -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" diff --git a/frontend/src/business/components/api/test/components/ApiBody.vue b/frontend/src/business/components/api/test/components/ApiBody.vue index 96fb5dcbaa..5b0a235e65 100644 --- a/frontend/src/business/components/api/test/components/ApiBody.vue +++ b/frontend/src/business/components/api/test/components/ApiBody.vue @@ -2,17 +2,22 @@
- {{$t('api_test.request.body_kv')}} + {{ $t('api_test.request.body_kv') }} - {{$t('api_test.request.body_text')}} + {{ $t('api_test.request.body_text') }} - - +
@@ -22,7 +27,7 @@ diff --git a/frontend/src/business/components/api/test/components/ApiVariable.vue b/frontend/src/business/components/api/test/components/ApiVariable.vue index ed208bd3f5..de092e23cb 100644 --- a/frontend/src/business/components/api/test/components/ApiVariable.vue +++ b/frontend/src/business/components/api/test/components/ApiVariable.vue @@ -34,13 +34,14 @@
- + + diff --git a/frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue b/frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue index 36f620121f..a3e8ff3c1f 100644 --- a/frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue +++ b/frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue @@ -46,7 +46,7 @@ - + @@ -64,6 +68,12 @@ + + + + + + @@ -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, diff --git a/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue b/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue index 0172dac219..8e7418242f 100644 --- a/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue +++ b/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue @@ -1,7 +1,7 @@ @@ -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) diff --git a/frontend/src/business/components/api/test/model/JMX.js b/frontend/src/business/components/api/test/model/JMX.js index 2b62154c07..be589de537 100644 --- a/frontend/src/business/components/api/test/model/JMX.js +++ b/frontend/src/business/components/api/test/model/JMX.js @@ -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); diff --git a/frontend/src/business/components/api/test/model/ScenarioModel.js b/frontend/src/business/components/api/test/model/ScenarioModel.js index 82d8363730..c7df9181a4 100644 --- a/frontend/src/business/components/api/test/model/ScenarioModel.js +++ b/frontend/src/business/components/api/test/model/ScenarioModel.js @@ -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}); diff --git a/frontend/src/business/components/common/components/MsCodeEdit.vue b/frontend/src/business/components/common/components/MsCodeEdit.vue index 5e50d959db..4c0f1ca14f 100644 --- a/frontend/src/business/components/common/components/MsCodeEdit.vue +++ b/frontend/src/business/components/common/components/MsCodeEdit.vue @@ -1,5 +1,5 @@ + + diff --git a/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TemplateComponent.vue b/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TemplateComponent.vue index 911bddafe9..20f1782041 100644 --- a/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TemplateComponent.vue +++ b/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TemplateComponent.vue @@ -7,6 +7,7 @@ + @@ -16,6 +17,7 @@ + @@ -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: { diff --git a/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TestResultComponent.vue b/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TestResultComponent.vue index c65c738aa5..8bca789386 100644 --- a/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TestResultComponent.vue +++ b/frontend/src/business/components/track/plan/view/comonents/report/TemplateComponent/TestResultComponent.vue @@ -18,6 +18,36 @@ :label="$t('test_track.plan_view.case_count')" width="180"> + + + + + + + + + + + + @@ -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 } ] } diff --git a/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportTemplateEdit.vue b/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportTemplateEdit.vue index 87a24f035b..d5a022954b 100644 --- a/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportTemplateEdit.vue +++ b/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportTemplateEdit.vue @@ -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); diff --git a/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportView.vue b/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportView.vue index a9f7514615..47e43d6537 100644 --- a/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportView.vue +++ b/frontend/src/business/components/track/plan/view/comonents/report/TestCaseReportView.vue @@ -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); } diff --git a/frontend/src/common/js/constants.js b/frontend/src/common/js/constants.js index 302477cd60..fb0ee6caa2 100644 --- a/frontend/src/common/js/constants.js +++ b/frontend/src/common/js/constants.js @@ -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"}, ] diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 4753741251..3e0d070600 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -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.", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 3791cba509..2d9ece3007 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -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: "组件库", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 6aa9cdbc46..93e5b2e650 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -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: "拖拽組件庫中組件,添加至右側,預覽報告效果,每個系統組件只能添加壹個。", diff --git a/frontend/src/login/Login.vue b/frontend/src/login/Login.vue index 6dd0ccc8b5..d188be5973 100644 --- a/frontend/src/login/Login.vue +++ b/frontend/src/login/Login.vue @@ -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 = "/" } });