This commit is contained in:
wenyann 2021-03-11 14:10:33 +08:00
commit 8fd1ddbf2b
34 changed files with 1890 additions and 124495 deletions

View File

@ -40,6 +40,8 @@ public class MsJSR223Processor extends MsTestElement {
if (StringUtils.isNotEmpty(name) && !config.isOperating()) { if (StringUtils.isNotEmpty(name) && !config.isOperating()) {
processor.setName(this.getName() + "<->" + name); processor.setName(this.getName() + "<->" + name);
} }
processor.setProperty("MS-ID", this.getId());
processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName()); processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
processor.setProperty("cacheKey", "true"); processor.setProperty("cacheKey", "true");

View File

@ -82,6 +82,7 @@ public class MsDubboSampler extends MsTestElement {
} }
sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName()); sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboSampleGui")); sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboSampleGui"));
sampler.setProperty("MS-ID", this.getId());
sampler.addTestElement(configCenter(this.getConfigCenter())); sampler.addTestElement(configCenter(this.getConfigCenter()));
sampler.addTestElement(registryCenter(this.getRegistryCenter())); sampler.addTestElement(registryCenter(this.getRegistryCenter()));

View File

@ -106,6 +106,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName()); sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui")); sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
sampler.setProperty("MS-ID", this.getId());
sampler.setMethod(this.getMethod()); sampler.setMethod(this.getMethod());
sampler.setContentEncoding("UTF-8"); sampler.setContentEncoding("UTF-8");
sampler.setConnectTimeout(this.getConnectTimeout() == null ? "6000" : this.getConnectTimeout()); sampler.setConnectTimeout(this.getConnectTimeout() == null ? "6000" : this.getConnectTimeout());

View File

@ -119,6 +119,8 @@ public class MsJDBCSampler extends MsTestElement {
} }
sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName()); sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI")); sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
sampler.setProperty("MS-ID", this.getId());
// request.getDataSource() 是ID需要转换为Name // request.getDataSource() 是ID需要转换为Name
sampler.setProperty("dataSource", this.dataSource.getName()); sampler.setProperty("dataSource", this.dataSource.getName());
sampler.setProperty("query", this.getQuery()); sampler.setProperty("query", this.getQuery());

View File

@ -117,6 +117,7 @@ public class MsTCPSampler extends MsTestElement {
if (StringUtils.isNotEmpty(name) && !config.isOperating()) { if (StringUtils.isNotEmpty(name) && !config.isOperating()) {
tcpSampler.setName(this.getName() + "<->" + name); tcpSampler.setName(this.getName() + "<->" + name);
} }
tcpSampler.setProperty("MS-ID", this.getId());
tcpSampler.setProperty(TestElement.TEST_CLASS, TCPSampler.class.getName()); tcpSampler.setProperty(TestElement.TEST_CLASS, TCPSampler.class.getName());
tcpSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TCPSamplerGui")); tcpSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TCPSamplerGui"));

View File

