Merge branch 'dev' of https://github.com/fit2cloudrd/metersphere-server into dev
This commit is contained in:
commit
e5e7737f29
|
@ -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>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
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.user.SessionUtils;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/report")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
|
||||
public class APIReportController {
|
||||
|
||||
@Resource
|
||||
private APIReportService apiReportService;
|
||||
|
||||
@GetMapping("recent/{count}")
|
||||
public List<APIReportResult> recentTest(@PathVariable int count) {
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
QueryAPIReportRequest request = new QueryAPIReportRequest();
|
||||
request.setWorkspaceId(currentWorkspaceId);
|
||||
PageHelper.startPage(1, count, true);
|
||||
return apiReportService.recentTest(request);
|
||||
}
|
||||
|
||||
@PostMapping("/list/{goPage}/{pageSize}")
|
||||
public Pager<List<APIReportResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPIReportRequest request) {
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
return PageUtils.setPageInfo(page, apiReportService.list(request));
|
||||
}
|
||||
|
||||
@GetMapping("/get/{testId}")
|
||||
public ApiTestReport get(@PathVariable String testId) {
|
||||
return apiReportService.get(testId);
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestBody DeleteAPIReportRequest request) {
|
||||
apiReportService.delete(request);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.ApiTestService;
|
||||
import io.metersphere.api.service.APITestService;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
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;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class DeleteAPIReportRequest {
|
||||
|
||||
private String id;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
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;
|
||||
|
||||
}
|
|
@ -1,45 +1,149 @@
|
|||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
});
|
||||
System.err.println(count.addAndGet(sampleResults.size()));
|
||||
}
|
||||
|
||||
private void println(String name, Object value) {
|
||||
System.out.println(name + ": " + value);
|
||||
@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);
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
queue.clear();
|
||||
super.teardownTest(context);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
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++;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.metersphere.api.jmeter;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ResponseAssertionResult {
|
||||
|
||||
private String name;
|
||||
|
||||
private String message;
|
||||
|
||||
private boolean pass;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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<>();
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -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 ApiTestService {
|
||||
public class APITestService {
|
||||
|
||||
@Resource
|
||||
private ApiTestMapper apiTestMapper;
|
||||
|
@ -50,28 +49,21 @@ public class ApiTestService {
|
|||
return extApiTestMapper.list(request);
|
||||
}
|
||||
|
||||
public String save(SaveAPITestRequest request, List<MultipartFile> files) {
|
||||
public void create(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);
|
|
@ -6,7 +6,7 @@ import java.io.Serializable;
|
|||
|
||||
@Data
|
||||
public class LoadTestReportLog implements Serializable {
|
||||
private Long id;
|
||||
private String id;
|
||||
|
||||
private String reportId;
|
||||
|
||||
|
|
|
@ -114,52 +114,62 @@ public class LoadTestReportLogExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdEqualTo(Long value) {
|
||||
public Criteria andIdEqualTo(String value) {
|
||||
addCriterion("id =", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotEqualTo(Long value) {
|
||||
public Criteria andIdNotEqualTo(String value) {
|
||||
addCriterion("id <>", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdGreaterThan(Long value) {
|
||||
public Criteria andIdGreaterThan(String value) {
|
||||
addCriterion("id >", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdGreaterThanOrEqualTo(Long value) {
|
||||
public Criteria andIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("id >=", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLessThan(Long value) {
|
||||
public Criteria andIdLessThan(String value) {
|
||||
addCriterion("id <", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLessThanOrEqualTo(Long value) {
|
||||
public Criteria andIdLessThanOrEqualTo(String 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<Long> values) {
|
||||
public Criteria andIdNotIn(List<String> values) {
|
||||
addCriterion("id not in", values, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdBetween(Long value1, Long value2) {
|
||||
public Criteria andIdBetween(String value1, String value2) {
|
||||
addCriterion("id between", value1, value2, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotBetween(Long value1, Long value2) {
|
||||
public Criteria andIdNotBetween(String value1, String value2) {
|
||||
addCriterion("id not between", value1, value2, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import java.io.Serializable;
|
|||
|
||||
@Data
|
||||
public class LoadTestReportResult implements Serializable {
|
||||
private Long id;
|
||||
private String id;
|
||||
|
||||
private String reportId;
|
||||
|
||||
|
|
|
@ -114,52 +114,62 @@ public class LoadTestReportResultExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdEqualTo(Long value) {
|
||||
public Criteria andIdEqualTo(String value) {
|
||||
addCriterion("id =", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotEqualTo(Long value) {
|
||||
public Criteria andIdNotEqualTo(String value) {
|
||||
addCriterion("id <>", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdGreaterThan(Long value) {
|
||||
public Criteria andIdGreaterThan(String value) {
|
||||
addCriterion("id >", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdGreaterThanOrEqualTo(Long value) {
|
||||
public Criteria andIdGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("id >=", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLessThan(Long value) {
|
||||
public Criteria andIdLessThan(String value) {
|
||||
addCriterion("id <", value, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdLessThanOrEqualTo(Long value) {
|
||||
public Criteria andIdLessThanOrEqualTo(String 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<Long> values) {
|
||||
public Criteria andIdNotIn(List<String> values) {
|
||||
addCriterion("id not in", values, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdBetween(Long value1, Long value2) {
|
||||
public Criteria andIdBetween(String value1, String value2) {
|
||||
addCriterion("id between", value1, value2, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andIdNotBetween(Long value1, Long value2) {
|
||||
public Criteria andIdNotBetween(String value1, String value2) {
|
||||
addCriterion("id not between", value1, value2, "id");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ public interface LoadTestReportLogMapper {
|
|||
|
||||
int deleteByExample(LoadTestReportLogExample example);
|
||||
|
||||
int deleteByPrimaryKey(Long id);
|
||||
int deleteByPrimaryKey(String id);
|
||||
|
||||
int insert(LoadTestReportLog record);
|
||||
|
||||
|
@ -21,7 +21,7 @@ public interface LoadTestReportLogMapper {
|
|||
|
||||
List<LoadTestReportLog> selectByExample(LoadTestReportLogExample example);
|
||||
|
||||
LoadTestReportLog selectByPrimaryKey(Long id);
|
||||
LoadTestReportLog selectByPrimaryKey(String id);
|
||||
|
||||
int updateByExampleSelective(@Param("record") LoadTestReportLog record, @Param("example") LoadTestReportLogExample example);
|
||||
|
||||
|
|
|
@ -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.Long" resultMap="ResultMapWithBLOBs">
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" 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.Long">
|
||||
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
|
||||
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>
|
|
@ -11,7 +11,7 @@ public interface LoadTestReportResultMapper {
|
|||
|
||||
int deleteByExample(LoadTestReportResultExample example);
|
||||
|
||||
int deleteByPrimaryKey(Long id);
|
||||
int deleteByPrimaryKey(String id);
|
||||
|
||||
int insert(LoadTestReportResult record);
|
||||
|
||||
|
@ -21,7 +21,7 @@ public interface LoadTestReportResultMapper {
|
|||
|
||||
List<LoadTestReportResult> selectByExample(LoadTestReportResultExample example);
|
||||
|
||||
LoadTestReportResult selectByPrimaryKey(Long id);
|
||||
LoadTestReportResult selectByPrimaryKey(String id);
|
||||
|
||||
int updateByExampleSelective(@Param("record") LoadTestReportResult record, @Param("example") LoadTestReportResultExample example);
|
||||
|
||||
|
|
|
@ -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.Long" resultMap="ResultMapWithBLOBs">
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" 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.Long">
|
||||
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
|
||||
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>
|
|
@ -1,6 +1,7 @@
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -1,53 +0,0 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
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;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/report")
|
||||
public class ApiReportController {
|
||||
|
||||
@Resource
|
||||
private ApiReportService 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) {
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
ReportRequest request = new ReportRequest();
|
||||
request.setWorkspaceId(currentWorkspaceId);
|
||||
PageHelper.startPage(1, count);
|
||||
return apiReportService.getRecentReportList(request);
|
||||
}
|
||||
|
||||
@PostMapping("/list/all/{goPage}/{pageSize}")
|
||||
public Pager<List<ApiReportDTO>> getReportList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ReportRequest request) {
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
return PageUtils.setPageInfo(page, apiReportService.getReportList(request));
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{reportId}")
|
||||
public void deleteReport(@PathVariable String reportId) {
|
||||
apiReportService.deleteReport(reportId);
|
||||
}
|
||||
|
||||
@GetMapping("/test/pro/info/{reportId}")
|
||||
public ApiReportDTO getReportTestAndProInfo(@PathVariable String reportId) {
|
||||
return apiReportService.getReportTestAndProInfo(reportId);
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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> getWorkspaceMemberRoles(@PathVariable String workspaceId, @PathVariable String userId) {
|
||||
return userRoleService.getWorkspaceMemberRoles(workspaceId, userId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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_result` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`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;
|
||||
|
||||
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,
|
||||
`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;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `load_test_report_result` (
|
||||
`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;
|
||||
|
||||
|
||||
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`)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<div class="main-content">
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
|
||||
:show-create="false"/>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="description"
|
||||
:label="$t('commons.description')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="150"
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="handleView(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>
|
||||
<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 {
|
||||
components: {MsTableHeader, MsTablePagination},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
condition: {name: ""},
|
||||
projectId: null,
|
||||
tableData: [],
|
||||
multipleSelection: [],
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(self => {
|
||||
self.testId = to.params.testId;
|
||||
self.search();
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
let param = {
|
||||
name: this.condition.name,
|
||||
};
|
||||
|
||||
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;
|
||||
});
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
handleView(report) {
|
||||
this.$router.push({
|
||||
path: '/api/report/view/' + report.id,
|
||||
})
|
||||
},
|
||||
handleDelete(report) {
|
||||
this.$alert(this.$t('load_test.delete_confirm') + report.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this.result = this.$post("/api/report/delete", {id: report.id}, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-content {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,169 +0,0 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<el-table :data="tableData" class="test-content">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="description"
|
||||
: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')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="150"
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="handleEdit(scope.row)" type="primary" icon="el-icon-edit" 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>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ApiTestReport",
|
||||
created: function () {
|
||||
this.initTableData();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
queryPath: "/api/report/list/all",
|
||||
deletePath: "/api/report/delete/",
|
||||
condition: "",
|
||||
projectId: null,
|
||||
tableData: [],
|
||||
multipleSelection: [],
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
loading: false,
|
||||
testId: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initTableData() {
|
||||
let param = {
|
||||
name: this.condition,
|
||||
};
|
||||
this.result = this.$post(this.buildPagePath(this.queryPath), 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;
|
||||
},
|
||||
handleEdit(report) {
|
||||
this.$router.push({
|
||||
path: '/api/report/view/' + report.id
|
||||
})
|
||||
},
|
||||
handleDelete(report) {
|
||||
this.$alert(this.$t('load_test.delete_confirm') + report.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this._handleDelete(report);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
_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>
|
||||
.test-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-page {
|
||||
padding-top: 20px;
|
||||
margin-right: -9px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -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',
|
||||
|
|
|
@ -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="test-content">
|
||||
<el-table :data="tableData" class="table-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>
|
||||
.test-content {
|
||||
.table-content {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
|
@ -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 = generateId();
|
||||
this.id = uuid();
|
||||
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 = generateId();
|
||||
this.id = uuid();
|
||||
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.putRequestHeader(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.addRequestArguments(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.putResponseAssertion(this.getAssertion(regex));
|
||||
httpSamplerProxy.put(this.getAssertion(regex));
|
||||
})
|
||||
}
|
||||
|
||||
if (assertions.duration.isValid()) {
|
||||
httpSamplerProxy.putDurationAssertion(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) {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<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>
|
|
@ -6,6 +6,7 @@
|
|||
:effect="effect">
|
||||
<el-button @click="exec()"
|
||||
@click.stop="clickStop"
|
||||
@keydown.enter.native.prevent
|
||||
circle
|
||||
:type="type"
|
||||
:icon="icon"
|
||||
|
|
|
@ -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: "fucReport",
|
||||
component: ApiTestReport
|
||||
path: "report/list/:testId",
|
||||
name: "ApiReportList",
|
||||
component: ApiReportList
|
||||
},
|
||||
{
|
||||
path: "report/view/:reportId",
|
||||
name: "fucReportView",
|
||||
name: "ApiReportView",
|
||||
component: ApiReportView
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
|
|
|
@ -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: /^[\u4e00-\u9fa5_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;
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
|
|
|
@ -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';
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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: {},
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<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>
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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': '首页'
|
||||
|
|
Loading…
Reference in New Issue