feat(接口定义): 合并master 最新代码

This commit is contained in:
fit2-zhao 2020-11-27 13:58:25 +08:00
commit 22108b3048
77 changed files with 993 additions and 778 deletions

View File

@ -6,11 +6,11 @@ ARG MS_VERSION=dev
RUN mkdir -p /opt/apps && mkdir -p /opt/jmeter
COPY backend/target/backend-1.4.jar /opt/apps
COPY backend/target/backend-1.5.jar /opt/apps
COPY backend/target/classes/jmeter/ /opt/jmeter/
ENV JAVA_APP_JAR=/opt/apps/backend-1.4.jar
ENV JAVA_APP_JAR=/opt/apps/backend-1.5.jar
ENV AB_OFF=true

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>metersphere-server</artifactId>
<groupId>io.metersphere</groupId>
<version>1.4</version>
<version>1.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -156,7 +156,7 @@
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.2</version>
<version>2.7.0</version>
</dependency>
<dependency>

View File

@ -146,7 +146,12 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
ScenarioResult scenarioResult;
if (!scenarios.containsKey(scenarioId)) {
scenarioResult = new ScenarioResult();
scenarioResult.setId(scenarioId);
try {
scenarioResult.setId(Integer.parseInt(scenarioId));
} catch (Exception e) {
scenarioResult.setId(0);
LogUtil.error("场景ID转换异常: " + e.getMessage());
}
scenarioResult.setName(scenarioName);
scenarios.put(scenarioId, scenarioResult);
} else {
@ -203,13 +208,13 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
testPlanTestCaseService.updateTestCaseStates(ids, TestPlanTestCaseStatus.Failure.name());
}
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
try {
sendTask(report, testResult);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}

View File

@ -8,7 +8,7 @@ import java.util.List;
@Data
public class ScenarioResult {
private String id;
private Integer id;
private String name;

View File

@ -50,7 +50,7 @@ public class JmeterDocumentParser {
}
return documentToBytes(document);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
return source;
}
}
@ -161,7 +161,9 @@ public class JmeterDocumentParser {
break;
case "Argument.value":
String textContent = ele.getTextContent();
if (StringUtils.startsWith(textContent, "@")) {
ele.setTextContent(ScriptEngineUtils.calculate(textContent));
}
break;
default:
break;

View File

@ -8,6 +8,7 @@ import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.parse.ApiImportParser;
import io.metersphere.api.parse.ApiImportParserFactory;
import io.metersphere.api.parse.JmeterDocumentParser;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiTestFileMapper;
import io.metersphere.base.mapper.ApiTestMapper;
@ -85,9 +86,9 @@ public class APITestService {
return extApiTestMapper.listByIds(request.getIds());
}
public void create(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
public void create(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
ApiTest test = createTest(request);
ApiTest test = createTest(request, file);
createBodyFiles(test, bodyUploadIds, bodyFiles);
}
private ApiTest createTest(SaveAPITestRequest request, MultipartFile file) {
@ -101,13 +102,17 @@ public class APITestService {
return test;
}
public void update(SaveAPITestRequest request, List<MultipartFile> bodyFiles) {
public void update(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
deleteFileByTestId(request.getId());
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
request.setBodyUploadIds(null);
ApiTest test = updateTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles);
saveFile(test.getId(), file);
}
private void createBodyFiles(ApiTest test, List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
@ -126,7 +131,7 @@ public class APITestService {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("upload_fail"));
}
}
@ -169,7 +174,7 @@ public class APITestService {
try {
FileUtil.copyDir(sourceFile, new File(targetDir));
} catch (IOException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("upload_fail"));
}
}
@ -212,14 +217,27 @@ public class APITestService {
}
}
public String run(SaveAPITestRequest request,String runMode) {
public String run(SaveAPITestRequest request) {
ApiTestFile file = getFileByTestId(request.getId());
if (file == null) {
MSException.throwException(Translator.get("file_cannot_be_null"));
}
byte[] bytes = fileService.loadFileAsBytes(file.getFileId());
// 解析 xml 处理 mock 数据
bytes = JmeterDocumentParser.parse(bytes);
InputStream is = new ByteArrayInputStream(bytes);
APITestResult apiTest = get(request.getId());
if (SessionUtils.getUser() == null) {
apiTest.setUserId(request.getUserId());
}
String reportId = apiReportService.create(apiTest, request.getTriggerMode());
/*if (request.getTriggerMode().equals("SCHEDULE")) {
List<Notice> notice = noticeService.queryNotice(request.getId());
mailService.sendHtml(reportId,notice,"api");
}*/
changeStatus(request.getId(), APITestStatus.Running);
jMeterService.run(request.getId(), request.getName(), request.getScenarioDefinition(), null,runMode);
jMeterService.run(request.getId(), null, is);
return reportId;
}
@ -260,8 +278,6 @@ public class APITestService {
}
private ApiTest createTest(SaveAPITestRequest request) {
checkQuota();
request.setBodyUploadIds(null);
checkNameExist(request);
final ApiTest test = new ApiTest();
test.setId(request.getId());
@ -276,6 +292,14 @@ public class APITestService {
return test;
}
private void saveFile(String testId, MultipartFile 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);
@ -287,12 +311,17 @@ public class APITestService {
fileService.deleteFileByIds(fileIds);
}
}
private void saveFile(String testId, MultipartFile file) {
final FileMetadata fileMetadata = fileService.saveFile(file);
ApiTestFile apiTestFile = new ApiTestFile();
apiTestFile.setTestId(testId);
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
private ApiTestFile getFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
final List<ApiTestFile> ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample);
apiTestFileMapper.selectByExample(ApiTestFileExample);
if (!CollectionUtils.isEmpty(ApiTestFiles)) {
return ApiTestFiles.get(0);
} else {
return null;
}
}
public void updateSchedule(Schedule request) {
@ -312,17 +341,6 @@ public class APITestService {
schedule.setType(ScheduleType.CRON.name());
return schedule;
}
private ApiTestFile getFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
final List<ApiTestFile> ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample);
apiTestFileMapper.selectByExample(ApiTestFileExample);
if (!CollectionUtils.isEmpty(ApiTestFiles)) {
return ApiTestFiles.get(0);
} else {
return null;
}
}
private void addOrUpdateApiTestCronJob(Schedule request) {
scheduleService.addOrUpdateCronJob(request, ApiTestJob.getJobKey(request.getResourceId()), ApiTestJob.getTriggerKey(request.getResourceId()), ApiTestJob.class);
@ -398,7 +416,10 @@ public class APITestService {
return schedules;
}
public String runDebug(SaveAPITestRequest request, List<MultipartFile> bodyFiles,String runMode) {
public String runDebug(SaveAPITestRequest request, MultipartFile file, List<MultipartFile> bodyFiles) {
if (file == null) {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
}
updateTest(request);
APITestResult apiTest = get(request.getId());
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
@ -409,7 +430,17 @@ public class APITestService {
}
String reportId = apiReportService.createDebugReport(apiTest);
jMeterService.run(request.getId(), request.getName(), request.getScenarioDefinition(), reportId,runMode);
InputStream is = null;
try {
byte[] bytes = file.getBytes();
// 解析 xml 处理 mock 数据
bytes = JmeterDocumentParser.parse(bytes);
is = new ByteArrayInputStream(bytes);
} catch (IOException e) {
LogUtil.error(e.getMessage(), e);
}
jMeterService.run(request.getId(), reportId, is);
return reportId;
}
@ -420,12 +451,10 @@ public class APITestService {
}
}
public void mergeCreate(SaveAPITestRequest request, List<String> selectIds) {
ApiTest test = createTest(request);
selectIds.forEach(sourceId -> copyBodyFiles(test.getId(), sourceId));
}
public String getJMX(SaveAPITestRequest request) {
return jMeterService.getJMX(jMeterService.getHashTree(request.getId(), request.getName(), request.getScenarioDefinition()));
public void mergeCreate(SaveAPITestRequest request, MultipartFile file, List<String> selectIds) {
ApiTest test = createTest(request, file);
selectIds.forEach(sourceId -> {
copyBodyFiles(test.getId(), sourceId);
});
}
}

View File

@ -133,6 +133,7 @@
LEFT JOIN user ON user.id = r.user_id
<where>
r.test_id = #{testId}
and r.status != 'Debug'
</where>
ORDER BY r.update_time DESC
</select>

View File

@ -258,7 +258,8 @@
</select>
<select id="list" resultType="io.metersphere.track.dto.TestCaseDTO">
select test_case.* from test_case
select <include refid="io.metersphere.base.mapper.TestCaseMapper.Base_Column_List"/>
from test_case
<where>
<if test="request.combine != null">
<include refid="combine">

View File

@ -95,8 +95,7 @@ public interface ParamConstants {
PASSWORD("smtp.password", 4),
SSL("smtp.ssl", 5),
TLS("smtp.tls", 6),
SMTP("smtp.smtp", 7);
/* ANON("smtp.anon", 7);*/
ANON("smtp.anon", 7);
private String key;
private Integer value;

View File

@ -88,7 +88,7 @@ public class CompressUtils {
gzip.finish();
return obj.toByteArray();
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
return data;
}
}
@ -116,7 +116,7 @@ public class CompressUtils {
baos.flush();
return baos.toByteArray();
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
return data;
}
}