@ -239,7 +239,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
LogUtil.error(e.getMessage(), e); LogUtil.error(e.getMessage(), e);
} }
} }
sendTask(report, reportUrl, testResult); sendTask(report, reportUrl, testResult);
} }
private static void sendTask(ApiTestReport report, String reportUrl, TestResult testResult) { private static void sendTask(ApiTestReport report, String reportUrl, TestResult testResult) {
@ -303,6 +303,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
private RequestResult getRequestResult(SampleResult result) { private RequestResult getRequestResult(SampleResult result) {
RequestResult requestResult = new RequestResult(); RequestResult requestResult = new RequestResult();
requestResult.setId(result.getSamplerId());
requestResult.setName(result.getSampleLabel()); requestResult.setName(result.getSampleLabel());
requestResult.setUrl(result.getUrlAsString()); requestResult.setUrl(result.getUrlAsString());
requestResult.setMethod(getMethod(result)); requestResult.setMethod(getMethod(result));

View File

@ -7,6 +7,8 @@ import java.util.List;
@Data @Data
public class RequestResult { public class RequestResult {
// 请求ID
private String id;
private String name; private String name;

View File

@ -1,18 +1,35 @@
package io.metersphere.commons.user; package io.metersphere.commons.user;
import io.metersphere.commons.utils.CodingUtil;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
@Setter
@Getter
public class SessionUser extends UserDTO implements Serializable { public class SessionUser extends UserDTO implements Serializable {
public static final String secret = "9a9rdqPlTqhpZzkq";
public static final String iv = "1Av7hf9PgHusUHRm";
private static final long serialVersionUID = -7149638440406959033L; private static final long serialVersionUID = -7149638440406959033L;
private String csrfToken;
private SessionUser() {
}
public static SessionUser fromUser(UserDTO user) { public static SessionUser fromUser(UserDTO user) {
SessionUser sessionUser = new SessionUser(); SessionUser sessionUser = new SessionUser();
BeanUtils.copyProperties(user, sessionUser); BeanUtils.copyProperties(user, sessionUser);
List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.random(6), "" + System.currentTimeMillis());
sessionUser.csrfToken = CodingUtil.aesEncrypt(StringUtils.join(infos, "|"), secret, iv);
return sessionUser; return sessionUser;
} }

View File

@ -62,14 +62,14 @@ public class SessionUtils {
} }
public static String getCurrentWorkspaceId() { public static String getCurrentWorkspaceId() {
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastWorkspaceId(); return getUser().getLastWorkspaceId();
} }
public static String getCurrentOrganizationId() { public static String getCurrentOrganizationId() {
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastOrganizationId(); return getUser().getLastOrganizationId();
} }
public static String getCurrentProjectId() { public static String getCurrentProjectId() {
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastProjectId(); return getUser().getLastProjectId();
} }
} }

View File

@ -44,6 +44,10 @@ public class ShiroUtils {
// filterChainDefinitionMap.put("/document/**", "anon"); // filterChainDefinitionMap.put("/document/**", "anon");
} }
public static void ignoreCsrfFilter(Map<String, String> filterChainDefinitionMap) {
filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf
}
public static Cookie getSessionIdCookie(){ public static Cookie getSessionIdCookie(){
SimpleCookie sessionIdCookie = new SimpleCookie(); SimpleCookie sessionIdCookie = new SimpleCookie();
sessionIdCookie.setPath("/"); sessionIdCookie.setPath("/");

View File

@ -2,6 +2,7 @@ package io.metersphere.config;
import io.metersphere.commons.utils.ShiroUtils; import io.metersphere.commons.utils.ShiroUtils;
import io.metersphere.security.ApiKeyFilter; import io.metersphere.security.ApiKeyFilter;
import io.metersphere.security.CsrfFilter;
import io.metersphere.security.UserModularRealmAuthenticator; import io.metersphere.security.UserModularRealmAuthenticator;
import io.metersphere.security.realm.LdapRealm; import io.metersphere.security.realm.LdapRealm;
import io.metersphere.security.realm.ShiroDBRealm; import io.metersphere.security.realm.ShiroDBRealm;
@ -44,10 +45,14 @@ public class ShiroConfig implements EnvironmentAware {
shiroFilterFactoryBean.setSuccessUrl("/"); shiroFilterFactoryBean.setSuccessUrl("/");
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter()); shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap(); Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap); ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
filterChainDefinitionMap.put("/**", "apikey, authc"); ShiroUtils.ignoreCsrfFilter(filterChainDefinitionMap);
filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
return shiroFilterFactoryBean; return shiroFilterFactoryBean;
} }

View File

@ -1,6 +1,5 @@
package io.metersphere.controller; package io.metersphere.controller;
import io.metersphere.commons.constants.SsoMode;
import io.metersphere.commons.constants.UserSource; import io.metersphere.commons.constants.UserSource;
import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
@ -10,7 +9,6 @@ import io.metersphere.service.UserService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils; import org.apache.shiro.SecurityUtils;
import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -24,8 +22,6 @@ public class LoginController {
@Resource @Resource
private UserService userService; private UserService userService;
@Resource @Resource
private Environment env;
@Resource
private BaseDisplayService baseDisplayService; private BaseDisplayService baseDisplayService;
@GetMapping(value = "/isLogin") @GetMapping(value = "/isLogin")
@ -37,10 +33,6 @@ public class LoginController {
} }
return ResultHolder.success(user); return ResultHolder.success(user);
} }
String ssoMode = env.getProperty("sso.mode");
if (ssoMode != null && StringUtils.equalsIgnoreCase(SsoMode.CAS.name(), ssoMode)) {
return ResultHolder.error("sso");
}
return ResultHolder.error(""); return ResultHolder.error("");
} }

