diff --git a/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java new file mode 100644 index 0000000000..c658c42f84 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssues.java @@ -0,0 +1,17 @@ +package io.metersphere.base.domain; + +import java.io.Serializable; +import lombok.Data; + +@Data +public class TestCaseIssues implements Serializable { + private String id; + + private String testCaseId; + + 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 new file mode 100644 index 0000000000..4870ff4ad4 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/TestCaseIssuesExample.java @@ -0,0 +1,480 @@ +package io.metersphere.base.domain; + +import java.util.ArrayList; +import java.util.List; + +public class TestCaseIssuesExample { + protected String orderByClause; + + protected boolean distinct; + + protected List oredCriteria; + + public TestCaseIssuesExample() { + 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 andTestCaseIdIsNull() { + addCriterion("test_case_id is null"); + return (Criteria) this; + } + + public Criteria andTestCaseIdIsNotNull() { + addCriterion("test_case_id is not null"); + return (Criteria) this; + } + + public Criteria andTestCaseIdEqualTo(String value) { + addCriterion("test_case_id =", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdNotEqualTo(String value) { + addCriterion("test_case_id <>", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdGreaterThan(String value) { + addCriterion("test_case_id >", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdGreaterThanOrEqualTo(String value) { + addCriterion("test_case_id >=", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdLessThan(String value) { + addCriterion("test_case_id <", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdLessThanOrEqualTo(String value) { + addCriterion("test_case_id <=", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdLike(String value) { + addCriterion("test_case_id like", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdNotLike(String value) { + addCriterion("test_case_id not like", value, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdIn(List values) { + addCriterion("test_case_id in", values, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdNotIn(List values) { + addCriterion("test_case_id not in", values, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdBetween(String value1, String value2) { + addCriterion("test_case_id between", value1, value2, "testCaseId"); + return (Criteria) this; + } + + public Criteria andTestCaseIdNotBetween(String value1, String value2) { + addCriterion("test_case_id not between", value1, value2, "testCaseId"); + return (Criteria) this; + } + + public Criteria andIssuesIdIsNull() { + addCriterion("issues_id is null"); + return (Criteria) this; + } + + public Criteria andIssuesIdIsNotNull() { + addCriterion("issues_id is not null"); + return (Criteria) this; + } + + public Criteria andIssuesIdEqualTo(String value) { + addCriterion("issues_id =", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdNotEqualTo(String value) { + addCriterion("issues_id <>", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdGreaterThan(String value) { + addCriterion("issues_id >", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdGreaterThanOrEqualTo(String value) { + addCriterion("issues_id >=", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdLessThan(String value) { + addCriterion("issues_id <", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdLessThanOrEqualTo(String value) { + addCriterion("issues_id <=", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdLike(String value) { + addCriterion("issues_id like", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdNotLike(String value) { + addCriterion("issues_id not like", value, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdIn(List values) { + addCriterion("issues_id in", values, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdNotIn(List values) { + addCriterion("issues_id not in", values, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdBetween(String value1, String value2) { + addCriterion("issues_id between", value1, value2, "issuesId"); + return (Criteria) this; + } + + public Criteria andIssuesIdNotBetween(String value1, String value2) { + 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 { + + 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/mapper/TestCaseIssuesMapper.java b/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.java new file mode 100644 index 0000000000..e0b9e56c33 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.java @@ -0,0 +1,30 @@ +package io.metersphere.base.mapper; + +import io.metersphere.base.domain.TestCaseIssues; +import io.metersphere.base.domain.TestCaseIssuesExample; +import java.util.List; +import org.apache.ibatis.annotations.Param; + +public interface TestCaseIssuesMapper { + long countByExample(TestCaseIssuesExample example); + + int deleteByExample(TestCaseIssuesExample example); + + int deleteByPrimaryKey(String id); + + int insert(TestCaseIssues record); + + int insertSelective(TestCaseIssues record); + + List selectByExample(TestCaseIssuesExample example); + + TestCaseIssues selectByPrimaryKey(String id); + + int updateByExampleSelective(@Param("record") TestCaseIssues record, @Param("example") TestCaseIssuesExample example); + + int updateByExample(@Param("record") TestCaseIssues record, @Param("example") TestCaseIssuesExample example); + + int updateByPrimaryKeySelective(TestCaseIssues record); + + int updateByPrimaryKey(TestCaseIssues record); +} \ 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 new file mode 100644 index 0000000000..9a4bd3460f --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/TestCaseIssuesMapper.xml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + 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, test_case_id, issues_id, platform + + + + + delete from test_case_issues + where id = #{id,jdbcType=VARCHAR} + + + delete from test_case_issues + + + + + + insert into test_case_issues (id, test_case_id, issues_id, + platform) + values (#{id,jdbcType=VARCHAR}, #{testCaseId,jdbcType=VARCHAR}, #{issuesId,jdbcType=VARCHAR}, + #{platform,jdbcType=VARCHAR}) + + + insert into test_case_issues + + + id, + + + test_case_id, + + + issues_id, + + + platform, + + + + + #{id,jdbcType=VARCHAR}, + + + #{testCaseId,jdbcType=VARCHAR}, + + + #{issuesId,jdbcType=VARCHAR}, + + + #{platform,jdbcType=VARCHAR}, + + + + + + update test_case_issues + + + id = #{record.id,jdbcType=VARCHAR}, + + + test_case_id = #{record.testCaseId,jdbcType=VARCHAR}, + + + issues_id = #{record.issuesId,jdbcType=VARCHAR}, + + + platform = #{record.platform,jdbcType=VARCHAR}, + + + + + + + + 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} + + + + + + update test_case_issues + + + test_case_id = #{testCaseId,jdbcType=VARCHAR}, + + + 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} + where id = #{id,jdbcType=VARCHAR} + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/constants/DedectManagePlatform.java b/backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java similarity index 63% rename from backend/src/main/java/io/metersphere/commons/constants/DedectManagePlatform.java rename to backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java index 4daa6e514a..e898914154 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/DedectManagePlatform.java +++ b/backend/src/main/java/io/metersphere/commons/constants/IssuesManagePlatform.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; -public enum DedectManagePlatform { +public enum IssuesManagePlatform { Tapd, Jira } diff --git a/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java b/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java new file mode 100644 index 0000000000..20a1b9323e --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java @@ -0,0 +1,42 @@ +package io.metersphere.commons.utils; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +public class RestTemplateUtils { + + private static RestTemplate restTemplate; + + private static void getTemplate() { + restTemplate = (RestTemplate) CommonBeanFactory.getBean("restTemplate"); + } + + public static String get(String url, HttpHeaders httpHeaders) { + getTemplate(); + try { + HttpEntity requestEntity = new HttpEntity<>(httpHeaders); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); + return responseEntity.getBody(); + } catch (Exception e) { + throw new RuntimeException("调用接口失败", e); + } + } + + public static String post(String url, Object paramMap, HttpHeaders httpHeaders) { + getTemplate(); + try { + HttpEntity requestEntity = new HttpEntity<>((MultiValueMap) paramMap, httpHeaders); + RestTemplate restTemplate = new RestTemplate(); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + return responseEntity.getBody(); + } catch (Exception e) { + throw new RuntimeException("调用接口失败", e); + } + + } + +} diff --git a/backend/src/main/java/io/metersphere/service/IssuesService.java b/backend/src/main/java/io/metersphere/service/IssuesService.java new file mode 100644 index 0000000000..d8135f9222 --- /dev/null +++ b/backend/src/main/java/io/metersphere/service/IssuesService.java @@ -0,0 +1,177 @@ +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/track/controller/TestCaseIssuesController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java new file mode 100644 index 0000000000..f832b05940 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java @@ -0,0 +1,27 @@ +package io.metersphere.track.controller; + +import io.metersphere.service.IssuesService; +import io.metersphere.track.dto.IssuesDTO; +import io.metersphere.track.request.testcase.IssuesRequest; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +@RequestMapping("issues") +@RestController +public class TestCaseIssuesController { + + @Resource + private IssuesService issuesService; + + @PostMapping("/add") + public void addIssues(@RequestBody IssuesRequest issuesRequest) { + issuesService.addIssues(issuesRequest); + } + + @GetMapping("/get/{id}") + public List getIssues(@PathVariable String id) { + return issuesService.getIssues(id); + } +} diff --git a/backend/src/main/java/io/metersphere/track/dto/IssuesDTO.java b/backend/src/main/java/io/metersphere/track/dto/IssuesDTO.java new file mode 100644 index 0000000000..c9dc0a985b --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/dto/IssuesDTO.java @@ -0,0 +1,13 @@ +package io.metersphere.track.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class IssuesDTO { + private String id; + private String title; + private String status; + private String description; +} diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java new file mode 100644 index 0000000000..e5658e3684 --- /dev/null +++ b/backend/src/main/java/io/metersphere/track/request/testcase/IssuesRequest.java @@ -0,0 +1,13 @@ +package io.metersphere.track.request.testcase; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class IssuesRequest { + private String title; + private String content; + private String projectId; + private String testCaseId; +} 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 fbf6627134..c48d2d627c 100644 --- a/backend/src/main/resources/db/migration/V13__service_integration.sql +++ b/backend/src/main/resources/db/migration/V13__service_integration.sql @@ -8,4 +8,11 @@ create table service_integration primary key (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - +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 +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/frontend/src/business/components/api/test/components/ApiBody.vue b/frontend/src/business/components/api/test/components/ApiBody.vue index 64dad3d17a..96fb5dcbaa 100644 --- a/frontend/src/business/components/api/test/components/ApiBody.vue +++ b/frontend/src/business/components/api/test/components/ApiBody.vue @@ -11,7 +11,7 @@ - +
@@ -21,24 +21,25 @@ diff --git a/frontend/src/business/components/api/test/components/ApiVariable.vue b/frontend/src/business/components/api/test/components/ApiVariable.vue new file mode 100644 index 0000000000..20877c661d --- /dev/null +++ b/frontend/src/business/components/api/test/components/ApiVariable.vue @@ -0,0 +1,343 @@ + + + + + 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 c85e6213ce..62e09b6fd7 100644 --- a/frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue +++ b/frontend/src/business/components/api/test/components/request/ApiHttpRequestForm.vue @@ -26,9 +26,10 @@ - {{request.environment ? request.environment.name + ': ' : ''}} - {{displayUrl}} - {{$t('api_test.request.please_configure_environment_in_scenario')}} + {{ request.environment ? request.environment.name + ': ' : '' }} + {{ displayUrl }} + {{ $t('api_test.request.please_configure_environment_in_scenario') }} @@ -39,12 +40,15 @@ - {{$t('load_test.save_and_run')}} + {{ $t('load_test.save_and_run') }} + - + @@ -66,16 +70,18 @@ import MsApiKeyValue from "../ApiKeyValue"; import MsApiBody from "../ApiBody"; import MsApiAssertions from "../assertion/ApiAssertions"; -import {HttpRequest, KeyValue} from "../../model/ScenarioModel"; +import {HttpRequest, KeyValue, Scenario} from "../../model/ScenarioModel"; 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"; export default { name: "MsApiHttpRequestForm", - components: {ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue}, + components: {MsApiVariable, ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiBody, MsApiKeyValue}, props: { request: HttpRequest, + scenario: Scenario, isReadOnly: { type: Boolean, default: false @@ -91,109 +97,109 @@ export default { } }; return { - activeName: "parameters", - rules: { - name: [ - {max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'} - ], - url: [ - {max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'}, - {validator: validateURL, trigger: 'blur'} - ], - path: [ - {max: 500, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'}, - ] - }, - headerSuggestions: REQUEST_HEADERS + activeName: "parameters", + rules: { + name: [ + {max: 300, message: this.$t('commons.input_limit', [1, 300]), trigger: 'blur'} + ], + url: [ + {max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'}, + {validator: validateURL, trigger: 'blur'} + ], + path: [ + {max: 500, message: this.$t('commons.input_limit', [0, 500]), trigger: 'blur'}, + ] + }, + headerSuggestions: REQUEST_HEADERS + } + }, + + methods: { + urlChange() { + if (!this.request.url) return; + let url = this.getURL(this.addProtocol(this.request.url)); + if (url) { + this.request.url = decodeURIComponent(url.origin + url.pathname); } }, - - methods: { - urlChange() { - if (!this.request.url) return; - let url = this.getURL(this.addProtocol(this.request.url)); - if (url) { - this.request.url = decodeURIComponent(url.origin + url.pathname); - } - }, - pathChange() { - if (!this.request.path) return; - let url = this.getURL(this.displayUrl); - let urlStr = url.origin + url.pathname; - let envUrl = this.request.environment.protocol + '://' + this.request.environment.socket; - this.request.path = decodeURIComponent(urlStr.substring(envUrl.length, urlStr.length)); - }, - getURL(urlStr) { - try { - let url = new URL(urlStr); - url.searchParams.forEach((value, key) => { - if (key && value) { - this.request.parameters.splice(0, 0, new KeyValue(key, value)); - } - }); - return url; - } catch (e) { - this.$error(this.$t('api_test.request.url_invalid'), 2000); - } - }, - methodChange(value) { - if (value === 'GET' && this.activeName === 'body') { - this.activeName = 'parameters'; - } - }, - useEnvironmentChange(value) { - if (value && !this.request.environment) { - this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000); - this.request.useEnvironment = false; - } - this.$refs["request"].clearValidate(); - }, - addProtocol(url) { - if (url) { - if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) { - return "https://" + url; + pathChange() { + if (!this.request.path) return; + let url = this.getURL(this.displayUrl); + let urlStr = url.origin + url.pathname; + let envUrl = this.request.environment.protocol + '://' + this.request.environment.socket; + this.request.path = decodeURIComponent(urlStr.substring(envUrl.length, urlStr.length)); + }, + getURL(urlStr) { + try { + let url = new URL(urlStr); + url.searchParams.forEach((value, key) => { + if (key && value) { + this.request.parameters.splice(0, 0, new KeyValue(key, value)); } - } + }); return url; - }, - runDebug() { - this.$emit('runDebug'); + } catch (e) { + this.$error(this.$t('api_test.request.url_invalid'), 2000); } }, - - computed: { - isNotGet() { - return this.request.method !== "GET"; - }, - displayUrl() { - return this.request.environment ? this.request.environment.protocol + '://' + this.request.environment.socket + (this.request.path ? this.request.path : '') : ''; + methodChange(value) { + if (value === 'GET' && this.activeName === 'body') { + this.activeName = 'parameters'; } + }, + useEnvironmentChange(value) { + if (value && !this.request.environment) { + this.$error(this.$t('api_test.request.please_add_environment_to_scenario'), 2000); + this.request.useEnvironment = false; + } + this.$refs["request"].clearValidate(); + }, + addProtocol(url) { + if (url) { + if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) { + return "https://" + url; + } + } + return url; + }, + runDebug() { + this.$emit('runDebug'); + } + }, + + computed: { + isNotGet() { + return this.request.method !== "GET"; + }, + displayUrl() { + return this.request.environment ? this.request.environment.protocol + '://' + this.request.environment.socket + (this.request.path ? this.request.path : '') : ''; } } +} 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 0455d26f69..0172dac219 100644 --- a/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue +++ b/frontend/src/business/components/api/test/components/request/ApiRequestForm.vue @@ -1,27 +1,28 @@