This commit is contained in:
chenjianxing 2020-12-23 14:42:12 +08:00
commit ae42f30ca5
33 changed files with 217 additions and 179 deletions

8
Jenkinsfile vendored
View File

@ -7,7 +7,7 @@ pipeline {
options { quietPeriod(600) } options { quietPeriod(600) }
parameters { parameters {
string(name: 'IMAGE_NAME', defaultValue: 'metersphere', description: '构建后的 Docker 镜像名称') string(name: 'IMAGE_NAME', defaultValue: 'metersphere', description: '构建后的 Docker 镜像名称')
string(name: 'IMAGE_FREFIX', defaultValue: 'registry.cn-qingdao.aliyuncs.com/metersphere', description: '构建后的 Docker 镜像带仓库名的前缀') string(name: 'IMAGE_PREFIX', defaultValue: 'registry.cn-qingdao.aliyuncs.com/metersphere', description: '构建后的 Docker 镜像带仓库名的前缀')
} }
stages { stages {
stage('Build/Test') { stage('Build/Test') {
@ -19,9 +19,9 @@ pipeline {
} }
stage('Docker build & push') { stage('Docker build & push') {
steps { steps {
sh "docker build --build-arg MS_VERSION=\${TAG_NAME:-\$BRANCH_NAME}-b\${BUILD_NUMBER} -t ${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME} ." sh "docker build --build-arg MS_VERSION=\${TAG_NAME:-\$BRANCH_NAME}-\${GIT_COMMIT:0:8} -t ${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME} ."
sh "docker tag ${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME} ${IMAGE_FREFIX}/${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME}" sh "docker tag ${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME} ${IMAGE_PREFIX}/${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME}"
sh "docker push ${IMAGE_FREFIX}/${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME}" sh "docker push ${IMAGE_PREFIX}/${IMAGE_NAME}:\${TAG_NAME:-\$BRANCH_NAME}"
} }
} }
} }

View File

@ -11,7 +11,7 @@ import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.DashboardTestDTO; import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -27,7 +27,7 @@ public class APIReportController {
@Resource @Resource
private APIReportService apiReportService; private APIReportService apiReportService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("recent/{count}") @GetMapping("recent/{count}")
public List<APIReportResult> recentTest(@PathVariable int count) { public List<APIReportResult> recentTest(@PathVariable int count) {
@ -41,7 +41,7 @@ public class APIReportController {
@GetMapping("/list/{testId}/{goPage}/{pageSize}") @GetMapping("/list/{testId}/{goPage}/{pageSize}")
public Pager<List<APIReportResult>> listByTestId(@PathVariable String testId, @PathVariable int goPage, @PathVariable int pageSize) { public Pager<List<APIReportResult>> listByTestId(@PathVariable String testId, @PathVariable int goPage, @PathVariable int pageSize) {
checkOwnerService.checkApiTestOwner(testId); checkPermissionService.checkApiTestOwner(testId);
Page<Object> page = PageHelper.startPage(goPage, pageSize, true); Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, apiReportService.listByTestId(testId)); return PageUtils.setPageInfo(page, apiReportService.listByTestId(testId));
@ -60,6 +60,7 @@ public class APIReportController {
} }
@PostMapping("/delete") @PostMapping("/delete")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void delete(@RequestBody DeleteAPIReportRequest request) { public void delete(@RequestBody DeleteAPIReportRequest request) {
apiReportService.delete(request); apiReportService.delete(request);
} }
@ -70,6 +71,7 @@ public class APIReportController {
} }
@PostMapping("/batch/delete") @PostMapping("/batch/delete")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void deleteAPIReportBatch(@RequestBody DeleteAPIReportRequest reportRequest) { public void deleteAPIReportBatch(@RequestBody DeleteAPIReportRequest reportRequest) {
apiReportService.deleteAPIReportBatch(reportRequest); apiReportService.deleteAPIReportBatch(reportRequest);
} }

View File

@ -15,11 +15,13 @@ import io.metersphere.base.domain.ApiTest;
import io.metersphere.base.domain.Schedule; import io.metersphere.base.domain.Schedule;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.CronUtils;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao; import io.metersphere.dto.ScheduleDao;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.service.ScheduleService; import io.metersphere.service.ScheduleService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
@ -27,7 +29,6 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -46,7 +47,7 @@ public class APITestController {
@Resource @Resource
private ApiDefinitionService apiDefinitionService; private ApiDefinitionService apiDefinitionService;
@Resource @Resource
private CheckOwnerService checkownerService; private CheckPermissionService checkownerService;
@Resource @Resource
private ApiTestCaseService apiTestCaseService; private ApiTestCaseService apiTestCaseService;
@Resource @Resource

View File

@ -14,6 +14,7 @@ import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.service.CheckPermissionService;
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
@ -30,6 +31,8 @@ import java.util.List;
public class ApiDefinitionController { public class ApiDefinitionController {
@Resource @Resource
private ApiDefinitionService apiDefinitionService; private ApiDefinitionService apiDefinitionService;
@Resource
private CheckPermissionService checkPermissionService;
@PostMapping("/list/{goPage}/{pageSize}") @PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<ApiDefinitionResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { public Pager<List<ApiDefinitionResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {
@ -39,26 +42,34 @@ public class ApiDefinitionController {
} }
@PostMapping(value = "/create", consumes = {"multipart/form-data"}) @PostMapping(value = "/create", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void create(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) { public void create(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkPermissionService.checkReadOnlyUser();
apiDefinitionService.create(request, bodyFiles); apiDefinitionService.create(request, bodyFiles);
} }
@PostMapping(value = "/update", consumes = {"multipart/form-data"}) @PostMapping(value = "/update", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void update(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) { public void update(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
checkPermissionService.checkReadOnlyUser();
apiDefinitionService.update(request, bodyFiles); apiDefinitionService.update(request, bodyFiles);
} }
@GetMapping("/delete/{id}") @GetMapping("/delete/{id}")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void delete(@PathVariable String id) { public void delete(@PathVariable String id) {
checkPermissionService.checkReadOnlyUser();
apiDefinitionService.delete(id); apiDefinitionService.delete(id);
} }
@PostMapping("/deleteBatch") @PostMapping("/deleteBatch")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void deleteBatch(@RequestBody List<String> ids) { public void deleteBatch(@RequestBody List<String> ids) {
apiDefinitionService.deleteBatch(ids); apiDefinitionService.deleteBatch(ids);
} }
@PostMapping("/removeToGc") @PostMapping("/removeToGc")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void removeToGc(@RequestBody List<String> ids) { public void removeToGc(@RequestBody List<String> ids) {
apiDefinitionService.removeToGc(ids); apiDefinitionService.removeToGc(ids);
} }

View File

@ -5,7 +5,7 @@ import io.metersphere.api.dto.definition.DragModuleRequest;
import io.metersphere.api.service.ApiModuleService; import io.metersphere.api.service.ApiModuleService;
import io.metersphere.base.domain.ApiModule; import io.metersphere.base.domain.ApiModule;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -21,17 +21,17 @@ public class ApiModuleController {
@Resource @Resource
ApiModuleService apiModuleService; ApiModuleService apiModuleService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("/list/{projectId}/{protocol}") @GetMapping("/list/{projectId}/{protocol}")
public List<ApiModuleDTO> getNodeByProjectId(@PathVariable String projectId,@PathVariable String protocol) { public List<ApiModuleDTO> getNodeByProjectId(@PathVariable String projectId,@PathVariable String protocol) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
return apiModuleService.getNodeTreeByProjectId(projectId,protocol); return apiModuleService.getNodeTreeByProjectId(projectId,protocol);
} }
@GetMapping("/list/plan/{planId}/{protocol}") @GetMapping("/list/plan/{planId}/{protocol}")
public List<ApiModuleDTO> getNodeByPlanId(@PathVariable String planId, @PathVariable String protocol) { public List<ApiModuleDTO> getNodeByPlanId(@PathVariable String planId, @PathVariable String protocol) {
checkOwnerService.checkTestPlanOwner(planId); checkPermissionService.checkTestPlanOwner(planId);
return apiModuleService.getNodeByPlanId(planId, protocol); return apiModuleService.getNodeByPlanId(planId, protocol);
} }

View File

@ -5,14 +5,13 @@ import io.metersphere.api.dto.automation.DragApiScenarioModuleRequest;
import io.metersphere.api.service.ApiScenarioModuleService; import io.metersphere.api.service.ApiScenarioModuleService;
import io.metersphere.base.domain.ApiScenarioModule; import io.metersphere.base.domain.ApiScenarioModule;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
@RequestMapping("/api/automation/module") @RequestMapping("/api/automation/module")
@RestController @RestController
@ -22,11 +21,11 @@ public class ApiScenarioModuleController {
@Resource @Resource
ApiScenarioModuleService apiScenarioModuleService; ApiScenarioModuleService apiScenarioModuleService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("/list/{projectId}") @GetMapping("/list/{projectId}")
public List<ApiScenarioModuleDTO> getNodeByProjectId(@PathVariable String projectId) { public List<ApiScenarioModuleDTO> getNodeByProjectId(@PathVariable String projectId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
return apiScenarioModuleService.getNodeTreeByProjectId(projectId); return apiScenarioModuleService.getNodeTreeByProjectId(projectId);
} }
@ -44,7 +43,7 @@ public class ApiScenarioModuleController {
@GetMapping("/list/plan/{planId}") @GetMapping("/list/plan/{planId}")
public List<ApiScenarioModuleDTO> getNodeByPlanId(@PathVariable String planId) { public List<ApiScenarioModuleDTO> getNodeByPlanId(@PathVariable String planId) {
checkOwnerService.checkTestPlanOwner(planId); checkPermissionService.checkTestPlanOwner(planId);
return apiScenarioModuleService.getNodeByPlanId(planId); return apiScenarioModuleService.getNodeByPlanId(planId);
} }

View File

@ -3,7 +3,7 @@ package io.metersphere.api.controller;
import io.metersphere.api.service.ApiTestEnvironmentService; import io.metersphere.api.service.ApiTestEnvironmentService;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs; import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -19,11 +19,11 @@ public class ApiTestEnvironmentController {
@Resource @Resource
ApiTestEnvironmentService apiTestEnvironmentService; ApiTestEnvironmentService apiTestEnvironmentService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("/list/{projectId}") @GetMapping("/list/{projectId}")
public List<ApiTestEnvironmentWithBLOBs> list(@PathVariable String projectId) { public List<ApiTestEnvironmentWithBLOBs> list(@PathVariable String projectId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
return apiTestEnvironmentService.list(projectId); return apiTestEnvironmentService.list(projectId);
} }

View File

@ -119,9 +119,15 @@ public class MsHTTPSamplerProxy extends MsTestElement {
sampler.setPort(config.getConfig().getHttpConfig().getPort()); sampler.setPort(config.getConfig().getHttpConfig().getPort());
sampler.setProtocol(config.getConfig().getHttpConfig().getProtocol()); sampler.setProtocol(config.getConfig().getHttpConfig().getProtocol());
url = config.getConfig().getHttpConfig().getProtocol() + "://" + config.getConfig().getHttpConfig().getSocket(); url = config.getConfig().getHttpConfig().getProtocol() + "://" + config.getConfig().getHttpConfig().getSocket();
// 补充如果是完整URL 则用自身URL
boolean isUrl = false;
if (StringUtils.isNotEmpty(this.getUrl()) && isURL(this.getUrl())) {
url = this.getUrl();
isUrl = true;
}
URL urlObject = new URL(url); URL urlObject = new URL(url);
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath(); String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
if (StringUtils.isNotBlank(this.getPath())) { if (StringUtils.isNotBlank(this.getPath()) && !isUrl) {
envPath += this.getPath(); envPath += this.getPath();
} }
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) { if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
@ -243,6 +249,15 @@ public class MsHTTPSamplerProxy extends MsTestElement {
tree.add(headerManager); tree.add(headerManager);
} }
public boolean isURL(String str) {
//转换为小写
try {
new URL(str);
return true;
} catch (Exception e) {
return false;
}
}
private boolean isRest() { private boolean isRest() {
return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0; return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0;

View File

@ -98,11 +98,14 @@ public class Body {
return StringUtils.equals(type, XML); return StringUtils.equals(type, XML);
} }
public boolean isWwwFROM() { public void initKvs() {
return StringUtils.equals(type, WWW_FROM); this.kvs = new ArrayList<>();
this.kvs.add(new KeyValue());
} }
public boolean isFromData() { public void initBinary() {
return StringUtils.equals(type, FORM_DATA); this.binary = new ArrayList<>();
this.binary.add(new KeyValue());
} }
} }

View File

@ -15,7 +15,7 @@ public class KeyValue {
private List<BodyFile> files; private List<BodyFile> files;
private String description; private String description;
private String contentType; private String contentType;
private boolean enable; private boolean enable = true;
private boolean encode = true; private boolean encode = true;
private boolean required; private boolean required;

View File

@ -1,7 +1,6 @@
package io.metersphere.api.dto.scenario.controller; package io.metersphere.api.dto.scenario.controller;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@Data @Data
public class IfController { public class IfController {
@ -11,46 +10,4 @@ public class IfController {
private String variable; private String variable;
private String operator; private String operator;
private String value; private String value;
public boolean isValid() {
if (StringUtils.contains(operator, "empty")) {
return StringUtils.isNotBlank(variable);
}
return StringUtils.isNotBlank(variable) && StringUtils.isNotBlank(operator) && StringUtils.isNotBlank(value);
}
public String getLabel() {
if (isValid()) {
String label = variable + " " + operator;
if (StringUtils.isNotBlank(value)) {
label += " " + this.value;
}
return label;
}
return "";
}
public String getCondition() {
String variable = "\"" + this.variable + "\"";
String operator = this.operator;
String value = "\"" + this.value + "\"";
if (StringUtils.contains(operator, "~")) {
value = "\".*" + this.value + ".*\"";
}
if (StringUtils.equals(operator, "is empty")) {
variable = "empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "!empty(" + variable + ")";
operator = "";
value = "";
}
return "${__jexl3(" + variable + operator + value + ")}";
}
} }

View File

@ -20,6 +20,7 @@ import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -72,7 +73,7 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
protected ApiDefinitionResult buildApiDefinition(String id, String name, String path, String method) { protected ApiDefinitionResult buildApiDefinition(String id, String name, String path, String method) {
ApiDefinitionResult apiDefinition = new ApiDefinitionResult(); ApiDefinitionResult apiDefinition = new ApiDefinitionResult();
apiDefinition.setName(name); apiDefinition.setName(name);
apiDefinition.setPath(path); apiDefinition.setPath(formatPath(path));
apiDefinition.setProtocol(RequestType.HTTP); apiDefinition.setProtocol(RequestType.HTTP);
apiDefinition.setMethod(method); apiDefinition.setMethod(method);
apiDefinition.setId(id); apiDefinition.setId(id);
@ -81,17 +82,34 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
return apiDefinition; return apiDefinition;
} }
private String formatPath(String url) {
try {
URL urlObject = new URL(url);
StringBuffer pathBuffer = new StringBuffer(urlObject.getPath());
if (StringUtils.isNotEmpty(urlObject.getQuery())) {
pathBuffer.append("?").append(urlObject.getQuery());
}
return pathBuffer.toString();
} catch (Exception ex) {
return url;
}
}
protected MsHTTPSamplerProxy buildRequest(String name, String path, String method) { protected MsHTTPSamplerProxy buildRequest(String name, String path, String method) {
MsHTTPSamplerProxy request = new MsHTTPSamplerProxy(); MsHTTPSamplerProxy request = new MsHTTPSamplerProxy();
request.setName(name); request.setName(name);
request.setPath(path); // 路径去掉域名/IP 地址保留方法名称及参数
request.setPath(formatPath(path));
request.setMethod(method); request.setMethod(method);
request.setProtocol(RequestType.HTTP); request.setProtocol(RequestType.HTTP);
request.setId(UUID.randomUUID().toString()); request.setId(UUID.randomUUID().toString());
request.setHeaders(new ArrayList<>()); request.setHeaders(new ArrayList<>());
request.setArguments(new ArrayList<>()); request.setArguments(new ArrayList<>());
request.setRest(new ArrayList<>()); request.setRest(new ArrayList<>());
request.setBody(new Body()); Body body = new Body();
body.initKvs();
body.initBinary();
request.setBody(body);
return request; return request;
} }

View File

@ -51,5 +51,11 @@
INNER JOIN `schedule` sch ON apiTest.id = sch.resource_id INNER JOIN `schedule` sch ON apiTest.id = sch.resource_id
INNER JOIN `user` u ON u.id = sch.user_id INNER JOIN `user` u ON u.id = sch.user_id
WHERE sch.`enable` = true AND sch.workspace_id = #{0,jdbcType=VARCHAR} WHERE sch.`enable` = true AND sch.workspace_id = #{0,jdbcType=VARCHAR}
UNION
SELECT apiTest.`name` AS scenario,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime
FROM api_scenario apiTest
INNER JOIN `schedule` sch ON apiTest.id = sch.resource_id
INNER JOIN `user` u ON u.id = sch.user_id
WHERE sch.`enable` = true AND sch.workspace_id = #{0,jdbcType=VARCHAR}
</select> </select>
</mapper> </mapper>

View File

@ -9,7 +9,7 @@ import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.ProjectRequest; import io.metersphere.controller.request.ProjectRequest;
import io.metersphere.dto.ProjectDTO; import io.metersphere.dto.ProjectDTO;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.service.ProjectService; import io.metersphere.service.ProjectService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
@ -24,7 +24,7 @@ public class ProjectController {
@Resource @Resource
private ProjectService projectService; private ProjectService projectService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("/listAll") @GetMapping("/listAll")
public List<ProjectDTO> listAll() { public List<ProjectDTO> listAll() {
@ -74,7 +74,7 @@ public class ProjectController {
@GetMapping("/delete/{projectId}") @GetMapping("/delete/{projectId}")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
public void deleteProject(@PathVariable(value = "projectId") String projectId) { public void deleteProject(@PathVariable(value = "projectId") String projectId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
projectService.deleteProject(projectId); projectService.deleteProject(projectId);
} }

View File

@ -53,6 +53,7 @@ public class PerformanceReportController {
} }
@PostMapping("/delete/{reportId}") @PostMapping("/delete/{reportId}")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void deleteReport(@PathVariable String reportId) { public void deleteReport(@PathVariable String reportId) {
reportService.deleteReport(reportId); reportService.deleteReport(reportId);
} }
@ -130,6 +131,7 @@ public class PerformanceReportController {
} }
@PostMapping("/batch/delete") @PostMapping("/batch/delete")
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
public void deleteReportBatch(@RequestBody DeleteReportRequest reportRequest) { public void deleteReportBatch(@RequestBody DeleteReportRequest reportRequest) {
reportService.deleteReportBatch(reportRequest); reportService.deleteReportBatch(reportRequest);
} }

View File

@ -14,7 +14,7 @@ import io.metersphere.dto.DashboardTestDTO;
import io.metersphere.dto.LoadTestDTO; import io.metersphere.dto.LoadTestDTO;
import io.metersphere.dto.ScheduleDao; import io.metersphere.dto.ScheduleDao;
import io.metersphere.performance.service.PerformanceTestService; import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.service.FileService; import io.metersphere.service.FileService;
import io.metersphere.track.request.testplan.*; import io.metersphere.track.request.testplan.*;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
@ -37,7 +37,7 @@ public class PerformanceTestController {
@Resource @Resource
private FileService fileService; private FileService fileService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("recent/{count}") @GetMapping("recent/{count}")
public List<LoadTestDTO> recentTestPlans(@PathVariable int count) { public List<LoadTestDTO> recentTestPlans(@PathVariable int count) {
@ -59,14 +59,14 @@ public class PerformanceTestController {
@GetMapping("/list/{projectId}") @GetMapping("/list/{projectId}")
public List<LoadTest> list(@PathVariable String projectId) { public List<LoadTest> list(@PathVariable String projectId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
return performanceTestService.getLoadTestByProjectId(projectId); return performanceTestService.getLoadTestByProjectId(projectId);
} }
@GetMapping("/state/get/{testId}") @GetMapping("/state/get/{testId}")
public LoadTest listByTestId(@PathVariable String testId) { public LoadTest listByTestId(@PathVariable String testId) {
checkOwnerService.checkPerformanceTestOwner(testId); checkPermissionService.checkPerformanceTestOwner(testId);
return performanceTestService.getLoadTestBytestId(testId); return performanceTestService.getLoadTestBytestId(testId);
} }
@ -75,6 +75,7 @@ public class PerformanceTestController {
@RequestPart("request") SaveTestPlanRequest request, @RequestPart("request") SaveTestPlanRequest request,
@RequestPart(value = "file") List<MultipartFile> files @RequestPart(value = "file") List<MultipartFile> files
) { ) {
checkPermissionService.checkReadOnlyUser();
return performanceTestService.save(request, files); return performanceTestService.save(request, files);
} }
@ -83,37 +84,39 @@ public class PerformanceTestController {
@RequestPart("request") EditTestPlanRequest request, @RequestPart("request") EditTestPlanRequest request,
@RequestPart(value = "file", required = false) List<MultipartFile> files @RequestPart(value = "file", required = false) List<MultipartFile> files
) { ) {
checkOwnerService.checkPerformanceTestOwner(request.getId()); checkPermissionService.checkReadOnlyUser();
checkPermissionService.checkPerformanceTestOwner(request.getId());
return performanceTestService.edit(request, files); return performanceTestService.edit(request, files);
} }
@GetMapping("/get/{testId}") @GetMapping("/get/{testId}")
public LoadTestDTO get(@PathVariable String testId) { public LoadTestDTO get(@PathVariable String testId) {
checkOwnerService.checkPerformanceTestOwner(testId); checkPermissionService.checkPerformanceTestOwner(testId);
return performanceTestService.get(testId); return performanceTestService.get(testId);
} }
@GetMapping("/get-advanced-config/{testId}") @GetMapping("/get-advanced-config/{testId}")
public String getAdvancedConfiguration(@PathVariable String testId) { public String getAdvancedConfiguration(@PathVariable String testId) {
checkOwnerService.checkPerformanceTestOwner(testId); checkPermissionService.checkPerformanceTestOwner(testId);
return performanceTestService.getAdvancedConfiguration(testId); return performanceTestService.getAdvancedConfiguration(testId);
} }
@GetMapping("/get-load-config/{testId}") @GetMapping("/get-load-config/{testId}")
public String getLoadConfiguration(@PathVariable String testId) { public String getLoadConfiguration(@PathVariable String testId) {
checkOwnerService.checkPerformanceTestOwner(testId); checkPermissionService.checkPerformanceTestOwner(testId);
return performanceTestService.getLoadConfiguration(testId); return performanceTestService.getLoadConfiguration(testId);
} }
@GetMapping("/get-jmx-content/{testId}") @GetMapping("/get-jmx-content/{testId}")
public String getJmxContent(@PathVariable String testId) { public String getJmxContent(@PathVariable String testId) {
checkOwnerService.checkPerformanceTestOwner(testId); checkPermissionService.checkPerformanceTestOwner(testId);
return performanceTestService.getJmxContent(testId); return performanceTestService.getJmxContent(testId);
} }
@PostMapping("/delete") @PostMapping("/delete")
public void delete(@RequestBody DeleteTestPlanRequest request) { public void delete(@RequestBody DeleteTestPlanRequest request) {
checkOwnerService.checkPerformanceTestOwner(request.getId()); checkPermissionService.checkReadOnlyUser();
checkPermissionService.checkPerformanceTestOwner(request.getId());
performanceTestService.delete(request); performanceTestService.delete(request);
} }
@ -129,7 +132,7 @@ public class PerformanceTestController {
@GetMapping("/file/metadata/{testId}") @GetMapping("/file/metadata/{testId}")
public List<FileMetadata> getFileMetadata(@PathVariable String testId) { public List<FileMetadata> getFileMetadata(@PathVariable String testId) {
checkOwnerService.checkPerformanceTestOwner(testId); checkPermissionService.checkPerformanceTestOwner(testId);
return fileService.getFileMetadataByTestId(testId); return fileService.getFileMetadataByTestId(testId);
} }

View File

@ -9,7 +9,6 @@ import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -18,7 +17,7 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Service @Service
public class CheckOwnerService { public class CheckPermissionService {
@Resource @Resource
private ProjectMapper projectMapper; private ProjectMapper projectMapper;
@Resource @Resource
@ -32,6 +31,20 @@ public class CheckOwnerService {
@Resource @Resource
private ExtTestCaseReviewMapper extTestCaseReviewMapper; private ExtTestCaseReviewMapper extTestCaseReviewMapper;
public void checkReadOnlyUser() {
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
Set<String> collect = Objects.requireNonNull(SessionUtils.getUser()).getUserRoles().stream()
.filter(ur ->
StringUtils.equals(ur.getRoleId(), RoleConstants.TEST_VIEWER))
.map(UserRole::getSourceId)
.filter(sourceId -> StringUtils.equals(currentWorkspaceId, sourceId))
.collect(Collectors.toSet());
if (CollectionUtils.isNotEmpty(collect)) {
throw new RuntimeException(Translator.get("check_owner_read_only"));
}
}
public void checkProjectOwner(String projectId) { public void checkProjectOwner(String projectId) {
Set<String> workspaceIds = getUserRelatedWorkspaceIds(); Set<String> workspaceIds = getUserRelatedWorkspaceIds();
Project project = projectMapper.selectByPrimaryKey(projectId); Project project = projectMapper.selectByPrimaryKey(projectId);
@ -42,7 +55,7 @@ public class CheckOwnerService {
return; return;
} }
if (!workspaceIds.contains(project.getWorkspaceId())) { if (!workspaceIds.contains(project.getWorkspaceId())) {
throw new UnauthorizedException(Translator.get("check_owner_project")); throw new RuntimeException(Translator.get("check_owner_project"));
} }
} }
@ -67,7 +80,7 @@ public class CheckOwnerService {
int result = extApiTestMapper.checkApiTestOwner(testId, workspaceIds); int result = extApiTestMapper.checkApiTestOwner(testId, workspaceIds);
if (result == 0) { if (result == 0) {
throw new UnauthorizedException(Translator.get("check_owner_test")); throw new RuntimeException(Translator.get("check_owner_test"));
} }
} }
@ -83,7 +96,7 @@ public class CheckOwnerService {
int result = extLoadTestMapper.checkLoadTestOwner(testId, workspaceIds); int result = extLoadTestMapper.checkLoadTestOwner(testId, workspaceIds);
if (result == 0) { if (result == 0) {
throw new UnauthorizedException(Translator.get("check_owner_test")); throw new RuntimeException(Translator.get("check_owner_test"));
} }
} }
@ -95,7 +108,7 @@ public class CheckOwnerService {
int result = extTestCaseMapper.checkIsHave(caseId, workspaceIds); int result = extTestCaseMapper.checkIsHave(caseId, workspaceIds);
if (result == 0) { if (result == 0) {
throw new UnauthorizedException(Translator.get("check_owner_case")); throw new RuntimeException(Translator.get("check_owner_case"));
} }
} }
@ -106,7 +119,7 @@ public class CheckOwnerService {
} }
int result = extTestPlanMapper.checkIsHave(planId, workspaceIds); int result = extTestPlanMapper.checkIsHave(planId, workspaceIds);
if (result == 0) { if (result == 0) {
throw new UnauthorizedException(Translator.get("check_owner_plan")); throw new RuntimeException(Translator.get("check_owner_plan"));
} }
} }
@ -117,7 +130,7 @@ public class CheckOwnerService {
} }
int result = extTestCaseReviewMapper.checkIsHave(reviewId, workspaceIds); int result = extTestCaseReviewMapper.checkIsHave(reviewId, workspaceIds);
if (result == 0) { if (result == 0) {
throw new UnauthorizedException(Translator.get("check_owner_review")); throw new RuntimeException(Translator.get("check_owner_review"));
} }
} }
} }

View File

@ -11,7 +11,7 @@ import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.excel.domain.ExcelResponse; import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.service.FileService; import io.metersphere.service.FileService;
import io.metersphere.track.dto.TestCaseDTO; import io.metersphere.track.dto.TestCaseDTO;
import io.metersphere.track.request.testcase.EditTestCaseRequest; import io.metersphere.track.request.testcase.EditTestCaseRequest;
@ -39,7 +39,7 @@ public class TestCaseController {
@Resource @Resource
TestCaseService testCaseService; TestCaseService testCaseService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@Resource @Resource
private FileService fileService; private FileService fileService;
@ -51,7 +51,7 @@ public class TestCaseController {
@GetMapping("/list/{projectId}") @GetMapping("/list/{projectId}")
public List<TestCaseDTO> list(@PathVariable String projectId) { public List<TestCaseDTO> list(@PathVariable String projectId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
QueryTestCaseRequest request = new QueryTestCaseRequest(); QueryTestCaseRequest request = new QueryTestCaseRequest();
request.setProjectId(projectId); request.setProjectId(projectId);
return testCaseService.listTestCase(request); return testCaseService.listTestCase(request);
@ -94,13 +94,13 @@ public class TestCaseController {
@GetMapping("/get/{testCaseId}") @GetMapping("/get/{testCaseId}")
public TestCaseWithBLOBs getTestCase(@PathVariable String testCaseId) { public TestCaseWithBLOBs getTestCase(@PathVariable String testCaseId) {
checkOwnerService.checkTestCaseOwner(testCaseId); checkPermissionService.checkTestCaseOwner(testCaseId);
return testCaseService.getTestCase(testCaseId); return testCaseService.getTestCase(testCaseId);
} }
@GetMapping("/project/{testCaseId}") @GetMapping("/project/{testCaseId}")
public Project getProjectByTestCaseId(@PathVariable String testCaseId) { public Project getProjectByTestCaseId(@PathVariable String testCaseId) {
checkOwnerService.checkTestCaseOwner(testCaseId); checkPermissionService.checkTestCaseOwner(testCaseId);
return testCaseService.getProjectByTestCaseId(testCaseId); return testCaseService.getProjectByTestCaseId(testCaseId);
} }
@ -119,14 +119,14 @@ public class TestCaseController {
@PostMapping("/delete/{testCaseId}") @PostMapping("/delete/{testCaseId}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public int deleteTestCase(@PathVariable String testCaseId) { public int deleteTestCase(@PathVariable String testCaseId) {
checkOwnerService.checkTestCaseOwner(testCaseId); checkPermissionService.checkTestCaseOwner(testCaseId);
return testCaseService.deleteTestCase(testCaseId); return testCaseService.deleteTestCase(testCaseId);
} }
@PostMapping("/import/{projectId}/{userId}") @PostMapping("/import/{projectId}/{userId}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId, @PathVariable String userId) { public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId, @PathVariable String userId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
return testCaseService.testCaseImport(file, projectId, userId); return testCaseService.testCaseImport(file, projectId, userId);
} }

View File

@ -2,7 +2,7 @@ package io.metersphere.track.controller;
import io.metersphere.base.domain.TestCaseNode; import io.metersphere.base.domain.TestCaseNode;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.track.dto.TestCaseNodeDTO; import io.metersphere.track.dto.TestCaseNodeDTO;
import io.metersphere.track.request.testcase.DragNodeRequest; import io.metersphere.track.request.testcase.DragNodeRequest;
import io.metersphere.track.request.testcase.QueryNodeRequest; import io.metersphere.track.request.testcase.QueryNodeRequest;
@ -22,11 +22,11 @@ public class TestCaseNodeController {
@Resource @Resource
TestCaseNodeService testCaseNodeService; TestCaseNodeService testCaseNodeService;
@Resource @Resource
private CheckOwnerService checkOwnerService; private CheckPermissionService checkPermissionService;
@GetMapping("/list/{projectId}") @GetMapping("/list/{projectId}")
public List<TestCaseNodeDTO> getNodeByProjectId(@PathVariable String projectId) { public List<TestCaseNodeDTO> getNodeByProjectId(@PathVariable String projectId) {
checkOwnerService.checkProjectOwner(projectId); checkPermissionService.checkProjectOwner(projectId);
return testCaseNodeService.getNodeTreeByProjectId(projectId); return testCaseNodeService.getNodeTreeByProjectId(projectId);
} }
@ -43,13 +43,13 @@ public class TestCaseNodeController {
@GetMapping("/list/plan/{planId}") @GetMapping("/list/plan/{planId}")
public List<TestCaseNodeDTO> getNodeByPlanId(@PathVariable String planId) { public List<TestCaseNodeDTO> getNodeByPlanId(@PathVariable String planId) {
checkOwnerService.checkTestPlanOwner(planId); checkPermissionService.checkTestPlanOwner(planId);
return testCaseNodeService.getNodeByPlanId(planId); return testCaseNodeService.getNodeByPlanId(planId);
} }
@GetMapping("/list/review/{reviewId}") @GetMapping("/list/review/{reviewId}")
public List<TestCaseNodeDTO> getNodeByReviewId(@PathVariable String reviewId) { public List<TestCaseNodeDTO> getNodeByReviewId(@PathVariable String reviewId) {
checkOwnerService.checkTestReviewOwner(reviewId); checkPermissionService.checkTestReviewOwner(reviewId);
return testCaseNodeService.getNodeByReviewId(reviewId); return testCaseNodeService.getNodeByReviewId(reviewId);
} }

View File

@ -9,7 +9,7 @@ import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.track.dto.TestCaseReviewDTO; import io.metersphere.track.dto.TestCaseReviewDTO;
import io.metersphere.track.dto.TestReviewDTOWithMetric; import io.metersphere.track.dto.TestReviewDTOWithMetric;
import io.metersphere.track.request.testreview.QueryCaseReviewRequest; import io.metersphere.track.request.testreview.QueryCaseReviewRequest;
@ -35,7 +35,7 @@ public class TestCaseReviewController {
@Resource @Resource
TestReviewProjectService testReviewProjectService; TestReviewProjectService testReviewProjectService;
@Resource @Resource
CheckOwnerService checkOwnerService; CheckPermissionService checkPermissionService;
@PostMapping("/list/{goPage}/{pageSize}") @PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<TestCaseReviewDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryCaseReviewRequest request) { public Pager<List<TestCaseReviewDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryCaseReviewRequest request) {
@ -75,7 +75,7 @@ public class TestCaseReviewController {
@GetMapping("/delete/{reviewId}") @GetMapping("/delete/{reviewId}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void deleteCaseReview(@PathVariable String reviewId) { public void deleteCaseReview(@PathVariable String reviewId) {
checkOwnerService.checkTestReviewOwner(reviewId); checkPermissionService.checkTestReviewOwner(reviewId);
testCaseReviewService.deleteCaseReview(reviewId); testCaseReviewService.deleteCaseReview(reviewId);
} }
@ -107,14 +107,14 @@ public class TestCaseReviewController {
@GetMapping("/get/{reviewId}") @GetMapping("/get/{reviewId}")
public TestCaseReview getTestReview(@PathVariable String reviewId) { public TestCaseReview getTestReview(@PathVariable String reviewId) {
checkOwnerService.checkTestReviewOwner(reviewId); checkPermissionService.checkTestReviewOwner(reviewId);
return testCaseReviewService.getTestReview(reviewId); return testCaseReviewService.getTestReview(reviewId);
} }
@PostMapping("/edit/status/{reviewId}") @PostMapping("/edit/status/{reviewId}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editTestPlanStatus(@PathVariable String reviewId) { public void editTestPlanStatus(@PathVariable String reviewId) {
checkOwnerService.checkTestReviewOwner(reviewId); checkPermissionService.checkTestReviewOwner(reviewId);
testCaseReviewService.editTestReviewStatus(reviewId); testCaseReviewService.editTestReviewStatus(reviewId);
} }

View File

@ -8,7 +8,7 @@ import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.service.CheckOwnerService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.track.dto.TestCaseReportMetricDTO; import io.metersphere.track.dto.TestCaseReportMetricDTO;
import io.metersphere.track.dto.TestPlanDTO; import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.dto.TestPlanDTOWithMetric; import io.metersphere.track.dto.TestPlanDTOWithMetric;
@ -34,7 +34,7 @@ public class TestPlanController {
@Resource @Resource
TestPlanProjectService testPlanProjectService; TestPlanProjectService testPlanProjectService;
@Resource @Resource
CheckOwnerService checkOwnerService; CheckPermissionService checkPermissionService;
@PostMapping("/list/{goPage}/{pageSize}") @PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<TestPlanDTOWithMetric>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestPlanRequest request) { public Pager<List<TestPlanDTOWithMetric>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestPlanRequest request) {
@ -73,7 +73,7 @@ public class TestPlanController {
@PostMapping("/get/{testPlanId}") @PostMapping("/get/{testPlanId}")
public TestPlan getTestPlan(@PathVariable String testPlanId) { public TestPlan getTestPlan(@PathVariable String testPlanId) {
checkOwnerService.checkTestPlanOwner(testPlanId); checkPermissionService.checkTestPlanOwner(testPlanId);
return testPlanService.getTestPlan(testPlanId); return testPlanService.getTestPlan(testPlanId);
} }
@ -92,14 +92,14 @@ public class TestPlanController {
@PostMapping("/edit/status/{planId}") @PostMapping("/edit/status/{planId}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editTestPlanStatus(@PathVariable String planId) { public void editTestPlanStatus(@PathVariable String planId) {
checkOwnerService.checkTestPlanOwner(planId); checkPermissionService.checkTestPlanOwner(planId);
testPlanService.editTestPlanStatus(planId); testPlanService.editTestPlanStatus(planId);
} }
@PostMapping("/delete/{testPlanId}") @PostMapping("/delete/{testPlanId}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public int deleteTestPlan(@PathVariable String testPlanId) { public int deleteTestPlan(@PathVariable String testPlanId) {
checkOwnerService.checkTestPlanOwner(testPlanId); checkPermissionService.checkTestPlanOwner(testPlanId);
return testPlanService.deleteTestPlan(testPlanId); return testPlanService.deleteTestPlan(testPlanId);
} }

@ -1 +1 @@
Subproject commit bb494fc68a2367359c9048fa7250c7618de4afb6 Subproject commit 9f4a9bbf46fc1333dbcccea21f83e27e3ec10b1f

View File

@ -164,6 +164,7 @@ check_owner_case=The current user does not have permission to operate this use c
check_owner_plan=The current user does not have permission to operate this plan check_owner_plan=The current user does not have permission to operate this plan
check_owner_review=The current user does not have permission to operate this review check_owner_review=The current user does not have permission to operate this review
check_owner_comment=The current user does not have permission to manipulate this comment check_owner_comment=The current user does not have permission to manipulate this comment
check_owner_read_only=The current user in this workspace is a read-only user
upload_content_is_null=Imported content is empty upload_content_is_null=Imported content is empty
test_plan_notification=Test plan notification test_plan_notification=Test plan notification
task_defect_notification=Task defect notification task_defect_notification=Task defect notification

View File

@ -165,6 +165,7 @@ check_owner_case=当前用户没有操作此用例的权限
check_owner_plan=当前用户没有操作此计划的权限 check_owner_plan=当前用户没有操作此计划的权限
check_owner_review=当前用户没有操作此评审的权限 check_owner_review=当前用户没有操作此评审的权限
check_owner_comment=当前用户没有操作此评论的权限 check_owner_comment=当前用户没有操作此评论的权限
check_owner_read_only=当前用户在此工作空间为只读用户
upload_content_is_null=导入内容为空 upload_content_is_null=导入内容为空
test_plan_notification=测试计划通知 test_plan_notification=测试计划通知
task_defect_notification=缺陷任务通知 task_defect_notification=缺陷任务通知

View File

@ -166,6 +166,7 @@ check_owner_case=當前用戶沒有操作此用例的權限
check_owner_plan=當前用戶沒有操作此計劃的權限 check_owner_plan=當前用戶沒有操作此計劃的權限
check_owner_review=當前用戶沒有操作此評審的權限 check_owner_review=當前用戶沒有操作此評審的權限
check_owner_comment=當前用戶沒有操作此評論的權限 check_owner_comment=當前用戶沒有操作此評論的權限
check_owner_read_only=當前用戶在此工作空間為只讀用戶
upload_content_is_null=導入內容為空 upload_content_is_null=導入內容為空
test_plan_notification=測試計畫通知 test_plan_notification=測試計畫通知
task_defect_notification=缺陷任務通知 task_defect_notification=缺陷任務通知

View File

@ -1,14 +1,14 @@
<template> <template>
<ms-container> <ms-container>
<ms-aside-container> <ms-aside-container>
<ms-api-scenario-module <ms-api-scenario-module
@nodeSelectEvent="nodeChange" @nodeSelectEvent="nodeChange"
@refreshTable="refresh" @refreshTable="refresh"
@saveAsEdit="editScenario" @saveAsEdit="editScenario"
@setModuleOptions="setModuleOptions" @setModuleOptions="setModuleOptions"
@enableTrash="enableTrash" @enableTrash="enableTrash"
:type="'edit'" :type="'edit'"
ref="nodeTree"/> ref="nodeTree"/>
</ms-aside-container> </ms-aside-container>
<ms-main-container> <ms-main-container>

View File

@ -37,7 +37,12 @@
<el-collapse-transition> <el-collapse-transition>
<div v-if="request.active"> <div v-if="request.active">
<div v-if="request.protocol === 'HTTP'"> <div v-if="request.protocol === 'HTTP'">
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-model="request.url" style="width: 85%;margin-top: 10px" size="small"> <el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-if="request.url" v-model="request.url" style="width: 85%;margin-top: 10px" size="small">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-input>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-else v-model="request.path" style="width: 85%;margin-top: 10px" size="small">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small"> <el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/> <el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select> </el-select>
@ -96,20 +101,10 @@
try { try {
let urlObject = new URL(this.request.url); let urlObject = new URL(this.request.url);
let url = urlObject.protocol + "//" + urlObject.host + "/"; let url = urlObject.protocol + "//" + urlObject.host + "/";
if (url) {
let path = this.request.url.substr(url.length);
if (!path.startsWith('/')) {
path = "/" + path;
}
this.request.path = path;
} else {
this.request.url = this.request.path;
}
} catch (e) { } catch (e) {
if (this.request.url) { if (this.request.url) {
this.request.path = this.request.url; this.request.path = this.request.url;
} else { this.request.url = undefined;
this.request.url = this.request.path;
} }
} }
} }

View File

@ -606,7 +606,8 @@
if (dropType != "inner") { if (dropType != "inner") {
return true; return true;
} }
else if (dropType === "inner" && ELEMENTS.get(dropNode.data.type).indexOf(draggingNode.data.type) != -1) { else if (dropType === "inner" && dropNode.data.referenced != 'REF' && dropNode.data.referenced != 'Deleted'
&& ELEMENTS.get(dropNode.data.type).indexOf(draggingNode.data.type) != -1) {
return true; return true;
} }
return false; return false;
@ -944,8 +945,4 @@
font-size: 13px; font-size: 13px;
} }
/deep/ .el-form-item__content {
line-height: 100%;
}
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<input-tag v-model="data" placeholder="输入回车添加"></input-tag> <input-tag v-model="data" placeholder="输入回车添加" class="ms-input-div ms-input-tag-wrapper ms-input ms-input-remove"></input-tag>
</template> </template>
<script> <script>
@ -31,14 +31,22 @@
<style scoped> <style scoped>
/deep/ .vue-input-tag-wrapper { ms-input-tag-wrapper >>> .vue-input-tag-wrapper {
border-radius: 2px; border-radius: 2px;
border: 1px solid #a5d24a; border: 1px solid #a5d24a;
color: #909399; color: #909399;
display: inline-block; display: inline-block;
} }
/deep/ .input-tag { ms-input-remove >>> .remove {
color: #909399;
}
.ms-input-div {
border-radius: 4px;
}
.ms-input >>> .input-tag {
display: inline-block; display: inline-block;
font-size: 12px; font-size: 12px;
min-width: auto; min-width: auto;
@ -57,12 +65,4 @@
padding: 0 5px; padding: 0 5px;
line-height: 19px; line-height: 19px;
} }
/deep/ .remove {
color: #909399;
}
/deep/ .el-form-item__content {
line-height: 100%;
}
</style> </style>

View File

@ -16,7 +16,7 @@
<el-form-item> <el-form-item>
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand" <el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" v-if="testCase===undefined"> @command="handleCommand" size="small" v-if="testCase===undefined && !scenario">
{{$t('commons.test')}} {{$t('commons.test')}}
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item> <el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
@ -76,7 +76,9 @@
method: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}], method: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
url: [ url: [
{max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'}, {max: 500, required: true, message: this.$t('commons.input_limit', [1, 500]), trigger: 'blur'},
{validator: validateURL, trigger: 'blur'} /*
{validator: validateURL, trigger: 'blur'}
*/
], ],
}, },
debugForm: {method: REQ_METHOD[0].id, environmentId: ""}, debugForm: {method: REQ_METHOD[0].id, environmentId: ""},

View File

@ -7,7 +7,7 @@
v-if="request.controller && request.controller.isValid()"> v-if="request.controller && request.controller.isValid()">
<el-row type="flex" align="middle"> <el-row type="flex" align="middle">
<font-awesome-icon :icon="['fas', 'random']"/> <font-awesome-icon :icon="['fas', 'random']"/>
<div class="condition-label">{{ request.controller.label() }}</div> <div class="condition-label">{{ request.controller.getLabel() }}</div>
</el-row> </el-row>
</el-button> </el-button>
</div> </div>
@ -19,7 +19,7 @@
v-if="request.timer && request.timer.isValid()"> v-if="request.timer && request.timer.isValid()">
<el-row type="flex" align="middle"> <el-row type="flex" align="middle">
<font-awesome-icon :icon="['fas', 'clock']"/> <font-awesome-icon :icon="['fas', 'clock']"/>
<div class="condition-label">{{ request.timer && request.timer.label() }}</div> <div class="condition-label">{{ request.timer && request.timer.getLabel() }}</div>
</el-row> </el-row>
</el-button> </el-button>
</div> </div>

View File

@ -962,7 +962,7 @@ export class IfController extends Controller {
return !!this.variable && !!this.operator && !!this.value; return !!this.variable && !!this.operator && !!this.value;
} }
label() { getLabel() {
if (this.isValid()) { if (this.isValid()) {
let label = this.variable; let label = this.variable;
if (this.operator) label += " " + this.operator; if (this.operator) label += " " + this.operator;
@ -998,7 +998,7 @@ export class ConstantTimer extends Timer {
return this.delay > 0; return this.delay > 0;
} }
label() { getLabel() {
if (this.isValid()) { if (this.isValid()) {
return this.delay + " ms"; return this.delay + " ms";
} }
@ -1362,14 +1362,14 @@ class JMXGenerator {
addConstantsTimer(sampler, request) { addConstantsTimer(sampler, request) {
if (request.timer && request.timer.isValid() && request.timer.enable) { if (request.timer && request.timer.isValid() && request.timer.enable) {
sampler.put(new JMXConstantTimer(request.timer.label(), request.timer)); sampler.put(new JMXConstantTimer(request.timer.getLabel(), request.timer));
} }
} }
getController(sampler, request) { getController(sampler, request) {
if (request.controller.isValid() && request.controller.enable) { if (request.controller.isValid() && request.controller.enable) {
if (request.controller instanceof IfController) { if (request.controller instanceof IfController) {
let name = request.controller.label(); let name = request.controller.getLabel();
let variable = "\"" + request.controller.variable + "\""; let variable = "\"" + request.controller.variable + "\"";
let operator = request.controller.operator; let operator = request.controller.operator;
let value = "\"" + request.controller.value + "\""; let value = "\"" + request.controller.value + "\"";

View File

@ -75,22 +75,25 @@
<div class="node-line" v-if="form.type === 'K8S'" v-xpack> <div class="node-line" v-if="form.type === 'K8S'" v-xpack>
<el-row> <el-row>
<el-col> <el-col>
<el-form-item prop="masterUrl" label="Master URL"> <el-form-item label="Master URL"
:rules="requiredRules">
<el-input v-model="item.masterUrl" autocomplete="new-password"/> <el-input v-model="item.masterUrl" autocomplete="new-password"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col> <el-col>
<el-form-item prop="password" label="Token"> <el-form-item label="Token"
:rules="requiredRules">
<el-input v-model="item.token" type="password" show-password autocomplete="new-password"/> <el-input v-model="item.token" type="password" show-password autocomplete="new-password"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col> <el-col>
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')"> <el-form-item :label="$t('test_resource_pool.max_threads')"
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number> :rules="requiredRules">
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -98,17 +101,19 @@
<div class="node-line" v-if="form.type === 'NODE'"> <div class="node-line" v-if="form.type === 'NODE'">
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item prop="ip" label="IP"> <el-form-item label="IP" :rules="requiredRules">
<el-input v-model="item.ip" autocomplete="off"/> <el-input v-model="item.ip" autocomplete="off"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item prop="port" label="Port" style="padding-left: 20px"> <el-form-item label="Port" style="padding-left: 20px"
:rules="requiredRules">
<el-input-number v-model="item.port" :min="1" :max="65535"></el-input-number> <el-input-number v-model="item.port" :min="1" :max="65535"></el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')" <el-form-item :label="$t('test_resource_pool.max_threads')"
:rules="requiredRules"
style="padding-left: 20px"> style="padding-left: 20px">
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number> <el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number>
</el-form-item> </el-form-item>
@ -165,21 +170,24 @@
<div class="node-line" v-if="form.type === 'K8S'" v-xpack> <div class="node-line" v-if="form.type === 'K8S'" v-xpack>
<el-row> <el-row>
<el-col> <el-col>
<el-form-item prop="masterUrl" label="Master URL"> <el-form-item label="Master URL"
:rules="requiredRules">
<el-input v-model="item.masterUrl" autocomplete="off"/> <el-input v-model="item.masterUrl" autocomplete="off"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col> <el-col>
<el-form-item prop="password" label="Token"> <el-form-item label="Token"
:rules="requiredRules">
<el-input v-model="item.token" type="password" show-password autocomplete="off"/> <el-input v-model="item.token" type="password" show-password autocomplete="off"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col> <el-col>
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')"> <el-form-item :label="$t('test_resource_pool.max_threads')"
:rules="requiredRules">
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number> <el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -188,17 +196,19 @@
<div class="node-line" v-if="form.type === 'NODE'"> <div class="node-line" v-if="form.type === 'NODE'">
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item prop="ip" label="IP"> <el-form-item label="IP" :rules="requiredRules">
<el-input v-model="item.ip" autocomplete="off"/> <el-input v-model="item.ip" autocomplete="off"/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item prop="port" label="Port" style="padding-left: 20px"> <el-form-item label="Port" style="padding-left: 20px"
:rules="requiredRules">
<el-input-number v-model="item.port" :min="1" :max="65535"></el-input-number> <el-input-number v-model="item.port" :min="1" :max="65535"></el-input-number>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item prop="maxConcurrency" :label="$t('test_resource_pool.max_threads')" <el-form-item :label="$t('test_resource_pool.max_threads')"
:rules="requiredRules"
style="padding-left: 20px"> style="padding-left: 20px">
<el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number> <el-input-number v-model="item.maxConcurrency" :min="1" :max="1000000000"></el-input-number>
</el-form-item> </el-form-item>
@ -253,6 +263,7 @@ export default {
pageSize: 5, pageSize: 5,
total: 0, total: 0,
form: {}, form: {},
requiredRules: [{required: true, message: this.$t('test_resource_pool.fill_the_data'), trigger: 'blur'}],
rule: { rule: {
name: [ name: [
{required: true, message: this.$t('test_resource_pool.input_pool_name'), trigger: 'blur'}, {required: true, message: this.$t('test_resource_pool.input_pool_name'), trigger: 'blur'},