View File

@ -1,11 +1,8 @@
package io.metersphere.controller; package io.metersphere.controller;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.Organization;
import io.metersphere.base.domain.User; import io.metersphere.base.domain.User;
import io.metersphere.base.domain.Workspace;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser; import io.metersphere.commons.user.SessionUser;
@ -29,7 +26,6 @@ import io.metersphere.service.WorkspaceService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
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.checkerframework.checker.units.qual.C;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;

View File

@ -14,8 +14,7 @@ public class EngineContext {
private String reportId; private String reportId;
private Integer resourceIndex; private Integer resourceIndex;
private Map<String, Object> properties = new HashMap<>(); private Map<String, Object> properties = new HashMap<>();
private Map<String, String> testData = new HashMap<>(); private Map<String, byte[]> testResourceFiles = new HashMap<>();
private Map<String, byte[]> testJars = new HashMap<>();
public String getTestId() { public String getTestId() {
return testId; return testId;
@ -69,14 +68,6 @@ public class EngineContext {
this.fileType = fileType; this.fileType = fileType;
} }
public Map<String, String> getTestData() {
return testData;
}
public void setTestData(Map<String, String> testData) {
this.testData = testData;
}
public String getResourcePoolId() { public String getResourcePoolId() {
return resourcePoolId; return resourcePoolId;
} }
@ -111,11 +102,11 @@ public class EngineContext {
} }
public Map<String, byte[]> getTestJars() { public Map<String, byte[]> getTestResourceFiles() {
return testJars; return testResourceFiles;
} }
public void setTestJars(Map<String, byte[]> testJars) { public void setTestResourceFiles(Map<String, byte[]> testResourceFiles) {
this.testJars = testJars; this.testResourceFiles = testResourceFiles;
} }
} }

View File

@ -92,8 +92,7 @@ public class EngineFactory {
} }
List<FileMetadata> jmxFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JMX.name())).collect(Collectors.toList()); List<FileMetadata> jmxFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JMX.name())).collect(Collectors.toList());
List<FileMetadata> csvFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.CSV.name())).collect(Collectors.toList()); List<FileMetadata> resourceFiles = fileMetadataList.stream().filter(f -> !StringUtils.equalsIgnoreCase(f.getType(), FileType.JMX.name())).collect(Collectors.toList());
List<FileMetadata> jarFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JAR.name())).collect(Collectors.toList());
// 合并上传的jmx // 合并上传的jmx
byte[] jmxBytes = mergeJmx(jmxFiles); byte[] jmxBytes = mergeJmx(jmxFiles);
final EngineContext engineContext = new EngineContext(); final EngineContext engineContext = new EngineContext();
@ -156,22 +155,13 @@ public class EngineFactory {
MSException.throwException(e); MSException.throwException(e);
} }
if (CollectionUtils.isNotEmpty(csvFiles)) { if (CollectionUtils.isNotEmpty(resourceFiles)) {
Map<String, String> data = new HashMap<>();
csvFiles.forEach(cf -> {
FileContent csvContent = fileService.getFileContent(cf.getId());
data.put(cf.getName(), new String(csvContent.getFile()));
});
engineContext.setTestData(data);
}
if (CollectionUtils.isNotEmpty(jarFiles)) {
Map<String, byte[]> data = new HashMap<>(); Map<String, byte[]> data = new HashMap<>();
jarFiles.forEach(jf -> { resourceFiles.forEach(cf -> {
FileContent content = fileService.getFileContent(jf.getId()); FileContent csvContent = fileService.getFileContent(cf.getId());
data.put(jf.getName(), content.getFile()); data.put(cf.getName(), csvContent.getFile());
}); });
engineContext.setTestJars(data); engineContext.setTestResourceFiles(data);
} }
return engineContext; return engineContext;

