Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MeterSphere
metersphere
提交
e5e7737f
M
metersphere
项目概览
MeterSphere
/
metersphere
上一次同步 大约 3 年
通知
25
Star
1
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
metersphere
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
e5e7737f
编写于
5月 08, 2020
作者:
C
chenjianxing
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'dev' of
https://github.com/fit2cloudrd/metersphere-server
into dev
上级
c5fb0118
b5e7b746
变更
54
隐藏空白更改
内联
并排
Showing
54 changed file
with
1107 addition
and
415 deletion
+1107
-415
backend/pom.xml
backend/pom.xml
+12
-6
backend/src/main/java/io/metersphere/api/controller/APIReportController.java
...va/io/metersphere/api/controller/APIReportController.java
+56
-0
backend/src/main/java/io/metersphere/api/controller/APITestController.java
...java/io/metersphere/api/controller/APITestController.java
+13
-12
backend/src/main/java/io/metersphere/api/dto/APIReportResult.java
...src/main/java/io/metersphere/api/dto/APIReportResult.java
+13
-0
backend/src/main/java/io/metersphere/api/dto/DeleteAPIReportRequest.java
...n/java/io/metersphere/api/dto/DeleteAPIReportRequest.java
+11
-0
backend/src/main/java/io/metersphere/api/dto/QueryAPIReportRequest.java
...in/java/io/metersphere/api/dto/QueryAPIReportRequest.java
+16
-0
backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java
...a/io/metersphere/api/jmeter/APIBackendListenerClient.java
+126
-22
backend/src/main/java/io/metersphere/api/jmeter/RequestResult.java
...rc/main/java/io/metersphere/api/jmeter/RequestResult.java
+32
-0
backend/src/main/java/io/metersphere/api/jmeter/ResponseAssertionResult.java
...va/io/metersphere/api/jmeter/ResponseAssertionResult.java
+13
-0
backend/src/main/java/io/metersphere/api/jmeter/ResponseResult.java
...c/main/java/io/metersphere/api/jmeter/ResponseResult.java
+28
-0
backend/src/main/java/io/metersphere/api/jmeter/ScenarioResult.java
...c/main/java/io/metersphere/api/jmeter/ScenarioResult.java
+42
-0
backend/src/main/java/io/metersphere/api/jmeter/TestResult.java
...d/src/main/java/io/metersphere/api/jmeter/TestResult.java
+41
-0
backend/src/main/java/io/metersphere/api/service/APIReportService.java
...ain/java/io/metersphere/api/service/APIReportService.java
+66
-0
backend/src/main/java/io/metersphere/api/service/APITestService.java
.../main/java/io/metersphere/api/service/APITestService.java
+32
-30
backend/src/main/java/io/metersphere/base/domain/LoadTestReportLog.java
...in/java/io/metersphere/base/domain/LoadTestReportLog.java
+1
-1
backend/src/main/java/io/metersphere/base/domain/LoadTestReportLogExample.java
.../io/metersphere/base/domain/LoadTestReportLogExample.java
+20
-10
backend/src/main/java/io/metersphere/base/domain/LoadTestReportResult.java
...java/io/metersphere/base/domain/LoadTestReportResult.java
+1
-1
backend/src/main/java/io/metersphere/base/domain/LoadTestReportResultExample.java
.../metersphere/base/domain/LoadTestReportResultExample.java
+20
-10
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportLogMapper.java
...a/io/metersphere/base/mapper/LoadTestReportLogMapper.java
+2
-2
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportLogMapper.xml
...va/io/metersphere/base/mapper/LoadTestReportLogMapper.xml
+13
-13
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportResultMapper.java
...o/metersphere/base/mapper/LoadTestReportResultMapper.java
+2
-2
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportResultMapper.xml
...io/metersphere/base/mapper/LoadTestReportResultMapper.xml
+13
-13
backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.java
...o/metersphere/base/mapper/ext/ExtApiTestReportMapper.java
+5
-3
backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml
...io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml
+32
-13
backend/src/main/java/io/metersphere/controller/TestResourcePoolController.java
...io/metersphere/controller/TestResourcePoolController.java
+10
-0
backend/src/main/java/io/metersphere/controller/UserRoleController.java
...in/java/io/metersphere/controller/UserRoleController.java
+1
-1
backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java
...o/metersphere/engine/kubernetes/KubernetesTestEngine.java
+1
-1
backend/src/main/java/io/metersphere/service/ApiReportService.java
...rc/main/java/io/metersphere/service/ApiReportService.java
+0
-42
backend/src/main/java/io/metersphere/service/PerformanceTestService.java
...n/java/io/metersphere/service/PerformanceTestService.java
+1
-0
backend/src/main/java/io/metersphere/service/TestResourcePoolService.java
.../java/io/metersphere/service/TestResourcePoolService.java
+47
-8
backend/src/main/resources/db/migration/V2__metersphere_ddl.sql
...d/src/main/resources/db/migration/V2__metersphere_ddl.sql
+23
-28
frontend/src/business/components/api/head/ApiHeaderMenus.vue
frontend/src/business/components/api/head/ApiHeaderMenus.vue
+2
-2
frontend/src/business/components/api/report/ApiReportList.vue
...tend/src/business/components/api/report/ApiReportList.vue
+124
-0
frontend/src/business/components/api/test/ApiTestConfig.vue
frontend/src/business/components/api/test/ApiTestConfig.vue
+19
-9
frontend/src/business/components/api/test/ApiTestList.vue
frontend/src/business/components/api/test/ApiTestList.vue
+3
-4
frontend/src/business/components/api/test/model/JMX.js
frontend/src/business/components/api/test/model/JMX.js
+39
-32
frontend/src/business/components/api/test/model/ScenarioModel.js
...d/src/business/components/api/test/model/ScenarioModel.js
+52
-18
frontend/src/business/components/common/components/MsRolesTag.vue
.../src/business/components/common/components/MsRolesTag.vue
+35
-0
frontend/src/business/components/common/components/MsTipButton.vue
...src/business/components/common/components/MsTipButton.vue
+1
-0
frontend/src/business/components/common/router/router.js
frontend/src/business/components/common/router/router.js
+5
-5
frontend/src/business/components/performance/report/PerformanceTestReport.vue
...s/components/performance/report/PerformanceTestReport.vue
+2
-8
frontend/src/business/components/project/MsProject.vue
frontend/src/business/components/project/MsProject.vue
+1
-1
frontend/src/business/components/settings/Setting.vue
frontend/src/business/components/settings/Setting.vue
+1
-1
frontend/src/business/components/settings/organization/OrganizationMember.vue
...s/components/settings/organization/OrganizationMember.vue
+11
-12
frontend/src/business/components/settings/organization/OrganizationWorkspace.vue
...omponents/settings/organization/OrganizationWorkspace.vue
+21
-21
frontend/src/business/components/settings/personal/PersonSetting.vue
...c/business/components/settings/personal/PersonSetting.vue
+10
-7
frontend/src/business/components/settings/system/Organization.vue
.../src/business/components/settings/system/Organization.vue
+18
-21
frontend/src/business/components/settings/system/SystemWorkspace.vue
...c/business/components/settings/system/SystemWorkspace.vue
+18
-17
frontend/src/business/components/settings/system/TestResourcePool.vue
.../business/components/settings/system/TestResourcePool.vue
+18
-12
frontend/src/business/components/settings/system/User.vue
frontend/src/business/components/settings/system/User.vue
+8
-7
frontend/src/business/components/settings/workspace/WorkspaceMember.vue
...usiness/components/settings/workspace/WorkspaceMember.vue
+19
-18
frontend/src/business/components/track/case/components/TestCaseMove.vue
...usiness/components/track/case/components/TestCaseMove.vue
+2
-2
frontend/src/i18n/en-US.js
frontend/src/i18n/en-US.js
+2
-0
frontend/src/i18n/zh-CN.js
frontend/src/i18n/zh-CN.js
+3
-0
未找到文件。
backend/pom.xml
浏览文件 @
e5e7737f
...
...
@@ -42,7 +42,6 @@
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-configuration-processor
</artifactId>
<optional>
true
</optional>
</dependency>
<dependency>
...
...
@@ -53,7 +52,9 @@
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-test
</artifactId>
<scope>
test
</scope>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-aop
</artifactId>
...
...
@@ -66,7 +67,6 @@
<dependency>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
<scope>
provided
</scope>
</dependency>
<dependency>
...
...
@@ -119,10 +119,6 @@
<groupId>
commons-codec
</groupId>
<artifactId>
commons-codec
</artifactId>
</dependency>
<dependency>
<groupId>
junit
</groupId>
<artifactId>
junit
</artifactId>
</dependency>
<dependency>
<groupId>
com.alibaba
</groupId>
...
...
@@ -188,6 +184,16 @@
<artifactId>
spring-boot-maven-plugin
</artifactId>
<configuration>
<addResources>
true
</addResources>
<excludes>
<exclude>
<groupId>
org.projectlombok
</groupId>
<artifactId>
lombok
</artifactId>
</exclude>
<exclude>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-configuration-processor
</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
...
...
backend/src/main/java/io/metersphere/
controller/Api
ReportController.java
→
backend/src/main/java/io/metersphere/
api/controller/API
ReportController.java
浏览文件 @
e5e7737f
package
io.metersphere.controller
;
package
io.metersphere.
api.
controller
;
import
com.github.pagehelper.Page
;
import
com.github.pagehelper.PageHelper
;
import
io.metersphere.api.dto.APIReportResult
;
import
io.metersphere.api.dto.DeleteAPIReportRequest
;
import
io.metersphere.api.dto.QueryAPIReportRequest
;
import
io.metersphere.api.service.APIReportService
;
import
io.metersphere.base.domain.ApiTestReport
;
import
io.metersphere.commons.constants.RoleConstants
;
import
io.metersphere.commons.utils.PageUtils
;
import
io.metersphere.commons.utils.Pager
;
import
io.metersphere.controller.request.ReportRequest
;
import
io.metersphere.dto.ApiReportDTO
;
import
io.metersphere.service.ApiReportService
;
import
io.metersphere.user.SessionUtils
;
import
org.apache.shiro.authz.annotation.Logical
;
import
org.apache.shiro.authz.annotation.RequiresRoles
;
import
org.springframework.web.bind.annotation.*
;
import
javax.annotation.Resource
;
import
java.util.List
;
import
javax.annotation.Resource
;
@RestController
@RequestMapping
(
value
=
"/api/report"
)
public
class
ApiReportController
{
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_MANAGER
,
RoleConstants
.
TEST_USER
,
RoleConstants
.
TEST_VIEWER
},
logical
=
Logical
.
OR
)
public
class
APIReportController
{
@Resource
private
A
pi
ReportService
apiReportService
;
private
A
PI
ReportService
apiReportService
;
@GetMapping
(
"/recent/{count}"
)
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_MANAGER
,
RoleConstants
.
TEST_USER
,
RoleConstants
.
TEST_VIEWER
},
logical
=
Logical
.
OR
)
public
List
<
ApiTestReport
>
recentProjects
(
@PathVariable
int
count
)
{
@GetMapping
(
"recent/{count}"
)
public
List
<
APIReportResult
>
recentTest
(
@PathVariable
int
count
)
{
String
currentWorkspaceId
=
SessionUtils
.
getCurrentWorkspaceId
();
ReportRequest
request
=
new
ReportRequest
();
QueryAPIReportRequest
request
=
new
QueryAPI
ReportRequest
();
request
.
setWorkspaceId
(
currentWorkspaceId
);
PageHelper
.
startPage
(
1
,
count
);
return
apiReportService
.
getRecentReportLi
st
(
request
);
PageHelper
.
startPage
(
1
,
count
,
true
);
return
apiReportService
.
recentTe
st
(
request
);
}
@PostMapping
(
"/list/
all/
{goPage}/{pageSize}"
)
public
Pager
<
List
<
A
piReportDTO
>>
getReportList
(
@PathVariable
int
goPage
,
@PathVariable
int
pageSize
,
@RequestBody
ReportRequest
request
)
{
@PostMapping
(
"/list/{goPage}/{pageSize}"
)
public
Pager
<
List
<
A
PIReportResult
>>
list
(
@PathVariable
int
goPage
,
@PathVariable
int
pageSize
,
@RequestBody
QueryAPI
ReportRequest
request
)
{
Page
<
Object
>
page
=
PageHelper
.
startPage
(
goPage
,
pageSize
,
true
);
return
PageUtils
.
setPageInfo
(
page
,
apiReportService
.
getReportList
(
request
));
request
.
setWorkspaceId
(
SessionUtils
.
getCurrentWorkspaceId
());
return
PageUtils
.
setPageInfo
(
page
,
apiReportService
.
list
(
request
));
}
@
PostMapping
(
"/delete/{repor
tId}"
)
public
void
deleteReport
(
@PathVariable
String
repor
tId
)
{
apiReportService
.
deleteReport
(
repor
tId
);
@
GetMapping
(
"/get/{tes
tId}"
)
public
ApiTestReport
get
(
@PathVariable
String
tes
tId
)
{
return
apiReportService
.
get
(
tes
tId
);
}
@
GetMapping
(
"/test/pro/info/{reportId}
"
)
public
ApiReportDTO
getReportTestAndProInfo
(
@PathVariable
String
reportId
)
{
return
apiReportService
.
getReportTestAndProInfo
(
reportId
);
@
PostMapping
(
"/delete
"
)
public
void
delete
(
@RequestBody
DeleteAPIReportRequest
request
)
{
apiReportService
.
delete
(
request
);
}
}
backend/src/main/java/io/metersphere/api/controller/APITestController.java
浏览文件 @
e5e7737f
...
...
@@ -6,13 +6,11 @@ import io.metersphere.api.dto.APITestResult;
import
io.metersphere.api.dto.DeleteAPITestRequest
;
import
io.metersphere.api.dto.QueryAPITestRequest
;
import
io.metersphere.api.dto.SaveAPITestRequest
;
import
io.metersphere.api.service.A
pi
TestService
;
import
io.metersphere.api.service.A
PI
TestService
;
import
io.metersphere.base.domain.ApiTestWithBLOBs
;
import
io.metersphere.commons.constants.RoleConstants
;
import
io.metersphere.commons.utils.PageUtils
;
import
io.metersphere.commons.utils.Pager
;
import
io.metersphere.controller.request.testplan.SaveTestPlanRequest
;
import
io.metersphere.service.FileService
;
import
io.metersphere.user.SessionUtils
;
import
org.apache.shiro.authz.annotation.Logical
;
import
org.apache.shiro.authz.annotation.RequiresRoles
;
...
...
@@ -28,9 +26,7 @@ import javax.annotation.Resource;
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_MANAGER
,
RoleConstants
.
TEST_USER
,
RoleConstants
.
TEST_VIEWER
},
logical
=
Logical
.
OR
)
public
class
APITestController
{
@Resource
private
ApiTestService
apiTestService
;
@Resource
private
FileService
fileService
;
private
APITestService
apiTestService
;
@GetMapping
(
"recent/{count}"
)
public
List
<
APITestResult
>
recentTest
(
@PathVariable
int
count
)
{
...
...
@@ -48,9 +44,14 @@ public class APITestController {
return
PageUtils
.
setPageInfo
(
page
,
apiTestService
.
list
(
request
));
}
@PostMapping
(
value
=
"/save"
,
consumes
=
{
"multipart/form-data"
})
public
String
save
(
@RequestPart
(
"request"
)
SaveAPITestRequest
request
,
@RequestPart
(
value
=
"files"
)
List
<
MultipartFile
>
files
)
{
return
apiTestService
.
save
(
request
,
files
);
@PostMapping
(
value
=
"/create"
,
consumes
=
{
"multipart/form-data"
})
public
void
create
(
@RequestPart
(
"request"
)
SaveAPITestRequest
request
,
@RequestPart
(
value
=
"files"
)
List
<
MultipartFile
>
files
)
{
apiTestService
.
create
(
request
,
files
);
}
@PostMapping
(
value
=
"/update"
,
consumes
=
{
"multipart/form-data"
})
public
void
update
(
@RequestPart
(
"request"
)
SaveAPITestRequest
request
,
@RequestPart
(
value
=
"files"
)
List
<
MultipartFile
>
files
)
{
apiTestService
.
update
(
request
,
files
);
}
@GetMapping
(
"/get/{testId}"
)
...
...
@@ -63,8 +64,8 @@ public class APITestController {
apiTestService
.
delete
(
request
);
}
@PostMapping
(
value
=
"/run"
,
consumes
=
{
"multipart/form-data"
}
)
public
String
run
(
@RequestPart
(
"request"
)
SaveAPITestRequest
request
,
@RequestPart
(
value
=
"files"
)
List
<
MultipartFile
>
files
)
{
return
apiTestService
.
run
(
request
,
files
);
@PostMapping
(
value
=
"/run"
)
public
void
run
(
@RequestBody
SaveAPITestRequest
request
)
{
apiTestService
.
run
(
request
);
}
}
backend/src/main/java/io/metersphere/api/dto/APIReportResult.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.dto
;
import
io.metersphere.base.domain.ApiTestReport
;
import
io.metersphere.base.domain.ApiTestWithBLOBs
;
import
lombok.Getter
;
import
lombok.Setter
;
@Setter
@Getter
public
class
APIReportResult
extends
ApiTestReport
{
private
String
projectName
;
}
backend/src/main/java/io/metersphere/api/dto/DeleteAPIReportRequest.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.dto
;
import
lombok.Getter
;
import
lombok.Setter
;
@Setter
@Getter
public
class
DeleteAPIReportRequest
{
private
String
id
;
}
backend/src/main/java/io/metersphere/api/dto/QueryAPIReportRequest.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.dto
;
import
lombok.Getter
;
import
lombok.Setter
;
@Getter
@Setter
public
class
QueryAPIReportRequest
{
private
String
id
;
private
String
projectId
;
private
String
name
;
private
String
workspaceId
;
private
boolean
recent
=
false
;
}
backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java
浏览文件 @
e5e7737f
package
io.metersphere.api.jmeter
;
import
io.metersphere.api.service.APIReportService
;
import
io.metersphere.api.service.APITestService
;
import
io.metersphere.commons.constants.APITestStatus
;
import
io.metersphere.commons.utils.CommonBeanFactory
;
import
io.metersphere.commons.utils.LogUtil
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.jmeter.assertions.AssertionResult
;
import
org.apache.jmeter.samplers.SampleResult
;
import
org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient
;
import
org.apache.jmeter.visualizers.backend.BackendListenerContext
;
import
java.io.Serializable
;
import
java.util.ArrayList
;
import
java.util.LinkedHashMap
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
/**
* JMeter BackendListener扩展, jmx脚本中使用
*/
public
class
APIBackendListenerClient
extends
AbstractBackendListenerClient
implements
Serializable
{
private
final
AtomicInteger
count
=
new
AtomicInteger
();
// 与前端JMXGenerator的SPLIT对应,用于获取 测试名称 和 测试ID
private
final
static
String
SPLIT
=
"@@:"
;
// 测试ID作为key
private
final
Map
<
String
,
List
<
SampleResult
>>
queue
=
new
ConcurrentHashMap
<>();
@Override
public
void
handleSampleResults
(
List
<
SampleResult
>
sampleResults
,
BackendListenerContext
context
)
{
System
.
out
.
println
(
context
.
getParameter
(
"id"
));
sampleResults
.
forEach
(
result
->
{
for
(
AssertionResult
assertionResult
:
result
.
getAssertionResults
())
{
System
.
out
.
println
(
assertionResult
.
getName
()
+
": "
+
assertionResult
.
isError
());
System
.
out
.
println
(
assertionResult
.
getName
()
+
": "
+
assertionResult
.
isFailure
());
System
.
out
.
println
(
assertionResult
.
getName
()
+
": "
+
assertionResult
.
getFailureMessage
());
// 将不同的测试脚本按测试ID分开
String
label
=
result
.
getSampleLabel
();
if
(!
label
.
contains
(
SPLIT
))
{
LogUtil
.
error
(
"request name format is invalid, name: "
+
label
);
return
;
}
String
name
=
label
.
split
(
SPLIT
)[
0
];
String
testId
=
label
.
split
(
SPLIT
)[
1
];
if
(!
queue
.
containsKey
(
testId
))
{
List
<
SampleResult
>
testResults
=
new
ArrayList
<>();
queue
.
put
(
testId
,
testResults
);
}
result
.
setSampleLabel
(
name
);
queue
.
get
(
testId
).
add
(
result
);
});
}
@Override
public
void
teardownTest
(
BackendListenerContext
context
)
throws
Exception
{
APITestService
apiTestService
=
CommonBeanFactory
.
getBean
(
APITestService
.
class
);
if
(
apiTestService
==
null
)
{
LogUtil
.
error
(
"apiTestService is required"
);
return
;
}
APIReportService
apiReportService
=
CommonBeanFactory
.
getBean
(
APIReportService
.
class
);
if
(
apiReportService
==
null
)
{
LogUtil
.
error
(
"apiReportService is required"
);
return
;
}
queue
.
forEach
((
id
,
sampleResults
)
->
{
TestResult
testResult
=
new
TestResult
();
testResult
.
setId
(
id
);
testResult
.
setTotal
(
sampleResults
.
size
());
// key: 场景Id
final
Map
<
String
,
ScenarioResult
>
scenarios
=
new
LinkedHashMap
<>();
sampleResults
.
forEach
(
result
->
{
String
thread
=
StringUtils
.
substringBeforeLast
(
result
.
getThreadName
(),
" "
);
String
scenarioName
=
StringUtils
.
substringBefore
(
thread
,
SPLIT
);
String
scenarioId
=
StringUtils
.
substringAfter
(
thread
,
SPLIT
);
ScenarioResult
scenarioResult
;
if
(!
scenarios
.
containsKey
(
scenarioId
))
{
scenarioResult
=
new
ScenarioResult
();
scenarioResult
.
setId
(
scenarioId
);
scenarioResult
.
setName
(
scenarioName
);
scenarios
.
put
(
scenarioId
,
scenarioResult
);
}
else
{
scenarioResult
=
scenarios
.
get
(
scenarioId
);
}
println
(
"getSampleLabel"
,
result
.
getSampleLabel
());
println
(
"getErrorCount"
,
result
.
getErrorCount
());
println
(
"getRequestHeaders"
,
result
.
getRequestHeaders
());
println
(
"getResponseHeaders"
,
result
.
getResponseHeaders
());
println
(
"getSampleLabel"
,
result
.
getSampleLabel
());
println
(
"getSampleLabel"
,
result
.
getSampleLabel
());
println
(
"getResponseCode"
,
result
.
getResponseCode
());
println
(
"getResponseCode size"
,
result
.
getResponseData
().
length
);
println
(
"getLatency"
,
result
.
getLatency
());
println
(
"end - start"
,
result
.
getEndTime
()
-
result
.
getStartTime
());
println
(
"getTimeStamp"
,
result
.
getTimeStamp
());
println
(
"getTime"
,
result
.
getTime
());
if
(
result
.
isSuccessful
())
{
scenarioResult
.
addSuccess
();
testResult
.
addSuccess
();
}
else
{
scenarioResult
.
addError
();
testResult
.
addError
();
}
RequestResult
requestResult
=
getRequestResult
(
result
);
scenarioResult
.
getRequestResult
().
add
(
requestResult
);
testResult
.
addPassAssertions
(
requestResult
.
getPassAssertions
());
testResult
.
addTotalAssertions
(
requestResult
.
getTotalAssertions
());
scenarioResult
.
addPassAssertions
(
requestResult
.
getPassAssertions
());
scenarioResult
.
addTotalAssertions
(
requestResult
.
getTotalAssertions
());
});
testResult
.
getScenarios
().
addAll
(
scenarios
.
values
());
apiTestService
.
changeStatus
(
id
,
APITestStatus
.
Completed
);
apiReportService
.
save
(
testResult
);
});
System
.
err
.
println
(
count
.
addAndGet
(
sampleResults
.
size
()));
queue
.
clear
();
super
.
teardownTest
(
context
);
}
private
void
println
(
String
name
,
Object
value
)
{
System
.
out
.
println
(
name
+
": "
+
value
);
private
RequestResult
getRequestResult
(
SampleResult
result
)
{
RequestResult
requestResult
=
new
RequestResult
();
requestResult
.
setName
(
result
.
getSampleLabel
());
requestResult
.
setUrl
(
result
.
getUrlAsString
());
requestResult
.
setSuccess
(
result
.
isSuccessful
());
requestResult
.
setBody
(
result
.
getSamplerData
());
requestResult
.
setHeaders
(
result
.
getRequestHeaders
());
requestResult
.
setRequestSize
(
result
.
getSentBytes
());
requestResult
.
setTotalAssertions
(
result
.
getAssertionResults
().
length
);
ResponseResult
responseResult
=
requestResult
.
getResponseResult
();
responseResult
.
setBody
(
result
.
getResponseDataAsString
());
responseResult
.
setHeaders
(
result
.
getResponseHeaders
());
responseResult
.
setLatency
(
result
.
getLatency
());
responseResult
.
setResponseCode
(
result
.
getResponseCode
());
responseResult
.
setResponseSize
(
result
.
getResponseData
().
length
);
responseResult
.
setResponseTime
(
result
.
getTime
());
responseResult
.
setResponseMessage
(
result
.
getResponseMessage
());
for
(
AssertionResult
assertionResult
:
result
.
getAssertionResults
())
{
ResponseAssertionResult
responseAssertionResult
=
getResponseAssertionResult
(
assertionResult
);
if
(
responseAssertionResult
.
isPass
())
{
requestResult
.
addPassAssertions
();
}
responseResult
.
getAssertions
().
add
(
responseAssertionResult
);
}
return
requestResult
;
}
private
ResponseAssertionResult
getResponseAssertionResult
(
AssertionResult
assertionResult
)
{
ResponseAssertionResult
responseAssertionResult
=
new
ResponseAssertionResult
();
responseAssertionResult
.
setMessage
(
assertionResult
.
getFailureMessage
());
responseAssertionResult
.
setName
(
assertionResult
.
getName
());
responseAssertionResult
.
setPass
(!
assertionResult
.
isFailure
());
return
responseAssertionResult
;
}
}
backend/src/main/java/io/metersphere/api/jmeter/RequestResult.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.jmeter
;
import
lombok.Data
;
@Data
public
class
RequestResult
{
private
String
name
;
private
String
url
;
private
long
requestSize
;
private
boolean
success
;
private
String
headers
;
private
String
cookies
;
private
String
body
;
private
int
totalAssertions
=
0
;
private
int
passAssertions
=
0
;
private
final
ResponseResult
responseResult
=
new
ResponseResult
();
public
void
addPassAssertions
()
{
this
.
passAssertions
++;
}
}
backend/src/main/java/io/metersphere/api/jmeter/ResponseAssertionResult.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.jmeter
;
import
lombok.Data
;
@Data
public
class
ResponseAssertionResult
{
private
String
name
;
private
String
message
;
private
boolean
pass
;
}
backend/src/main/java/io/metersphere/api/jmeter/ResponseResult.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.jmeter
;
import
lombok.Data
;
import
java.util.ArrayList
;
import
java.util.List
;
@Data
public
class
ResponseResult
{
private
String
responseCode
;
private
String
responseMessage
;
private
long
responseTime
;
private
long
latency
;
private
long
responseSize
;
private
String
headers
;
private
String
body
;
private
final
List
<
ResponseAssertionResult
>
assertions
=
new
ArrayList
<>();
}
backend/src/main/java/io/metersphere/api/jmeter/ScenarioResult.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.jmeter
;
import
lombok.Data
;
import
java.util.ArrayList
;
import
java.util.List
;
@Data
public
class
ScenarioResult
{
private
String
id
;
private
String
name
;
private
long
responseTime
;
private
int
error
=
0
;
private
int
success
=
0
;
private
int
totalAssertions
=
0
;
private
int
passAssertions
=
0
;
private
final
List
<
RequestResult
>
requestResult
=
new
ArrayList
<>();
public
void
addError
()
{
this
.
error
++;
}
public
void
addSuccess
()
{
this
.
success
++;
}
public
void
addTotalAssertions
(
int
count
)
{
this
.
totalAssertions
+=
count
;
}
public
void
addPassAssertions
(
int
count
)
{
this
.
passAssertions
+=
count
;
}
}
backend/src/main/java/io/metersphere/api/jmeter/TestResult.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.jmeter
;
import
lombok.Data
;
import
java.util.ArrayList
;
import
java.util.List
;
@Data
public
class
TestResult
{
private
String
id
;
private
int
success
=
0
;
private
int
error
=
0
;
private
int
total
=
0
;
private
int
totalAssertions
=
0
;
private
int
passAssertions
=
0
;
private
final
List
<
ScenarioResult
>
scenarios
=
new
ArrayList
<>();
public
void
addError
()
{
this
.
error
++;
}
public
void
addSuccess
()
{
this
.
success
++;
}
public
void
addTotalAssertions
(
int
count
)
{
this
.
totalAssertions
+=
count
;
}
public
void
addPassAssertions
(
int
count
)
{
this
.
passAssertions
+=
count
;
}
}
backend/src/main/java/io/metersphere/api/service/APIReportService.java
0 → 100644
浏览文件 @
e5e7737f
package
io.metersphere.api.service
;
import
com.alibaba.fastjson.JSONObject
;
import
io.metersphere.api.dto.APIReportResult
;
import
io.metersphere.api.dto.DeleteAPIReportRequest
;
import
io.metersphere.api.dto.QueryAPIReportRequest
;
import
io.metersphere.api.jmeter.TestResult
;
import
io.metersphere.base.domain.ApiTestReport
;
import
io.metersphere.base.domain.ApiTestWithBLOBs
;
import
io.metersphere.base.mapper.ApiTestReportMapper
;
import
io.metersphere.base.mapper.ext.ExtApiTestReportMapper
;
import
io.metersphere.commons.constants.APITestStatus
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
java.util.List
;
import
java.util.UUID
;
import
javax.annotation.Resource
;
@Service
@Transactional
(
rollbackFor
=
Exception
.
class
)
public
class
APIReportService
{
@Resource
private
APITestService
apiTestService
;
@Resource
private
ApiTestReportMapper
apiTestReportMapper
;
@Resource
private
ExtApiTestReportMapper
extApiTestReportMapper
;
public
List
<
APIReportResult
>
list
(
QueryAPIReportRequest
request
)
{
return
extApiTestReportMapper
.
list
(
request
);
}
public
List
<
APIReportResult
>
recentTest
(
QueryAPIReportRequest
request
)
{
request
.
setRecent
(
true
);
return
extApiTestReportMapper
.
list
(
request
);
}
public
ApiTestReport
get
(
String
id
)
{
return
apiTestReportMapper
.
selectByPrimaryKey
(
id
);
}
public
List
<
APIReportResult
>
listByTestId
(
String
testId
)
{
return
extApiTestReportMapper
.
listByTestId
(
testId
);
}
public
void
delete
(
DeleteAPIReportRequest
request
)
{
apiTestReportMapper
.
deleteByPrimaryKey
(
request
.
getId
());
}
public
void
save
(
TestResult
result
)
{
ApiTestWithBLOBs
test
=
apiTestService
.
get
(
result
.
getId
());
ApiTestReport
report
=
new
ApiTestReport
();
report
.
setId
(
UUID
.
randomUUID
().
toString
());
report
.
setTestId
(
result
.
getId
());
report
.
setName
(
test
.
getName
());
report
.
setDescription
(
test
.
getDescription
());
report
.
setContent
(
JSONObject
.
toJSONString
(
result
));
report
.
setCreateTime
(
System
.
currentTimeMillis
());
report
.
setUpdateTime
(
System
.
currentTimeMillis
());
report
.
setStatus
(
APITestStatus
.
Completed
.
name
());
apiTestReportMapper
.
insert
(
report
);
}
}
backend/src/main/java/io/metersphere/api/service/A
pi
TestService.java
→
backend/src/main/java/io/metersphere/api/service/A
PI
TestService.java
浏览文件 @
e5e7737f
...
...
@@ -13,22 +13,21 @@ import io.metersphere.commons.constants.APITestStatus;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.i18n.Translator
;
import
io.metersphere.service.FileService
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.web.multipart.MultipartFile
;
import
java.io.IOException
;
import
java.io.ByteArrayInputStream
;
import
java.io.InputStream
;
import
java.util.List
;
import
java.util.UUID
;
import
java.util.stream.Collectors
;
import
javax.annotation.Resource
;
@Service
@Transactional
(
rollbackFor
=
Exception
.
class
)
public
class
A
pi
TestService
{
public
class
A
PI
TestService
{
@Resource
private
ApiTestMapper
apiTestMapper
;
...
...
@@ -50,28 +49,21 @@ public class ApiTestService {
return
extApiTestMapper
.
list
(
request
);
}
public
String
sav
e
(
SaveAPITestRequest
request
,
List
<
MultipartFile
>
files
)
{
public
void
creat
e
(
SaveAPITestRequest
request
,
List
<
MultipartFile
>
files
)
{
if
(
files
==
null
||
files
.
isEmpty
())
{
throw
new
IllegalArgumentException
(
Translator
.
get
(
"file_cannot_be_null"
));
}
ApiTestWithBLOBs
test
=
createTest
(
request
);
saveFile
(
test
.
getId
(),
files
);
}
final
ApiTestWithBLOBs
test
;
if
(
StringUtils
.
isNotBlank
(
request
.
getId
()))
{
// 删除原来的文件
deleteFileByTestId
(
request
.
getId
());
test
=
updateTest
(
request
);
}
else
{
test
=
createTest
(
request
);
public
void
update
(
SaveAPITestRequest
request
,
List
<
MultipartFile
>
files
)
{
if
(
files
==
null
||
files
.
isEmpty
())
{
throw
new
IllegalArgumentException
(
Translator
.
get
(
"file_cannot_be_null"
));
}
// 保存新文件
files
.
forEach
(
file
->
{
final
FileMetadata
fileMetadata
=
fileService
.
saveFile
(
file
);
ApiTestFile
apiTestFile
=
new
ApiTestFile
();
apiTestFile
.
setTestId
(
test
.
getId
());
apiTestFile
.
setFileId
(
fileMetadata
.
getId
());
apiTestFileMapper
.
insert
(
apiTestFile
);
});
return
test
.
getId
();
deleteFileByTestId
(
request
.
getId
());
ApiTestWithBLOBs
test
=
updateTest
(
request
);
saveFile
(
test
.
getId
(),
files
);
}
public
ApiTestWithBLOBs
get
(
String
id
)
{
...
...
@@ -83,15 +75,15 @@ public class ApiTestService {
apiTestMapper
.
deleteByPrimaryKey
(
request
.
getId
());
}
public
String
run
(
SaveAPITestRequest
request
,
List
<
MultipartFile
>
files
)
{
String
id
=
save
(
request
,
files
);
try
{
changeStatus
(
request
.
getId
(),
APITestStatus
.
Running
);
jMeterService
.
run
(
files
.
get
(
0
).
getInputStream
());
}
catch
(
IOException
e
)
{
MSException
.
throwException
(
Translator
.
get
(
"api_load_script_error"
));
public
void
run
(
SaveAPITestRequest
request
)
{
ApiTestFile
file
=
getFileByTestId
(
request
.
getId
());
if
(
file
==
null
)
{
MSException
.
throwException
(
Translator
.
get
(
"file_cannot_be_null"
));
}
return
id
;
byte
[]
bytes
=
fileService
.
loadFileAsBytes
(
file
.
getFileId
());
InputStream
is
=
new
ByteArrayInputStream
(
bytes
);
changeStatus
(
request
.
getId
(),
APITestStatus
.
Running
);
jMeterService
.
run
(
is
);
}
public
void
changeStatus
(
String
id
,
APITestStatus
status
)
{
...
...
@@ -121,7 +113,7 @@ public class ApiTestService {
}
final
ApiTestWithBLOBs
test
=
new
ApiTestWithBLOBs
();
test
.
setId
(
UUID
.
randomUUID
().
toString
());
test
.
setId
(
request
.
getId
());
test
.
setName
(
request
.
getName
());
test
.
setProjectId
(
request
.
getProjectId
());
test
.
setScenarioDefinition
(
request
.
getScenarioDefinition
());
...
...
@@ -132,6 +124,16 @@ public class ApiTestService {
return
test
;
}
private
void
saveFile
(
String
testId
,
List
<
MultipartFile
>
files
)
{
files
.
forEach
(
file
->
{
final
FileMetadata
fileMetadata
=
fileService
.
saveFile
(
file
);
ApiTestFile
apiTestFile
=
new
ApiTestFile
();
apiTestFile
.
setTestId
(
testId
);
apiTestFile
.
setFileId
(
fileMetadata
.
getId
());
apiTestFileMapper
.
insert
(
apiTestFile
);
});
}
private
void
deleteFileByTestId
(
String
testId
)
{
ApiTestFileExample
ApiTestFileExample
=
new
ApiTestFileExample
();
ApiTestFileExample
.
createCriteria
().
andTestIdEqualTo
(
testId
);
...
...
backend/src/main/java/io/metersphere/base/domain/LoadTestReportLog.java
浏览文件 @
e5e7737f
...
...
@@ -6,7 +6,7 @@ import java.io.Serializable;
@Data
public
class
LoadTestReportLog
implements
Serializable
{
private
Lo
ng
id
;
private
Stri
ng
id
;
private
String
reportId
;
...
...
backend/src/main/java/io/metersphere/base/domain/LoadTestReportLogExample.java
浏览文件 @
e5e7737f
...
...
@@ -114,52 +114,62 @@ public class LoadTestReportLogExample {
return
(
Criteria
)
this
;
}
public
Criteria
andIdEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id ="
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdNotEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id <>"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdGreaterThan
(
Lo
ng
value
)
{
public
Criteria
andIdGreaterThan
(
Stri
ng
value
)
{
addCriterion
(
"id >"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdGreaterThanOrEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdGreaterThanOrEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id >="
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdLessThan
(
Lo
ng
value
)
{
public
Criteria
andIdLessThan
(
Stri
ng
value
)
{
addCriterion
(
"id <"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdLessThanOrEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdLessThanOrEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id <="
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdIn
(
List
<
Long
>
values
)
{
public
Criteria
andIdLike
(
String
value
)
{
addCriterion
(
"id like"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotLike
(
String
value
)
{
addCriterion
(
"id not like"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdIn
(
List
<
String
>
values
)
{
addCriterion
(
"id in"
,
values
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotIn
(
List
<
Lo
ng
>
values
)
{
public
Criteria
andIdNotIn
(
List
<
Stri
ng
>
values
)
{
addCriterion
(
"id not in"
,
values
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdBetween
(
Long
value1
,
Lo
ng
value2
)
{
public
Criteria
andIdBetween
(
String
value1
,
Stri
ng
value2
)
{
addCriterion
(
"id between"
,
value1
,
value2
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotBetween
(
Long
value1
,
Lo
ng
value2
)
{
public
Criteria
andIdNotBetween
(
String
value1
,
Stri
ng
value2
)
{
addCriterion
(
"id not between"
,
value1
,
value2
,
"id"
);
return
(
Criteria
)
this
;
}
...
...
backend/src/main/java/io/metersphere/base/domain/LoadTestReportResult.java
浏览文件 @
e5e7737f
...
...
@@ -6,7 +6,7 @@ import java.io.Serializable;
@Data
public
class
LoadTestReportResult
implements
Serializable
{
private
Lo
ng
id
;
private
Stri
ng
id
;
private
String
reportId
;
...
...
backend/src/main/java/io/metersphere/base/domain/LoadTestReportResultExample.java
浏览文件 @
e5e7737f
...
...
@@ -114,52 +114,62 @@ public class LoadTestReportResultExample {
return
(
Criteria
)
this
;
}
public
Criteria
andIdEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id ="
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdNotEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id <>"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdGreaterThan
(
Lo
ng
value
)
{
public
Criteria
andIdGreaterThan
(
Stri
ng
value
)
{
addCriterion
(
"id >"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdGreaterThanOrEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdGreaterThanOrEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id >="
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdLessThan
(
Lo
ng
value
)
{
public
Criteria
andIdLessThan
(
Stri
ng
value
)
{
addCriterion
(
"id <"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdLessThanOrEqualTo
(
Lo
ng
value
)
{
public
Criteria
andIdLessThanOrEqualTo
(
Stri
ng
value
)
{
addCriterion
(
"id <="
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdIn
(
List
<
Long
>
values
)
{
public
Criteria
andIdLike
(
String
value
)
{
addCriterion
(
"id like"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotLike
(
String
value
)
{
addCriterion
(
"id not like"
,
value
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdIn
(
List
<
String
>
values
)
{
addCriterion
(
"id in"
,
values
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotIn
(
List
<
Lo
ng
>
values
)
{
public
Criteria
andIdNotIn
(
List
<
Stri
ng
>
values
)
{
addCriterion
(
"id not in"
,
values
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdBetween
(
Long
value1
,
Lo
ng
value2
)
{
public
Criteria
andIdBetween
(
String
value1
,
Stri
ng
value2
)
{
addCriterion
(
"id between"
,
value1
,
value2
,
"id"
);
return
(
Criteria
)
this
;
}
public
Criteria
andIdNotBetween
(
Long
value1
,
Lo
ng
value2
)
{
public
Criteria
andIdNotBetween
(
String
value1
,
Stri
ng
value2
)
{
addCriterion
(
"id not between"
,
value1
,
value2
,
"id"
);
return
(
Criteria
)
this
;
}
...
...
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportLogMapper.java
浏览文件 @
e5e7737f
...
...
@@ -11,7 +11,7 @@ public interface LoadTestReportLogMapper {
int
deleteByExample
(
LoadTestReportLogExample
example
);
int
deleteByPrimaryKey
(
Lo
ng
id
);
int
deleteByPrimaryKey
(
Stri
ng
id
);
int
insert
(
LoadTestReportLog
record
);
...
...
@@ -21,7 +21,7 @@ public interface LoadTestReportLogMapper {
List
<
LoadTestReportLog
>
selectByExample
(
LoadTestReportLogExample
example
);
LoadTestReportLog
selectByPrimaryKey
(
Lo
ng
id
);
LoadTestReportLog
selectByPrimaryKey
(
Stri
ng
id
);
int
updateByExampleSelective
(
@Param
(
"record"
)
LoadTestReportLog
record
,
@Param
(
"example"
)
LoadTestReportLogExample
example
);
...
...
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportLogMapper.xml
浏览文件 @
e5e7737f
...
...
@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"io.metersphere.base.mapper.LoadTestReportLogMapper"
>
<resultMap
id=
"BaseResultMap"
type=
"io.metersphere.base.domain.LoadTestReportLog"
>
<id
column=
"id"
jdbcType=
"
BIGINT
"
property=
"id"
/>
<id
column=
"id"
jdbcType=
"
VARCHAR
"
property=
"id"
/>
<result
column=
"report_id"
jdbcType=
"VARCHAR"
property=
"reportId"
/>
<result
column=
"resource_id"
jdbcType=
"VARCHAR"
property=
"resourceId"
/>
</resultMap>
...
...
@@ -103,17 +103,17 @@
order by ${orderByClause}
</if>
</select>
<select
id=
"selectByPrimaryKey"
parameterType=
"java.lang.
Lo
ng"
resultMap=
"ResultMapWithBLOBs"
>
<select
id=
"selectByPrimaryKey"
parameterType=
"java.lang.
Stri
ng"
resultMap=
"ResultMapWithBLOBs"
>
select
<include
refid=
"Base_Column_List"
/>
,
<include
refid=
"Blob_Column_List"
/>
from load_test_report_log
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</select>
<delete
id=
"deleteByPrimaryKey"
parameterType=
"java.lang.
Lo
ng"
>
<delete
id=
"deleteByPrimaryKey"
parameterType=
"java.lang.
Stri
ng"
>
delete from load_test_report_log
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</delete>
<delete
id=
"deleteByExample"
parameterType=
"io.metersphere.base.domain.LoadTestReportLogExample"
>
delete from load_test_report_log
...
...
@@ -124,7 +124,7 @@
<insert
id=
"insert"
parameterType=
"io.metersphere.base.domain.LoadTestReportLog"
>
insert into load_test_report_log (id, report_id, resource_id,
content)
values (#{id,jdbcType=
BIGINT
}, #{reportId,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
values (#{id,jdbcType=
VARCHAR
}, #{reportId,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
#{content,jdbcType=LONGVARCHAR})
</insert>
<insert
id=
"insertSelective"
parameterType=
"io.metersphere.base.domain.LoadTestReportLog"
>
...
...
@@ -145,7 +145,7 @@
</trim>
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
#{id,jdbcType=
BIGINT
},
#{id,jdbcType=
VARCHAR
},
</if>
<if
test=
"reportId != null"
>
#{reportId,jdbcType=VARCHAR},
...
...
@@ -168,7 +168,7 @@
update load_test_report_log
<set>
<if
test=
"record.id != null"
>
id = #{record.id,jdbcType=
BIGINT
},
id = #{record.id,jdbcType=
VARCHAR
},
</if>
<if
test=
"record.reportId != null"
>
report_id = #{record.reportId,jdbcType=VARCHAR},
...
...
@@ -186,7 +186,7 @@
</update>
<update
id=
"updateByExampleWithBLOBs"
parameterType=
"map"
>
update load_test_report_log
set id = #{record.id,jdbcType=
BIGINT
},
set id = #{record.id,jdbcType=
VARCHAR
},
report_id = #{record.reportId,jdbcType=VARCHAR},
resource_id = #{record.resourceId,jdbcType=VARCHAR},
content = #{record.content,jdbcType=LONGVARCHAR}
...
...
@@ -196,7 +196,7 @@
</update>
<update
id=
"updateByExample"
parameterType=
"map"
>
update load_test_report_log
set id = #{record.id,jdbcType=
BIGINT
},
set id = #{record.id,jdbcType=
VARCHAR
},
report_id = #{record.reportId,jdbcType=VARCHAR},
resource_id = #{record.resourceId,jdbcType=VARCHAR}
<if
test=
"_parameter != null"
>
...
...
@@ -216,19 +216,19 @@
content = #{content,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</update>
<update
id=
"updateByPrimaryKeyWithBLOBs"
parameterType=
"io.metersphere.base.domain.LoadTestReportLog"
>
update load_test_report_log
set report_id = #{reportId,jdbcType=VARCHAR},
resource_id = #{resourceId,jdbcType=VARCHAR},
content = #{content,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</update>
<update
id=
"updateByPrimaryKey"
parameterType=
"io.metersphere.base.domain.LoadTestReportLog"
>
update load_test_report_log
set report_id = #{reportId,jdbcType=VARCHAR},
resource_id = #{resourceId,jdbcType=VARCHAR}
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</update>
</mapper>
\ No newline at end of file
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportResultMapper.java
浏览文件 @
e5e7737f
...
...
@@ -11,7 +11,7 @@ public interface LoadTestReportResultMapper {
int
deleteByExample
(
LoadTestReportResultExample
example
);
int
deleteByPrimaryKey
(
Lo
ng
id
);
int
deleteByPrimaryKey
(
Stri
ng
id
);
int
insert
(
LoadTestReportResult
record
);
...
...
@@ -21,7 +21,7 @@ public interface LoadTestReportResultMapper {
List
<
LoadTestReportResult
>
selectByExample
(
LoadTestReportResultExample
example
);
LoadTestReportResult
selectByPrimaryKey
(
Lo
ng
id
);
LoadTestReportResult
selectByPrimaryKey
(
Stri
ng
id
);
int
updateByExampleSelective
(
@Param
(
"record"
)
LoadTestReportResult
record
,
@Param
(
"example"
)
LoadTestReportResultExample
example
);
...
...
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportResultMapper.xml
浏览文件 @
e5e7737f
...
...
@@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"io.metersphere.base.mapper.LoadTestReportResultMapper"
>
<resultMap
id=
"BaseResultMap"
type=
"io.metersphere.base.domain.LoadTestReportResult"
>
<id
column=
"id"
jdbcType=
"
BIGINT
"
property=
"id"
/>
<id
column=
"id"
jdbcType=
"
VARCHAR
"
property=
"id"
/>
<result
column=
"report_id"
jdbcType=
"VARCHAR"
property=
"reportId"
/>
<result
column=
"report_key"
jdbcType=
"VARCHAR"
property=
"reportKey"
/>
</resultMap>
...
...
@@ -103,17 +103,17 @@
order by ${orderByClause}
</if>
</select>
<select
id=
"selectByPrimaryKey"
parameterType=
"java.lang.
Lo
ng"
resultMap=
"ResultMapWithBLOBs"
>
<select
id=
"selectByPrimaryKey"
parameterType=
"java.lang.
Stri
ng"
resultMap=
"ResultMapWithBLOBs"
>
select
<include
refid=
"Base_Column_List"
/>
,
<include
refid=
"Blob_Column_List"
/>
from load_test_report_result
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</select>
<delete
id=
"deleteByPrimaryKey"
parameterType=
"java.lang.
Lo
ng"
>
<delete
id=
"deleteByPrimaryKey"
parameterType=
"java.lang.
Stri
ng"
>
delete from load_test_report_result
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</delete>
<delete
id=
"deleteByExample"
parameterType=
"io.metersphere.base.domain.LoadTestReportResultExample"
>
delete from load_test_report_result
...
...
@@ -124,7 +124,7 @@
<insert
id=
"insert"
parameterType=
"io.metersphere.base.domain.LoadTestReportResult"
>
insert into load_test_report_result (id, report_id, report_key,
report_value)
values (#{id,jdbcType=
BIGINT
}, #{reportId,jdbcType=VARCHAR}, #{reportKey,jdbcType=VARCHAR},
values (#{id,jdbcType=
VARCHAR
}, #{reportId,jdbcType=VARCHAR}, #{reportKey,jdbcType=VARCHAR},
#{reportValue,jdbcType=LONGVARCHAR})
</insert>
<insert
id=
"insertSelective"
parameterType=
"io.metersphere.base.domain.LoadTestReportResult"
>
...
...
@@ -145,7 +145,7 @@
</trim>
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"id != null"
>
#{id,jdbcType=
BIGINT
},
#{id,jdbcType=
VARCHAR
},
</if>
<if
test=
"reportId != null"
>
#{reportId,jdbcType=VARCHAR},
...
...
@@ -168,7 +168,7 @@
update load_test_report_result
<set>
<if
test=
"record.id != null"
>
id = #{record.id,jdbcType=
BIGINT
},
id = #{record.id,jdbcType=
VARCHAR
},
</if>
<if
test=
"record.reportId != null"
>
report_id = #{record.reportId,jdbcType=VARCHAR},
...
...
@@ -186,7 +186,7 @@
</update>
<update
id=
"updateByExampleWithBLOBs"
parameterType=
"map"
>
update load_test_report_result
set id = #{record.id,jdbcType=
BIGINT
},
set id = #{record.id,jdbcType=
VARCHAR
},
report_id = #{record.reportId,jdbcType=VARCHAR},
report_key = #{record.reportKey,jdbcType=VARCHAR},
report_value = #{record.reportValue,jdbcType=LONGVARCHAR}
...
...
@@ -196,7 +196,7 @@
</update>
<update
id=
"updateByExample"
parameterType=
"map"
>
update load_test_report_result
set id = #{record.id,jdbcType=
BIGINT
},
set id = #{record.id,jdbcType=
VARCHAR
},
report_id = #{record.reportId,jdbcType=VARCHAR},
report_key = #{record.reportKey,jdbcType=VARCHAR}
<if
test=
"_parameter != null"
>
...
...
@@ -216,19 +216,19 @@
report_value = #{reportValue,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</update>
<update
id=
"updateByPrimaryKeyWithBLOBs"
parameterType=
"io.metersphere.base.domain.LoadTestReportResult"
>
update load_test_report_result
set report_id = #{reportId,jdbcType=VARCHAR},
report_key = #{reportKey,jdbcType=VARCHAR},
report_value = #{reportValue,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</update>
<update
id=
"updateByPrimaryKey"
parameterType=
"io.metersphere.base.domain.LoadTestReportResult"
>
update load_test_report_result
set report_id = #{reportId,jdbcType=VARCHAR},
report_key = #{reportKey,jdbcType=VARCHAR}
where id = #{id,jdbcType=
BIGINT
}
where id = #{id,jdbcType=
VARCHAR
}
</update>
</mapper>
\ No newline at end of file
backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.java
浏览文件 @
e5e7737f
package
io.metersphere.base.mapper.ext
;
import
io.metersphere.controller.request.ReportRequest
;
import
io.metersphere.api.dto.APIReportResult
;
import
io.metersphere.api.dto.QueryAPIReportRequest
;
import
io.metersphere.dto.ApiReportDTO
;
import
org.apache.ibatis.annotations.Param
;
...
...
@@ -8,7 +9,8 @@ import java.util.List;
public
interface
ExtApiTestReportMapper
{
List
<
ApiReportDTO
>
getReportList
(
@Param
(
"reportRequest"
)
ReportRequest
request
);
List
<
APIReportResult
>
list
(
@Param
(
"request"
)
QueryAPIReportRequest
request
);
List
<
APIReportResult
>
listByTestId
(
@Param
(
"testId"
)
String
testId
);
ApiReportDTO
getReportTestAndProInfo
(
@Param
(
"id"
)
String
id
);
}
backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestReportMapper.xml
浏览文件 @
e5e7737f
...
...
@@ -2,24 +2,43 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper
namespace=
"io.metersphere.base.mapper.ext.ExtApiTestReportMapper"
>
<select
id=
"getReportList"
resultType=
"io.metersphere.dto.ApiReportDTO"
>
select ltr.id, ltr.name, ltr.test_id as testId, ltr.description,
ltr.create_time as createTime, ltr.update_time as updateTime, ltr.status as status, lt.name as testName
from api_test_report ltr left join api_test lt on ltr.test_id = lt.id
<resultMap
id=
"BaseResultMap"
type=
"io.metersphere.api.dto.APIReportResult"
extends=
"io.metersphere.base.mapper.ApiTestReportMapper.BaseResultMap"
>
<result
column=
"project_name"
property=
"projectName"
/>
</resultMap>
<select
id=
"list"
resultMap=
"BaseResultMap"
>
SELECT t.name, t.description,
r.id, r.test_id, r.create_time, r.update_time, r.status,
project.name AS project_name
FROM api_test_report r JOIN api_test t ON r.test_id = t.id
JOIN project ON project.id = t.project_id
<where>
<if
test=
"reportRequest.name != null"
>
AND ltr.name like CONCAT('%', #{reportRequest.name},'%')
<if
test=
"request.name != null"
>
AND r.name like CONCAT('%', #{request.name},'%')
</if>
<if
test=
"request.projectId != null"
>
AND project.id = #{request.projectId}
</if>
<if
test=
"request.workspaceId != null"
>
AND project.workspace_id = #{request.workspaceId,jdbcType=VARCHAR}
</if>
</where>
<if
test=
"request.recent"
>
ORDER BY r.update_time DESC
</if>
</select>
<select
id=
"getReportTestAndProInfo"
resultType=
"io.metersphere.dto.ApiReportDTO"
>
select ltr.id, ltr.name, ltr.test_id as testId, ltr.description,
ltr.create_time as createTime, ltr.update_time as updateTime, ltr.status as status, ltr.content as content,
lt.name as testName,
p.id as projectId, p.name as projectName
from api_test_report ltr left join api_test lt on ltr.test_id = lt.id left join project p on lt.project_id = p.id
where ltr.id = #{id}
<select
id=
"listByTestId"
resultMap=
"BaseResultMap"
>
SELECT t.name, t.description,
r.id, r.test_id, r.create_time, r.update_time, r.status,
project.name AS project_name
FROM api_test_report r JOIN api_test t ON r.test_id = t.id
JOIN project ON project.id = t.project_id
<where>
r.test_id = #{testId}
</where>
ORDER BY r.update_time DESC
</select>
</mapper>
\ No newline at end of file
backend/src/main/java/io/metersphere/controller/TestResourcePoolController.java
浏览文件 @
e5e7737f
...
...
@@ -3,11 +3,14 @@ package io.metersphere.controller;
import
com.github.pagehelper.Page
;
import
com.github.pagehelper.PageHelper
;
import
io.metersphere.base.domain.TestResourcePool
;
import
io.metersphere.commons.constants.RoleConstants
;
import
io.metersphere.commons.utils.PageUtils
;
import
io.metersphere.commons.utils.Pager
;
import
io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest
;
import
io.metersphere.dto.TestResourcePoolDTO
;
import
io.metersphere.service.TestResourcePoolService
;
import
org.apache.shiro.authz.annotation.Logical
;
import
org.apache.shiro.authz.annotation.RequiresRoles
;
import
org.springframework.web.bind.annotation.*
;
import
javax.annotation.Resource
;
...
...
@@ -15,6 +18,7 @@ import java.util.List;
@RequestMapping
(
"testresourcepool"
)
@RestController
@RequiresRoles
(
RoleConstants
.
ADMIN
)
public
class
TestResourcePoolController
{
@Resource
...
...
@@ -35,6 +39,11 @@ public class TestResourcePoolController {
testResourcePoolService
.
updateTestResourcePool
(
testResourcePoolDTO
);
}
@GetMapping
(
"/update/{poolId}/{status}"
)
public
void
updateTestResourcePoolStatus
(
@PathVariable
String
poolId
,
@PathVariable
String
status
)
{
testResourcePoolService
.
updateTestResourcePoolStatus
(
poolId
,
status
);
}
@PostMapping
(
"list/{goPage}/{pageSize}"
)
public
Pager
<
List
<
TestResourcePoolDTO
>>
listResourcePools
(
@PathVariable
int
goPage
,
@PathVariable
int
pageSize
,
@RequestBody
QueryResourcePoolRequest
request
)
{
Page
<
Object
>
page
=
PageHelper
.
startPage
(
goPage
,
pageSize
,
true
);
...
...
@@ -42,6 +51,7 @@ public class TestResourcePoolController {
}
@GetMapping
(
"list/all/valid"
)
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_MANAGER
,
RoleConstants
.
TEST_USER
,
RoleConstants
.
TEST_VIEWER
},
logical
=
Logical
.
OR
)
public
List
<
TestResourcePool
>
listValidResourcePools
()
{
return
testResourcePoolService
.
listValidResourcePools
();
}
...
...
backend/src/main/java/io/metersphere/controller/UserRoleController.java
浏览文件 @
e5e7737f
...
...
@@ -27,7 +27,7 @@ public class UserRoleController {
@GetMapping
(
"/list/ws/{workspaceId}/{userId}"
)
@RequiresRoles
(
value
=
{
RoleConstants
.
ADMIN
,
RoleConstants
.
ORG_ADMIN
},
logical
=
Logical
.
OR
)
public
List
<
Role
>
getWorkspaceMemberRole
(
@PathVariable
String
workspaceId
,
@PathVariable
String
userId
)
{
public
List
<
Role
>
getWorkspaceMemberRole
s
(
@PathVariable
String
workspaceId
,
@PathVariable
String
userId
)
{
return
userRoleService
.
getWorkspaceMemberRoles
(
workspaceId
,
userId
);
}
}
backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java
浏览文件 @
e5e7737f
...
...
@@ -63,7 +63,7 @@ public class KubernetesTestEngine extends AbstractEngine {
kubernetesProvider
.
confirmNamespace
(
context
.
getNamespace
());
// create cm
try
(
KubernetesClient
client
=
kubernetesProvider
.
getKubernetesClient
())
{
String
configMapName
=
context
.
getTestId
()
+
"-files"
;
String
configMapName
=
"jmeter-"
+
context
.
getTestId
()
+
"-files"
;
ConfigMap
configMap
=
client
.
configMaps
().
inNamespace
(
context
.
getNamespace
()).
withName
(
configMapName
).
get
();
if
(
configMap
==
null
)
{
ConfigMap
item
=
new
ConfigMap
();
...
...
backend/src/main/java/io/metersphere/service/ApiReportService.java
已删除
100644 → 0
浏览文件 @
c5fb0118
package
io.metersphere.service
;
import
io.metersphere.base.domain.ApiTestReport
;
import
io.metersphere.base.domain.ApiTestReportExample
;
import
io.metersphere.base.mapper.ApiTestReportMapper
;
import
io.metersphere.base.mapper.ext.ExtApiTestReportMapper
;
import
io.metersphere.controller.request.ReportRequest
;
import
io.metersphere.dto.ApiReportDTO
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
javax.annotation.Resource
;
import
java.util.List
;
@Service
@Transactional
(
rollbackFor
=
Exception
.
class
)
public
class
ApiReportService
{
@Resource
private
ApiTestReportMapper
ApiTestReportMapper
;
@Resource
private
ExtApiTestReportMapper
extApiTestReportMapper
;
public
List
<
ApiTestReport
>
getRecentReportList
(
ReportRequest
request
)
{
ApiTestReportExample
example
=
new
ApiTestReportExample
();
example
.
setOrderByClause
(
"update_time desc"
);
return
ApiTestReportMapper
.
selectByExample
(
example
);
}
public
List
<
ApiReportDTO
>
getReportList
(
ReportRequest
request
)
{
return
extApiTestReportMapper
.
getReportList
(
request
);
}
public
void
deleteReport
(
String
reportId
)
{
ApiTestReportMapper
.
deleteByPrimaryKey
(
reportId
);
}
public
ApiReportDTO
getReportTestAndProInfo
(
String
reportId
)
{
return
extApiTestReportMapper
.
getReportTestAndProInfo
(
reportId
);
}
}
backend/src/main/java/io/metersphere/service/PerformanceTestService.java
浏览文件 @
e5e7737f
...
...
@@ -228,6 +228,7 @@ public class PerformanceTestService {
List
<
TestResource
>
testResourceList
=
testResourceService
.
getResourcesByPoolId
(
resourcePoolId
);
testResourceList
.
forEach
(
r
->
{
LoadTestReportLog
record
=
new
LoadTestReportLog
();
record
.
setId
(
UUID
.
randomUUID
().
toString
());
record
.
setReportId
(
testReport
.
getId
());
record
.
setResourceId
(
r
.
getId
());
record
.
setContent
(
StringUtils
.
EMPTY
);
...
...
backend/src/main/java/io/metersphere/service/TestResourcePoolService.java
浏览文件 @
e5e7737f
...
...
@@ -72,6 +72,36 @@ public class TestResourcePoolService {
testResourcePoolMapper
.
updateByPrimaryKeySelective
(
testResourcePool
);
}
public
void
updateTestResourcePoolStatus
(
String
poolId
,
String
status
)
{
TestResourcePool
testResourcePool
=
testResourcePoolMapper
.
selectByPrimaryKey
(
poolId
);
if
(
testResourcePool
==
null
)
{
MSException
.
throwException
(
"Resource Pool not found."
);
}
testResourcePool
.
setUpdateTime
(
System
.
currentTimeMillis
());
testResourcePool
.
setStatus
(
status
);
// 禁用资源池
if
(
INVALID
.
name
().
equals
(
status
))
{
testResourcePoolMapper
.
updateByPrimaryKeySelective
(
testResourcePool
);
return
;
}
TestResourcePoolDTO
testResourcePoolDTO
=
new
TestResourcePoolDTO
();
try
{
BeanUtils
.
copyProperties
(
testResourcePoolDTO
,
testResourcePool
);
TestResourceExample
example2
=
new
TestResourceExample
();
example2
.
createCriteria
().
andTestResourcePoolIdEqualTo
(
poolId
);
List
<
TestResource
>
testResources
=
testResourceMapper
.
selectByExampleWithBLOBs
(
example2
);
testResourcePoolDTO
.
setResources
(
testResources
);
if
(
validateTestResourcePool
(
testResourcePoolDTO
))
{
testResourcePoolMapper
.
updateByPrimaryKeySelective
(
testResourcePool
);
}
else
{
MSException
.
throwException
(
"Resource Pool is invalid."
);
}
}
catch
(
IllegalAccessException
|
InvocationTargetException
e
)
{
LogUtil
.
error
(
e
);
}
}
public
List
<
TestResourcePoolDTO
>
listResourcePools
(
QueryResourcePoolRequest
request
)
{
TestResourcePoolExample
example
=
new
TestResourcePoolExample
();
TestResourcePoolExample
.
Criteria
criteria
=
example
.
createCriteria
();
...
...
@@ -96,15 +126,14 @@ public class TestResourcePoolService {
return
testResourcePoolDTOS
;
}
private
void
validateTestResourcePool
(
TestResourcePoolDTO
testResourcePool
)
{
private
boolean
validateTestResourcePool
(
TestResourcePoolDTO
testResourcePool
)
{
if
(
StringUtils
.
equalsIgnoreCase
(
testResourcePool
.
getType
(),
ResourcePoolTypeEnum
.
K8S
.
name
()))
{
validateK8s
(
testResourcePool
);
return
;
return
validateK8s
(
testResourcePool
);
}
validateNodes
(
testResourcePool
);
return
validateNodes
(
testResourcePool
);
}
private
void
validateNodes
(
TestResourcePoolDTO
testResourcePool
)
{
private
boolean
validateNodes
(
TestResourcePoolDTO
testResourcePool
)
{
if
(
CollectionUtils
.
isEmpty
(
testResourcePool
.
getResources
()))
{
MSException
.
throwException
(
Translator
.
get
(
"no_nodes_message"
));
}
...
...
@@ -121,19 +150,21 @@ public class TestResourcePoolService {
MSException
.
throwException
(
Translator
.
get
(
"duplicate_node_ip"
));
}
testResourcePool
.
setStatus
(
VALID
.
name
());
boolean
isValid
=
true
;
for
(
TestResource
resource
:
testResourcePool
.
getResources
())
{
NodeDTO
nodeDTO
=
JSON
.
parseObject
(
resource
.
getConfiguration
(),
NodeDTO
.
class
);
boolean
isValidate
=
validateNode
(
nodeDTO
);
if
(!
isValidate
)
{
testResourcePool
.
setStatus
(
ResourceStatusEnum
.
INVALID
.
name
());
resource
.
setStatus
(
ResourceStatusEnum
.
INVALID
.
name
());
isValid
=
false
;
}
else
{
resource
.
setStatus
(
VALID
.
name
());
}
resource
.
setTestResourcePoolId
(
testResourcePool
.
getId
());
updateTestResource
(
resource
);
}
return
isValid
;
}
private
boolean
validateNode
(
NodeDTO
node
)
{
...
...
@@ -145,7 +176,7 @@ public class TestResourcePoolService {
}
}
private
void
validateK8s
(
TestResourcePoolDTO
testResourcePool
)
{
private
boolean
validateK8s
(
TestResourcePoolDTO
testResourcePool
)
{
if
(
CollectionUtils
.
isEmpty
(
testResourcePool
.
getResources
())
||
testResourcePool
.
getResources
().
size
()
!=
1
)
{
throw
new
RuntimeException
(
Translator
.
get
(
"only_one_k8s"
));
...
...
@@ -153,18 +184,21 @@ public class TestResourcePoolService {
TestResource
testResource
=
testResourcePool
.
getResources
().
get
(
0
);
testResource
.
setTestResourcePoolId
(
testResourcePool
.
getId
());
boolean
isValid
;
try
{
KubernetesProvider
provider
=
new
KubernetesProvider
(
testResource
.
getConfiguration
());
provider
.
validateCredential
();
testResource
.
setStatus
(
VALID
.
name
());
testResourcePool
.
setStatus
(
VALID
.
name
());
isValid
=
true
;
}
catch
(
Exception
e
)
{
testResource
.
setStatus
(
ResourceStatusEnum
.
INVALID
.
name
());
testResourcePool
.
setStatus
(
ResourceStatusEnum
.
INVALID
.
name
());
isValid
=
false
;
}
deleteTestResource
(
testResourcePool
.
getId
());
updateTestResource
(
testResource
);
return
isValid
;
}
private
void
updateTestResource
(
TestResource
testResource
)
{
...
...
@@ -189,6 +223,10 @@ public class TestResourcePoolService {
List
<
TestResourcePoolDTO
>
testResourcePools
=
listResourcePools
(
request
);
// 重新校验 pool
for
(
TestResourcePoolDTO
pool
:
testResourcePools
)
{
// 手动设置成无效的, 排除
if
(
INVALID
.
name
().
equals
(
pool
.
getStatus
()))
{
continue
;
}
try
{
updateTestResourcePool
(
pool
);
}
catch
(
MSException
e
)
{
...
...
@@ -201,4 +239,5 @@ public class TestResourcePoolService {
example
.
createCriteria
().
andStatusEqualTo
(
ResourceStatusEnum
.
VALID
.
name
());
return
testResourcePoolMapper
.
selectByExample
(
example
);
}
}
backend/src/main/resources/db/migration/V2__metersphere_ddl.sql
浏览文件 @
e5e7737f
CREATE
TABLE
IF
NOT
EXISTS
`file_content`
(
`file_id`
varchar
(
64
)
COLLATE
utf8mb4_bin
NOT
NULL
COMMENT
'File ID'
,
`file_id`
varchar
(
64
)
NOT
NULL
COMMENT
'File ID'
,
`file`
longblob
COMMENT
'File content'
,
PRIMARY
KEY
(
`file_id`
)
)
...
...
@@ -70,29 +70,24 @@ CREATE TABLE IF NOT EXISTS `load_test_report_detail` (
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
CREATE
TABLE
IF
NOT
EXISTS
`load_test_report_log`
(
`id`
varchar
(
50
)
NOT
NULL
,
`report_id`
varchar
(
50
)
NOT
NULL
,
`resource_id`
varchar
(
50
)
DEFAULT
NULL
,
`content`
longtext
,
PRIMARY
KEY
(
`id`
),
KEY
`load_test_report_log_report_id_resource_name_index`
(
`report_id`
,
`resource_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
CREATE
TABLE
IF
NOT
EXISTS
`load_test_report_result`
(
`id`
bigint
(
20
)
NOT
NULL
AUTO_INCREMENT
,
`report_id`
varchar
(
50
)
NOT
NULL
,
`report_key`
varchar
(
64
)
DEFAULT
NULL
,
`report_value`
text
,
`id`
varchar
(
50
)
NOT
NULL
,
`report_id`
varchar
(
50
)
NOT
NULL
,
`report_key`
varchar
(
64
)
DEFAULT
NULL
,
`report_value`
text
,
PRIMARY
KEY
(
`id`
),
KEY
`load_test_report_result_report_id_report_key_index`
(
`report_id`
,
`report_key`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
CREATE
TABLE
IF
NOT
EXISTS
`load_test_report_log`
(
`id`
bigint
(
20
)
NOT
NULL
AUTO_INCREMENT
,
`report_id`
varchar
(
50
)
NOT
NULL
,
`resource_id`
varchar
(
50
)
DEFAULT
NULL
,
`content`
longtext
,
PRIMARY
KEY
(
`id`
),
KEY
`load_test_report_log_report_id_resource_name_index`
(
`report_id`
,
`resource_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
CREATE
TABLE
IF
NOT
EXISTS
`organization`
(
`id`
varchar
(
50
)
NOT
NULL
COMMENT
'Organization ID'
,
...
...
@@ -145,7 +140,7 @@ CREATE TABLE IF NOT EXISTS `system_parameter` (
CREATE
TABLE
IF
NOT
EXISTS
`test_resource`
(
`id`
varchar
(
50
)
NOT
NULL
COMMENT
'Test resource ID'
,
`test_resource_pool_id`
varchar
(
50
)
COLLATE
utf8mb4_bin
NOT
NULL
COMMENT
'Test resource pool ID this test resource belongs to'
,
`test_resource_pool_id`
varchar
(
50
)
NOT
NULL
COMMENT
'Test resource pool ID this test resource belongs to'
,
`configuration`
longtext
COMMENT
'Test resource configuration'
,
`status`
varchar
(
64
)
NOT
NULL
COMMENT
'Test resource status'
,
`create_time`
bigint
(
13
)
NOT
NULL
COMMENT
'Create timestamp'
,
...
...
@@ -217,13 +212,13 @@ CREATE TABLE IF NOT EXISTS `workspace` (
-- api start
CREATE
TABLE
IF
NOT
EXISTS
`api_test`
(
`id`
varchar
(
50
)
COLLATE
utf8mb4_bin
NOT
NULL
COMMENT
'Test ID'
,
`project_id`
varchar
(
50
)
COLLATE
utf8mb4_bin
NOT
NULL
COMMENT
'Project ID this test belongs to'
,
`name`
varchar
(
64
)
COLLATE
utf8mb4_bin
NOT
NULL
COMMENT
'Test name'
,
`description`
varchar
(
255
)
COLLATE
utf8mb4_bin
DEFAULT
NULL
COMMENT
'Test description'
,
`scenario_definition`
longtext
COLLATE
utf8mb4_bin
COMMENT
'Scenario definition (JSON format)'
,
`schedule`
longtext
COLLATE
utf8mb4_bin
COMMENT
'Test schedule (cron list)'
,
`status`
varchar
(
64
)
COLLATE
utf8mb4_bin
DEFAULT
NULL
,
`id`
varchar
(
50
)
NOT
NULL
COMMENT
'Test ID'
,
`project_id`
varchar
(
50
)
NOT
NULL
COMMENT
'Project ID this test belongs to'
,
`name`
varchar
(
64
)
NOT
NULL
COMMENT
'Test name'
,
`description`
varchar
(
255
)
DEFAULT
NULL
COMMENT
'Test description'
,
`scenario_definition`
longtext
COMMENT
'Scenario definition (JSON format)'
,
`schedule`
longtext
COMMENT
'Test schedule (cron list)'
,
`status`
varchar
(
64
)
DEFAULT
NULL
,
`create_time`
bigint
(
13
)
NOT
NULL
COMMENT
'Create timestamp'
,
`update_time`
bigint
(
13
)
NOT
NULL
COMMENT
'Update timestamp'
,
PRIMARY
KEY
(
`id`
)
...
...
frontend/src/business/components/api/head/ApiHeaderMenus.vue
浏览文件 @
e5e7737f
...
...
@@ -32,7 +32,7 @@
<
template
v-slot:title
>
{{
$t
(
'
commons.report
'
)
}}
</
template
>
<ms-recent-list
:options=
"reportRecent"
/>
<el-divider/>
<ms-show-all
:index=
"'/api/report/all'"
/>
<ms-show-all
:index=
"'/api/report/
list/
all'"
/>
<!-- <el-menu-item :index="reportViewPath" class="blank_item"></el-menu-item>-->
</el-submenu>
</el-menu>
...
...
@@ -90,7 +90,7 @@
title
:
this
.
$t
(
'
report.recent
'
),
url
:
"
/api/report/recent/5
"
,
index
:
function
(
item
)
{
return
'
/api/report/view
/
'
+
item
.
id
;
return
'
/api/report/view
?id=
'
+
item
.
id
;
}
}
}
...
...
frontend/src/business/components/api/report/Api
TestRepor
t.vue
→
frontend/src/business/components/api/report/Api
ReportLis
t.vue
浏览文件 @
e5e7737f
...
...
@@ -3,18 +3,10 @@
<div
class=
"main-content"
>
<el-card>
<template
v-slot:header
>
<el-row
type=
"flex"
justify=
"space-between"
align=
"middle"
>
<span
class=
"title"
>
{{
$t
(
'
commons.report
'
)
}}
</span>
<span
class=
"search"
>
<el-input
type=
"text"
size=
"small"
:placeholder=
"$t('report.search_by_name')"
prefix-icon=
"el-icon-search"
maxlength=
"60"
v-model=
"condition"
@
change=
"search"
clearable
/>
</span>
</el-row>
<ms-table-header
:condition.sync=
"condition"
@
search=
"search"
:title=
"$t('commons.test')"
:show-create=
"false"
/>
</
template
>
<el-table
:data=
"tableData"
class=
"test-content"
>
<el-table
:data=
"tableData"
class=
"table-content"
>
<el-table-column
prop=
"name"
:label=
"$t('commons.name')"
...
...
@@ -26,12 +18,6 @@
:label=
"$t('commons.description')"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop=
"testName"
:label=
"$t('report.test_name')"
width=
"150"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
width=
"250"
:label=
"$t('commons.create_time')"
>
...
...
@@ -50,86 +36,68 @@
width=
"150"
:label=
"$t('commons.operating')"
>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"handle
Edit(scope.row)"
type=
"primary"
icon=
"el-icon-edit
"
size=
"mini"
circle
/>
<el-button
@
click=
"handle
View(scope.row)"
type=
"primary"
icon=
"el-icon-s-data
"
size=
"mini"
circle
/>
<el-button
@
click=
"handleDelete(scope.row)"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
</
template
>
</el-table-column>
</el-table>
<div>
<el-row>
<el-col
:span=
"22"
:offset=
"1"
>
<div
class=
"table-page"
>
<el-pagination
@
size-change=
"handleSizeChange"
@
current-change=
"handleCurrentChange"
:current-page.sync=
"currentPage"
:page-sizes=
"[5, 10, 20, 50, 100]"
:page-size=
"pageSize"
layout=
"total, sizes, prev, pager, next, jumper"
:total=
"total"
>
</el-pagination>
</div>
</el-col>
</el-row>
</div>
<ms-table-pagination
:change=
"search"
:current-page.sync=
"currentPage"
:page-size.sync=
"pageSize"
:total=
"total"
/>
</el-card>
</div>
</div>
</template>
<
script
>
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
export
default
{
name
:
"
ApiTestReport
"
,
created
:
function
()
{
this
.
initTableData
();
},
components
:
{
MsTableHeader
,
MsTablePagination
},
data
()
{
return
{
result
:
{},
queryPath
:
"
/api/report/list/all
"
,
deletePath
:
"
/api/report/delete/
"
,
condition
:
""
,
condition
:
{
name
:
""
},
projectId
:
null
,
tableData
:
[],
multipleSelection
:
[],
currentPage
:
1
,
pageSize
:
5
,
total
:
0
,
loading
:
false
,
testId
:
null
,
loading
:
false
}
},
beforeRouteEnter
(
to
,
from
,
next
)
{
next
(
self
=>
{
self
.
testId
=
to
.
params
.
testId
;
self
.
search
();
});
},
methods
:
{
initTableData
()
{
search
()
{
let
param
=
{
name
:
this
.
condition
,
name
:
this
.
condition
.
name
,
};
this
.
result
=
this
.
$post
(
this
.
buildPagePath
(
this
.
queryPath
),
param
,
response
=>
{
if
(
this
.
testId
!==
'
all
'
)
{
param
.
testId
=
this
.
testId
;
}
let
url
=
"
/api/report/list/
"
+
this
.
currentPage
+
"
/
"
+
this
.
pageSize
this
.
result
=
this
.
$post
(
url
,
param
,
response
=>
{
let
data
=
response
.
data
;
this
.
total
=
data
.
itemCount
;
this
.
tableData
=
data
.
listObject
;
});
},
search
()
{
this
.
initTableData
();
},
buildPagePath
(
path
)
{
return
path
+
"
/
"
+
this
.
currentPage
+
"
/
"
+
this
.
pageSize
;
},
handleSizeChange
(
size
)
{
this
.
pageSize
=
size
;
this
.
initTableData
();
},
handleCurrentChange
(
current
)
{
this
.
currentPage
=
current
;
this
.
initTableData
();
},
handleSelectionChange
(
val
)
{
this
.
multipleSelection
=
val
;
},
handle
Edit
(
report
)
{
handle
View
(
report
)
{
this
.
$router
.
push
({
path
:
'
/api/report/view/
'
+
report
.
id
path
:
'
/api/report/view/
'
+
report
.
id
,
})
},
handleDelete
(
report
)
{
...
...
@@ -137,33 +105,20 @@
confirmButtonText
:
this
.
$t
(
'
commons.confirm
'
),
callback
:
(
action
)
=>
{
if
(
action
===
'
confirm
'
)
{
this
.
_handleDelete
(
report
);
this
.
result
=
this
.
$post
(
"
/api/report/delete
"
,
{
id
:
report
.
id
},
()
=>
{
this
.
$success
(
this
.
$t
(
'
commons.delete_success
'
));
this
.
search
();
});
}
}
});
},
_handleDelete
(
report
)
{
this
.
result
=
this
.
$post
(
this
.
deletePath
+
report
.
id
,
{},
()
=>
{
this
.
$message
({
message
:
this
.
$t
(
'
commons.delete_success
'
),
type
:
'
success
'
});
this
.
initTableData
();
});
},
}
}
}
</
script
>
<
style
scoped
>
.t
est
-content
{
.t
able
-content
{
width
:
100%
;
}
.table-page
{
padding-top
:
20px
;
margin-right
:
-9px
;
float
:
right
;
}
</
style
>
frontend/src/business/components/api/test/ApiTestConfig.vue
浏览文件 @
e5e7737f
...
...
@@ -16,7 +16,7 @@
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
type=
"primary"
plain
:disabled=
"isDisabled"
@
click=
"runTest"
>
<el-button
type=
"primary"
plain
@
click=
"runTest"
>
{{
$t
(
'
load_test.save_and_run
'
)
}}
</el-button>
<el-button
type=
"warning"
plain
@
click=
"cancel"
>
{{
$t
(
'
commons.cancel
'
)
}}
</el-button>
...
...
@@ -42,6 +42,7 @@
data
()
{
return
{
create
:
false
,
result
:
{},
projects
:
[],
change
:
false
,
...
...
@@ -65,8 +66,10 @@
this
.
projects
=
response
.
data
;
})
if
(
this
.
id
)
{
this
.
create
=
false
;
this
.
getTest
(
this
.
id
);
}
else
{
this
.
create
=
true
;
this
.
test
=
new
Test
();
if
(
this
.
$refs
.
config
)
{
this
.
$refs
.
config
.
reset
();
...
...
@@ -89,20 +92,28 @@
});
},
saveTest
:
function
()
{
this
.
change
=
false
;
this
.
result
=
this
.
$request
(
this
.
getOptions
(
"
/api/save
"
),
response
=>
{
this
.
test
.
id
=
response
.
data
;
this
.
save
(()
=>
{
this
.
$success
(
this
.
$t
(
'
commons.save_success
'
));
})
},
save
:
function
(
callback
)
{
this
.
change
=
false
;
let
url
=
this
.
create
?
"
/api/create
"
:
"
/api/update
"
;
this
.
result
=
this
.
$request
(
this
.
getOptions
(
url
),
response
=>
{
this
.
create
=
false
;
if
(
callback
)
callback
();
});
},
runTest
:
function
()
{
this
.
change
=
false
;
this
.
result
=
this
.
$request
(
this
.
getOptions
(
"
/api/run
"
),
response
=>
{
this
.
test
.
id
=
response
.
data
;
this
.
save
(()
=>
{
this
.
$success
(
this
.
$t
(
'
commons.save_success
'
));
});
this
.
result
=
this
.
$post
(
"
/api/run
"
,
{
id
:
this
.
test
.
id
},
response
=>
{
this
.
$success
(
this
.
$t
(
'
api_test.running
'
));
});
})
},
cancel
:
function
()
{
this
.
$router
.
push
(
'
/api/test/list/all
'
);
...
...
@@ -123,7 +134,6 @@
let
jmx
=
this
.
test
.
toJMX
();
let
blob
=
new
Blob
([
jmx
.
xml
],
{
type
:
"
application/octet-stream
"
});
formData
.
append
(
"
files
"
,
new
File
([
blob
],
jmx
.
name
));
console
.
log
(
jmx
.
xml
)
return
{
method
:
'
POST
'
,
...
...
frontend/src/business/components/api/test/ApiTestList.vue
浏览文件 @
e5e7737f
...
...
@@ -6,7 +6,7 @@
<ms-table-header
:condition.sync=
"condition"
@
search=
"search"
:title=
"$t('commons.test')"
@
create=
"create"
:createTip=
"$t('load_test.create')"
/>
</
template
>
<el-table
:data=
"tableData"
class=
"t
est
-content"
>
<el-table
:data=
"tableData"
class=
"t
able
-content"
>
<el-table-column
prop=
"name"
:label=
"$t('commons.name')"
...
...
@@ -70,8 +70,7 @@
currentPage
:
1
,
pageSize
:
5
,
total
:
0
,
loading
:
false
,
testId
:
null
,
loading
:
false
}
},
...
...
@@ -128,7 +127,7 @@
</
script
>
<
style
scoped
>
.t
est
-content
{
.t
able
-content
{
width
:
100%
;
}
</
style
>
frontend/src/business/components/api/test/model/JMX.js
浏览文件 @
e5e7737f
...
...
@@ -204,6 +204,39 @@ export class ThreadGroup extends DefaultTestElement {
}
}
export
class
PostThreadGroup
extends
DefaultTestElement
{
constructor
(
testName
)
{
super
(
'
PostThreadGroup
'
,
'
PostThreadGroupGui
'
,
'
PostThreadGroup
'
,
testName
||
'
tearDown Thread Group
'
);
this
.
intProp
(
"
ThreadGroup.num_threads
"
,
1
);
this
.
intProp
(
"
ThreadGroup.ramp_time
"
,
1
);
this
.
boolProp
(
"
ThreadGroup.scheduler
"
,
false
);
this
.
stringProp
(
"
ThreadGroup.on_sample_error
"
,
"
continue
"
);
let
loopAttrs
=
{
name
:
"
ThreadGroup.main_controller
"
,
elementType
:
"
LoopController
"
,
guiclass
:
"
LoopControlPanel
"
,
testclass
:
"
LoopController
"
,
testname
:
"
Loop Controller
"
,
enabled
:
"
true
"
};
let
loopController
=
this
.
add
(
new
Element
(
'
elementProp
'
,
loopAttrs
));
loopController
.
boolProp
(
'
LoopController.continue_forever
'
,
false
);
loopController
.
stringProp
(
'
LoopController.loops
'
,
1
);
}
}
export
class
DebugSampler
extends
DefaultTestElement
{
constructor
(
testName
)
{
super
(
'
DebugSampler
'
,
'
TestBeanGUI
'
,
'
DebugSampler
'
,
testName
||
'
Debug Sampler
'
);
this
.
boolProp
(
"
displayJMeterProperties
"
,
false
);
this
.
boolProp
(
"
displayJMeterVariables
"
,
true
);
this
.
boolProp
(
"
displaySystemProperties
"
,
false
);
}
}
export
class
HTTPSamplerProxy
extends
DefaultTestElement
{
constructor
(
testName
,
request
)
{
super
(
'
HTTPSamplerProxy
'
,
'
HttpTestSampleGui
'
,
'
HTTPSamplerProxy
'
,
testName
||
'
HTTP Request
'
);
...
...
@@ -219,37 +252,6 @@ export class HTTPSamplerProxy extends DefaultTestElement {
this
.
stringProp
(
"
HTTPSampler.port
"
,
this
.
request
.
port
);
}
}
addRequestArguments
(
arg
)
{
if
(
arg
instanceof
HTTPSamplerArguments
)
{
this
.
add
(
arg
);
}
}
addRequestBody
(
body
)
{
if
(
body
instanceof
HTTPSamplerArguments
)
{
this
.
boolProp
(
'
HTTPSampler.postBodyRaw
'
,
true
);
this
.
add
(
body
);
}
}
putRequestHeader
(
header
)
{
if
(
header
instanceof
HeaderManager
)
{
this
.
put
(
header
);
}
}
putResponseAssertion
(
assertion
)
{
if
(
assertion
instanceof
ResponseAssertion
)
{
this
.
put
(
assertion
);
}
}
putDurationAssertion
(
assertion
)
{
if
(
assertion
instanceof
DurationAssertion
)
{
this
.
put
(
assertion
);
}
}
}
// 这是一个Element
...
...
@@ -373,7 +375,9 @@ export class BackendListener extends DefaultTestElement {
constructor
(
testName
,
className
,
args
)
{
super
(
'
BackendListener
'
,
'
BackendListenerGui
'
,
'
BackendListener
'
,
testName
||
'
Backend Listener
'
);
this
.
stringProp
(
'
classname
'
,
className
);
this
.
add
(
new
ElementArguments
(
args
));
if
(
args
&&
args
.
length
>
0
)
{
this
.
add
(
new
ElementArguments
(
args
));
}
}
}
...
...
@@ -397,3 +401,6 @@ export class ElementArguments extends Element {
}
}
export
class
Class
{
}
frontend/src/business/components/api/test/model/ScenarioModel.js
浏览文件 @
e5e7737f
...
...
@@ -5,6 +5,8 @@ import {
TestElement
,
TestPlan
,
ThreadGroup
,
PostThreadGroup
,
DebugSampler
,
HeaderManager
,
HTTPSamplerArguments
,
ResponseCodeAssertion
,
...
...
@@ -13,9 +15,21 @@ import {
BackendListener
}
from
"
./JMX
"
;
export
const
generateId
=
function
()
{
return
Math
.
floor
(
Math
.
random
()
*
10000
);
};
export
const
uuid
=
function
()
{
let
d
=
new
Date
().
getTime
()
let
d2
=
(
performance
&&
performance
.
now
&&
(
performance
.
now
()
*
1000
))
||
0
;
return
'
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
'
.
replace
(
/
[
xy
]
/g
,
function
(
c
)
{
let
r
=
Math
.
random
()
*
16
;
if
(
d
>
0
)
{
r
=
(
d
+
r
)
%
16
|
0
;
d
=
Math
.
floor
(
d
/
16
);
}
else
{
r
=
(
d2
+
r
)
%
16
|
0
;
d2
=
Math
.
floor
(
d2
/
16
);
}
return
(
c
===
'
x
'
?
r
:
(
r
&
0x3
|
0x8
)).
toString
(
16
);
});
}
export
const
BODY_TYPE
=
{
KV
:
"
KeyValue
"
,
...
...
@@ -75,7 +89,7 @@ export class Test extends BaseConfig {
constructor
(
options
)
{
super
();
this
.
version
=
'
1.0.0
'
;
this
.
id
=
null
;
this
.
id
=
uuid
()
;
this
.
name
=
null
;
this
.
projectId
=
null
;
this
.
scenarioDefinition
=
[];
...
...
@@ -101,7 +115,7 @@ export class Test extends BaseConfig {
export
class
Scenario
extends
BaseConfig
{
constructor
(
options
)
{
super
();
this
.
id
=
generateI
d
();
this
.
id
=
uui
d
();
this
.
name
=
null
;
this
.
url
=
null
;
this
.
parameters
=
[];
...
...
@@ -122,7 +136,7 @@ export class Scenario extends BaseConfig {
export
class
Request
extends
BaseConfig
{
constructor
(
options
)
{
super
();
this
.
id
=
generateI
d
();
this
.
id
=
uui
d
();
this
.
name
=
null
;
this
.
url
=
null
;
this
.
method
=
null
;
...
...
@@ -144,6 +158,10 @@ export class Request extends BaseConfig {
options
.
assertions
=
new
Assertions
(
options
.
assertions
);
return
options
;
}
isValid
()
{
return
!!
this
.
url
&&
!!
this
.
method
}
}
export
class
Body
extends
BaseConfig
{
...
...
@@ -304,14 +322,23 @@ class JMeterTestPlan extends Element {
class
JMXGenerator
{
constructor
(
test
)
{
if
(
!
test
||
!
test
.
id
||
!
(
test
instanceof
Test
))
return
;
if
(
!
test
||
!
(
test
instanceof
Test
))
return
null
;
if
(
!
test
.
id
)
{
test
.
id
=
"
#NULL_TEST_ID#
"
;
}
const
SPLIT
=
"
@@:
"
;
let
testPlan
=
new
TestPlan
(
test
.
name
);
test
.
scenarioDefinition
.
forEach
(
scenario
=>
{
let
threadGroup
=
new
ThreadGroup
(
scenario
.
name
);
let
threadGroup
=
new
ThreadGroup
(
scenario
.
name
+
SPLIT
+
scenario
.
id
);
scenario
.
requests
.
forEach
(
request
=>
{
let
httpSamplerProxy
=
new
HTTPSamplerProxy
(
request
.
name
,
new
JMXRequest
(
request
));
if
(
!
request
.
isValid
())
return
;
// test.id用于处理结果时区分属于哪个测试
let
name
=
request
.
name
+
SPLIT
+
test
.
id
;
let
httpSamplerProxy
=
new
HTTPSamplerProxy
(
name
,
new
JMXRequest
(
request
));
this
.
addRequestHeader
(
httpSamplerProxy
,
request
);
...
...
@@ -326,8 +353,15 @@ class JMXGenerator {
threadGroup
.
put
(
httpSamplerProxy
);
})
this
.
addBackendListener
(
threadGroup
,
test
.
id
);
this
.
addBackendListener
(
threadGroup
);
testPlan
.
put
(
threadGroup
);
// 暂时不加
// let tearDownThreadGroup = new PostThreadGroup();
// tearDownThreadGroup.put(new DebugSampler(test.id));
// this.addBackendListener(tearDownThreadGroup);
//
// testPlan.put(tearDownThreadGroup);
})
this
.
jmeterTestPlan
=
new
JMeterTestPlan
();
...
...
@@ -338,14 +372,14 @@ class JMXGenerator {
let
name
=
request
.
name
+
"
Headers
"
;
let
headers
=
request
.
headers
.
filter
(
this
.
filter
);
if
(
headers
.
length
>
0
)
{
httpSamplerProxy
.
put
RequestHeader
(
new
HeaderManager
(
name
,
headers
));
httpSamplerProxy
.
put
(
new
HeaderManager
(
name
,
headers
));
}
}
addRequestArguments
(
httpSamplerProxy
,
request
)
{
let
args
=
request
.
parameters
.
filter
(
this
.
filter
)
if
(
args
.
length
>
0
)
{
httpSamplerProxy
.
add
RequestArguments
(
new
HTTPSamplerArguments
(
args
));
httpSamplerProxy
.
add
(
new
HTTPSamplerArguments
(
args
));
}
}
...
...
@@ -357,19 +391,20 @@ class JMXGenerator {
body
.
push
({
name
:
''
,
value
:
request
.
body
.
raw
});
}
httpSamplerProxy
.
addRequestBody
(
new
HTTPSamplerArguments
(
body
));
httpSamplerProxy
.
boolProp
(
'
HTTPSampler.postBodyRaw
'
,
true
);
httpSamplerProxy
.
add
(
new
HTTPSamplerArguments
(
body
));
}
addRequestAssertion
(
httpSamplerProxy
,
request
)
{
let
assertions
=
request
.
assertions
;
if
(
assertions
.
regex
.
length
>
0
)
{
assertions
.
regex
.
filter
(
this
.
filter
).
forEach
(
regex
=>
{
httpSamplerProxy
.
put
ResponseAssertion
(
this
.
getAssertion
(
regex
));
httpSamplerProxy
.
put
(
this
.
getAssertion
(
regex
));
})
}
if
(
assertions
.
duration
.
isValid
())
{
httpSamplerProxy
.
put
DurationAssertion
(
assertions
.
duration
.
type
,
assertions
.
duration
.
value
);
httpSamplerProxy
.
put
(
assertions
.
duration
.
type
,
assertions
.
duration
.
value
);
}
}
...
...
@@ -387,11 +422,10 @@ class JMXGenerator {
}
}
addBackendListener
(
threadGroup
,
testId
)
{
addBackendListener
(
threadGroup
)
{
let
testName
=
'
API Backend Listener
'
;
let
className
=
'
io.metersphere.api.jmeter.APIBackendListenerClient
'
;
let
args
=
[{
name
:
'
id
'
,
value
:
testId
}];
threadGroup
.
put
(
new
BackendListener
(
testName
,
className
,
args
));
threadGroup
.
put
(
new
BackendListener
(
testName
,
className
));
}
filter
(
config
)
{
...
...
frontend/src/business/components/common/components/MsRolesTag.vue
0 → 100644
浏览文件 @
e5e7737f
<
template
>
<div>
<ms-tag
v-for=
"(role, index) in roles"
:key=
"index"
:effect=
"effect"
:type=
"type"
:content=
"role.name"
/>
</div>
</
template
>
<
script
>
import
MsTag
from
"
./MsTag
"
;
export
default
{
name
:
"
MsRolesTag
"
,
components
:
{
MsTag
},
props
:
{
type
:
{
type
:
String
,
default
:
'
primary
'
,
},
roles
:
{
type
:
Array
},
effect
:
{
type
:
String
,
default
:
'
dark
'
,
}
}
}
</
script
>
<
style
scoped
>
</
style
>
frontend/src/business/components/common/components/MsTipButton.vue
浏览文件 @
e5e7737f
...
...
@@ -6,6 +6,7 @@
:effect=
"effect"
>
<el-button
@
click=
"exec()"
@
click.stop=
"clickStop"
@
keydown.enter.native.prevent
circle
:type=
"type"
:icon=
"icon"
...
...
frontend/src/business/components/common/router/router.js
浏览文件 @
e5e7737f
...
...
@@ -16,7 +16,6 @@ import PersonSetting from "../../settings/personal/PersonSetting";
import
SystemWorkspace
from
"
../../settings/system/SystemWorkspace
"
;
import
PerformanceChart
from
"
../../performance/report/components/PerformanceChart
"
;
import
PerformanceTestReport
from
"
../../performance/report/PerformanceTestReport
"
;
import
ApiTestReport
from
"
../../api/report/ApiTestReport
"
;
import
ApiTest
from
"
../../api/ApiTest
"
;
import
PerformanceTest
from
"
../../performance/PerformanceTest
"
;
import
ApiTestConfig
from
"
../../api/test/ApiTestConfig
"
;
...
...
@@ -30,6 +29,7 @@ import TestPlan from "../../track/plan/TestPlan";
import
TestPlanView
from
"
../../track/plan/view/TestPlanView
"
;
import
TestCase
from
"
../../track/case/TestCase
"
;
import
TestTrack
from
"
../../track/TestTrack
"
;
import
ApiReportList
from
"
../../api/report/ApiReportList
"
;
Vue
.
use
(
VueRouter
);
...
...
@@ -117,13 +117,13 @@ const router = new VueRouter({
component
:
MsProject
},
{
path
:
"
report/
:type
"
,
name
:
"
fucRepor
t
"
,
component
:
Api
TestRepor
t
path
:
"
report/
list/:testId
"
,
name
:
"
ApiReportLis
t
"
,
component
:
Api
ReportLis
t
},
{
path
:
"
report/view/:reportId
"
,
name
:
"
fuc
ReportView
"
,
name
:
"
Api
ReportView
"
,
component
:
ApiReportView
}
]
...
...
frontend/src/business/components/performance/report/PerformanceTestReport.vue
浏览文件 @
e5e7737f
...
...
@@ -136,10 +136,10 @@
},
handleEdit
(
report
)
{
if
(
report
.
status
===
"
Error
"
)
{
this
.
$warning
(
"
报告生成错误,无法查看!
"
);
this
.
$warning
(
this
.
$t
(
'
report.generation_error
'
)
);
return
false
}
else
if
(
report
.
status
===
"
Starting
"
)
{
this
.
$info
(
"
报告生成中...
"
)
this
.
$info
(
this
.
$t
(
'
being_generated
'
)
)
return
false
}
this
.
$router
.
push
({
...
...
@@ -172,10 +172,4 @@
width
:
100%
;
}
.table-page
{
padding-top
:
20px
;
margin-right
:
-9px
;
float
:
right
;
}
</
style
>
frontend/src/business/components/project/MsProject.vue
浏览文件 @
e5e7737f
...
...
@@ -31,7 +31,7 @@
</el-form>
<
template
v-slot:footer
>
<div
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submit('form')"
size=
"medium"
>
<el-button
type=
"primary"
@
keydown.enter.native.prevent
@
click=
"submit('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</div>
...
...
frontend/src/business/components/settings/Setting.vue
浏览文件 @
e5e7737f
...
...
@@ -3,7 +3,7 @@
<div
class=
"menus"
>
<ms-current-user/>
<el-divider/>
<h1>
设置
</h1>
<h1>
{{
$t
(
'
commons.setting
'
)
}}
</h1>
<ms-setting-menu/>
</div>
<div
class=
"container"
>
...
...
frontend/src/business/components/settings/organization/OrganizationMember.vue
浏览文件 @
e5e7737f
...
...
@@ -11,17 +11,12 @@
<el-table-column
prop=
"phone"
:label=
"$t('commons.phone')"
/>
<el-table-column
prop=
"roles"
:label=
"$t('commons.role')"
width=
"140"
>
<
template
v-slot:default=
"scope"
>
<el-tag
v-for=
"(role, index) in scope.row.roles"
:key=
"index"
size=
"mini"
effect=
"dark"
>
{{
role
.
name
}}
</el-tag>
<ms-roles-tag
:roles=
"scope.row.roles"
/>
</
template
>
</el-table-column>
<el-table-column>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"del(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -58,8 +53,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submitForm('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"submitForm('form')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -92,8 +88,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateOrgMember('updateUserForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateOrgMember('updateUserForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -105,10 +102,12 @@
import
{
TokenKey
}
from
"
../../../../common/js/constants
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsRolesTag
from
"
../../common/components/MsRolesTag
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsOrganizationMember
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsRolesTag
,
MsTableOperator
},
created
()
{
this
.
initTableData
();
},
...
...
frontend/src/business/components/settings/organization/OrganizationWorkspace.vue
浏览文件 @
e5e7737f
...
...
@@ -16,10 +16,7 @@
</el-table-column>
<el-table-column>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"del(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -38,8 +35,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submit('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"submit('form')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -55,26 +53,23 @@
<el-table-column
prop=
"phone"
:label=
"$t('commons.phone')"
/>
<el-table-column
:label=
"$t('commons.role')"
width=
"120"
>
<
template
v-slot:default=
"scope"
>
<el-tag
v-for=
"(role, index) in scope.row.roles"
:key=
"index"
size=
"mini"
effect=
"dark"
type=
"success"
>
{{
role
.
name
}}
</el-tag>
<ms-roles-tag
:roles=
"scope.row.roles"
type=
"success"
/>
</
template
>
</el-table-column>
<el-table-column
:label=
"$t('commons.operating')"
>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"editMember(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"delMember(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"editMember(scope.row)"
@
deleteClick=
"delMember(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
<ms-table-pagination
:change=
"dialogSearch"
:current-page.sync=
"dialogCurrentPage"
:page-size.sync=
"dialogPageSize"
<ms-table-pagination
:change=
"dialogSearch"
:current-page.sync=
"dialogCurrentPage"
:page-size.sync=
"dialogPageSize"
:total=
"dialogTotal"
/>
</el-dialog>
<!-- add workspace member dialog -->
<el-dialog
:title=
"$t('member.create')"
:visible.sync=
"dialogWsMemberAddVisible"
width=
"30%"
:destroy-on-close=
"true"
<el-dialog
:title=
"$t('member.create')"
:visible.sync=
"dialogWsMemberAddVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"memberForm"
ref=
"form"
:rules=
"wsMemberRule"
label-position=
"right"
label-width=
"100px"
size=
"small"
>
...
...
@@ -105,14 +100,16 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submitForm('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"submitForm('form')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
<!-- update workspace member dialog -->
<el-dialog
:title=
"$t('member.modify')"
:visible.sync=
"dialogWsMemberUpdateVisible"
width=
"30%"
:destroy-on-close=
"true"
<el-dialog
:title=
"$t('member.modify')"
:visible.sync=
"dialogWsMemberUpdateVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"memberForm"
label-position=
"right"
label-width=
"100px"
size=
"small"
ref=
"updateUserForm"
>
<el-form-item
label=
"ID"
prop=
"id"
>
...
...
@@ -141,8 +138,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateOrgMember('updateUserForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateOrgMember('updateUserForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -156,10 +154,12 @@
import
{
TokenKey
}
from
"
../../../../common/js/constants
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsRolesTag
from
"
../../common/components/MsRolesTag
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsOrganizationWorkspace
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsRolesTag
,
MsTableOperator
},
mounted
()
{
this
.
list
();
},
...
...
frontend/src/business/components/settings/personal/PersonSetting.vue
浏览文件 @
e5e7737f
...
...
@@ -27,8 +27,10 @@
</el-table-column>
</el-table>
<el-dialog
:title=
"$t('member.modify_personal_info')"
:visible.sync=
"updateVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"form"
label-position=
"right"
label-width=
"100px"
size=
"small"
:rules=
"rule"
ref=
"updateUserForm"
>
<el-dialog
:title=
"$t('member.modify_personal_info')"
:visible.sync=
"updateVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"form"
label-position=
"right"
label-width=
"100px"
size=
"small"
:rules=
"rule"
ref=
"updateUserForm"
>
<el-form-item
label=
"ID"
prop=
"id"
>
<el-input
v-model=
"form.id"
autocomplete=
"off"
:disabled=
"true"
/>
</el-form-item>
...
...
@@ -44,8 +46,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateUser('updateUserForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateUser('updateUserForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -68,7 +71,7 @@
rule
:
{
name
:
[
{
required
:
true
,
message
:
this
.
$t
(
'
member.input_name
'
),
trigger
:
'
blur
'
},
{
min
:
2
,
max
:
10
,
message
:
this
.
$t
(
'
commons.input_limit
'
,
[
2
,
10
]),
trigger
:
'
blur
'
},
{
min
:
2
,
max
:
10
,
message
:
this
.
$t
(
'
commons.input_limit
'
,
[
2
,
10
]),
trigger
:
'
blur
'
},
{
required
:
true
,
pattern
:
/^
[\u
4e00-
\u
9fa5_a-zA-Z0-9.·-
]
+$/
,
...
...
@@ -85,7 +88,7 @@
}
],
email
:
[
{
required
:
true
,
message
:
this
.
$t
(
'
member.input_email
'
),
trigger
:
'
blur
'
},
{
required
:
true
,
message
:
this
.
$t
(
'
member.input_email
'
),
trigger
:
'
blur
'
},
{
required
:
true
,
pattern
:
/^
([
A-Za-z0-9_
\-
.
])
+@
([
A-Za-z0-9
]
+
\.)
+
[
A-Za-z
]{2,6}
$/
,
...
...
@@ -112,7 +115,7 @@
updateUser
(
updateUserForm
)
{
this
.
$refs
[
updateUserForm
].
validate
(
valide
=>
{
if
(
valide
)
{
this
.
result
=
this
.
$post
(
this
.
updatePath
,
this
.
form
,
response
=>
{
this
.
result
=
this
.
$post
(
this
.
updatePath
,
this
.
form
,
response
=>
{
this
.
$success
(
this
.
$t
(
'
commons.modify_success
'
));
localStorage
.
setItem
(
TokenKey
,
JSON
.
stringify
(
response
.
data
));
this
.
updateVisible
=
false
;
...
...
frontend/src/business/components/settings/system/Organization.vue
浏览文件 @
e5e7737f
...
...
@@ -19,10 +19,7 @@
</el-table-column>
<el-table-column
:label=
"$t('commons.operating')"
>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"del(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -41,17 +38,12 @@
<el-table-column
prop=
"phone"
:label=
"$t('commons.phone')"
/>
<el-table-column
:label=
"$t('commons.role')"
width=
"140"
>
<
template
v-slot:default=
"scope"
>
<el-tag
v-for=
"(role, index) in scope.row.roles"
:key=
"index"
size=
"mini"
effect=
"dark"
>
{{
role
.
name
}}
</el-tag>
<ms-roles-tag
:roles=
"scope.row.roles"
/>
</
template
>
</el-table-column>
<el-table-column
:label=
"$t('commons.operating')"
>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"editMember(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"delMember(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"editMember(scope.row)"
@
deleteClick=
"delMember(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -74,8 +66,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"createOrganization('createOrganization')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"createOrganization('createOrganization')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -95,9 +88,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;
"
@
click=
"updateOrganization('updateOrganizationForm')"
size=
"medium"
>
{{
$t
(
'
organization.modify
'
)
}}
</el-button>
<el-button
@
click=
"updateOrganization('updateOrganizationForm')"
@
keydown.enter.native.prevent
type=
"primary
"
size=
"medium"
>
{{
$t
(
'
organization.modify
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -135,8 +128,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submitForm('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"submitForm('form')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -172,8 +166,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateOrgMember('updateUserForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateOrgMember('updateUserForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -185,10 +180,12 @@
import
MsCreateBox
from
"
../CreateBox
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsRolesTag
from
"
../../common/components/MsRolesTag
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsOrganization
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsRolesTag
,
MsTableOperator
},
data
()
{
return
{
queryPath
:
'
/organization/list
'
,
...
...
frontend/src/business/components/settings/system/SystemWorkspace.vue
浏览文件 @
e5e7737f
...
...
@@ -18,8 +18,7 @@
</el-table-column>
<el-table-column>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"del(scope.row)"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -50,7 +49,7 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submit('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
type=
"primary"
@
keydown.enter.native.prevent
@
click=
"submit('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -78,7 +77,7 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
@
click=
"updateWorkspace('updateForm')"
onkeydown=
"return false;"
<el-button
type=
"primary"
@
click=
"updateWorkspace('updateForm')"
@
keydown.enter.native.prevent
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
...
...
@@ -96,17 +95,12 @@
<el-table-column
prop=
"phone"
:label=
"$t('commons.phone')"
/>
<el-table-column
:label=
"$t('commons.role')"
width=
"120"
>
<
template
v-slot:default=
"scope"
>
<el-tag
v-for=
"(role, index) in scope.row.roles"
:key=
"index"
size=
"mini"
effect=
"dark"
type=
"success"
>
{{
role
.
name
}}
</el-tag>
<ms-roles-tag
:roles=
"scope.row.roles"
type=
"success"
/>
</
template
>
</el-table-column>
<el-table-column
:label=
"$t('commons.operating')"
>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"editMember(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"delMember(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"editMember(scope.row)"
@
deleteClick=
"delMember(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -116,7 +110,8 @@
</el-dialog>
<!-- add workspace member dialog -->
<el-dialog
:title=
"$t('member.create')"
:visible.sync=
"dialogWsMemberAddVisible"
width=
"30%"
:destroy-on-close=
"true"
<el-dialog
:title=
"$t('member.create')"
:visible.sync=
"dialogWsMemberAddVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"memberForm"
ref=
"form"
:rules=
"wsMemberRule"
label-position=
"right"
label-width=
"100px"
size=
"small"
>
...
...
@@ -147,13 +142,16 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submitForm('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
type=
"primary"
@
keydown.enter.native.prevent
@
click=
"submitForm('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
<!-- update workspace member dialog -->
<el-dialog
:title=
"$t('member.modify')"
:visible.sync=
"dialogWsMemberUpdateVisible"
width=
"30%"
:destroy-on-close=
"true"
<el-dialog
:title=
"$t('member.modify')"
:visible.sync=
"dialogWsMemberUpdateVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"memberForm"
label-position=
"right"
label-width=
"100px"
size=
"small"
ref=
"updateUserForm"
>
<el-form-item
label=
"ID"
prop=
"id"
>
...
...
@@ -182,8 +180,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
@
click=
"updateOrgMember('updateUserForm')"
onkeydown=
"return false;"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
type=
"primary"
@
click=
"updateOrgMember('updateUserForm')"
@
keydown.enter.native.prevent
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
...
...
@@ -197,10 +196,12 @@
import
{
Message
}
from
"
element-ui
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsRolesTag
from
"
../../common/components/MsRolesTag
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsSystemWorkspace
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsRolesTag
,
MsTableOperator
},
mounted
()
{
this
.
list
();
},
...
...
frontend/src/business/components/settings/system/TestResourcePool.vue
浏览文件 @
e5e7737f
...
...
@@ -38,8 +38,7 @@
</el-table-column>
<el-table-column>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"del(scope.row)"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -122,9 +121,10 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"createTestResourcePool('createTestResourcePoolForm')"
size=
"medium"
>
{{
$t
(
'
commons.create
'
)
}}
</el-button>
<el-button
@
click=
"createTestResourcePool('createTestResourcePoolForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.create
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -202,9 +202,10 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateTestResourcePool('updateTestResourcePoolForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateTestResourcePool('updateTestResourcePoolForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -216,10 +217,11 @@
import
MsCreateBox
from
"
../CreateBox
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsTestResourcePool
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsTableOperator
},
data
()
{
return
{
result
:
{},
...
...
@@ -413,9 +415,13 @@
this
.
form
=
{};
},
changeSwitch
(
row
)
{
this
.
result
=
this
.
$post
(
'
/testresourcepool/update
'
,
row
).
then
(()
=>
{
this
.
$success
(
this
.
$t
(
'
test_resource_pool.status_change_success
'
));
})
this
.
result
=
this
.
$get
(
'
/testresourcepool/update/
'
+
row
.
id
+
'
/
'
+
row
.
status
)
.
then
(()
=>
{
this
.
$success
(
this
.
$t
(
'
test_resource_pool.status_change_success
'
));
}).
catch
(()
=>
{
this
.
$error
(
this
.
$t
(
'
test_resource_pool.status_change_failed
'
));
row
.
status
=
'
INVALID
'
;
})
}
}
}
...
...
frontend/src/business/components/settings/system/User.vue
浏览文件 @
e5e7737f
...
...
@@ -30,8 +30,7 @@
</el-table-column>
<el-table-column
:label=
"$t('commons.operating')"
>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
/>
<el-button
@
click=
"del(scope.row)"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -59,8 +58,8 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;
"
@
click=
"createUser('createUserForm')"
size=
"medium"
>
创建
</el-button>
<el-button
@
click=
"createUser('createUserForm')"
@
keydown.enter.native.prevent
type=
"primary
"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -83,8 +82,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateUser('updateUserForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateUser('updateUserForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -96,10 +96,11 @@
import
MsCreateBox
from
"
../CreateBox
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsUser
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsTableOperator
},
data
()
{
return
{
queryPath
:
'
/user/special/list
'
,
...
...
frontend/src/business/components/settings/workspace/WorkspaceMember.vue
浏览文件 @
e5e7737f
...
...
@@ -9,19 +9,14 @@
<el-table-column
prop=
"name"
:label=
"$t('commons.username')"
/>
<el-table-column
prop=
"email"
:label=
"$t('commons.email')"
/>
<el-table-column
prop=
"phone"
:label=
"$t('commons.phone')"
/>
<el-table-column
prop=
"roles"
label=
"角色
"
width=
"120"
>
<el-table-column
prop=
"roles"
:label=
"$t('commons.role')
"
width=
"120"
>
<
template
v-slot:default=
"scope"
>
<el-tag
v-for=
"(role, index) in scope.row.roles"
:key=
"index"
size=
"mini"
effect=
"dark"
type=
"success"
>
{{
role
.
name
}}
</el-tag>
<ms-roles-tag
:roles=
"scope.row.roles"
type=
"success"
/>
</
template
>
</el-table-column>
<el-table-column>
<
template
v-slot:default=
"scope"
>
<el-button
@
click=
"edit(scope.row)"
onkeydown=
"return false;"
type=
"primary"
icon=
"el-icon-edit"
size=
"mini"
circle
v-permission=
"['test_manager']"
/>
<el-button
@
click=
"del(scope.row)"
onkeydown=
"return false;"
type=
"danger"
icon=
"el-icon-delete"
size=
"mini"
circle
v-permission=
"['test_manager']"
/>
<ms-table-operator
@
editClick=
"edit(scope.row)"
@
deleteClick=
"del(scope.row)"
v-permission=
"['test_manager']"
/>
</
template
>
</el-table-column>
</el-table>
...
...
@@ -29,9 +24,10 @@
:total=
"total"
/>
</el-card>
<el-dialog
title=
"添加成员"
:visible.sync=
"createVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-dialog
:title=
"$t('member.create')"
:visible.sync=
"createVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"form"
ref=
"form"
:rules=
"rules"
label-position=
"right"
label-width=
"100px"
size=
"small"
>
<el-form-item
label=
"成员
"
prop=
"userIds"
>
<el-form-item
:label=
"$t('commons.member')
"
prop=
"userIds"
>
<el-select
v-model=
"form.userIds"
multiple
:placeholder=
"$t('member.please_choose_member')"
class=
"select-width"
>
<el-option
...
...
@@ -44,7 +40,7 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item
label=
"角色
"
prop=
"roleIds"
>
<el-form-item
:label=
"$t('commons.role')
"
prop=
"roleIds"
>
<el-select
v-model=
"form.roleIds"
multiple
:placeholder=
"$t('role.please_choose_role')"
class=
"select-width"
>
<el-option
v-for=
"item in form.roles"
...
...
@@ -57,13 +53,15 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"submitForm('form')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"submitForm('form')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
<el-dialog
title=
"修改成员"
:visible.sync=
"updateVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-dialog
:title=
"$t('member.modify')"
:visible.sync=
"updateVisible"
width=
"30%"
:destroy-on-close=
"true"
@
close=
"closeFunc"
>
<el-form
:model=
"form"
label-position=
"right"
label-width=
"100px"
size=
"small"
ref=
"updateUserForm"
>
<el-form-item
label=
"ID"
prop=
"id"
>
<el-input
v-model=
"form.id"
autocomplete=
"off"
:disabled=
"true"
/>
...
...
@@ -77,7 +75,7 @@
<el-form-item
:label=
"$t('commons.phone')"
prop=
"phone"
>
<el-input
v-model=
"form.phone"
autocomplete=
"off"
/>
</el-form-item>
<el-form-item
label=
"角色
"
prop=
"roleIds"
>
<el-form-item
:label=
"$t('commons.role')
"
prop=
"roleIds"
>
<el-select
v-model=
"form.roleIds"
multiple
:placeholder=
"$t('role.please_choose_role')"
class=
"select-width"
>
<el-option
v-for=
"item in form.allroles"
...
...
@@ -90,8 +88,9 @@
</el-form>
<
template
v-slot:footer
>
<span
class=
"dialog-footer"
>
<el-button
type=
"primary"
onkeydown=
"return false;"
@
click=
"updateWorkspaceMember('updateUserForm')"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
<el-button
@
click=
"updateWorkspaceMember('updateUserForm')"
@
keydown.enter.native.prevent
type=
"primary"
size=
"medium"
>
{{
$t
(
'
commons.save
'
)
}}
</el-button>
</span>
</
template
>
</el-dialog>
...
...
@@ -104,10 +103,12 @@
import
{
TokenKey
}
from
"
../../../../common/js/constants
"
;
import
MsTablePagination
from
"
../../common/pagination/TablePagination
"
;
import
MsTableHeader
from
"
../../common/components/MsTableHeader
"
;
import
MsRolesTag
from
"
../../common/components/MsRolesTag
"
;
import
MsTableOperator
from
"
../../common/components/MsTableOperator
"
;
export
default
{
name
:
"
MsMember
"
,
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
},
components
:
{
MsCreateBox
,
MsTablePagination
,
MsTableHeader
,
MsRolesTag
,
MsTableOperator
},
data
()
{
return
{
result
:
{},
...
...
frontend/src/business/components/track/case/components/TestCaseMove.vue
浏览文件 @
e5e7737f
<
template
>
<el-dialog
:title=
"$t('test_track.
plan_view.change_executor
')"
<el-dialog
:title=
"$t('test_track.
case.move
')"
:visible.sync=
"dialogVisible"
:before-close=
"close"
width=
"20%"
>
<el-select
v-model.trim=
"module"
:placeholder=
"$t('test_track.
plan_view.select_executor
')"
:placeholder=
"$t('test_track.
case.move
')"
filterable
>
<el-option
v-for=
"item in moduleOptions"
:key=
"item.id"
:label=
"item.path"
:value=
"item.id"
></el-option>
...
...
frontend/src/i18n/en-US.js
浏览文件 @
e5e7737f
...
...
@@ -2,6 +2,7 @@ export default {
commons
:
{
'
workspace
'
:
'
Workspace
'
,
'
organization
'
:
'
Organization
'
,
'
setting
'
:
'
Setting
'
,
'
project
'
:
'
Project
'
,
'
name
'
:
'
Name
'
,
'
description
'
:
'
Description
'
,
...
...
@@ -314,6 +315,7 @@ export default {
'
fill_the_data
'
:
'
Please complete the data
'
,
'
delete_prompt
'
:
'
This operation will permanently delete the resource pool, continue?
'
,
'
status_change_success
'
:
'
Successfully changed the status!
'
,
'
status_change_failed
'
:
'
Failed to change the status!
'
,
},
i18n
:
{
'
home
'
:
'
Home
'
...
...
frontend/src/i18n/zh-CN.js
浏览文件 @
e5e7737f
...
...
@@ -2,6 +2,7 @@ export default {
commons
:
{
'
workspace
'
:
'
工作空间
'
,
'
organization
'
:
'
组织
'
,
'
setting
'
:
'
设置
'
,
'
project
'
:
'
项目
'
,
'
name
'
:
'
名称
'
,
'
description
'
:
'
描述
'
,
...
...
@@ -186,6 +187,7 @@ export default {
},
api_test
:
{
save_and_run
:
"
保存并执行
"
,
running
:
"
正在执行
"
,
reset
:
"
重置
"
,
input_name
:
"
请输入测试名称
"
,
select_project
:
"
请选择项目
"
,
...
...
@@ -359,6 +361,7 @@ export default {
'
fill_the_data
'
:
'
请完善数据
'
,
'
delete_prompt
'
:
'
此操作将永久删除该资源池, 是否继续?
'
,
'
status_change_success
'
:
'
状态修改成功!
'
,
'
status_change_failed
'
:
'
状态修改失败!
'
,
},
i18n
:
{
'
home
'
:
'
首页
'
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录