This commit is contained in:
chenjianxing 2020-11-05 17:16:48 +08:00
commit 63e234b74b
42 changed files with 478 additions and 339 deletions

View File

@ -104,9 +104,8 @@ public class APITestController {
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) {
String testId = request.getId();
checkownerService.checkApiTestOwner(testId);
apiTestService.delete(testId);
checkownerService.checkApiTestOwner(request.getId());
apiTestService.delete(request);
}
@PostMapping(value = "/run")

View File

@ -8,4 +8,8 @@ import lombok.Setter;
public class DeleteAPITestRequest {
private String id;
/**
* 是否强制删除删除项目时不检查关联关系强制删除资源
*/
private boolean forceDelete = false;
}

View File

@ -193,8 +193,11 @@ public class APITestService {
return extApiTestMapper.getApiTestByProjectId(projectId);
}
public void delete(String testId) {
public void delete(DeleteAPITestRequest request) {
String testId = request.getId();
if (!request.isForceDelete()) {
testCaseService.checkIsRelateTest(testId);
}
deleteFileByTestId(testId);
apiReportService.deleteByTestId(testId);
scheduleService.deleteByResourceId(testId);

View File

@ -3,7 +3,7 @@ package io.metersphere.commons.constants;
public enum FileType {
JMX(".jmx"), CSV(".csv"), JSON(".json"), PDF(".pdf"),
JPG(".jpg"), PNG(".png"), JPEG(".jpeg"), DOC(".doc"),
XLSX(".xlsx"), DOCX(".docx");
XLSX(".xlsx"), DOCX(".docx"), JAR(".jar");
// 保存后缀
private String suffix;

View File

@ -43,6 +43,8 @@ public class ShiroConfig implements EnvironmentAware {
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
filterChainDefinitionMap.put("/display/info", "anon");
filterChainDefinitionMap.put("/display/file/*", "anon");
filterChainDefinitionMap.put("/**", "apikey, authc");
return shiroFilterFactoryBean;
}

View File

@ -144,7 +144,7 @@ public class MailService {
context.put("testCaseName", testCaseWithBLOBs.getName());
context.put("description", request.getDescription());
context.put("url", baseSystemConfigDTO.getUrl());
context.put("id", testCaseWithBLOBs.getId());
context.put("id", request.getReviewId());
try {
String commentTemplate = IOUtils.toString(this.getClass().getResource("/mail/ReviewComments.html"), StandardCharsets.UTF_8);
sendReviewNotice(addresseeIdList(messageDetail, userIds, eventType), context, commentTemplate);

View File

@ -17,6 +17,7 @@ public class EngineContext {
private Map<String, Object> properties = new HashMap<>();
private Map<String, String> testData = new HashMap<>();
private Map<String, String> env = new HashMap<>();
private Map<String, byte[]> testJars = new HashMap<>();
public String getTestId() {
return testId;
@ -126,4 +127,13 @@ public class EngineContext {
public void setResourceIndex(Integer resourceIndex) {
this.resourceIndex = resourceIndex;
}
public Map<String, byte[]> getTestJars() {
return testJars;
}
public void setTestJars(Map<String, byte[]> testJars) {
this.testJars = testJars;
}
}

View File

@ -63,6 +63,7 @@ public class EngineFactory {
});
List<FileMetadata> csvFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.CSV.name())).collect(Collectors.toList());
List<FileMetadata> jarFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JAR.name())).collect(Collectors.toList());
final FileContent fileContent = fileService.getFileContent(jmxFile.getId());
if (fileContent == null) {
MSException.throwException(Translator.get("run_load_test_file_content_not_found") + loadTest.getId());
@ -125,6 +126,15 @@ public class EngineFactory {
engineContext.setTestData(data);
}
if (CollectionUtils.isNotEmpty(jarFiles)) {
Map<String, byte[]> data = new HashMap<>();
jarFiles.forEach(jf -> {
FileContent content = fileService.getFileContent(jf.getId());
data.put(jf.getName(), content.getFile());
});
engineContext.setTestJars(data);
}
return engineContext;
}

View File

@ -83,6 +83,7 @@ public class DockerTestEngine extends AbstractEngine {
testRequest.setFileString(content);
testRequest.setImage(JMETER_IMAGE);
testRequest.setTestData(context.getTestData());
testRequest.setTestJars(context.getTestJars());
testRequest.setEnv(context.getEnv());

View File

@ -14,4 +14,5 @@ public class TestRequest extends BaseRequest {
private String image;
private Map<String, String> testData = new HashMap<>();
private Map<String, String> env = new HashMap<>();
private Map<String, byte[]> testJars = new HashMap<>();
}

View File

@ -47,10 +47,9 @@ public class PerformanceNoticeTask {
}
public void registerNoticeTask(LoadTestReportWithBLOBs loadTestReport) {
executorService.submit(() -> {
while (isRunning) {
int count = 20;
while (count-- > 0) {
LoadTestReportWithBLOBs loadTestReportFromDatabase = loadTestReportMapper.selectByPrimaryKey(loadTestReport.getId());
if (StringUtils.equals(loadTestReportFromDatabase.getStatus(), PerformanceTestStatus.Completed.name())) {
isRunning = false;
sendSuccessNotice(loadTestReportFromDatabase);
@ -61,13 +60,13 @@ public class PerformanceNoticeTask {
sendFailNotice(loadTestReportFromDatabase);
return;
}
count--;
try {
Thread.sleep(1000 * 60);// 每分钟检查 loadtest 的状态
Thread.sleep(1000 * 4L);// 每分钟检查 loadtest 的状态
} catch (InterruptedException e) {
LogUtil.error(e);
}
}
});
}
public void sendSuccessNotice(LoadTestReportWithBLOBs loadTestReport) {

View File

@ -85,7 +85,9 @@ public class PerformanceTestService {
public void delete(DeleteTestPlanRequest request) {
String testId = request.getId();
if (!request.isForceDelete()) {
testCaseService.checkIsRelateTest(testId);
}
LoadTestReportExample loadTestReportExample = new LoadTestReportExample();
loadTestReportExample.createCriteria().andTestIdEqualTo(testId);

View File

@ -14,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@ -141,4 +142,12 @@ public class FileService {
example.createCriteria().andIdIn(fileIds);
return fileMetadataMapper.selectByExample(example);
}
public void deleteFileById(String fileId) {
deleteFileByIds(Collections.singletonList(fileId));
}
public FileMetadata getFileMetadataById(String fileId) {
return fileMetadataMapper.selectByPrimaryKey(fileId);
}
}

View File

@ -1,5 +1,6 @@
package io.metersphere.service;
import io.metersphere.api.dto.DeleteAPITestRequest;
import io.metersphere.api.dto.QueryAPITestRequest;
import io.metersphere.api.service.APITestService;
import io.metersphere.base.domain.LoadTest;
@ -96,6 +97,7 @@ public class ProjectService {
loadTestIdList.forEach(loadTestId -> {
DeleteTestPlanRequest deleteTestPlanRequest = new DeleteTestPlanRequest();
deleteTestPlanRequest.setId(loadTestId);
deleteTestPlanRequest.setForceDelete(true);
performanceTestService.delete(deleteTestPlanRequest);
});
@ -122,7 +124,10 @@ public class ProjectService {
QueryAPITestRequest request = new QueryAPITestRequest();
request.setProjectId(projectId);
apiTestService.list(request).forEach(test -> {
apiTestService.delete(test.getId());
DeleteAPITestRequest deleteAPITestRequest = new DeleteAPITestRequest();
deleteAPITestRequest.setId(test.getId());
deleteAPITestRequest.setForceDelete(true);
apiTestService.delete(deleteAPITestRequest);
});
}

View File

@ -73,8 +73,14 @@ public class TestResourcePoolService {
LoadTestExample example = new LoadTestExample();
example.createCriteria()
.andTestResourcePoolIdEqualTo(testResourcePoolId);
if (loadTestMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("test_resource_pool_is_use"));
List<LoadTest> loadTests = loadTestMapper.selectByExample(example);
StringBuilder loadTestNames = new StringBuilder();
if (loadTests.size() > 0) {
for (LoadTest loadTest : loadTests) {
loadTestNames = loadTestNames.append(loadTest.getName()).append(",");
}
String str = loadTestNames.substring(0, loadTestNames.length() - 1);
MSException.throwException(Translator.get("load_test") + " " + str + " " + Translator.get("test_resource_pool_is_use"));
}
}

View File

@ -1,4 +1,13 @@
package io.metersphere.track.request.testplan;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class DeleteTestPlanRequest extends TestPlanRequest {
/**
* 是否强制删除删除项目时不检查关联关系强制删除资源
*/
private boolean forceDelete = false;
}

View File

@ -7,5 +7,6 @@ import lombok.Setter;
@Getter
@Setter
public class SaveCommentRequest extends TestCaseComment {
private String reviewId;
}

View File

@ -546,7 +546,7 @@ public class TestCaseService {
for (TestCase testCase : testCases) {
caseName = caseName.append(testCase.getName()).append(",");
}
String str = caseName.toString().substring(0, caseName.length() - 1);
String str = caseName.substring(0, caseName.length() - 1);
MSException.throwException(Translator.get("related_case_del_fail_prefix") + " " + str + " " + Translator.get("related_case_del_fail_suffix"));
}
}

@ -1 +1 @@
Subproject commit ee74568be0beba46da19616f5832e83f9164c688
Subproject commit 24047fea950a74f7848a9fdaa857a22b884c4ce2

View File

@ -57,7 +57,8 @@ workspace_not_exists=Workspace is not exists
test_resource_pool_id_is_null=Test Resource Pool ID cannot be null
test_resource_pool_name_is_null=Test Resource Pool name cannot be null
test_resource_pool_name_already_exists=The test resource pool name already exists
test_resource_pool_is_use=The test resource pool is in use and cannot be deleted
load_test=Load Test
test_resource_pool_is_use=This resource pool is in use and cannot be deleted
#project
project_name_is_null=Project name cannot be null
project_name_already_exists=The project name already exists

View File

@ -57,7 +57,8 @@ workspace_not_exists=工作空间不存在
test_resource_pool_id_is_null=资源池ID不能为空
test_resource_pool_name_is_null=资源池名称不能为空
test_resource_pool_name_already_exists=资源池名称已存在
test_resource_pool_is_use=资源池正在使用中,无法删除
load_test=性能测试
test_resource_pool_is_use=正在使用此资源池,无法删除
#project
project_name_is_null=项目名称不能为空
project_name_already_exists=项目名称已存在

View File

@ -57,7 +57,8 @@ workspace_not_exists=工作空間不存在
test_resource_pool_id_is_null=資源池ID不能為空
test_resource_pool_name_is_null=資源池名稱不能為空
test_resource_pool_name_already_exists=資源池名稱已存在
test_resource_pool_is_use=資源池正在使用中,無法刪除
load_test=性能測試
test_resource_pool_is_use=正在使用此資源池,無法刪除
#project
project_name_is_null=項目名稱不能為空
project_name_already_exists=項目名稱已存在

View File

@ -10,7 +10,7 @@
${testCaseName}<br/>
添加评论:${description}<br/>
点击下面链接进入用例评审页面</p>
<a href="${url}/#/track/review/view">${url}/#/track/review/view</a>
<a href="${url}/#/track/review/view/${id}">${url}/#/track/review/view/${id}</a>
</div>
</body>
</html>

View File

@ -1215,7 +1215,7 @@ view.results.tree.renderers_order=.RenderAsText,.RenderAsRegexp,.RenderAsBoundar
# All entries will be added to the class path of the system class loader
# and also to the path of the JMeter internal loader.
# Paths with spaces may cause problems for the JVM
#user.classpath=../classes;../lib
user.classpath=/test/
# List of directories (separated by ;) that JMeter will search for utility
# and plugin dependency classes.

View File

@ -7,7 +7,8 @@
</el-row>
<el-row id="header-top" type="flex" justify="space-between" align="middle">
<el-col :span="12">
<a class="logo"/>
<img v-if="logoId" :src="'/display/file/' + logoId" style="width: 156px;height: 37px;" alt="">
<a v-else class="logo"/>
<ms-top-menus/>
</el-col>
@ -29,10 +30,11 @@
import MsUser from "./components/common/head/HeaderUser";
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
import {saveLocalStorage} from "../common/js/utils";
import {saveLocalStorage} from "@/common/js/utils";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {};
const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {};
export default {
name: 'app',
@ -42,6 +44,7 @@
licenseHeader: null,
auth: false,
header: {},
logoId: '_blank',
}
},
beforeCreate() {
@ -54,6 +57,10 @@
if (header.default !== undefined) {
this.licenseHeader = "LicenseMessage";
}
//
if (display.default !== undefined) {
display.default.valid(this);
}
} else {
window.location.href = "/login"
}

View File

@ -1,7 +1,7 @@
<template>
<div v-loading="result.loading">
<el-upload
accept=".jmx,.csv"
accept=".jmx,.csv,.jar"
drag
action=""
:limit="fileNumLimit"
@ -77,7 +77,7 @@ export default {
fileList: [],
tableData: [],
uploadList: [],
fileNumLimit: 5,
fileNumLimit: 10,
};
},
created() {
@ -197,7 +197,7 @@ export default {
return this.fileList;//
},
validConfig() {
let newJmxNum = 0, oldJmxNum = 0, newCsvNum = 0, oldCsvNum = 0;
let newJmxNum = 0, oldJmxNum = 0, newCsvNum = 0, oldCsvNum = 0, newJarNum = 0, oldJarNum = 0;
if (this.uploadList.length > 0) {
this.uploadList.forEach(f => {
if (f.name.toLowerCase().endsWith(".jmx")) {
@ -206,6 +206,9 @@ export default {
if (f.name.toLowerCase().endsWith(".csv")) {
newCsvNum++;
}
if (f.name.toLowerCase().endsWith(".jar")) {
newJarNum++;
}
});
}
if (this.fileList.length > 0) {
@ -216,9 +219,12 @@ export default {
if (f.name.toLowerCase().endsWith(".csv")) {
oldCsvNum++;
}
if (f.name.toLowerCase().endsWith(".jar")) {
oldJarNum++;
}
});
}
if (newCsvNum + oldCsvNum > this.fileNumLimit - 1) {
if (newCsvNum + oldCsvNum + newJarNum + oldJarNum > this.fileNumLimit - 1) {
this.handleExceed();
return false;
}

View File

@ -204,7 +204,11 @@ export default {
})
},
removeRowTask(index, data) { //
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -205,7 +205,12 @@ export default {
})
},
removeRowTask(index, data) { //
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -196,7 +196,11 @@ export default {
})
},
removeRowTask(index, data) { //
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -210,7 +210,11 @@ export default {
})
},
removeRowTask(index, data) { //
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -211,7 +211,11 @@ export default {
})
},
removeRowTask(index, data) { //
if (!data[index].identification) {
data.splice(index, 1)
} else {
data[index].isSet = false
}
},
deleteRowTask(index, data) { //
this.result = this.$get("/notice/delete/message/" + data.identification, response => {

View File

@ -32,7 +32,9 @@ export default {
component: () => import('@/business/components/settings/system/SystemParameterSetting'),
meta: {system: true, title: 'commons.system_parameter_setting'}
},
...requireContext.keys().map(key => requireContext(key).system),...requireContext.keys().map(key => requireContext(key).license),
...requireContext.keys().map(key => requireContext(key).system),
...requireContext.keys().map(key => requireContext(key).license),
...requireContext.keys().map(key => requireContext(key).display),
{
path: 'organizationmember',
component: () => import('@/business/components/settings/organization/OrganizationMember'),

View File

@ -295,7 +295,6 @@ export default {
this.infoList = resources;
},
del(row) {
window.console.log(row);
this.$confirm(this.$t('test_resource_pool.delete_prompt'), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),

View File

@ -368,8 +368,9 @@ export default {
}
],
phone: [
{required: true, message: this.$t('user.input_phone'), trigger: 'blur'},
{
required: false,
required: true,
pattern: '^1(3|4|5|7|8)\\d{9}$',
message: this.$t('user.mobile_number_format_is_incorrect'),
trigger: 'blur'

View File

@ -40,7 +40,8 @@ export default {
components: {ReviewCommentItem},
props: {
caseId: String,
comments: Array
comments: Array,
reviewId:String,
},
data() {
return {
@ -53,6 +54,7 @@ export default {
let comment = {};
comment.caseId = this.caseId;
comment.description = this.textarea;
comment.reviewId=this.reviewId;
if (!this.textarea) {
this.$warning(this.$t('test_track.comment.description_is_null'));
return;

View File

@ -229,7 +229,7 @@
<i class="el-icon-refresh" @click="getComments(testCase)"
style="margin-left:10px;font-size: 14px; cursor: pointer"/>
</template>
<review-comment :comments="comments" :case-id="testCase.caseId" @getComments="getComments"/>
<review-comment :comments="comments" :case-id="testCase.caseId" :review-id="testCase.reviewId" @getComments="getComments"/>
</el-card>
</el-col>
</div>

View File

@ -300,6 +300,7 @@ export default {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
handleEdit(testCase, index) {
console.log(testCase)
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;

@ -1 +1 @@
Subproject commit cc38137a69a0f20fadece9c0f9f50a9468c4ace9
Subproject commit b9091a47b197faef77c72b134b7cf604fe3209f0

View File

@ -180,6 +180,14 @@ export default {
invalid: 'invalid',
expired: 'expired',
},
display: {
title: 'Theme',
logo: 'System LOGO',
loginLogo: 'Picture on the right side of the login page',
loginImage: 'Login page upper left corner LOGO',
loginTitle: 'Login page prompt information',
pageTitle: 'Page Title',
},
system_config: {
base_config: 'Base Config',
base: {
@ -389,7 +397,7 @@ export default {
file_status: 'File Status',
last_modify_time: 'Modify time',
upload_tips: 'Drag files here, or <em> click to upload </em>',
upload_type: 'Only JMX/CSV files can be uploaded',
upload_type: 'Only JMX/CSV/JAR files can be uploaded',
related_file_not_found: "No related test file found!",
delete_file_confirm: 'Confirm delete file:',
file_size_limit: "The number of files exceeds the limit",

View File

@ -180,6 +180,14 @@ export default {
invalid: '无效',
expired: '已过期',
},
display: {
title: '显示设置',
logo: '系统 LOGO',
loginLogo: '登陆页面右侧图片',
loginImage: '登录页左上角 LOGO',
loginTitle: '登陆页面提示信息',
pageTitle: '页面 Title',
},
system_config: {
base_config: '基本配置',
base: {
@ -387,7 +395,7 @@ export default {
file_status: '文件状态',
last_modify_time: '修改时间',
upload_tips: '将文件拖到此处,或<em>点击上传</em>',
upload_type: '只能上传JMX/CSV文件',
upload_type: '只能上传JMX/CSV/JAR文件',
related_file_not_found: "未找到关联的测试文件!",
delete_file_confirm: '确认删除文件: ',
file_size_limit: "文件个数超出限制!",

View File

@ -180,6 +180,14 @@ export default {
invalid: '無效',
expired: '已過期',
},
display: {
title: '顯示設置',
logo: '系統 LOGO',
loginLogo: '登陸頁面右側圖片',
loginImage: '登錄頁左上角 LOGO',
loginTitle: '登陸頁面提示信息',
pageTitle: '頁面 Title',
},
system_config: {
base_config: '基本配置',
base: {
@ -389,7 +397,7 @@ export default {
file_status: '文件狀態',
last_modify_time: '修改時間',
upload_tips: '將文件拖到此處,或<em>點擊上傳</em>',
upload_type: '只能上傳JMX/CSV文件',
upload_type: '只能上傳JMX/CSV/JAR文件',
related_file_not_found: "未找到關聯的測試文件!",
delete_file_confirm: '確認刪除文件: ',
file_size_limit: "文件個數超出限制!",

View File

@ -4,7 +4,8 @@
<el-col :span="12">
<el-form :model="form" :rules="rules" ref="form">
<div class="logo">
<img src="../assets/logo-dark-MeterSphere.svg" style="width: 224px" alt="">
<img v-if="loginLogoId" :src="'/display/file/' + loginLogoId" style="width: 224px;height: 45px;" alt="">
<img v-else src="../assets/logo-dark-MeterSphere.svg" style="width: 224px; " alt="">
</div>
<div class="title">
<span id="s1">{{ $t('commons.login') }}</span>
@ -40,17 +41,20 @@
</div>
</el-form>
</el-col>
<el-col :span="12" class="image">
<div></div>
<el-col :span="12">
<img v-if="loginImageId" :src="'/display/file/' + loginImageId" style="height: 560px; width: 100%">
<img v-else src="../assets/info.png" style="height: 560px; width: 100%">
</el-col>
</el-row>
</div>
</template>
<script>
import {saveLocalStorage} from '../common/js/utils';
import {DEFAULT_LANGUAGE} from "../common/js/constants";
import {saveLocalStorage} from '@/common/js/utils';
import {DEFAULT_LANGUAGE} from "@/common/js/constants";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {};
export default {
name: "Login",
@ -82,11 +86,18 @@
},
msg: '',
ready: false,
openLdap: false
openLdap: false,
loginLogoId: '_blank',
loginImageId: '_blank',
}
},
beforeCreate() {
this.$get("/isLogin").then(response => {
this.result = this.$get("/isLogin").then(response => {
if (display.default !== undefined) {
display.default.valid(this);
}
if (!response.data.success) {
if (response.data.message === 'sso') {
window.location.href = "/sso/login"
@ -108,6 +119,7 @@
// ,,
document.addEventListener("keydown", this.watchEnter);
},
destroyed() {
//
document.removeEventListener("keydown", this.watchEnter);