View File

@ -51,17 +51,9 @@ public class JmeterFileService {
// 每个测试生成一个文件夹 // 每个测试生成一个文件夹
files.put(fileName, context.getContent().getBytes(StandardCharsets.UTF_8)); files.put(fileName, context.getContent().getBytes(StandardCharsets.UTF_8));
// 保存测试数据文件
Map<String, String> testData = context.getTestData();
if (!CollectionUtils.isEmpty(testData)) {
for (String k : testData.keySet()) {
String v = testData.get(k);
files.put(k, v.getBytes(StandardCharsets.UTF_8));
}
}
// 保存 byte[] jar // 保存 byte[]
Map<String, byte[]> jarFiles = context.getTestJars(); Map<String, byte[]> jarFiles = context.getTestResourceFiles();
if (!CollectionUtils.isEmpty(jarFiles)) { if (!CollectionUtils.isEmpty(jarFiles)) {
for (String k : jarFiles.keySet()) { for (String k : jarFiles.keySet()) {
byte[] v = jarFiles.get(k); byte[] v = jarFiles.get(k);

View File

@ -0,0 +1,93 @@
package io.metersphere.security;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.CodingUtil;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.SessionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpHeaders;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CsrfFilter extends AnonymousFilter {
private static final String TOKEN_NAME = "CSRF-TOKEN";
@Override
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
if (!SecurityUtils.getSubject().isAuthenticated()) {
((HttpServletResponse) response).setHeader("Authentication-Status", "invalid");
return true;
}
// api 过来的请求
if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request))) {
return true;
}
// websocket 不需要csrf
String websocketKey = httpServletRequest.getHeader("Sec-WebSocket-Key");
if (StringUtils.isNotBlank(websocketKey)) {
return true;
}
// 请求头取出的token value
String csrfToken = httpServletRequest.getHeader(TOKEN_NAME);
// 校验 token
validateToken(csrfToken);
// 校验 referer
validateReferer(httpServletRequest);
return true;
}
private void validateReferer(HttpServletRequest request) {
Environment env = CommonBeanFactory.getBean(Environment.class);
String domains = env.getProperty("referer.urls");
if (StringUtils.isBlank(domains)) {
// 没有配置不校验
return;
}
String[] split = StringUtils.split(domains, ",");
String referer = request.getHeader(HttpHeaders.REFERER);
if (split != null) {
if (!ArrayUtils.contains(split, referer)) {
throw new RuntimeException("csrf error");
}
}
}
private void validateToken(String csrfToken) {
if (StringUtils.isBlank(csrfToken)) {
throw new RuntimeException("csrf token is empty");
}
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
if (signatureArray.length != 3) {
throw new RuntimeException("invalid token");
}
long signatureTime;
try {
signatureTime = Long.parseLong(signatureArray[2]);
} catch (Exception e) {
throw new RuntimeException(e);
}
Environment env = CommonBeanFactory.getBean(Environment.class);
long timeout = env.getProperty("session.timeout", Long.class, 43200L);
if (Math.abs(System.currentTimeMillis() - signatureTime) > timeout * 1000) {
throw new RuntimeException("expired token");
}
if (!StringUtils.equals(SessionUtils.getUserId(), signatureArray[0])) {
throw new RuntimeException("Please check csrf token.");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@ import MsUser from "./components/common/head/HeaderUser";
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs"; import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
import MsLanguageSwitch from "./components/common/head/LanguageSwitch"; import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
import {saveLocalStorage} from "@/common/js/utils"; import {saveLocalStorage} from "@/common/js/utils";
import {registerRequestHeaders} from "@/common/js/ajax";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/); const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {}; const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {};
@ -53,6 +54,7 @@ export default {
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
localStorage.setItem("store", JSON.stringify(this.$store.state)) localStorage.setItem("store", JSON.stringify(this.$store.state))
}) })
registerRequestHeaders();
}, },
beforeCreate() { beforeCreate() {
this.$get("/isLogin").then(response => { this.$get("/isLogin").then(response => {

View File

@ -100,6 +100,19 @@
active() { active() {
this.isActive = !this.isActive; this.isActive = !this.isActive;
}, },
formatResult(res) {
let resMap = new Map;
if (res && res.scenarios) {
res.scenarios.forEach(item => {
if (item && item.requestResults) {
item.requestResults.forEach(req => {
resMap.set(req.id, req);
})
}
})
}
this.$emit('refresh', resMap);
},
getReport() { getReport() {
this.init(); this.init();
if (this.reportId) { if (this.reportId) {
@ -113,7 +126,7 @@
if (!this.content) { if (!this.content) {
this.content = {scenarios: []}; this.content = {scenarios: []};
} }
this.$emit('refresh'); this.formatResult(this.content);
} catch (e) { } catch (e) {
throw e; throw e;
} }

View File

@ -184,7 +184,7 @@
<!-- 调试结果 --> <!-- 调试结果 -->
<el-drawer v-if="type!=='detail'" :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr" <el-drawer v-if="type!=='detail'" :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr"
:withHeader="true" :modal="false" size="90%"> :withHeader="true" :modal="false" size="90%">
<ms-api-report-detail :report-id="reportId" :debug="true" :currentProjectId="projectId"/> <ms-api-report-detail :report-id="reportId" :debug="true" :currentProjectId="projectId" @refresh="detailRefresh"/>
</el-drawer> </el-drawer>
<!--场景公共参数--> <!--场景公共参数-->
@ -291,7 +291,8 @@
response: {}, response: {},
projectIds: new Set, projectIds: new Set,
projectEnvMap: new Map, projectEnvMap: new Map,
projectList: [] projectList: [],
debugResult: new Map,
} }
}, },
created() { created() {
@ -548,21 +549,32 @@
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) { if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
this.recursiveSorting(arr[i].hashTree); this.recursiveSorting(arr[i].hashTree);
} }
// debug
if (this.debugResult && this.debugResult.get(arr[i].id)) {
arr[i].requestResult = this.debugResult.get(arr[i].id);
}
} }
}, },
sort() { sort() {
for (let i in this.scenarioDefinition) { for (let i in this.scenarioDefinition) {
//
this.scenarioDefinition[i].index = Number(i) + 1; this.scenarioDefinition[i].index = Number(i) + 1;
//
if (this.scenarioDefinition[i].type === ELEMENT_TYPE.LoopController && this.scenarioDefinition[i].hashTree if (this.scenarioDefinition[i].type === ELEMENT_TYPE.LoopController && this.scenarioDefinition[i].hashTree
&& this.scenarioDefinition[i].hashTree.length > 1) { && this.scenarioDefinition[i].hashTree.length > 1) {
this.scenarioDefinition[i].countController.proceed = true; this.scenarioDefinition[i].countController.proceed = true;
} }
// ID
if (!this.scenarioDefinition[i].projectId) { if (!this.scenarioDefinition[i].projectId) {
this.scenarioDefinition[i].projectId = getCurrentProjectID(); this.scenarioDefinition[i].projectId = getCurrentProjectID();
} }
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) { if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
this.recursiveSorting(this.scenarioDefinition[i].hashTree); this.recursiveSorting(this.scenarioDefinition[i].hashTree);
} }
// debug
if (this.debugResult && this.debugResult.get(this.scenarioDefinition[i].id)) {
this.scenarioDefinition[i].requestResult = this.debugResult.get(this.scenarioDefinition[i].id);
}
} }
}, },
addCustomizeApi(request) { addCustomizeApi(request) {
@ -576,6 +588,7 @@
this.customizeRequest = {}; this.customizeRequest = {};
this.sort(); this.sort();
this.reload(); this.reload();
this.initProjectIds();
}, },
addScenario(arr) { addScenario(arr) {
if (arr && arr.length > 0) { if (arr && arr.length > 0) {
@ -1025,6 +1038,11 @@
arr.forEach(a => this.projectIds.add(a)); arr.forEach(a => this.projectIds.add(a));
}) })
}) })
},
detailRefresh(result) {
//
this.debugResult = result;
this.sort()
} }
} }
} }

