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 af84159f0c..50e2ee6ea5 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -369,6 +369,16 @@
2.6
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ **/jmeter/lib/**/*.jar
+
+
+
+
org.apache.maven.plugins
maven-antrun-plugin
@@ -396,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/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/utils/RestTemplateUtils.java b/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java
index b4f6fdd323..0f66e13177 100644
--- a/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java
+++ b/backend/src/main/java/io/metersphere/commons/utils/RestTemplateUtils.java
@@ -23,7 +23,8 @@ public class RestTemplateUtils {
ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
return responseEntity.getBody();
} catch (Exception e) {
- MSException.throwException("接口调用失败:" + e.getMessage());
+ LogUtil.error(e.getMessage(), e);
+ MSException.throwException("Tapd接口调用失败:" + e.getMessage());
return null;
}
}
@@ -36,7 +37,8 @@ public class RestTemplateUtils {
ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
return responseEntity.getBody();
} catch (Exception e) {
- MSException.throwException("接口调用失败:" + e.getMessage());
+ LogUtil.error(e.getMessage(), e);
+ MSException.throwException("Tapd接口调用失败:" + e.getMessage());
return null;
}
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/track/controller/TestCaseIssuesController.java b/backend/src/main/java/io/metersphere/track/controller/TestCaseIssuesController.java
index a977736aa3..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.base.domain.Issues;
-import io.metersphere.service.IssuesService;
+import io.metersphere.track.service.IssuesService;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.springframework.web.bind.annotation.*;
@@ -30,4 +30,9 @@ public class TestCaseIssuesController {
issuesService.testAuth(platform);
}
+ @GetMapping("/close/{id}")
+ public void closeLocalIssue(@PathVariable String id) {
+ issuesService.closeLocalIssue(id);
+ }
+
}
diff --git a/backend/src/main/java/io/metersphere/service/IssuesService.java b/backend/src/main/java/io/metersphere/track/service/IssuesService.java
similarity index 91%
rename from backend/src/main/java/io/metersphere/service/IssuesService.java
rename to backend/src/main/java/io/metersphere/track/service/IssuesService.java
index 73a9e4e7b3..9afc632d83 100644
--- a/backend/src/main/java/io/metersphere/service/IssuesService.java
+++ b/backend/src/main/java/io/metersphere/track/service/IssuesService.java
@@ -1,4 +1,4 @@
-package io.metersphere.service;
+package io.metersphere.track.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@@ -15,8 +15,9 @@ 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 io.metersphere.track.service.TestCaseService;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -26,6 +27,7 @@ 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;
@@ -63,7 +65,6 @@ public class IssuesService {
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange("https://api.tapd.cn/quickstart/testauth", HttpMethod.GET, requestEntity, String.class);
} catch (Exception e) {
- System.out.println(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException("验证失败!");
}
@@ -154,12 +155,6 @@ public class IssuesService {
String tapdId = getTapdProjectId(issuesRequest.getTestCaseId());
String jiraKey = getJiraProjectKey(issuesRequest.getTestCaseId());
- if (tapd || jira) {
- if (StringUtils.isBlank(tapdId) && StringUtils.isBlank(jiraKey)) {
- MSException.throwException("集成了缺陷管理平台,但未进行项目关联!");
- }
- }
-
if (tapd) {
// 是否关联了项目
if (StringUtils.isNotBlank(tapdId)) {
@@ -173,7 +168,7 @@ public class IssuesService {
}
}
- if (!tapd && !jira) {
+ if (StringUtils.isBlank(tapdId) && StringUtils.isBlank(jiraKey)) {
addLocalIssues(issuesRequest);
}
@@ -275,7 +270,14 @@ public class IssuesService {
HttpEntity requestEntity = new HttpEntity<>(json, requestHeaders);
RestTemplate restTemplate = new RestTemplate();
//post
- ResponseEntity responseEntity = restTemplate.exchange(url + "/rest/api/2/issue", HttpMethod.POST, requestEntity, String.class);
+ 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();
}
@@ -315,7 +317,7 @@ public class IssuesService {
HttpEntity requestEntity = new HttpEntity<>(headers);
RestTemplate restTemplate = new RestTemplate();
//post
- ResponseEntity responseEntity = null;
+ ResponseEntity responseEntity;
Issues issues = new Issues();
try {
responseEntity = restTemplate.exchange(url + "/rest/api/2/issue/" + issuesId, HttpMethod.GET, requestEntity, String.class);
@@ -336,8 +338,15 @@ public class IssuesService {
issues.setDescription(description);
issues.setStatus(status);
issues.setPlatform(IssuesManagePlatform.Jira.toString());
- } catch (Exception e) {
+ } 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;
@@ -381,7 +390,7 @@ public class IssuesService {
List issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Tapd.toString());
- List issuesIds = issues.stream().map(issue -> issue.getId()).collect(Collectors.toList());
+ List issuesIds = issues.stream().map(Issues::getId).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
Issues dto = getTapdIssues(tapdId, issuesId);
if (StringUtils.isBlank(dto.getId())) {
@@ -423,7 +432,7 @@ public class IssuesService {
List issues = extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Jira.toString());
- List issuesIds = issues.stream().map(issue -> issue.getId()).collect(Collectors.toList());
+ List issuesIds = issues.stream().map(Issues::getId).collect(Collectors.toList());
issuesIds.forEach(issuesId -> {
Issues dto = getJiraIssues(headers, url, issuesId);
if (StringUtils.isBlank(dto.getId())) {
@@ -445,7 +454,11 @@ public class IssuesService {
}
public List getLocalIssues(String caseId) {
- return extIssuesMapper.getIssues(caseId, IssuesManagePlatform.Local.toString());
+ 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) {
@@ -471,4 +484,11 @@ public class IssuesService {
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 b86032cbbd..8eff343619 160000
--- a/backend/src/main/java/io/metersphere/xpack
+++ b/backend/src/main/java/io/metersphere/xpack
@@ -1 +1 @@
-Subproject commit b86032cbbda9a9e6028308aa95a887cff2192f1c
+Subproject commit 8eff343619df1572e1cded52f173257ef4b518a1
diff --git a/backend/src/main/resources/i18n/messages_en_US.properties b/backend/src/main/resources/i18n/messages_en_US.properties
index 6f07a9bf57..cc3c37684b 100644
--- a/backend/src/main/resources/i18n/messages_en_US.properties
+++ b/backend/src/main/resources/i18n/messages_en_US.properties
@@ -118,7 +118,6 @@ test_case_already_exists_excel=There are duplicate test cases in the import file
test_case_module_already_exists=The module name already exists at the same level
api_test_name_already_exists=Test name already exists
functional_method_tip=Functional test not support auto method
-
#ldap
ldap_url_is_null=LDAP address is empty
ldap_dn_is_null=LDAP binding DN is empty
@@ -136,4 +135,10 @@ login_fail_email_null=Login failed, user mailbox is empty
login_fail_ou_error=Login failed, please check the user OU
login_fail_filter_error=Login failed, please check the user filter
check_ldap_mapping=Check LDAP attribute mapping
-ldap_mapping_value_null=LDAP user attribute mapping field is empty
\ No newline at end of file
+ldap_mapping_value_null=LDAP user attribute mapping field is empty
+#quota
+quota_workspace_excess_org_api=The total number of interface tests in the workspace cannot exceed the organization's quota
+quota_workspace_excess_org_performance=The total number of performance tests for the workspace cannot exceed the organization's quota
+quota_workspace_excess_org_max_threads=The maximum concurrent number of workspaces cannot exceed the quota of the organization
+quota_workspace_excess_org_max_duration=The stress test duration of the workspace cannot exceed the organization's quota
+quota_workspace_excess_org_resource_pool=The resource pool of the workspace cannot exceed the resource pool of the organization
\ No newline at end of file
diff --git a/backend/src/main/resources/i18n/messages_zh_CN.properties b/backend/src/main/resources/i18n/messages_zh_CN.properties
index a4e890d91c..e7870873c5 100644
--- a/backend/src/main/resources/i18n/messages_zh_CN.properties
+++ b/backend/src/main/resources/i18n/messages_zh_CN.properties
@@ -118,7 +118,6 @@ test_case_already_exists_excel=导入文件中存在重复用例
test_case_module_already_exists=同层级下已存在该模块名称
api_test_name_already_exists=测试名称已经存在
functional_method_tip=功能测试不支持自动方式
-
#ldap
ldap_url_is_null=LDAP地址为空
ldap_dn_is_null=LDAP绑定DN为空
@@ -137,5 +136,12 @@ login_fail_ou_error=登录失败,请检查用户OU
login_fail_filter_error=登录失败,请检查用户过滤器
check_ldap_mapping=检查LDAP属性映射
ldap_mapping_value_null=LDAP用户属性映射字段为空值
+#quota
+quota_workspace_excess_org_api=工作空间的接口测试数量总和不能超过组织的配额
+quota_workspace_excess_org_performance=工作空间的性能测试数量总和不能超过组织的配额
+quota_workspace_excess_org_max_threads=工作空间的最大并发数不能超过组织的配额
+quota_workspace_excess_org_max_duration=工作空间的压测时长不能超过组织的配额
+quota_workspace_excess_org_resource_pool=工作空间的资源池不能超过组织的资源池范围
+
diff --git a/backend/src/main/resources/i18n/messages_zh_TW.properties b/backend/src/main/resources/i18n/messages_zh_TW.properties
index 326ed9f4f1..00aaf178ce 100644
--- a/backend/src/main/resources/i18n/messages_zh_TW.properties
+++ b/backend/src/main/resources/i18n/messages_zh_TW.properties
@@ -118,7 +118,6 @@ test_case_already_exists_excel=導入文件中存在重復用例
test_case_module_already_exists=同層級下已存在該模塊名稱
api_test_name_already_exists=測試名稱已經存在
functional_method_tip=功能測試不支持自動方式
-
#ldap
ldap_url_is_null=LDAP地址為空
ldap_dn_is_null=LDAP綁定DN為空
@@ -137,3 +136,9 @@ login_fail_ou_error=登錄失敗,請檢查用戶OU
login_fail_filter_error=登錄失敗,請檢查用戶過濾器
check_ldap_mapping=檢查LDAP屬性映射
ldap_mapping_value_null=LDAP用戶屬性映射預設為空值
+#quota
+quota_workspace_excess_org_api=工作空間的接口測試數量總和不能超過組織的配額
+quota_workspace_excess_org_performance=工作空間的性能測試數量總和不能超過組織的配額
+quota_workspace_excess_org_max_threads=工作空間的最大並發數不能超過組織的配額
+quota_workspace_excess_org_max_duration=工作空間的壓測時長不能超過組織的配額
+quota_workspace_excess_org_resource_pool=工作空間的資源池不能超過組織的資源池範圍
diff --git a/frontend/src/business/components/settings/organization/DefectManagement.vue b/frontend/src/business/components/settings/organization/IssuesManagement.vue
similarity index 90%
rename from frontend/src/business/components/settings/organization/DefectManagement.vue
rename to frontend/src/business/components/settings/organization/IssuesManagement.vue
index 8b5b25ebac..7192c130d8 100644
--- a/frontend/src/business/components/settings/organization/DefectManagement.vue
+++ b/frontend/src/business/components/settings/organization/IssuesManagement.vue
@@ -1,6 +1,6 @@
-