View File

@ -48,10 +48,6 @@ public class JsonPathUtils {
String o_json_path = "$" + jsonPath.next().replaceAll("/", ".");
String value = JSONPath.eval(jsonObject, o_json_path).toString();
if(o_json_path.toLowerCase().contains("id")) {
continue;
}
if(value.equals("") || value.equals("[]") || o_json_path.equals("")) {
continue;
}

View File

@ -18,7 +18,7 @@ public class ScriptEngineUtils {
String script = IOUtils.toString(ScriptEngineUtils.class.getResource("/javascript/func.js"), StandardCharsets.UTF_8);
engine.eval(script);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -26,7 +26,7 @@ public class ScriptEngineUtils {
try {
return engine.eval("calculate('" + input + "')").toString();
} catch (ScriptException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
return input;
}
}

View File

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

View File

@ -53,13 +53,13 @@ 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() {
public List<TestResourcePoolDTO> listValidResourcePools() {
return testResourcePoolService.listValidResourcePools();
}
@GetMapping("list/quota/valid")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
public List<TestResourcePool> listValidQuotaResourcePools() {
public List<TestResourcePoolDTO> listValidQuotaResourcePools() {
return testResourcePoolService.listValidQuotaResourcePools();
}

View File

@ -7,4 +7,5 @@ import lombok.Setter;
@Setter
public class QueryResourcePoolRequest {
private String name;
private String status;
}

View File

@ -72,13 +72,15 @@ public class AppStartListener implements ApplicationListener<ApplicationReadyEve
*/
private void loadJars() {
List<JarConfig> jars = jarConfigService.list();
try {
jars.forEach(jarConfig -> {
try {
NewDriverManager.loadJar(jarConfig.getPath());
});
} catch (Exception e) {
e.printStackTrace();
LogUtil.error(e.getMessage(), e);
}
});
}
}

View File

@ -66,7 +66,7 @@ public class DingTaskService {
try {
response = client.execute(request);
} catch (ApiException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
System.out.println(response.getErrcode());
}

View File

@ -2,10 +2,7 @@ package io.metersphere.notice.service;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.UserMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.ParamConstants;
import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.EncryptUtils;
import io.metersphere.commons.utils.LogUtil;
@ -22,6 +19,7 @@ import io.metersphere.track.request.testreview.SaveTestCaseReviewRequest;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.mail.MailException;
@ -67,7 +65,7 @@ public class MailService {
}
sendApiOrLoadNotification(addresseeIdList(messageDetail, userIds, eventType), context, performanceTemplate, loadTestReport.getTriggerMode());
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -88,7 +86,7 @@ public class MailService {
}
sendApiOrLoadNotification(addresseeIdList(messageDetail, userIds, eventType), context, apiTemplate, apiTestReport.getTriggerMode());
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -120,7 +118,7 @@ public class MailService {
try {
javaMailSender.send(mimeMessage);
} catch (MailException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
//测试评审
@ -131,7 +129,7 @@ public class MailService {
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/ReviewEnd.html"), StandardCharsets.UTF_8);
sendReviewNotice(addresseeIdList(messageDetail, userIds, eventType), context, endTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -141,7 +139,7 @@ public class MailService {
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/ReviewDelete.html"), StandardCharsets.UTF_8);
sendReviewNotice(addresseeIdList(messageDetail, userIds, eventType), context, endTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -158,7 +156,7 @@ public class MailService {
String commentTemplate = IOUtils.toString(this.getClass().getResource("/mail/ReviewComments.html"), StandardCharsets.UTF_8);
sendReviewNotice(addresseeIdList(messageDetail, userIds, eventType), context, commentTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -168,7 +166,7 @@ public class MailService {
String reviewerTemplate = IOUtils.toString(this.getClass().getResource("/mail/ReviewInitiate.html"), StandardCharsets.UTF_8);
sendReviewNotice(addresseeIdList(messageDetail, userIds, eventType), context, reviewerTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -207,7 +205,7 @@ public class MailService {
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/TestPlanStart.html"), StandardCharsets.UTF_8);
sendTestPlanNotice(addresseeIdList(messageDetail, userIds, eventType), context, endTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -219,7 +217,7 @@ public class MailService {
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/TestPlanEnd.html"), StandardCharsets.UTF_8);
sendTestPlanNotice(addresseeIdList(messageDetail, userIds, eventType), context, endTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -231,7 +229,7 @@ public class MailService {
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/TestPlanDelete.html"), StandardCharsets.UTF_8);
sendTestPlanNotice(addresseeIdList(messageDetail, userIds, eventType), context, endTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -268,7 +266,7 @@ public class MailService {
String endTemplate = IOUtils.toString(this.getClass().getResource("/mail/IssuesCreate.html"), StandardCharsets.UTF_8);
sendIssuesNotice(addresseeIdList(messageDetail, userIds, eventType), context, endTemplate);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -321,6 +319,15 @@ public class MailService {
context.put("start", start);
context.put("end", end);
context.put("id", reviewRequest.getId());
String status = "";
if (StringUtils.equals(TestPlanStatus.Underway.name(), reviewRequest.getStatus())) {
status = "进行中";
} else if (StringUtils.equals(TestPlanStatus.Prepare.name(), reviewRequest.getStatus())) {
status = "未开始";
} else if (StringUtils.equals(TestPlanStatus.Completed.name(), reviewRequest.getStatus())) {
status = "已完成";
}
context.put("status", status);
return context;
}
@ -347,19 +354,29 @@ public class MailService {
context.put("start", start);
context.put("end", end);
context.put("id", testPlan.getId());
String status = "";
if (StringUtils.equals(TestPlanStatus.Underway.name(), testPlan.getStatus())) {
status = "进行中";
} else if (StringUtils.equals(TestPlanStatus.Prepare.name(), testPlan.getStatus())) {
status = "未开始";
} else if (StringUtils.equals(TestPlanStatus.Completed.name(), testPlan.getStatus())) {
status = "已完成";
}
context.put("status", status);
User user = userMapper.selectByPrimaryKey(testPlan.getCreator());
context.put("creator", user.getName());
return context;
}
private JavaMailSenderImpl getMailSender() {
Properties props = new Properties();
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
List<SystemParameter> paramList = systemParameterService.getParamList(ParamConstants.Classify.MAIL.getValue());
javaMailSender.setDefaultEncoding("UTF-8");
javaMailSender.setProtocol("smtps");
javaMailSender.setProtocol("smtp");
props.put("mail.smtp.auth", "true");
for (SystemParameter p : paramList) {
switch (p.getParamKey()) {
case "smtp.host":
@ -374,14 +391,30 @@ public class MailService {
case "smtp.password":
javaMailSender.setPassword(EncryptUtils.aesDecrypt(p.getParamValue()).toString());
break;
case "smtp.ssl":
javaMailSender.setProtocol("smtps");
if (BooleanUtils.toBoolean(p.getParamValue())) {
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
break;
case "smtp.tls":
String result = BooleanUtils.toString(BooleanUtils.toBoolean(p.getParamValue()), "true", "false");
props.put("mail.smtp.starttls.enable", result);
props.put("mail.smtp.starttls.required", result);
break;
case "smtp.anon":
boolean isAnon = BooleanUtils.toBoolean(p.getParamValue());
if (isAnon) {
props.put("mail.smtp.auth", "false");
javaMailSender.setUsername(null);
javaMailSender.setPassword(null);
}
break;
default:
break;
}
}
Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.timeout", "30000");
props.put("mail.smtp.connectiontimeout", "5000");
javaMailSender.setJavaMailProperties(props);
@ -396,6 +429,7 @@ public class MailService {
} else {
template = RegExUtils.replaceAll(template, "\\$\\{" + k + "}", "未设置");
}
}
}
return template;
@ -405,7 +439,7 @@ public class MailService {
List<String> addresseeIdList = new ArrayList<>();
if (StringUtils.equals(eventType, messageDetail.getEvent())) {
messageDetail.getUserIds().forEach(u -> {
if (!StringUtils.equals(NoticeConstants.EXECUTOR, u) && !StringUtils.equals(NoticeConstants.EXECUTOR, u) && !StringUtils.equals(NoticeConstants.MAINTAINER, u)) {
if (!StringUtils.equals(NoticeConstants.EXECUTOR, u) && !StringUtils.equals(NoticeConstants.FOUNDER, u) && !StringUtils.equals(NoticeConstants.MAINTAINER, u)) {
addresseeIdList.add(u);
}
if (StringUtils.equals(NoticeConstants.CREATE, eventType) && StringUtils.equals(NoticeConstants.EXECUTOR, u)) {

View File

@ -13,6 +13,7 @@ import io.metersphere.notice.controller.request.MessageRequest;
import io.metersphere.notice.domain.MessageDetail;
import io.metersphere.notice.domain.MessageSettingDetail;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -51,7 +52,7 @@ public class NoticeService {
long time = System.currentTimeMillis();
String identification = UUID.randomUUID().toString();
list.getUserIds().forEach(m -> {
checkUserIdExist(m, list);
checkUserIdExist(m, list,orgId);
MessageTask message = new MessageTask();
message.setId(UUID.randomUUID().toString());
message.setEvent(list.getEvent());
@ -68,9 +69,13 @@ public class NoticeService {
});
}
private void checkUserIdExist(String userId, MessageDetail list) {
private void checkUserIdExist(String userId, MessageDetail list,String orgId) {
MessageTaskExample example = new MessageTaskExample();
example.createCriteria().andUserIdEqualTo(userId).andEventEqualTo(list.getEvent()).andTypeEqualTo(list.getType()).andTaskTypeEqualTo(list.getTaskType()).andWebhookEqualTo(list.getWebhook());
if (StringUtils.isBlank(list.getTestId())) {
example.createCriteria().andUserIdEqualTo(userId).andEventEqualTo(list.getEvent()).andTypeEqualTo(list.getType()).andTaskTypeEqualTo(list.getTaskType()).andWebhookEqualTo(list.getWebhook()).andOrganizationIdEqualTo(orgId);
} else {
example.createCriteria().andUserIdEqualTo(userId).andEventEqualTo(list.getEvent()).andTypeEqualTo(list.getType()).andTaskTypeEqualTo(list.getTaskType()).andWebhookEqualTo(list.getWebhook()).andTestIdEqualTo(list.getTestId()).andOrganizationIdEqualTo(orgId);
}
if (messageTaskMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("message_task_already_exists"));
}

View File

@ -59,7 +59,7 @@ public class WxChatTaskService {
SendResult result = WxChatbotClient.send(Webhook, message);
System.out.println(result);
} catch (IOException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}

View File

@ -139,10 +139,10 @@ public class EngineFactory {
String content = engineSourceParser.parse(engineContext, source);
engineContext.setContent(content);
} catch (MSException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(e);
}

View File

@ -64,10 +64,10 @@ public class DockerTestEngine extends AbstractEngine {
try {
context = EngineFactory.createContext(loadTest, resource.getId(), ratio, this.getStartTime(), this.getReportId(), resourceIndex);
} catch (MSException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(e);
}

View File

@ -65,7 +65,7 @@ public class PerformanceNoticeTask {
//查询定时任务是否关闭
Thread.sleep(1000 * 30);// 每分钟检查 loadtest 的状态
} catch (InterruptedException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
});

View File

@ -258,7 +258,7 @@ public class PerformanceTestService {
soc.close();
}
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("load_test_kafka_invalid"));
}
}
@ -304,7 +304,7 @@ public class PerformanceTestService {
} catch (MSException e) {
// 启动失败之后清理任务
engine.stop();
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
loadTest.setStatus(PerformanceTestStatus.Error.name());
loadTest.setDescription(e.getMessage());
loadTestMapper.updateByPrimaryKeySelective(loadTest);

View File

@ -119,7 +119,7 @@ public class JarConfigService {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("upload_fail"));
}
return filePath;

View File

@ -87,7 +87,14 @@ public class SystemParameterService {
javaMailSender.setUsername(hashMap.get(ParamConstants.MAIL.ACCOUNT.getKey()));
javaMailSender.setPassword(hashMap.get(ParamConstants.MAIL.PASSWORD.getKey()));
Properties props = new Properties();
boolean isAnon = Boolean.parseBoolean(hashMap.get(ParamConstants.MAIL.ANON.getKey()));
if (isAnon) {
props.put("mail.smtp.auth", "false");
javaMailSender.setUsername(null);
javaMailSender.setPassword(null);
} else {
props.put("mail.smtp.auth", "true");
}
if (BooleanUtils.toBoolean(hashMap.get(ParamConstants.MAIL.SSL.getKey()))) {
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
@ -100,7 +107,7 @@ public class SystemParameterService {
try {
javaMailSender.testConnection();
} catch (MessagingException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
MSException.throwException(Translator.get("connection_failed"));
}
}

View File

@ -135,7 +135,7 @@ public class TestResourcePoolService {
MSException.throwException("Resource Pool is invalid.");
}
} catch (IllegalAccessException | InvocationTargetException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -145,6 +145,9 @@ public class TestResourcePoolService {
if (StringUtils.isNotBlank(request.getName())) {
criteria.andNameLike(StringUtils.wrapIfMissing(request.getName(), "%"));
}
if (StringUtils.isNotBlank(request.getStatus())) {
criteria.andStatusEqualTo(request.getStatus());
}
example.setOrderByClause("update_time desc");
List<TestResourcePool> testResourcePools = testResourcePoolMapper.selectByExample(example);
List<TestResourcePoolDTO> testResourcePoolDTOS = new ArrayList<>();
@ -158,7 +161,7 @@ public class TestResourcePoolService {
testResourcePoolDTO.setResources(testResources);
testResourcePoolDTOS.add(testResourcePoolDTO);
} catch (IllegalAccessException | InvocationTargetException e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
});
return testResourcePoolDTOS;
@ -207,7 +210,7 @@ public class TestResourcePoolService {
ResponseEntity<String> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class);
return HttpStatus.OK.equals(entity.getStatusCode());
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
return false;
}
}
@ -232,7 +235,7 @@ public class TestResourcePoolService {
return testResourcePoolMapper.selectByPrimaryKey(resourcePoolId);
}
public List<TestResourcePool> listValidResourcePools() {
public List<TestResourcePoolDTO> listValidResourcePools() {
QueryResourcePoolRequest request = new QueryResourcePoolRequest();
List<TestResourcePoolDTO> testResourcePools = listResourcePools(request);
// 重新校验 pool
@ -249,16 +252,15 @@ public class TestResourcePoolService {
testResourcePoolMapper.updateByPrimaryKeySelective(pool);
}
}
TestResourcePoolExample example = new TestResourcePoolExample();
example.createCriteria().andStatusEqualTo(ResourceStatusEnum.VALID.name());
return testResourcePoolMapper.selectByExample(example);
request.setStatus(VALID.name());
return listResourcePools(request);
}
public List<TestResourcePool> listValidQuotaResourcePools() {
public List<TestResourcePoolDTO> listValidQuotaResourcePools() {
return filterQuota(listValidResourcePools());
}
private List<TestResourcePool> filterQuota(List<TestResourcePool> list) {
private List<TestResourcePoolDTO> filterQuota(List<TestResourcePoolDTO> list) {
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
if (quotaService != null) {
Set<String> pools = quotaService.getQuotaResourcePools();

View File

@ -1,26 +0,0 @@
package io.metersphere.track.issue;
import io.metersphere.commons.utils.EncryptUtils;
public class ZentaoUtils {
/**
* @param code Zentao 应用代号
* @param key Zentao 密钥
* @return token
*/
public static String getToken(String code, String key, String time) {
return (String) EncryptUtils.md5Encrypt(code + key + time);
}
/**
* @param url Zentao url
* @param code Zentao 应用代号
* @param key Zentao 密钥
* @return url
*/
public static String getUrl(String url, String code, String key) {
String time = String.valueOf(System.currentTimeMillis());;
return url + "api.php?" + "code=" + code + "&time=" + time + "&token=" + getToken(code, key, time);
}
}

View File

@ -116,7 +116,7 @@ public class IssuesService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}

View File

@ -84,7 +84,7 @@ public class TestCaseCommentService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}

View File

@ -127,7 +127,7 @@ public class TestCaseReviewService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -187,7 +187,6 @@ public class TestCaseReviewService {
testCaseReviewMapper.updateByPrimaryKeySelective(testCaseReview);
List<String> userIds = new ArrayList<>();
userIds.addAll(testCaseReview.getUserIds());
if (StringUtils.equals(TestPlanStatus.Completed.name(), testCaseReview.getStatus())) {
try {
String context = getReviewContext(testCaseReview, NoticeConstants.UPDATE);
MessageSettingDetail messageSettingDetail = noticeService.searchMessage();
@ -201,13 +200,12 @@ public class TestCaseReviewService {
wxChatTaskService.sendWechatRobot(r, userIds, context, NoticeConstants.UPDATE);
break;
case NoticeConstants.EMAIL:
mailService.sendReviewerNotice(r, userIds, testCaseReview, NoticeConstants.UPDATE);
mailService.sendEndNotice(r, userIds, testCaseReview, NoticeConstants.UPDATE);
break;
}
});
} catch (Exception e) {
LogUtil.error(e);
}
LogUtil.error(e.getMessage(), e);
}
}
@ -318,7 +316,7 @@ public class TestCaseReviewService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -468,7 +466,7 @@ public class TestCaseReviewService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
}
@ -585,7 +583,15 @@ public class TestCaseReviewService {
if (StringUtils.equals(NoticeConstants.CREATE, type)) {
context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "待开始,计划开始时间是" + start + "计划结束时间为" + end + "请跟进";
} else if (StringUtils.equals(NoticeConstants.UPDATE, type)) {
context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "已完成,计划开始时间是" + start + "计划结束时间为" + end + "已完成";
String status = "";
if (StringUtils.equals(TestPlanStatus.Underway.name(), reviewRequest.getStatus())) {
status = "进行中";
} else if (StringUtils.equals(TestPlanStatus.Prepare.name(), reviewRequest.getStatus())) {
status = "未开始";
} else if (StringUtils.equals(TestPlanStatus.Completed.name(), reviewRequest.getStatus())) {
status = "已完成";
}
context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + status;
} else if (StringUtils.equals(NoticeConstants.DELETE, type)) {
context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + "已删除";
}

View File

@ -96,7 +96,7 @@ public class TestPlanService {
@Resource
UserMapper userMapper;
public void addTestPlan(AddTestPlanRequest testPlan) {
public synchronized void addTestPlan(AddTestPlanRequest testPlan) {
if (getTestPlanByName(testPlan.getName()).size() > 0) {
MSException.throwException(Translator.get("plan_name_already_exists"));
}
@ -137,11 +137,11 @@ public class TestPlanService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
public synchronized List<TestPlan> getTestPlanByName(String name) {
public List<TestPlan> getTestPlanByName(String name) {
TestPlanExample example = new TestPlanExample();
example.createCriteria().andWorkspaceIdEqualTo(SessionUtils.getCurrentWorkspaceId())
.andNameEqualTo(name);
@ -159,13 +159,16 @@ public class TestPlanService {
//进行中状态写入实际开始时间
if (TestPlanStatus.Underway.name().equals(testPlan.getStatus())) {
testPlan.setActualStartTime(System.currentTimeMillis());
} else if (TestPlanStatus.Completed.name().equals(testPlan.getStatus())) {
//已完成写入实际完成时间
testPlan.setActualEndTime(System.currentTimeMillis());
}
List<String> userIds = new ArrayList<>();
userIds.add(testPlan.getPrincipal());
AddTestPlanRequest testPlans = new AddTestPlanRequest();
//已完成写入实际完成时间
testPlan.setActualEndTime(System.currentTimeMillis());
int i = testPlanMapper.updateByPrimaryKeySelective(testPlan);
if (!StringUtils.isBlank(testPlan.getStatus())) {
try {
BeanUtils.copyBean(testPlans, getTestPlan(testPlan.getId()));
String context = getTestPlanContext(testPlans, NoticeConstants.UPDATE);
@ -180,16 +183,15 @@ public class TestPlanService {
wxChatTaskService.sendWechatRobot(r, userIds, context, NoticeConstants.UPDATE);
break;
case NoticeConstants.EMAIL:
mailService.sendTestPlanStartNotice(r, userIds, testPlans, NoticeConstants.UPDATE);
mailService.sendTestPlanEndNotice(r, userIds, testPlans, NoticeConstants.UPDATE);
break;
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
return testPlanMapper.updateByPrimaryKeySelective(testPlan);
return i;
}
private void editTestPlanProject(TestPlanDTO testPlan) {
@ -270,7 +272,7 @@ public class TestPlanService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
return num;
}
@ -511,7 +513,7 @@ public class TestPlanService {
}
});
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
@ -557,11 +559,19 @@ public class TestPlanService {
}
String context = "";
if (StringUtils.equals(NoticeConstants.CREATE, type)) {
context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "待开始,计划开始时间是" + start + "计划结束时间为" + end + "请跟进";
context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "待开始,计划开始时间是:" + "'" + start + "'" + ";" + "计划结束时间是:" + "'" + end + "'" + " " + "请跟进";
} else if (StringUtils.equals(NoticeConstants.UPDATE, type)) {
context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "已完成,计划开始时间是" + start + "计划结束时间为" + end + "已完成";
String status = "";
if (StringUtils.equals(TestPlanStatus.Underway.name(), testPlan.getStatus())) {
status = "进行中";
} else if (StringUtils.equals(TestPlanStatus.Prepare.name(), testPlan.getStatus())) {
status = "未开始";
} else if (StringUtils.equals(TestPlanStatus.Completed.name(), testPlan.getStatus())) {
status = "已完成";
}
context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "计划开始时间是:" + "'" + start + "'" + ";" + "计划结束时间是:" + "'" + end + "'" + " " + status;
} else if (StringUtils.equals(NoticeConstants.DELETE, type)) {
context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + "已删除";
context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "计划开始时间是:" + "'" + start + "'" + ";" + "计划结束时间是:" + "'" + end + "'" + " " + "已删除";
}
return context;
}

View File

@ -106,7 +106,7 @@ public class ReportWebSocket {
}
Thread.sleep(20 * 1000L);
} catch (Exception e) {
LogUtil.error(e);
LogUtil.error(e.getMessage(), e);
}
}
}

View File

@ -283,17 +283,20 @@ public class XmindCaseParser {
// 测试步骤处理
List<Attached> steps = new LinkedList<>();
StringBuilder rc = new StringBuilder();
if (attacheds != null && !attacheds.isEmpty()) {
attacheds.forEach(item -> {
if (isAvailable(item.getTitle(), PC_REGEX)) {
testCase.setPrerequisite(replace(item.getTitle(), PC_REGEX));
} else if (isAvailable(item.getTitle(), RC_REGEX)) {
testCase.setRemark(replace(item.getTitle(), RC_REGEX));
rc.append(replace(item.getTitle(), RC_REGEX));
rc.append("\n");
} else {
steps.add(item);
}
});
}
testCase.setRemark(rc.toString());
testCase.setSteps(this.getSteps(steps));
testCases.add(testCase);
// 校验合规性

View File

@ -6,7 +6,7 @@
</head>
<body>
<div>
<p style="text-align: left"> ${maintainer} 维护的<br/>
<p style="text-align: left"> ${maintainer} <br/>
${testCaseName}<br/>
添加评论:${description}<br/>
点击下面链接进入用例评审页面</p>

View File

@ -7,10 +7,10 @@
<body>
<div>
<p style="text-align: left">${creator} 发起的:<br>
${reviewName}已完成<br>
${reviewName}<br>
计划开始时间是:${start}<br>
计划结束时间为:${end}<br>
已完成<br>
${status}<br>
点击下面链接进入用例评审页面</p>
<a href="${url}/#/track/review/view">${url}/#/track/review/view</a>
</div>

View File

@ -7,10 +7,10 @@
<body>
<div>
<p style="text-align: left">${creator} 创建的:<br>
${testPlanName}已完成<br>
${testPlanName}<br>
计划开始时间是:${start}<br>
计划结束时间为:${end}<br>
已完成<br>
${status}<br>
点击下面链接进入测试计划页面</p>
<a href="${url}/#/track/plan/view/${id}">${url}/#/track/plan/view</a>
</div>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>metersphere-server</artifactId>
<groupId>io.metersphere</groupId>
<version>1.4</version>
<version>1.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -7,8 +7,7 @@
</el-row>
<el-row id="header-top" type="flex" justify="space-between" align="middle">
<el-col :span="12">
<img v-if="logoId" :src="'/display/file/' + logoId" style="width: 156px;height: 37px;" alt="">
<a v-else class="logo"/>
<img :src="'/display/file/logo'" style="width: 156px;height: 37px;" alt="">
<ms-top-menus/>
</el-col>

View File

@ -46,10 +46,9 @@ import MsScenarioResults from "./components/ScenarioResults";
import MsContainer from "@/business/components/common/components/MsContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import MsApiReportExport from "./ApiReportExport";
import {exportPdf} from "@/common/js/utils";
import html2canvas from "html2canvas";
import MsApiReportViewHeader from "./ApiReportViewHeader";
import {RequestFactory} from "../test/model/ScenarioModel";
import {windowPrint} from "../../../../common/js/utils";
export default {
name: "MsApiReportViewDetail",
@ -154,23 +153,16 @@ export default {
this.scenarioName = requestResult.scenarioName;
});
},
handleExport(name) {
this.loading = true;
handleExport() {
this.reportExportVisible = true;
let reset = this.exportReportReset;
this.$nextTick(function () {
html2canvas(document.getElementById('apiTestReport'), {
// scale: 2,
}).then(function (canvas) {
exportPdf(name, [canvas]);
this.$nextTick(() => {
windowPrint('apiTestReport', 0.57);
reset();
});
});
},
exportReportReset() {
this.reportExportVisible = false;
this.loading = false;
this.$router.go(0);
}
},

View File

@ -72,6 +72,7 @@ export default {
methods: {
search() {
this.condition.excludeId = this.excludeId;
this.condition.projectId = this.projectId;
let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;

View File

@ -17,10 +17,9 @@
:scenario="scenario"
:extract="extract"
type="body"
:description="$t('api_test.request.parameters_desc')"
v-if="body.isKV()"/>
<div class="body-raw" v-if="body.type == 'Raw'">
<ms-code-edit :mode="body.format" :read-only="isReadOnly" :data.sync="body.raw" :modes="modes" ref="codeEdit"/>
<ms-code-edit :mode="body.format" :enable-format="false" :read-only="isReadOnly" :data.sync="body.raw" :modes="modes" ref="codeEdit"/>
</div>
</div>

View File

@ -29,7 +29,7 @@
</el-form-item>
<el-form-item :label="$t('api_test.request.sql.pool_max')" prop="poolMax">
<el-input-number size="small" :disabled="isReadOnly" v-model="currentConfig.poolMax" :placeholder="$t('commons.please_select')" :max="1000*10000000" :min="0"/>
<el-input-number size="small" :disabled="isReadOnly" v-model="currentConfig.poolMax" :placeholder="$t('commons.please_select')" :max="100" :min="0"/>
</el-form-item>

View File

@ -157,7 +157,7 @@ export class Test extends BaseConfig {
constructor(options) {
super();
this.type = "MS API CONFIG";
this.version = '1.4.0';
this.version = '1.5.0';
this.id = uuid();
this.name = undefined;
this.projectId = undefined;
@ -609,8 +609,8 @@ export class DatabaseConfig extends BaseConfig {
super();
this.id = undefined;
this.name = undefined;
this.poolMax = undefined;
this.timeout = undefined;
this.poolMax = 1;
this.timeout = 100000;
this.driver = undefined;
this.dbUrl = undefined;
this.username = undefined;

View File

@ -28,6 +28,12 @@
init: {
type: Function
},
enableFormat: {
type: Boolean,
default() {
return true;
}
},
readOnly: {
type: Boolean,
default() {
@ -74,6 +80,7 @@
}
},
format() {
if (this.enableFormat) {
switch (this.mode) {
case 'json':
this.formatData = formatJson(this.data);
@ -89,6 +96,9 @@
this.formatData = this.data;
}
}
} else {
this.formatData = this.data;
}
}
}
}

View File

@ -11,6 +11,11 @@
<br/>
</el-col>
</el-row>
<el-row class="tip" v-if="withTip">
<span>
<slot class="tip"></slot>
</span>
</el-row>
<el-row>
<el-col :span="15">
<el-input v-model="value" :placeholder="$t('commons.input_content')"/>
@ -41,6 +46,12 @@
default() {
return this.$t('commons.title')
}
},
withTip: {
type: Boolean,
default() {
return false
}
}
},
methods: {
@ -80,5 +91,9 @@
font-weight: bold;
}
.tip {
margin-bottom: 20px;
color: red;
}
</style>

View File

@ -73,7 +73,9 @@
this.$emit('scheduleChange');
},
flashResultList() {
if (this.$refs.crontabResult) {
this.$refs.crontabResult.expressionChange();
}
},
cancelRefresh() {
if (this.refreshScheduler) {

View File

@ -31,7 +31,7 @@
</el-dialog>
</el-tab-pane>
<el-tab-pane :label="$t('schedule.task_notification')" name="second">
<schedule-task-notification :test-id="testId" :schedule-receiver-options="scheduleReceiverOptions"></schedule-task-notification>
<schedule-task-notification :is-tester-permission="isTesterPermission" :test-id="testId" :schedule-receiver-options="scheduleReceiverOptions"></schedule-task-notification>
</el-tab-pane>
</el-tabs>
</div>
@ -46,6 +46,7 @@ import CrontabResult from "../cron/CrontabResult";
import {cronValidate} from "@/common/js/cron";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import ScheduleTaskNotification from "../../settings/organization/components/ScheduleTaskNotification";
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
function defaultCustomValidate() {
return {pass: true};
}
@ -113,10 +114,13 @@ export default {
name: '',
organizationId: this.currentUser().lastOrganizationId
};
if (this.isTesterPermission) {
this.result = this.$post('user/org/member/list/all', param, response => {
this.scheduleReceiverOptions = response.data
});
}
},
/* handleClick() {
if (this.activeName === "second") {
@ -188,6 +192,11 @@ export default {
let time1 = new Date(resultList[0]);
let time2 = new Date(resultList[1]);
return time2 - time1;
},
},
computed: {
isTesterPermission() {
return checkoutTestManagerOrTestUser();
}
}
}

View File

@ -9,10 +9,8 @@
</div>
</div>
<div class="report-right">
<div class="test">
<img class="logo" src="@/assets/logo-MeterSphere.png">
</div>
</div>
</div>
</template>

View File

@ -271,12 +271,14 @@ export default {
let reset = this.exportReportReset;
this.$nextTick(function () {
setTimeout(() => {
html2canvas(document.getElementById('performanceReportExport'), {
// scale: 2
scale: 2
}).then(function (canvas) {
exportPdf(name, [canvas]);
reset();
});
}, 1000);
});
},
exportReportReset() {

View File

@ -30,7 +30,7 @@
:placeholder="$t('load_test.input_thread_num')"
v-model="threadGroup.threadNumber"
@change="calculateChart(threadGroup)"
:min="1"
:min="resourcePoolResourceLength"
size="mini"/>
</el-form-item>
<br>
@ -141,6 +141,7 @@ export default {
activeNames: ["0"],
threadGroups: [],
serializeThreadgroups: false,
resourcePoolResourceLength: 1
}
},
mounted() {
@ -368,6 +369,11 @@ export default {
if (handler.rampUpTime < handler.step) {
handler.step = handler.rampUpTime;
}
// 线
let resourcePool = this.resourcePools.filter(v => v.id === this.resourcePool)[0];
if (resourcePool) {
this.resourcePoolResourceLength = resourcePool.resources.length;
}
handler.options = {
xAxis: {
type: 'category',

View File

@ -204,6 +204,7 @@ export default {
});
}
},
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
@ -282,7 +283,18 @@ export default {
},
openEnvironmentConfig(project) {
this.$refs.environmentConfig.open(project.id);
},
handleEvent(event) {
if (event.keyCode === 13) {
this.submit('form')
}
},
},
created() {
document.addEventListener('keydown', this.handleEvent)
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleEvent);
}
}
</script>

View File

@ -2,7 +2,7 @@
<div>
<el-row>
<el-col :span="10">
<el-button icon="el-icon-circle-plus-outline" plain size="mini" @click="handleAddTaskModel('scheduleTask')">
<el-button :disabled="!isTesterPermission" icon="el-icon-circle-plus-outline" plain size="mini" @click="handleAddTaskModel('scheduleTask')">
{{ $t('organization.message.create_new_notification') }}
</el-button>
</el-col>
@ -69,12 +69,14 @@
type="primary"
size="mini"
v-show="scope.row.isSet"
:disabled="!isTesterPermission"
@click="handleAddTask(scope.$index,scope.row)"
>{{ $t('commons.add') }}
</el-button>
<el-button
size="mini"
v-show="scope.row.isSet"
:disabled="!isTesterPermission"
@click.native.prevent="removeRowTask(scope.$index,form.scheduleTask)"
>{{ $t('commons.cancel') }}
</el-button>
@ -82,6 +84,7 @@
type="primary"
size="mini"
v-show="!scope.row.isSet"
:disabled="!isTesterPermission"
@click="handleEditTask(scope.$index,scope.row)"
>{{ $t('commons.edit') }}</el-button>
<el-button
@ -89,6 +92,7 @@
icon="el-icon-delete"
size="mini"
v-show="!scope.row.isSet"
:disabled="!isTesterPermission"
@click.native.prevent="deleteRowTask(scope.$index,scope.row)"
></el-button>
</template>
@ -106,6 +110,10 @@ export default {
props: {
testId:String,
scheduleReceiverOptions:Array,
isTesterPermission: {
type: Boolean,
default: true
}
},
data() {
return {
@ -120,7 +128,6 @@ export default {
identification: "",
isReadOnly: false,
testId:this.testId,
}],
},
scheduleEventOptions: [
@ -193,7 +200,6 @@ export default {
},
addTask(data) {
let list = [];
data.isSet = false;
list.push(data);
let param = {};
param.messageDetail = list;

View File

@ -86,6 +86,7 @@
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {getCurrentUser, listenGoBack, removeGoBackListener} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import {PHONE_REGEX} from "@/common/js/regex";
export default {
name: "MsPersonSetting",
@ -115,7 +116,7 @@
phone: [
{
required: false,
pattern: '^1(3|4|5|7|8)\\d{9}$',
pattern: PHONE_REGEX,
message: this.$t('member.mobile_number_format_is_incorrect'),
trigger: 'blur'
}

View File

@ -45,7 +45,7 @@
<el-checkbox v-model="formInline.TLS" :label="$t('system_parameter_setting.TLS')"></el-checkbox>
</div>
<div style="border: 0px;margin-bottom: 20px">
<el-checkbox v-model="formInline.SMTP" :label="$t('system_parameter_setting.SMTP')"></el-checkbox>
<el-checkbox v-model="formInline.ANON" :label="$t('system_parameter_setting.SMTP')"></el-checkbox>
</div>
<template v-slot:footer>
</template>
@ -120,7 +120,7 @@ export default {
this.$set(this.formInline, "password", response.data[3].paramValue);
this.$set(this.formInline, "SSL", JSON.parse(response.data[4].paramValue));
this.$set(this.formInline, "TLS", JSON.parse(response.data[5].paramValue));
this.$set(this.formInline, "SMTP", JSON.parse(response.data[6].paramValue));
this.$set(this.formInline, "ANON", JSON.parse(response.data[6].paramValue));
this.$nextTick(() => {
this.$refs.formInline.clearValidate();
})
@ -143,7 +143,7 @@ export default {
"smtp.password": this.formInline.password,
"smtp.ssl": this.formInline.SSL,
"smtp.tls": this.formInline.TLS,
"smtp.smtp": this.formInline.SMTP,
"smtp.anon": this.formInline.ANON,
};
this.$refs[formInline].validate((valid) => {
if (valid) {
@ -173,7 +173,7 @@ export default {
{paramKey: "smtp.password", paramValue: this.formInline.password, type: "password", sort: 4},
{paramKey: "smtp.ssl", paramValue: this.formInline.SSL, type: "text", sort: 5},
{paramKey: "smtp.tls", paramValue: this.formInline.TLS, type: "text", sort: 6},
{paramKey: "smtp.smtp", paramValue: this.formInline.SMTP, type: "text", sort: 7}
{paramKey: "smtp.anon", paramValue: this.formInline.ANON, type: "text", sort: 7}
]
this.$refs[formInline].validate(valid => {

View File

@ -338,6 +338,7 @@ import {hasRole, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsRolesTag from "../../common/components/MsRolesTag";
import {ROLE_ADMIN} from "@/common/js/constants";
import {getCurrentUser} from "../../../../common/js/utils";
import {PHONE_REGEX} from "@/common/js/regex";
export default {
name: "MsUser",
@ -380,7 +381,7 @@ export default {
rule: {
id: [
{required: true, message: this.$t('user.input_id'), trigger: 'blur'},
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'},
{min: 1, max: 50, message: this.$t('commons.input_limit', [1, 50]), trigger: 'blur'},
{
required: true,
pattern: '^[^\u4e00-\u9fa5]+$',
@ -401,7 +402,7 @@ export default {
{required: true, message: this.$t('user.input_phone'), trigger: 'blur'},
{
required: true,
pattern: '^1(3|4|5|7|8)\\d{9}$',
pattern: PHONE_REGEX,
message: this.$t('user.mobile_number_format_is_incorrect'),
trigger: 'blur'
}

View File

@ -71,7 +71,8 @@
name: "TestCase",
components: {
MsMainContainer,
MsAsideContainer, MsContainer, TestCaseMove, TestCaseList, NodeTree, TestCaseEdit, SelectMenu, BatchMove},
MsAsideContainer, MsContainer, TestCaseMove, TestCaseList, NodeTree, TestCaseEdit, SelectMenu, BatchMove
},
comments: {},
data() {
return {

View File

@ -161,6 +161,7 @@ export default {
data() {
return {
result: {},
testCase: {},
dialogFormVisible: false,
readOnly: true,
form: {
@ -190,9 +191,15 @@ export default {
]
};
},
mounted() {
this.$get('test/case/get/' + this.testCaseId, response => {
this.testCase = response.data;
});
},
methods: {},
props: {
testCase: {
type: Object
testCaseId: {
type: String
}
},
}

View File

@ -256,9 +256,9 @@
<script>
import {TokenKey, WORKSPACE_ID} from '../../../../../common/js/constants';
import {TokenKey, WORKSPACE_ID} from '@/common/js/constants';
import MsDialogFooter from '../../../common/components/MsDialogFooter'
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import {Message} from "element-ui";
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";

View File

@ -8,7 +8,7 @@
:tip="$t('commons.search_by_name_or_id')"
:create-tip="$t('test_track.case.create')" @create="testCaseCreate">
<template v-slot:title>
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="showAll"/>
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh"/>
</template>
<template v-slot:button>
<ms-table-button :is-tester-permission="true" icon="el-icon-download"
@ -35,6 +35,7 @@
@filter-change="filter"
@select-all="handleSelectAll"
@select="handleSelectionChange"
@cell-mouse-enter="showPopover"
row-key="id"
class="test-content adjust-table">
<el-table-column
@ -65,7 +66,7 @@
width="60%"
trigger="hover"
>
<test-case-detail :test-case="scope.row"/>
<test-case-detail v-if="currentCaseId === scope.row.id" :test-case-id="currentCaseId"/>
<span slot="reference">{{ scope.row.name }}</span>
</el-popover>
</template>
@ -166,11 +167,11 @@ import MethodTableItem from "../../common/tableItems/planview/MethodTableItem";
import MsTableOperator from "../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableButton from "../../../common/components/MsTableButton";
import {_filter, _sort} from "../../../../../common/js/utils";
import {_filter, _sort} from "@/common/js/utils";
import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components";
import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit";
import {WORKSPACE_ID} from "../../../../../common/js/constants";
import {WORKSPACE_ID} from "@/common/js/constants";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import StatusTableItem from "@/business/components/track/common/tableItems/planview/StatusTableItem";
import TestCaseDetail from "./TestCaseDetail";
@ -262,7 +263,8 @@ export default {
{name: this.$t('test_track.case.auto'), id: 'auto'}
],
maintainer: [],
}
},
currentCaseId: null
}
},
props: {
@ -290,6 +292,8 @@ export default {
},
methods: {
initTableData() {
this.condition.planId = "";
this.condition.nodeIds = [];
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
@ -322,10 +326,16 @@ export default {
this.$emit('testCaseEdit');
},
handleEdit(testCase) {
this.$get('test/case/get/' + testCase.id, response => {
let testCase = response.data;
this.$emit('testCaseEdit', testCase);
});
},
handleCopy(testCase) {
this.$get('test/case/get/' + testCase.id, response => {
let testCase = response.data;
this.$emit('testCaseCopy', testCase);
});
},
handleDelete(testCase) {
this.$alert(this.$t('test_track.case.delete_confirm') + '\'' + testCase.name + '\'' + "", '', {
@ -369,10 +379,6 @@ export default {
this.selectRows.clear();
this.$emit('refresh');
},
showAll() {
this.condition = {components: TEST_CASE_CONFIGS};
this.getData();
},
showDetail(row, event, column) {
this.$emit('testCaseDetail', row);
},
@ -488,6 +494,11 @@ export default {
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.valueArr.maintainer = response.data;
});
},
showPopover(row, column, cell) {
if (column.property === 'name') {
this.currentCaseId = row.id;
}
}
}
}

View File

@ -193,8 +193,7 @@ export default {
let param = {};
Object.assign(param, this.form);
param.name = param.name.trim();
if (param.name === '') {
this.$warning(this.$t('test_track.plan.input_plan_name'));
if (!this.validate(param)) {
return;
}
param.workspaceId = localStorage.getItem(WORKSPACE_ID);
@ -227,6 +226,17 @@ export default {
}
});
},
validate(param) {
if (param.name === '') {
this.$warning(this.$t('test_track.plan.input_plan_name'));
return false;
}
if (param.plannedStartTime > param.plannedEndTime) {
this.$warning(this.$t('commons.date.data_time_error'));
return false;
}
return true;
},
editTestPlan(param) {
this.$post('/test/plan/' + this.operationType, param, () => {
this.$success(this.$t('commons.save_success'));

View File

@ -137,7 +137,9 @@
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
<test-case-report-view @refresh="initTableData" ref="testCaseReportView"/>
<ms-delete-confirm :title="$t('test_track.plan.plan_delete')" @delete="_handleDelete" ref="deleteConfirm"/>
<ms-delete-confirm :title="$t('test_track.plan.plan_delete')" @delete="_handleDelete" ref="deleteConfirm" :with-tip="enableDeleteTip">
{{$t('test_track.plan.plan_delete_tip')}}
</ms-delete-confirm>
</el-card>
</template>
@ -171,6 +173,7 @@ export default {
data() {
return {
result: {},
enableDeleteTip: false,
queryPath: "/test/plan/list",
deletePath: "/test/plan/delete",
condition: {
@ -249,6 +252,7 @@ export default {
});
},
handleDelete(testPlan) {
this.enableDeleteTip = testPlan.status === 'Underway' ? true : false;
this.$refs.deleteConfirm.open(testPlan);
},
_handleDelete(testPlan) {

View File

@ -215,6 +215,16 @@
item.checked = false;
});
flag ? this.testCases = tableData : this.testCases = this.testCases.concat(tableData);
//
let hash = {}
this.testCases = this.testCases.reduce((item, next) => {
if (!hash[next.id]) {
hash[next.id] = true
item.push(next)
}
return item
}, [])
this.lineStatus = tableData.length === 50 && this.testCases.length < this.total;
});
}

View File

@ -210,14 +210,15 @@
let reset = this.exportReportReset;
this.$nextTick(function () {
setTimeout(() => {
html2canvas(document.getElementById('testCaseReportExport'), {
// scale: 2
scale: 2
}).then(function(canvas) {
exportPdf(name, [canvas]);
reset();
});
}, 1000);
});
},
exportReportReset() {
this.reportExportVisible = false;
@ -225,7 +226,7 @@
},
}
}
</script>
</script>cd
<style scoped>

View File

@ -23,9 +23,10 @@
resize="none"
:autosize="{ minRows: 4, maxRows: 4}"
@keyup.ctrl.enter.native="sendComment"
:disabled="isReadOnly"
>
</el-input>
<el-button type="primary" size="mini" class="send-btn" @click="sendComment">
<el-button type="primary" size="mini" class="send-btn" @click="sendComment" :disabled="isReadOnly">
{{ $t('test_track.comment.send') }}
</el-button>
</div>
@ -34,6 +35,7 @@
<script>
import ReviewCommentItem from "./ReviewCommentItem";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
export default {
name: "ReviewComment",
@ -47,8 +49,12 @@ export default {
return {
result: {},
textarea: '',
isReadOnly: false
}
},
created() {
this.isReadOnly = !checkoutTestManagerOrTestUser();
},
methods: {
sendComment() {
let comment = {};

View File

@ -219,6 +219,15 @@
item.checked = false;
});
flag ? this.testReviews = tableData : this.testReviews = this.testReviews.concat(tableData);
//
let hash = {}
this.testReviews = this.testReviews.reduce((item, next) => {
if (!hash[next.id]) {
hash[next.id] = true
item.push(next)
}
return item
}, [])
this.lineStatus = tableData.length === 50 && this.testReviews.length < this.total;
});

View File

@ -108,3 +108,9 @@ body {
margin: 5px 0;
border-radius: 5px;
}
/* 修复带长度限制的文本框,内容太长造成的无法查看内容的问题 */
.el-input__inner[maxlength] {
padding-right: 60px;
}

View File

@ -43,8 +43,10 @@ export default {
success(response.data);
} else {
window.console.warn(response.data);
if (response.data.message) {
Message.warning(response.data.message);
}
}
result.loading = false;
}

View File

@ -0,0 +1 @@
export const PHONE_REGEX = '^1(3|4|5|7|8|9)\\d{9}$';

View File

@ -275,3 +275,17 @@ export function exportPdf(name, canvasList) {
}
export function windowPrint(id, zoom) {
//根据div标签ID拿到div中的局部内容
let bdhtml=window.document.body.innerHTML;
let el = document.getElementById(id);
var jubuData = el.innerHTML;
document.getElementsByTagName('body')[0].style.zoom=zoom;
//把获取的 局部div内容赋给body标签, 相当于重置了 body里的内容
window.document.body.innerHTML= jubuData;
//调用打印功能
window.print();
window.document.body.innerHTML=bdhtml;//重新给页面内容赋值;
return false;
}

View File

@ -893,6 +893,7 @@ export default {
actual_start_time: "Actual Start Time",
actual_end_time: "Actual End Time",
plan_delete_confirm: "All use cases under this plan will be deleted,confirm delete test plan: ",
plan_delete_tip: "The test plan is under way, please confirm and delete it!",
plan_delete: "Delete test plan",
},
review: {

View File

@ -470,7 +470,7 @@ export default {
title: "jar包管理",
jar_file: "jar包",
delete_tip: "删除需重启服务后生效",
file_exist: "该项目下已存在jar包",
file_exist: "该项目下已存在jar包",
upload_limit_size: "上传文件大小不能超过 30MB!",
},
definition: {
@ -896,6 +896,7 @@ export default {
actual_start_time: "实际开始",
actual_end_time: "实际结束",
plan_delete_confirm: "将删除该测试计划下所有用例,确认删除测试计划: ",
plan_delete_tip: "该测试计划正在进行中,请确认再删除!",
plan_delete: "删除计划",
},
review: {
@ -1063,7 +1064,7 @@ export default {
SMTP_password: 'SMTP密码',
SSL: '开启SSL(如果SMTP端口是465通常需要启用SSL)',
TLS: '开启TLS(如果SMTP端口是587通常需要启用TLS)',
SMTP: '是否匿名 SMTP',
SMTP: '是否免密 SMTP',
host: '主机号不能为空',
port: '端口号不能为空',
account: '账户不能为空',

View File

@ -470,7 +470,7 @@ export default {
title: "jar包管理",
jar_file: "jar包",
delete_tip: "刪除需重啟服務後生效",
file_exist: "該項目下已存在jar包",
file_exist: "該項目下已存在jar包",
upload_limit_size: "上傳文件大小不能超過 30MB!",
},
definition: {
@ -894,6 +894,7 @@ export default {
actual_start_time: "實際開始",
actual_end_time: "實際結束",
plan_delete_confirm: "將刪除該測試計劃下所有用例,確認刪除測試計劃: ",
plan_delete_tip: "該測試計劃正在進行中,請確認再刪除!",
plan_delete: "刪除計劃",
},
review: {
@ -1061,7 +1062,7 @@ export default {
SMTP_password: 'SMTP密碼',
SSL: '開啟SSL(如果SMTP端口是465通常需要啟用SSL)',
TLS: '開啟TLS(如果SMTP端口是587通常需要啟用TLS)',
SMTP: '是否匿名 SMTP',
SMTP: '是否免密 SMTP',
host: '主機號不能為空',
port: '端口號不能為空',
account: '賬戶不能為空',

View File

@ -4,8 +4,7 @@
<el-col :span="12">
<el-form :model="form" :rules="rules" ref="form">
<div class="logo">
<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="">
<img :src="'/display/file/loginLogo'" style="width: 224px;height: 45px;" alt="">
</div>
<div class="title">
<span id="s1">{{ loginTitle }}</span>
@ -41,8 +40,7 @@
</el-form>
</el-col>
<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%">
<img :src="'/display/file/loginImage'" style="height: 560px; width: 100%">
</el-col>
</el-row>
</div>
@ -86,8 +84,6 @@ export default {
msg: '',
ready: false,
openLdap: false,
loginLogoId: '_blank',
loginImageId: '_blank',
loginTitle: this.$t("commons.login") + " MeterSphere"
}
},
@ -264,6 +260,10 @@ export default {
background: url(../assets/info.png);
height: 560px;
}
.login-logo {
background: url(../assets/logo-dark-MeterSphere.svg);
}
</style>
<style>

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.metersphere</groupId>
<artifactId>metersphere-server</artifactId>
<version>1.4</version>
<version>1.5</version>
<packaging>pom</packaging>
<parent>