View File

@ -112,7 +112,17 @@ export default {
} }
}) })
} else { } else {
sign = false; //
if (this.envMap && this.envMap.size > 0) {
this.projectIds.forEach(id => {
if (!this.envMap.get(id)) {
sign = false;
return false;
}
})
} else {
sign = false;
}
} }
if (!sign) { if (!sign) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -117,6 +117,36 @@
</el-form> </el-form>
</el-col> </el-col>
</el-row> </el-row>
<el-row>
<el-col :span="8">
<el-form :inline="true">
<el-form-item>
<div>
{{ $t('load_test.granularity') }}
<el-popover
placement="bottom"
width="400"
trigger="hover">
<el-table :data="granularityData">
<el-table-column property="start" :label="$t('load_test.duration')">
<template v-slot:default="scope">
<span>{{ scope.row.start }} - {{ scope.row.end }}</span>
</template>
</el-table-column>
<el-table-column property="granularity" :label="$t('load_test.granularity')"/>
</el-table>
<i slot="reference" class="el-icon-info pointer"/>
</el-popover>
</div>
</el-form-item>
<el-form-item>
<el-select v-model="granularity" :placeholder="$t('commons.please_select')" size="mini" clearable>
<el-option v-for="op in granularityData" :key="op.granularity" :label="op.granularity" :value="op.granularity"></el-option>
</el-select>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div> </div>
</template> </template>
@ -134,6 +164,18 @@ export default {
domains: [], domains: [],
params: [], params: [],
statusCodeStr: '', statusCodeStr: '',
granularity: undefined,
granularityData: [
{start: 0, end: 100, granularity: 1},
{start: 101, end: 500, granularity: 5},
{start: 501, end: 1000, granularity: 10},
{start: 1001, end: 3000, granularity: 30},
{start: 3001, end: 6000, granularity: 60},
{start: 6001, end: 30000, granularity: 300},
{start: 30001, end: 60000, granularity: 600},
{start: 60001, end: 180000, granularity: 1800},
{start: 180001, end: 360000, granularity: 3600},
]
} }
}, },
props: { props: {
@ -166,6 +208,7 @@ export default {
this.statusCodeStr = this.statusCode.join(','); this.statusCodeStr = this.statusCode.join(',');
this.domains = data.domains || []; this.domains = data.domains || [];
this.params = data.params || []; this.params = data.params || [];
this.granularity = data.granularity;
} }
}); });
}, },
@ -252,6 +295,7 @@ export default {
statusCode: statusCode, statusCode: statusCode,
params: this.params, params: this.params,
domains: this.domains, domains: this.domains,
granularity: this.granularity,
}; };
}, },
} }
@ -287,4 +331,8 @@ export default {
align: center; align: center;
} }
.pointer {
cursor: pointer;
}
</style> </style>

View File

@ -70,7 +70,7 @@
<el-row type="flex" justify="start" align="middle"> <el-row type="flex" justify="start" align="middle">
<el-upload <el-upload
style="padding-right: 10px;" style="padding-right: 10px;"
accept=".jar,.csv" accept=".jar,.csv,.json,.pdf,.jpg,.png,.jpeg,.doc,.docx,.xlsx"
action="" action=""
:limit="fileNumLimit" :limit="fileNumLimit"
multiple multiple
@ -170,7 +170,7 @@ export default {
fileList: [], fileList: [],
tableData: [], tableData: [],
uploadList: [], uploadList: [],
metadataIdList:[], metadataIdList: [],
fileNumLimit: 10, fileNumLimit: 10,
threadGroups: [], threadGroups: [],
loadFileVisible: false, loadFileVisible: false,
@ -276,7 +276,10 @@ export default {
let self = this; let self = this;
let file = uploadResources.file; let file = uploadResources.file;
self.uploadList.push(file); self.uploadList.push(file);
let type = file.name.substring(file.name.lastIndexOf(".") + 1);
if (type.toLowerCase() !== 'jmx') {
return;
}
let jmxReader = new FileReader(); let jmxReader = new FileReader();
jmxReader.onload = (event) => { jmxReader.onload = (event) => {
self.threadGroups = self.threadGroups.concat(findThreadGroup(event.target.result, file.name)); self.threadGroups = self.threadGroups.concat(findThreadGroup(event.target.result, file.name));

@ -1 +1 @@
Subproject commit 360d7214d15951ae11b3973add795305a5c3d035 Subproject commit 290ffd9eb52b1a42243adb35ac3ee61f8295bb8f

View File

@ -22,7 +22,6 @@ import {left2RightDrag, bottom2TopDrag, right2LeftDrag} from "../common/js/direc
import JsonSchemaEditor from './components/common/json-schema/schema/index'; import JsonSchemaEditor from './components/common/json-schema/schema/index';
import JSONPathPicker from 'vue-jsonpath-picker'; import JSONPathPicker from 'vue-jsonpath-picker';
import VueClipboard from 'vue-clipboard2' import VueClipboard from 'vue-clipboard2'
import 'default-passive-events'
Vue.use(JsonSchemaEditor); Vue.use(JsonSchemaEditor);
import VuePapaParse from 'vue-papa-parse' import VuePapaParse from 'vue-papa-parse'
Vue.use(VuePapaParse) Vue.use(VuePapaParse)

View File

@ -1,13 +1,23 @@
import {Message, MessageBox} from 'element-ui'; import {Message, MessageBox} from 'element-ui';
import axios from "axios"; import axios from "axios";
import i18n from '../../i18n/i18n' import i18n from '../../i18n/i18n'
import {TokenKey} from "@/common/js/constants";
export function registerRequestHeaders() {
axios.interceptors.request.use(config => {
let user = JSON.parse(localStorage.getItem(TokenKey));
if (user && user.csrfToken) {
config.headers['CSRF-TOKEN'] = user.csrfToken;
}
return config;
});
}
export default { export default {
install(Vue) { install(Vue) {
// 登入请求不重定向 // 登入请求不重定向
let unRedirectUrls = new Set(['signin','ldap/signin','/signin', '/ldap/signin']); let unRedirectUrls = new Set(['signin', 'ldap/signin', '/signin', '/ldap/signin']);
if (!axios) { if (!axios) {
window.console.error('You have to install axios'); window.console.error('You have to install axios');

View File

@ -478,7 +478,8 @@ export default {
delete_file: "The file already exists, please delete the file with the same name first!", delete_file: "The file already exists, please delete the file with the same name first!",
thread_num: 'Concurrent users:', thread_num: 'Concurrent users:',
input_thread_num: 'Please enter the number of threads', input_thread_num: 'Please enter the number of threads',
duration: 'Duration time (seconds):', duration: 'Duration time (seconds)',
granularity: 'Aggregation time (seconds)',
input_duration: 'Please enter a duration', input_duration: 'Please enter a duration',
rps_limit: 'RPS Limit:', rps_limit: 'RPS Limit:',
input_rps_limit: 'Please enter a limit', input_rps_limit: 'Please enter a limit',

View File

@ -475,7 +475,8 @@ export default {
delete_file: "文件已存在,请先删除同名文件!", delete_file: "文件已存在,请先删除同名文件!",
thread_num: '并发用户数:', thread_num: '并发用户数:',
input_thread_num: '请输入线程数', input_thread_num: '请输入线程数',
duration: '压测时长(秒):', duration: '压测时长(秒)',
granularity: '聚合时间(秒)',
input_duration: '请输入时长', input_duration: '请输入时长',
rps_limit: 'RPS上限', rps_limit: 'RPS上限',
input_rps_limit: '请输入限制', input_rps_limit: '请输入限制',

View File

@ -475,7 +475,8 @@ export default {
delete_file: "文件已存在,請先刪除同名文件!", delete_file: "文件已存在,請先刪除同名文件!",
thread_num: '並發用戶數:', thread_num: '並發用戶數:',
input_thread_num: '請輸入線程數', input_thread_num: '請輸入線程數',
duration: '壓測時長(秒):', duration: '壓測時長(秒)',
granularity: '聚合時間(秒)',
input_duration: '請輸入時長', input_duration: '請輸入時長',
rps_limit: 'RPS上限', rps_limit: 'RPS上限',
input_rps_limit: '請輸入限制', input_rps_limit: '請輸入限制',