fix: 解决冲突
This commit is contained in:
commit
1b541ab4fc
|
@ -422,6 +422,12 @@
|
|||
<version>${jmeter.version}</version>
|
||||
</dependency>
|
||||
<!-- 添加jmeter包支持导入的jmx能正常执行 -->
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -34,4 +34,14 @@ public class SaveApiPlanRequest {
|
|||
|
||||
private String projectId;
|
||||
|
||||
/**
|
||||
* 项目环境对应关系
|
||||
*/
|
||||
private Map<String, String> envMap;
|
||||
|
||||
/**
|
||||
* 用例的环境的对应关系
|
||||
*/
|
||||
private Map<String, List<String>> mapping;
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
package io.metersphere.api.dto.automation.parse;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import io.metersphere.api.dto.ApiTestImportRequest;
|
||||
import io.metersphere.api.dto.definition.parse.har.HarUtils;
|
||||
import io.metersphere.api.dto.definition.parse.har.model.*;
|
||||
import io.metersphere.api.dto.definition.request.MsScenario;
|
||||
import io.metersphere.api.dto.definition.request.MsTestElement;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.jmeter.RequestResult;
|
||||
import io.metersphere.api.jmeter.ResponseResult;
|
||||
import io.metersphere.api.parse.HarScenarioAbstractParser;
|
||||
import io.metersphere.api.service.ApiScenarioModuleService;
|
||||
import io.metersphere.base.domain.ApiScenarioModule;
|
||||
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class HarScenarioParser extends HarScenarioAbstractParser<ScenarioImport> {
|
||||
|
||||
@Override
|
||||
public ScenarioImport parse(InputStream source, ApiTestImportRequest request) {
|
||||
Har har = null;
|
||||
try {
|
||||
String sourceStr = getApiTestStr(source);
|
||||
har = HarUtils.read(sourceStr);
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(har)) {
|
||||
MSException.throwException("解析失败,请确认选择的是 Har 格式!");
|
||||
}
|
||||
|
||||
ScenarioImport scenarioImport = new ScenarioImport();
|
||||
|
||||
String harName = request.getFileName();
|
||||
// 场景步骤
|
||||
LinkedList<MsTestElement> apiScenarioWithBLOBs = new LinkedList<>();
|
||||
// ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs();
|
||||
// scenario.setName(harName);
|
||||
|
||||
MsScenario msScenario = new MsScenario();
|
||||
msScenario.setName(harName);
|
||||
this.projectId = request.getProjectId();
|
||||
if (!ObjectUtils.isEmpty(har.log)&&!ObjectUtils.isEmpty(har.log.entries)) {
|
||||
parseItem(har.log.entries, msScenario, apiScenarioWithBLOBs);
|
||||
}
|
||||
|
||||
// 生成场景对象
|
||||
List<ApiScenarioWithBLOBs> scenarioWithBLOBs = new LinkedList<>();
|
||||
parseScenarioWithBLOBs(scenarioWithBLOBs, msScenario, request);
|
||||
scenarioImport.setData(scenarioWithBLOBs);
|
||||
return scenarioImport;
|
||||
}
|
||||
|
||||
private void parseScenarioWithBLOBs(List<ApiScenarioWithBLOBs> scenarioWithBLOBsList, MsScenario msScenario, ApiTestImportRequest request) {
|
||||
ApiScenarioModule module = ApiScenarioImportUtil.getSelectModule(request.getModuleId());
|
||||
if (module == null) {
|
||||
ApiScenarioModuleService apiModuleService = CommonBeanFactory.getBean(ApiScenarioModuleService.class);
|
||||
module = apiModuleService.getNewModule(msScenario.getName(), projectId, 1);
|
||||
}
|
||||
|
||||
ApiScenarioWithBLOBs scenarioWithBLOBs = parseScenario(msScenario);
|
||||
if (module != null) {
|
||||
scenarioWithBLOBs.setApiScenarioModuleId(module.getId());
|
||||
scenarioWithBLOBs.setModulePath("/" + module.getName());
|
||||
}
|
||||
scenarioWithBLOBsList.add(scenarioWithBLOBs);
|
||||
}
|
||||
|
||||
private void parseItem(List<HarEntry> items, MsScenario scenario, LinkedList<MsTestElement> results) {
|
||||
for (HarEntry item : items) {
|
||||
MsHTTPSamplerProxy request = parseHar(item);
|
||||
if (request != null) {
|
||||
results.add(request);
|
||||
}
|
||||
request.setRequestResult(getRequestResult(request,item));
|
||||
}
|
||||
scenario.setHashTree(results);
|
||||
}
|
||||
|
||||
// private List<ScenarioVariable> parseScenarioVariable(List<PostmanKeyValue> postmanKeyValues) {
|
||||
// if (postmanKeyValues == null) {
|
||||
// return null;
|
||||
// }
|
||||
// List<ScenarioVariable> keyValues = new ArrayList<>();
|
||||
// postmanKeyValues.forEach(item -> keyValues.add(new ScenarioVariable(item.getKey(), item.getValue(), item.getDescription(), VariableTypeConstants.CONSTANT.name())));
|
||||
// return keyValues;
|
||||
// }
|
||||
private RequestResult getRequestResult(MsHTTPSamplerProxy samplerProxy,HarEntry harEntry) {
|
||||
HarRequest request = harEntry.request;
|
||||
HarResponse response = harEntry.response;
|
||||
|
||||
RequestResult requestResult = new RequestResult();
|
||||
requestResult.setName("Response");
|
||||
requestResult.setUrl(request.url);
|
||||
requestResult.setMethod(request.method);
|
||||
if(samplerProxy.getBody()!= null){
|
||||
List<KeyValue> keyValueList = new ArrayList<>();
|
||||
if(!ObjectUtils.isEmpty(request.queryString)){
|
||||
for (HarQueryParm model : request.queryString) {
|
||||
KeyValue keyValue = new KeyValue(model.name,model.value);
|
||||
keyValueList.add(keyValue);
|
||||
}
|
||||
}
|
||||
if(!ObjectUtils.isEmpty(request.postData)&&!ObjectUtils.isEmpty(request.postData.params)){
|
||||
for (HarPostParam model : request.postData.params) {
|
||||
KeyValue keyValue = new KeyValue(model.name,model.value);
|
||||
keyValueList.add(keyValue);
|
||||
}
|
||||
}
|
||||
|
||||
requestResult.setBody(JSONArray.toJSONString(keyValueList));
|
||||
}
|
||||
|
||||
requestResult.setHeaders(JSONArray.toJSONString(request.headers));
|
||||
requestResult.setRequestSize(request.bodySize);
|
||||
// requestResult.setStartTime(result.getStartTime());
|
||||
// requestResult.setEndTime(result.getEndTime());
|
||||
// requestResult.setTotalAssertions(result.getAssertionResults().length);
|
||||
// requestResult.setSuccess(result.isSuccessful());
|
||||
// requestResult.setError(result.getErrorCount());
|
||||
if(!ObjectUtils.isEmpty(request.cookies)){
|
||||
requestResult.setCookies(JSONArray.toJSONString(request.cookies));
|
||||
}
|
||||
|
||||
ResponseResult responseResult = requestResult.getResponseResult();
|
||||
responseResult.setHeaders(JSONArray.toJSONString(response.headers));
|
||||
// responseResult.setLatency(result.getLatency());
|
||||
responseResult.setResponseCode(String.valueOf(response.status));
|
||||
responseResult.setResponseSize(response.bodySize);
|
||||
// responseResult.setResponseTime(result.getTime());
|
||||
if(response.content != null && response.content.text != null){
|
||||
responseResult.setBody(response.content.text);
|
||||
responseResult.setResponseMessage(response.content.text);
|
||||
}
|
||||
|
||||
return requestResult;
|
||||
}
|
||||
}
|
|
@ -489,6 +489,7 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
|
|||
assertionJsonPath.setDescription(jsonPathAssertion.getName());
|
||||
assertionJsonPath.setExpression(jsonPathAssertion.getJsonPath());
|
||||
assertionJsonPath.setExpect(jsonPathAssertion.getExpectedValue());
|
||||
assertionJsonPath.setOption(jsonPathAssertion.getPropertyAsString("ASS_OPTION"));
|
||||
assertions.setName(jsonPathAssertion.getName());
|
||||
assertions.getJsonPath().add(assertionJsonPath);
|
||||
} else if (key instanceof XPath2Assertion) {
|
||||
|
|
|
@ -12,6 +12,8 @@ public class ScenarioImportParserFactory {
|
|||
return new PostmanScenarioParser();
|
||||
} else if (StringUtils.equals(ApiImportPlatform.Jmeter.name(), platform)) {
|
||||
return new MsJmeterParser();
|
||||
} else if (StringUtils.equals(ApiImportPlatform.Har.name(), platform)) {
|
||||
return new HarScenarioParser();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ public class ApiDefinitionImportParserFactory {
|
|||
return new PostmanDefinitionParser();
|
||||
} else if (StringUtils.equals(ApiImportPlatform.Swagger2.name(), platform)) {
|
||||
return new Swagger2Parser();
|
||||
}else if (StringUtils.equals(ApiImportPlatform.Har.name(), platform)) {
|
||||
return new HarParser();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package io.metersphere.api.dto.definition.parse;
|
||||
|
||||
|
||||
import io.metersphere.api.parse.ApiImportAbstractParser;
|
||||
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
||||
import io.metersphere.base.domain.ApiModule;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author song.tianyang
|
||||
* @Date 2021/3/10 11:15 上午
|
||||
* @Description
|
||||
*/
|
||||
public abstract class HarAbstractParser extends ApiImportAbstractParser<ApiDefinitionImport> {
|
||||
|
||||
protected void buildModule(ApiModule parentModule, ApiDefinitionWithBLOBs apiDefinition, List<String> tags) {
|
||||
if (tags != null) {
|
||||
tags.forEach(tag -> {
|
||||
ApiModule module = ApiDefinitionImportUtil.buildModule(parentModule, tag, this.projectId);
|
||||
apiDefinition.setModuleId(module.getId());
|
||||
});
|
||||
}else {
|
||||
apiDefinition.setModuleId(parentModule.getId());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
package io.metersphere.api.dto.definition.parse;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.ApiTestImportRequest;
|
||||
import io.metersphere.api.dto.definition.parse.har.HarUtils;
|
||||
import io.metersphere.api.dto.definition.parse.har.model.*;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||
import io.metersphere.api.dto.definition.response.HttpResponse;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.request.RequestType;
|
||||
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
||||
import io.metersphere.base.domain.ApiModule;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.XMLUtils;
|
||||
import io.swagger.models.Model;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author song.tianyang
|
||||
* @Date 2021/3/10 11:14 上午
|
||||
* @Description
|
||||
*/
|
||||
public class HarParser extends HarAbstractParser {
|
||||
|
||||
private Map<String, Model> definitions = null;
|
||||
|
||||
@Override
|
||||
public ApiDefinitionImport parse(InputStream source, ApiTestImportRequest request) {
|
||||
Har har = null;
|
||||
try {
|
||||
String sourceStr = getApiTestStr(source);
|
||||
har = HarUtils.read(sourceStr);
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(har)) {
|
||||
MSException.throwException("解析失败,请确认选择的是 Har 格式!");
|
||||
}
|
||||
|
||||
ApiDefinitionImport definitionImport = new ApiDefinitionImport();
|
||||
this.projectId = request.getProjectId();
|
||||
definitionImport.setData(parseRequests(har, request));
|
||||
return definitionImport;
|
||||
}
|
||||
|
||||
|
||||
private List<ApiDefinitionWithBLOBs> parseRequests(Har har, ApiTestImportRequest importRequest) {
|
||||
List<ApiDefinitionWithBLOBs> results = new ArrayList<>();
|
||||
|
||||
ApiModule parentNode = ApiDefinitionImportUtil.getSelectModule(importRequest.getModuleId());
|
||||
|
||||
List<HarEntry> harEntryList = new ArrayList<>();
|
||||
if (har.log != null && har.log.entries != null) {
|
||||
harEntryList = har.log.entries;
|
||||
}
|
||||
|
||||
for (HarEntry entry : harEntryList) {
|
||||
HarRequest harRequest = entry.request;
|
||||
|
||||
//默认取路径的最后一块
|
||||
String reqName = "";
|
||||
if (harRequest.url != null) {
|
||||
String[] nameArr = harRequest.url.split("/");
|
||||
reqName = nameArr[nameArr.length - 1];
|
||||
}
|
||||
|
||||
if (harRequest != null) {
|
||||
MsHTTPSamplerProxy request = super.buildRequest(reqName, harRequest.url, harRequest.method);
|
||||
ApiDefinitionWithBLOBs apiDefinition = super.buildApiDefinition(request.getId(), reqName, harRequest.url, harRequest.method, importRequest);
|
||||
parseParameters(harRequest, request);
|
||||
parseRequestBody(harRequest, request.getBody());
|
||||
addBodyHeader(request);
|
||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||
apiDefinition.setResponse(JSON.toJSONString(parseResponse(entry.response)));
|
||||
buildModule(parentNode, apiDefinition, null);
|
||||
results.add(apiDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private void parseParameters(HarRequest harRequest, MsHTTPSamplerProxy request) {
|
||||
List<HarQueryParm> queryStringList = harRequest.queryString;
|
||||
queryStringList.forEach(harQueryParm -> {
|
||||
parseQueryParameters(harQueryParm, request.getArguments());
|
||||
});
|
||||
List<HarHeader> harHeaderList = harRequest.headers;
|
||||
harHeaderList.forEach(harHeader -> {
|
||||
parseHeaderParameters(harHeader, request.getHeaders());
|
||||
});
|
||||
List<HarCookie> harCookieList = harRequest.cookies;
|
||||
harCookieList.forEach(harCookie -> {
|
||||
parseCookieParameters(harCookie, request.getHeaders());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private String getDefaultStringValue(String val) {
|
||||
return StringUtils.isBlank(val) ? "" : val;
|
||||
}
|
||||
|
||||
private void parseCookieParameters(HarCookie harCookie, List<KeyValue> headers) {
|
||||
addCookie(headers, harCookie.name, harCookie.value, harCookie.comment, false);
|
||||
}
|
||||
|
||||
private void parseHeaderParameters(HarHeader harHeader, List<KeyValue> headers) {
|
||||
addHeader(headers, harHeader.name, harHeader.value,harHeader.comment, "", false);
|
||||
}
|
||||
|
||||
private HttpResponse parseResponse(HarResponse response) {
|
||||
HttpResponse msResponse = new HttpResponse();
|
||||
msResponse.setBody(new Body());
|
||||
msResponse.setHeaders(new ArrayList<>());
|
||||
msResponse.setType(RequestType.HTTP);
|
||||
// todo 状态码要调整?
|
||||
msResponse.setStatusCode(new ArrayList<>());
|
||||
if (response != null) {
|
||||
String responseCode = String.valueOf(response.status);
|
||||
msResponse.getStatusCode().add(new KeyValue(responseCode, responseCode));
|
||||
parseResponseHeader(response, msResponse.getHeaders());
|
||||
parseResponseBody(response, msResponse.getBody());
|
||||
}
|
||||
return msResponse;
|
||||
}
|
||||
|
||||
private void parseResponseHeader(HarResponse response, List<KeyValue> msHeaders) {
|
||||
List<HarHeader> harHeaders = response.headers;
|
||||
if (harHeaders != null) {
|
||||
for (HarHeader header : harHeaders) {
|
||||
msHeaders.add(new KeyValue(header.name, header.value, header.comment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void parseResponseBody(HarResponse response, Body body) {
|
||||
parseResponseBody(response.content, body);
|
||||
}
|
||||
|
||||
private void parseRequestBody(HarRequest requestBody, Body body) {
|
||||
if (requestBody == null) {
|
||||
return;
|
||||
}
|
||||
HarPostData content = requestBody.postData;
|
||||
if (!StringUtils.equalsIgnoreCase("GET", requestBody.method) || requestBody.postData == null) {
|
||||
return;
|
||||
}
|
||||
String contentType = content.mimeType;
|
||||
if (StringUtils.isEmpty(contentType)) {
|
||||
body.setRaw(content.text);
|
||||
} else {
|
||||
Map<String, Schema> infoMap = new HashMap();
|
||||
|
||||
if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
|
||||
List<HarPostParam> postParams = content.params;
|
||||
for (HarPostParam postParam : postParams) {
|
||||
KeyValue kv = new KeyValue(postParam.name,postParam.value);
|
||||
body.getKvs().add(kv);
|
||||
}
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
List<HarPostParam> postParams = content.params;
|
||||
for (HarPostParam postParam : postParams) {
|
||||
KeyValue kv = new KeyValue(postParam.name,postParam.value);
|
||||
body.getKvs().add(kv);
|
||||
}
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_JSON_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
body.setRaw(content.text);
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_XML_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_XML_VALUE;
|
||||
body.setRaw(parseXmlBody(content.text));
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
List<HarPostParam> postParams = content.params;
|
||||
for (HarPostParam postParam : postParams) {
|
||||
KeyValue kv = new KeyValue(postParam.name,postParam.value);
|
||||
body.getKvs().add(kv);
|
||||
}
|
||||
} else {
|
||||
body.setRaw(content.text);
|
||||
}
|
||||
}
|
||||
body.setType(getBodyType(contentType));
|
||||
}
|
||||
|
||||
private void parseResponseBody(HarContent content, Body body) {
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
String contentType = content.mimeType;
|
||||
body.setType(getBodyType(contentType));
|
||||
body.setRaw(content.text);
|
||||
}
|
||||
|
||||
private String parseXmlBody(String xmlString) {
|
||||
JSONObject object = JSONObject.parseObject(getDefaultStringValue(xmlString));
|
||||
return XMLUtils.jsonToXmlStr(object);
|
||||
}
|
||||
|
||||
private void parseQueryParameters(HarQueryParm harQueryParm, List<KeyValue> arguments) {
|
||||
arguments.add(new KeyValue(harQueryParm.name, harQueryParm.value, harQueryParm.comment, false));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import io.metersphere.api.dto.definition.parse.har.model.Har;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Utility class for working with HAR files.
|
||||
*
|
||||
* @author sangupta
|
||||
*
|
||||
*/
|
||||
public class HarUtils {
|
||||
|
||||
public static Har read(File file) throws JsonSyntaxException, IOException {
|
||||
Har har = JSONObject.parseObject(FileUtils.readFileToString(file), Har.class);
|
||||
return har;
|
||||
}
|
||||
|
||||
|
||||
public static Har read(String harJson) throws JsonSyntaxException, IOException {
|
||||
if(StringUtils.isEmpty(harJson)) {
|
||||
throw new IllegalArgumentException("HAR Json cannot be null/empty");
|
||||
}
|
||||
Har har = JSONObject.parseObject(harJson, Har.class);
|
||||
return har;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
///**
|
||||
// *
|
||||
// * har - HAR file reader, writer and viewer
|
||||
// * Copyright (c) 2014, Sandeep Gupta
|
||||
// *
|
||||
// * http://sangupta.com/projects/har
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// *
|
||||
// */
|
||||
//
|
||||
//package io.metersphere.api.dto.definition.parse.har.command;
|
||||
//
|
||||
//import java.io.File;
|
||||
//
|
||||
//import com.sangupta.har.HarUtils;
|
||||
//import com.sangupta.har.model.Har;
|
||||
//import com.sangupta.har.model.HarEntry;
|
||||
//import com.sangupta.har.model.HarPage;
|
||||
//import com.sangupta.jerry.util.AssertUtils;
|
||||
//
|
||||
//import io.airlift.command.Arguments;
|
||||
//import io.airlift.command.Command;
|
||||
//
|
||||
//@Command(name = "view", description = "View HAR file")
|
||||
//public class ViewHar implements Runnable {
|
||||
//
|
||||
// @Arguments
|
||||
// private String file;
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// Har har = null;
|
||||
//
|
||||
// try {
|
||||
// har = HarUtils.read(new File(this.file));
|
||||
// } catch(Exception e) {
|
||||
// System.out.println("Error reading HAR file: " + e.getMessage());
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if(har.log == null || AssertUtils.isEmpty(har.log.pages)) {
|
||||
// System.out.println("HAR file has no pages!");
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // connect references
|
||||
// HarUtils.connectReferences(har);
|
||||
//
|
||||
// // start displaying
|
||||
// System.out.println("Number of pages viewed: " + har.log.pages.size());
|
||||
// System.out.println();
|
||||
//
|
||||
// for(HarPage page : har.log.pages) {
|
||||
// System.out.println(page);
|
||||
//
|
||||
// // output the calls for this page
|
||||
// for(HarEntry entry : page.entries) {
|
||||
// System.out.println("\t" + entry);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class Har {
|
||||
|
||||
public HarLog log;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Har [log=" + log + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarCache {
|
||||
|
||||
public HarCacheDetails beforeRequest;
|
||||
|
||||
public HarCacheDetails afterRequest;
|
||||
|
||||
public String comment;
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarCacheDetails {
|
||||
|
||||
public String expires;
|
||||
|
||||
public String lastAccess;
|
||||
|
||||
public String etag;
|
||||
|
||||
public String hitCount;
|
||||
|
||||
public String comment;
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarContent {
|
||||
|
||||
public long size;
|
||||
|
||||
public String mimeType;
|
||||
|
||||
public long compression;
|
||||
|
||||
public String text;
|
||||
|
||||
public String comment;
|
||||
|
||||
public String encoding;
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarCookie {
|
||||
|
||||
public String name;
|
||||
|
||||
public String value;
|
||||
|
||||
public String path;
|
||||
|
||||
public String expires;
|
||||
|
||||
public boolean httpOnly;
|
||||
|
||||
public boolean secure;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Cookie: " + this.name + "=" + this.value + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if(this.name == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof HarCookie)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.name == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HarCookie harCookie = (HarCookie) obj;
|
||||
return this.name.equals(harCookie.name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarCreator {
|
||||
|
||||
public String name;
|
||||
|
||||
public String version;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarCreator [name=" + name + ", version=" + version + ", comment=" + comment + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarEntry implements Comparable<HarEntry> {
|
||||
|
||||
public String pageref;
|
||||
|
||||
public String startedDateTime;
|
||||
|
||||
public double time;
|
||||
|
||||
public HarRequest request;
|
||||
|
||||
public HarResponse response;
|
||||
|
||||
public HarCache cache;
|
||||
|
||||
public HarTiming timings;
|
||||
|
||||
public String serverIPAddress;
|
||||
|
||||
public String connection;
|
||||
|
||||
public String comment;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarEntry [pageref=" + pageref + ", startedDateTime=" + startedDateTime + ", time=" + time + ", request="
|
||||
+ request + ", response=" + response + ", cache=" + cache + ", timings=" + timings
|
||||
+ ", serverIPAddress=" + serverIPAddress + ", connection=" + connection + ", comment=" + comment + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int compareTo(HarEntry o) {
|
||||
if(o == null) {
|
||||
return -1;
|
||||
}
|
||||
// parse the time and then return
|
||||
return this.startedDateTime.compareTo(o.startedDateTime);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarHeader {
|
||||
|
||||
public String name;
|
||||
|
||||
public String value;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Header: " + this.name + "=" + this.value + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if(this.name == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof HarHeader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.name == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HarHeader harHeader = (HarHeader) obj;
|
||||
return this.name.equals(harHeader.name);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HarLog {
|
||||
|
||||
public static final String DEFAULT_HAR_VERSION = "1.2";
|
||||
|
||||
public String version = DEFAULT_HAR_VERSION;
|
||||
|
||||
public HarCreator creator;
|
||||
|
||||
public HarCreator browser;
|
||||
|
||||
public List<HarPage> pages;
|
||||
|
||||
public List<HarEntry> entries;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarLog [version=" + version + ", creator=" + creator + ", browser=" + browser + ", pages=" + pages
|
||||
+ ", entries=" + entries + ", comment=" + comment + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HarPage {
|
||||
|
||||
public String startedDateTime;
|
||||
|
||||
public String id;
|
||||
|
||||
public String title;
|
||||
|
||||
public HarPageTiming pageTimings;
|
||||
|
||||
public String comment;
|
||||
|
||||
public transient List<HarEntry> entries;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarPage [startedDateTime=" + startedDateTime + ", id=" + id + ", title=" + title + ", pageTimings="
|
||||
+ pageTimings + ", comment=" + comment + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if(this.id == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof HarPage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.id == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HarPage harPage = (HarPage) obj;
|
||||
return this.id.equals(harPage.id);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarPageTiming {
|
||||
|
||||
public double onContentLoad;
|
||||
|
||||
public double onLoad;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarPageTiming [onContentLoad=" + onContentLoad + ", onLoad=" + onLoad + ", comment=" + comment + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HarPostData {
|
||||
|
||||
public String mimeType;
|
||||
|
||||
public List<HarPostParam> params;
|
||||
|
||||
public String text;
|
||||
|
||||
public String comment;
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarPostParam {
|
||||
|
||||
public String name;
|
||||
|
||||
public String value;
|
||||
|
||||
public String fileName;
|
||||
|
||||
public String contentType;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Post Param: " + this.name + "=" + this.value + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if(this.name == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof HarPostParam)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.name == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HarPostParam harPostParam = (HarPostParam) obj;
|
||||
return this.name.equals(harPostParam.name);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarQueryParm {
|
||||
|
||||
public String name;
|
||||
|
||||
public String value;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Query Param: " + this.name + "=" + this.value + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if(this.name == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this.name.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(!(obj instanceof HarQueryParm)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(this.name == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HarQueryParm harQueryParm = (HarQueryParm) obj;
|
||||
return this.name.equals(harQueryParm.name);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HarRequest {
|
||||
|
||||
public String method;
|
||||
|
||||
public String url;
|
||||
|
||||
public String httpVersion;
|
||||
|
||||
public List<HarCookie> cookies;
|
||||
|
||||
public List<HarHeader> headers;
|
||||
|
||||
public List<HarQueryParm> queryString;
|
||||
|
||||
public HarPostData postData;
|
||||
|
||||
public long headersSize;
|
||||
|
||||
public long bodySize;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.method + " " + this.url + " " + this.httpVersion;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HarResponse {
|
||||
|
||||
public int status;
|
||||
|
||||
public String statusText;
|
||||
|
||||
public String httpVersion;
|
||||
|
||||
public List<HarHeader> headers;
|
||||
|
||||
public List<HarCookie> cookies;
|
||||
|
||||
public HarContent content;
|
||||
|
||||
public String redirectURL;
|
||||
|
||||
public long headersSize;
|
||||
|
||||
public long bodySize;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HTTP " + this.status + " (" + this.statusText + ")";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
*
|
||||
* har - HAR file reader, writer and viewer
|
||||
* Copyright (c) 2014, Sandeep Gupta
|
||||
*
|
||||
* http://sangupta.com/projects/har
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package io.metersphere.api.dto.definition.parse.har.model;
|
||||
|
||||
public class HarTiming {
|
||||
|
||||
public double blocked;
|
||||
|
||||
public double dns;
|
||||
|
||||
public double connect;
|
||||
|
||||
public double send;
|
||||
|
||||
public double wait;
|
||||
|
||||
public double receive;
|
||||
|
||||
public double ssl;
|
||||
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarTiming [blocked=" + blocked + ", dns=" + dns + ", connect=" + connect + ", send=" + send + ", wait="
|
||||
+ wait + ", receive=" + receive + ", ssl=" + ssl + ", comment=" + comment + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ public class MsAssertionJsonPath extends MsAssertionType {
|
|||
private String expect;
|
||||
private String expression;
|
||||
private String description;
|
||||
private String option = "REGEX";
|
||||
|
||||
public MsAssertionJsonPath() {
|
||||
setType(MsAssertionType.JSON_PATH);
|
||||
|
|
|
@ -96,7 +96,12 @@ public class MsAssertions extends MsTestElement {
|
|||
assertion.setJsonValidationBool(true);
|
||||
assertion.setExpectNull(false);
|
||||
assertion.setInvert(false);
|
||||
assertion.setProperty("ASS_OPTION",assertionJsonPath.getOption());
|
||||
if (StringUtils.isEmpty(assertionJsonPath.getOption()) || "REGEX".equals(assertionJsonPath.getOption())) {
|
||||
assertion.setIsRegex(true);
|
||||
} else {
|
||||
assertion.setIsRegex(false);
|
||||
}
|
||||
return assertion;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ public class MsJSR223Processor extends MsTestElement {
|
|||
if (StringUtils.isNotEmpty(name) && !config.isOperating()) {
|
||||
processor.setName(this.getName() + "<->" + name);
|
||||
}
|
||||
processor.setProperty("MS-ID", this.getId());
|
||||
|
||||
processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
|
||||
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
processor.setProperty("cacheKey", "true");
|
||||
|
|
|
@ -82,6 +82,7 @@ public class MsDubboSampler extends MsTestElement {
|
|||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboSampleGui"));
|
||||
sampler.setProperty("MS-ID", this.getId());
|
||||
|
||||
sampler.addTestElement(configCenter(this.getConfigCenter()));
|
||||
sampler.addTestElement(registryCenter(this.getRegistryCenter()));
|
||||
|
|
|
@ -85,8 +85,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
@JSONField(ordinal = 34)
|
||||
private List<KeyValue> arguments;
|
||||
|
||||
// @JSONField(ordinal = 35)
|
||||
// private Object requestResult;
|
||||
@JSONField(ordinal = 35)
|
||||
private Object requestResult;
|
||||
|
||||
@JSONField(ordinal = 36)
|
||||
private MsAuthManager authManager;
|
||||
|
@ -106,6 +106,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
|
||||
sampler.setProperty("MS-ID", this.getId());
|
||||
sampler.setMethod(this.getMethod());
|
||||
sampler.setContentEncoding("UTF-8");
|
||||
sampler.setConnectTimeout(this.getConnectTimeout() == null ? "6000" : this.getConnectTimeout());
|
||||
|
|
|
@ -119,6 +119,8 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
sampler.setProperty("MS-ID", this.getId());
|
||||
|
||||
// request.getDataSource() 是ID,需要转换为Name
|
||||
sampler.setProperty("dataSource", this.dataSource.getName());
|
||||
sampler.setProperty("query", this.getQuery());
|
||||
|
|
|
@ -117,6 +117,7 @@ public class MsTCPSampler extends MsTestElement {
|
|||
if (StringUtils.isNotEmpty(name) && !config.isOperating()) {
|
||||
tcpSampler.setName(this.getName() + "<->" + name);
|
||||
}
|
||||
tcpSampler.setProperty("MS-ID", this.getId());
|
||||
|
||||
tcpSampler.setProperty(TestElement.TEST_CLASS, TCPSampler.class.getName());
|
||||
tcpSampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TCPSamplerGui"));
|
||||
|
|
|
@ -303,6 +303,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
|
||||
private RequestResult getRequestResult(SampleResult result) {
|
||||
RequestResult requestResult = new RequestResult();
|
||||
requestResult.setId(result.getSamplerId());
|
||||
requestResult.setName(result.getSampleLabel());
|
||||
requestResult.setUrl(result.getUrlAsString());
|
||||
requestResult.setMethod(getMethod(result));
|
||||
|
|
|
@ -7,6 +7,8 @@ import java.util.List;
|
|||
|
||||
@Data
|
||||
public class RequestResult {
|
||||
// 请求ID
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
package io.metersphere.api.parse;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.definition.parse.har.model.*;
|
||||
import io.metersphere.api.dto.definition.request.MsTestElement;
|
||||
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||
import io.metersphere.api.dto.parse.postman.PostmanEvent;
|
||||
import io.metersphere.api.dto.parse.postman.PostmanKeyValue;
|
||||
import io.metersphere.api.dto.parse.postman.PostmanRequest;
|
||||
import io.metersphere.api.dto.parse.postman.PostmanScript;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.commons.constants.MsRequestBodyType;
|
||||
import io.metersphere.commons.constants.PostmanRequestBodyMode;
|
||||
import io.metersphere.commons.utils.XMLUtils;
|
||||
import io.swagger.v3.oas.models.media.Schema;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class HarScenarioAbstractParser<T> extends ApiImportAbstractParser<T> {
|
||||
|
||||
protected MsHTTPSamplerProxy parseHar(HarEntry harEntry) {
|
||||
HarRequest harRequest = harEntry.request;
|
||||
if (harRequest == null) {
|
||||
return null;
|
||||
}
|
||||
MsHTTPSamplerProxy request = buildRequest(harRequest.url, harRequest.url,harRequest.method);
|
||||
if (StringUtils.isNotBlank(request.getPath())) {
|
||||
String path = request.getPath().split("\\?")[0];
|
||||
path = path.replace("{{", "${");
|
||||
path = path.replace("}}", "}");
|
||||
request.setPath(path);
|
||||
} else {
|
||||
request.setPath("/");
|
||||
}
|
||||
parseParameters(harRequest, request);
|
||||
parseRequestBody(harRequest, request.getBody());
|
||||
addBodyHeader(request);
|
||||
return request;
|
||||
}
|
||||
|
||||
private void parseParameters(HarRequest harRequest, MsHTTPSamplerProxy request) {
|
||||
List<HarQueryParm> queryStringList = harRequest.queryString;
|
||||
queryStringList.forEach(harQueryParm -> {
|
||||
parseQueryParameters(harQueryParm, request.getArguments());
|
||||
});
|
||||
List<HarHeader> harHeaderList = harRequest.headers;
|
||||
harHeaderList.forEach(harHeader -> {
|
||||
parseHeaderParameters(harHeader, request.getHeaders());
|
||||
});
|
||||
List<HarCookie> harCookieList = harRequest.cookies;
|
||||
harCookieList.forEach(harCookie -> {
|
||||
parseCookieParameters(harCookie, request.getHeaders());
|
||||
});
|
||||
}
|
||||
private void parseRequestBody(HarRequest requestBody, Body body) {
|
||||
if (requestBody == null) {
|
||||
return;
|
||||
}
|
||||
HarPostData content = requestBody.postData;
|
||||
if (!StringUtils.equalsIgnoreCase("GET", requestBody.method) || requestBody.postData == null) {
|
||||
return;
|
||||
}
|
||||
String contentType = content.mimeType;
|
||||
if (StringUtils.isEmpty(contentType)) {
|
||||
body.setRaw(content.text);
|
||||
} else {
|
||||
Map<String, Schema> infoMap = new HashMap();
|
||||
|
||||
if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
|
||||
List<HarPostParam> postParams = content.params;
|
||||
for (HarPostParam postParam : postParams) {
|
||||
KeyValue kv = new KeyValue(postParam.name,postParam.value);
|
||||
body.getKvs().add(kv);
|
||||
}
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
List<HarPostParam> postParams = content.params;
|
||||
for (HarPostParam postParam : postParams) {
|
||||
KeyValue kv = new KeyValue(postParam.name,postParam.value);
|
||||
body.getKvs().add(kv);
|
||||
}
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_JSON_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
|
||||
body.setRaw(content.text);
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_XML_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_XML_VALUE;
|
||||
body.setRaw(parseXmlBody(content.text));
|
||||
} else if (contentType.startsWith(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE)) {
|
||||
contentType = org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
|
||||
List<HarPostParam> postParams = content.params;
|
||||
for (HarPostParam postParam : postParams) {
|
||||
KeyValue kv = new KeyValue(postParam.name,postParam.value);
|
||||
body.getKvs().add(kv);
|
||||
}
|
||||
} else {
|
||||
body.setRaw(content.text);
|
||||
}
|
||||
}
|
||||
body.setType(getBodyType(contentType));
|
||||
}
|
||||
|
||||
|
||||
private void parseQueryParameters(HarQueryParm harQueryParm, List<KeyValue> arguments) {
|
||||
arguments.add(new KeyValue(harQueryParm.name, harQueryParm.value, harQueryParm.comment, false));
|
||||
}
|
||||
private void parseCookieParameters(HarCookie harCookie, List<KeyValue> headers) {
|
||||
addCookie(headers, harCookie.name, harCookie.value, harCookie.comment, false);
|
||||
}
|
||||
|
||||
private void parseHeaderParameters(HarHeader harHeader, List<KeyValue> headers) {
|
||||
addHeader(headers, harHeader.name, harHeader.value,harHeader.comment, "", false);
|
||||
}
|
||||
|
||||
private void addPreScript(MsHTTPSamplerProxy request, List<PostmanEvent> event) {
|
||||
if (request != null && CollectionUtils.isNotEmpty(event)) {
|
||||
StringBuilder scriptStr = new StringBuilder();
|
||||
event = event.stream()
|
||||
.filter(item -> item.getScript() != null)
|
||||
.collect(Collectors.toList());
|
||||
event.forEach(item -> {
|
||||
PostmanScript script = item.getScript();
|
||||
if (script != null && item.getListen().contains("prerequest")) {
|
||||
List<String> exec = script.getExec();
|
||||
if (CollectionUtils.isNotEmpty(exec)) {
|
||||
exec.forEach(col -> {
|
||||
if (StringUtils.isNotEmpty(col)) {
|
||||
scriptStr.append(col + "\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (StringUtils.isNotBlank(scriptStr)) {
|
||||
MsJSR223PreProcessor jsr223PreProcessor = new MsJSR223PreProcessor();
|
||||
jsr223PreProcessor.setName("JSR223PreProcessor");
|
||||
jsr223PreProcessor.setScriptLanguage("javascript");
|
||||
jsr223PreProcessor.setScript(scriptStr.toString());
|
||||
LinkedList<MsTestElement> hashTree = new LinkedList<>();
|
||||
hashTree.add(jsr223PreProcessor);
|
||||
request.setHashTree(hashTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<KeyValue> parseKeyValue(List<PostmanKeyValue> postmanKeyValues) {
|
||||
if (postmanKeyValues == null) {
|
||||
return null;
|
||||
}
|
||||
List<KeyValue> keyValues = new ArrayList<>();
|
||||
postmanKeyValues.forEach(item -> keyValues.add(new KeyValue(item.getKey(), item.getValue(), item.getDescription(), item.getContentType())));
|
||||
return keyValues;
|
||||
}
|
||||
|
||||
private void parseBody(Body body, PostmanRequest requestDesc) {
|
||||
JSONObject postmanBody = requestDesc.getBody();
|
||||
if (postmanBody == null) {
|
||||
return;
|
||||
}
|
||||
String bodyMode = postmanBody.getString("mode");
|
||||
if (StringUtils.isBlank(bodyMode)) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.RAW.value())) {
|
||||
parseRawBody(body, postmanBody, bodyMode);
|
||||
} else if (StringUtils.equalsAny(bodyMode, PostmanRequestBodyMode.FORM_DATA.value(), PostmanRequestBodyMode.URLENCODED.value())) {
|
||||
List<PostmanKeyValue> postmanKeyValues = JSON.parseArray(postmanBody.getString(bodyMode), PostmanKeyValue.class);
|
||||
body.setKvs(parseKeyValue(postmanKeyValues));
|
||||
if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.FORM_DATA.value())) {
|
||||
body.setType(Body.FORM_DATA);
|
||||
} else if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.URLENCODED.value())) {
|
||||
body.setType(Body.WWW_FROM);
|
||||
}
|
||||
} else if (StringUtils.equals(bodyMode, PostmanRequestBodyMode.FILE.value())) {
|
||||
body.setType(Body.BINARY);
|
||||
body.setKvs(new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
private String parseXmlBody(String xmlString) {
|
||||
JSONObject object = JSONObject.parseObject(getDefaultStringValue(xmlString));
|
||||
return XMLUtils.jsonToXmlStr(object);
|
||||
}
|
||||
|
||||
private void parseRawBody(Body body, JSONObject postmanBody, String bodyMode) {
|
||||
body.setRaw(postmanBody.getString(bodyMode));
|
||||
body.setType(MsRequestBodyType.RAW.value());
|
||||
JSONObject options = postmanBody.getJSONObject("options");
|
||||
if (options != null) {
|
||||
JSONObject raw = options.getJSONObject(PostmanRequestBodyMode.RAW.value());
|
||||
if (raw != null) {
|
||||
String bodyType = "";
|
||||
switch (raw.getString("language")) {
|
||||
case "json":
|
||||
bodyType = Body.JSON;
|
||||
break;
|
||||
case "xml":
|
||||
bodyType = Body.XML;
|
||||
break;
|
||||
default:
|
||||
bodyType = Body.RAW;
|
||||
}
|
||||
body.setType(bodyType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getDefaultStringValue(String val) {
|
||||
return StringUtils.isBlank(val) ? "" : val;
|
||||
}
|
||||
}
|
|
@ -611,27 +611,38 @@ public class ApiAutomationService {
|
|||
if (CollectionUtils.isEmpty(request.getPlanIds())) {
|
||||
MSException.throwException(Translator.get("plan id is null "));
|
||||
}
|
||||
List<String> scenarioIds = request.getScenarioIds();
|
||||
if (request.isSelectAllDate()) {
|
||||
scenarioIds = this.getAllScenarioIdsByFontedSelect(
|
||||
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
|
||||
}
|
||||
// List<String> scenarioIds = request.getScenarioIds();
|
||||
// if (request.isSelectAllDate()) {
|
||||
// scenarioIds = this.getAllScenarioIdsByFontedSelect(
|
||||
// request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
|
||||
// }
|
||||
Map<String, List<String>> mapping = request.getMapping();
|
||||
Map<String, String> envMap = request.getEnvMap();
|
||||
Set<String> set = mapping.keySet();
|
||||
List<TestPlanDTO> list = extTestPlanMapper.selectByIds(request.getPlanIds());
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
ExtTestPlanScenarioCaseMapper scenarioBatchMapper = sqlSession.getMapper(ExtTestPlanScenarioCaseMapper.class);
|
||||
ExtTestPlanApiCaseMapper apiCaseBatchMapper = sqlSession.getMapper(ExtTestPlanApiCaseMapper.class);
|
||||
|
||||
for (TestPlanDTO testPlan : list) {
|
||||
if (scenarioIds != null) {
|
||||
for (String scenarioId : scenarioIds) {
|
||||
if (!set.isEmpty()) {
|
||||
set.forEach(id -> {
|
||||
Map<String, String> newEnvMap = new HashMap<>(16);
|
||||
if (envMap != null && !envMap.isEmpty()) {
|
||||
List<String> lt = mapping.get(id);
|
||||
lt.forEach(l -> {
|
||||
newEnvMap.put(l, envMap.get(l));
|
||||
});
|
||||
}
|
||||
TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario();
|
||||
testPlanApiScenario.setId(UUID.randomUUID().toString());
|
||||
testPlanApiScenario.setApiScenarioId(scenarioId);
|
||||
testPlanApiScenario.setApiScenarioId(id);
|
||||
testPlanApiScenario.setTestPlanId(testPlan.getId());
|
||||
testPlanApiScenario.setCreateTime(System.currentTimeMillis());
|
||||
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiScenario.setEnvironment(JSON.toJSONString(newEnvMap));
|
||||
scenarioBatchMapper.insertIfNotExists(testPlanApiScenario);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (request.getApiIds() != null) {
|
||||
for (String caseId : request.getApiIds()) {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiDefinition implements Serializable {
|
||||
|
@ -38,5 +37,7 @@ public class ApiDefinition implements Serializable {
|
|||
|
||||
private String tags;
|
||||
|
||||
private String originalState;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -1193,6 +1193,76 @@ public class ApiDefinitionExample {
|
|||
addCriterion("tags not between", value1, value2, "tags");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNull() {
|
||||
addCriterion("original_state is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNotNull() {
|
||||
addCriterion("original_state is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateEqualTo(String value) {
|
||||
addCriterion("original_state =", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotEqualTo(String value) {
|
||||
addCriterion("original_state <>", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThan(String value) {
|
||||
addCriterion("original_state >", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("original_state >=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThan(String value) {
|
||||
addCriterion("original_state <", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThanOrEqualTo(String value) {
|
||||
addCriterion("original_state <=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLike(String value) {
|
||||
addCriterion("original_state like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotLike(String value) {
|
||||
addCriterion("original_state not like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIn(List<String> values) {
|
||||
addCriterion("original_state in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotIn(List<String> values) {
|
||||
addCriterion("original_state not in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateBetween(String value1, String value2) {
|
||||
addCriterion("original_state between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotBetween(String value1, String value2) {
|
||||
addCriterion("original_state not between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -43,5 +43,7 @@ public class ApiScenario implements Serializable {
|
|||
|
||||
private Integer num;
|
||||
|
||||
private String originalState;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -1393,6 +1393,76 @@ public class ApiScenarioExample {
|
|||
addCriterion("num not between", value1, value2, "num");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNull() {
|
||||
addCriterion("original_state is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIsNotNull() {
|
||||
addCriterion("original_state is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateEqualTo(String value) {
|
||||
addCriterion("original_state =", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotEqualTo(String value) {
|
||||
addCriterion("original_state <>", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThan(String value) {
|
||||
addCriterion("original_state >", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("original_state >=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThan(String value) {
|
||||
addCriterion("original_state <", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLessThanOrEqualTo(String value) {
|
||||
addCriterion("original_state <=", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateLike(String value) {
|
||||
addCriterion("original_state like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotLike(String value) {
|
||||
addCriterion("original_state not like", value, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateIn(List<String> values) {
|
||||
addCriterion("original_state in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotIn(List<String> values) {
|
||||
addCriterion("original_state not in", values, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateBetween(String value1, String value2) {
|
||||
addCriterion("original_state between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOriginalStateNotBetween(String value1, String value2) {
|
||||
addCriterion("original_state not between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<result column="path" jdbcType="VARCHAR" property="path" />
|
||||
<result column="num" jdbcType="INTEGER" property="num" />
|
||||
<result column="tags" jdbcType="VARCHAR" property="tags" />
|
||||
<result column="original_state" jdbcType="VARCHAR" property="originalState" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
|
||||
<result column="description" jdbcType="LONGVARCHAR" property="description" />
|
||||
|
@ -84,7 +85,7 @@
|
|||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, project_id, `name`, `method`, module_path, environment_id, schedule, `status`,
|
||||
module_id, user_id, create_time, update_time, protocol, `path`, num, tags
|
||||
module_id, user_id, create_time, update_time, protocol, `path`, num, tags, original_state
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
description, request, response
|
||||
|
@ -143,15 +144,15 @@
|
|||
schedule, `status`, module_id,
|
||||
user_id, create_time, update_time,
|
||||
protocol, `path`, num,
|
||||
tags, description, request,
|
||||
response)
|
||||
tags, original_state, description,
|
||||
request, response)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{method,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR}, #{environmentId,jdbcType=VARCHAR},
|
||||
#{schedule,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{moduleId,jdbcType=VARCHAR},
|
||||
#{userId,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{protocol,jdbcType=VARCHAR}, #{path,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER},
|
||||
#{tags,jdbcType=VARCHAR}, #{description,jdbcType=LONGVARCHAR}, #{request,jdbcType=LONGVARCHAR},
|
||||
#{response,jdbcType=LONGVARCHAR})
|
||||
#{tags,jdbcType=VARCHAR}, #{originalState,jdbcType=VARCHAR}, #{description,jdbcType=LONGVARCHAR},
|
||||
#{request,jdbcType=LONGVARCHAR}, #{response,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
|
||||
insert into api_definition
|
||||
|
@ -204,6 +205,9 @@
|
|||
<if test="tags != null">
|
||||
tags,
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state,
|
||||
</if>
|
||||
<if test="description != null">
|
||||
description,
|
||||
</if>
|
||||
|
@ -263,6 +267,9 @@
|
|||
<if test="tags != null">
|
||||
#{tags,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
#{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="description != null">
|
||||
#{description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -331,6 +338,9 @@
|
|||
<if test="record.tags != null">
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.originalState != null">
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.description != null">
|
||||
description = #{record.description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -363,6 +373,7 @@
|
|||
`path` = #{record.path,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
description = #{record.description,jdbcType=LONGVARCHAR},
|
||||
request = #{record.request,jdbcType=LONGVARCHAR},
|
||||
response = #{record.response,jdbcType=LONGVARCHAR}
|
||||
|
@ -387,7 +398,8 @@
|
|||
protocol = #{record.protocol,jdbcType=VARCHAR},
|
||||
`path` = #{record.path,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
tags = #{record.tags,jdbcType=VARCHAR}
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -440,6 +452,9 @@
|
|||
<if test="tags != null">
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="description != null">
|
||||
description = #{description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -469,6 +484,7 @@
|
|||
`path` = #{path,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
description = #{description,jdbcType=LONGVARCHAR},
|
||||
request = #{request,jdbcType=LONGVARCHAR},
|
||||
response = #{response,jdbcType=LONGVARCHAR}
|
||||
|
@ -490,7 +506,8 @@
|
|||
protocol = #{protocol,jdbcType=VARCHAR},
|
||||
`path` = #{path,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
tags = #{tags,jdbcType=VARCHAR}
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
original_state = #{originalState,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -21,6 +21,7 @@
|
|||
<result column="last_result" jdbcType="VARCHAR" property="lastResult" />
|
||||
<result column="report_id" jdbcType="VARCHAR" property="reportId" />
|
||||
<result column="num" jdbcType="INTEGER" property="num" />
|
||||
<result column="original_state" jdbcType="VARCHAR" property="originalState" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiScenarioWithBLOBs">
|
||||
<result column="scenario_definition" jdbcType="LONGVARCHAR" property="scenarioDefinition" />
|
||||
|
@ -87,7 +88,7 @@
|
|||
<sql id="Base_Column_List">
|
||||
id, project_id, tags, user_id, api_scenario_module_id, module_path, `name`, `level`,
|
||||
`status`, principal, step_total, follow_people, schedule, create_time, update_time,
|
||||
pass_rate, last_result, report_id, num
|
||||
pass_rate, last_result, report_id, num, original_state
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
scenario_definition, description
|
||||
|
@ -147,16 +148,16 @@
|
|||
principal, step_total, follow_people,
|
||||
schedule, create_time, update_time,
|
||||
pass_rate, last_result, report_id,
|
||||
num, scenario_definition, description
|
||||
)
|
||||
num, original_state, scenario_definition,
|
||||
description)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR},
|
||||
#{userId,jdbcType=VARCHAR}, #{apiScenarioModuleId,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR},
|
||||
#{name,jdbcType=VARCHAR}, #{level,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR},
|
||||
#{principal,jdbcType=VARCHAR}, #{stepTotal,jdbcType=INTEGER}, #{followPeople,jdbcType=VARCHAR},
|
||||
#{schedule,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{passRate,jdbcType=VARCHAR}, #{lastResult,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR},
|
||||
#{num,jdbcType=INTEGER}, #{scenarioDefinition,jdbcType=LONGVARCHAR}, #{description,jdbcType=LONGVARCHAR}
|
||||
)
|
||||
#{num,jdbcType=INTEGER}, #{originalState,jdbcType=VARCHAR}, #{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
#{description,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiScenarioWithBLOBs">
|
||||
insert into api_scenario
|
||||
|
@ -218,6 +219,9 @@
|
|||
<if test="num != null">
|
||||
num,
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state,
|
||||
</if>
|
||||
<if test="scenarioDefinition != null">
|
||||
scenario_definition,
|
||||
</if>
|
||||
|
@ -283,6 +287,9 @@
|
|||
<if test="num != null">
|
||||
#{num,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
#{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="scenarioDefinition != null">
|
||||
#{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -357,6 +364,9 @@
|
|||
<if test="record.num != null">
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="record.originalState != null">
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.scenarioDefinition != null">
|
||||
scenario_definition = #{record.scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -389,6 +399,7 @@
|
|||
last_result = #{record.lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{record.reportId,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
scenario_definition = #{record.scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
description = #{record.description,jdbcType=LONGVARCHAR}
|
||||
<if test="_parameter != null">
|
||||
|
@ -415,7 +426,8 @@
|
|||
pass_rate = #{record.passRate,jdbcType=VARCHAR},
|
||||
last_result = #{record.lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{record.reportId,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER}
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -477,6 +489,9 @@
|
|||
<if test="num != null">
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
</if>
|
||||
<if test="originalState != null">
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="scenarioDefinition != null">
|
||||
scenario_definition = #{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -506,6 +521,7 @@
|
|||
last_result = #{lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{reportId,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
scenario_definition = #{scenarioDefinition,jdbcType=LONGVARCHAR},
|
||||
description = #{description,jdbcType=LONGVARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
|
@ -529,7 +545,8 @@
|
|||
pass_rate = #{passRate,jdbcType=VARCHAR},
|
||||
last_result = #{lastResult,jdbcType=VARCHAR},
|
||||
report_id = #{reportId,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER}
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
original_state = #{originalState,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -235,7 +235,7 @@
|
|||
|
||||
<update id="removeToGc">
|
||||
update api_definition
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash'
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
|
@ -245,7 +245,7 @@
|
|||
|
||||
<update id="removeToGcByExample" parameterType="io.metersphere.base.domain.ApiDefinitionExample">
|
||||
update api_definition
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash', module_path = null, module_id = null
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
|
@ -255,7 +255,7 @@
|
|||
<update id="reduction">
|
||||
update api_definition
|
||||
set
|
||||
status = 'Underway'
|
||||
status = original_state
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
#{v}
|
||||
|
|
|
@ -275,7 +275,7 @@
|
|||
|
||||
<update id="removeToGc">
|
||||
update api_scenario
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash'
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
|
@ -285,7 +285,7 @@
|
|||
|
||||
<update id="removeToGcByExample" parameterType="io.metersphere.base.domain.ApiScenarioExample">
|
||||
update api_scenario
|
||||
set
|
||||
set original_state=status,
|
||||
status = 'Trash', module_path = null, api_scenario_module_id = null
|
||||
<if test="_parameter != null">
|
||||
<include refid="Example_Where_Clause" />
|
||||
|
@ -295,7 +295,7 @@
|
|||
<update id="reduction">
|
||||
update api_scenario
|
||||
set
|
||||
status = 'Underway'
|
||||
status = original_state
|
||||
where id in
|
||||
<foreach collection="ids" item="v" separator="," open="(" close=")">
|
||||
#{v}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
<insert id="insertIfNotExists" parameterType="io.metersphere.base.domain.TestPlanApiScenario">
|
||||
-- 查询没有数据再插入
|
||||
INSERT INTO test_plan_api_scenario(id, test_plan_id, api_scenario_id, create_time, update_time)
|
||||
SELECT #{request.id}, #{request.testPlanId}, #{request.apiScenarioId}, #{request.createTime}, #{request.updateTime}
|
||||
INSERT INTO test_plan_api_scenario(id, test_plan_id, api_scenario_id, create_time, update_time, environment)
|
||||
SELECT #{request.id}, #{request.testPlanId}, #{request.apiScenarioId}, #{request.createTime}, #{request.updateTime}, #{request.environment}
|
||||
FROM DUAL
|
||||
WHERE NOT EXISTS(
|
||||
SELECT id FROM
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum ApiImportPlatform {
|
||||
Metersphere, Postman, Swagger2, Plugin, Jmeter
|
||||
Metersphere, Postman, Swagger2, Plugin, Jmeter, Har
|
||||
}
|
||||
|
|
|
@ -1,18 +1,35 @@
|
|||
package io.metersphere.commons.user;
|
||||
|
||||
import io.metersphere.commons.utils.CodingUtil;
|
||||
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 java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
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 String csrfToken;
|
||||
|
||||
private SessionUser() {
|
||||
}
|
||||
|
||||
public static SessionUser fromUser(UserDTO user) {
|
||||
SessionUser sessionUser = new 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ public class SessionUtils {
|
|||
}
|
||||
|
||||
public static String getCurrentWorkspaceId() {
|
||||
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastWorkspaceId();
|
||||
return getUser().getLastWorkspaceId();
|
||||
}
|
||||
|
||||
public static String getCurrentOrganizationId() {
|
||||
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastOrganizationId();
|
||||
return getUser().getLastOrganizationId();
|
||||
}
|
||||
|
||||
public static String getCurrentProjectId() {
|
||||
return Optional.ofNullable(getUser()).orElse(new SessionUser()).getLastProjectId();
|
||||
return getUser().getLastProjectId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,10 @@ public class ShiroUtils {
|
|||
// filterChainDefinitionMap.put("/document/**", "anon");
|
||||
}
|
||||
|
||||
public static void ignoreCsrfFilter(Map<String, String> filterChainDefinitionMap) {
|
||||
filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf
|
||||
}
|
||||
|
||||
public static Cookie getSessionIdCookie(){
|
||||
SimpleCookie sessionIdCookie = new SimpleCookie();
|
||||
sessionIdCookie.setPath("/");
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.metersphere.config;
|
|||
|
||||
import io.metersphere.commons.utils.ShiroUtils;
|
||||
import io.metersphere.security.ApiKeyFilter;
|
||||
import io.metersphere.security.CsrfFilter;
|
||||
import io.metersphere.security.UserModularRealmAuthenticator;
|
||||
import io.metersphere.security.realm.LdapRealm;
|
||||
import io.metersphere.security.realm.ShiroDBRealm;
|
||||
|
@ -44,10 +45,14 @@ public class ShiroConfig implements EnvironmentAware {
|
|||
shiroFilterFactoryBean.setSuccessUrl("/");
|
||||
|
||||
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
|
||||
shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());
|
||||
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
|
||||
|
||||
ShiroUtils.loadBaseFilterChain(filterChainDefinitionMap);
|
||||
|
||||
filterChainDefinitionMap.put("/**", "apikey, authc");
|
||||
ShiroUtils.ignoreCsrfFilter(filterChainDefinitionMap);
|
||||
|
||||
filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import io.metersphere.commons.constants.SsoMode;
|
||||
import io.metersphere.commons.constants.UserSource;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
|
@ -10,7 +9,6 @@ import io.metersphere.service.UserService;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
|
@ -24,8 +22,6 @@ public class LoginController {
|
|||
@Resource
|
||||
private UserService userService;
|
||||
@Resource
|
||||
private Environment env;
|
||||
@Resource
|
||||
private BaseDisplayService baseDisplayService;
|
||||
|
||||
@GetMapping(value = "/isLogin")
|
||||
|
@ -37,10 +33,6 @@ public class LoginController {
|
|||
}
|
||||
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("");
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,11 @@ public class SystemParameterController {
|
|||
return SystemParameterService.getVersion();
|
||||
}
|
||||
|
||||
@GetMapping("/theme")
|
||||
public String getTheme() {
|
||||
return SystemParameterService.getValue("ui.theme");
|
||||
}
|
||||
|
||||
@GetMapping("/mail/info")
|
||||
@RequiresRoles(value = {RoleConstants.ADMIN})
|
||||
public MailInfo mailInfo() {
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package io.metersphere.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.Organization;
|
||||
import io.metersphere.base.domain.User;
|
||||
import io.metersphere.base.domain.Workspace;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
|
@ -29,7 +26,6 @@ import io.metersphere.service.WorkspaceService;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.checkerframework.checker.units.qual.C;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
|
|
@ -14,8 +14,7 @@ public class EngineContext {
|
|||
private String reportId;
|
||||
private Integer resourceIndex;
|
||||
private Map<String, Object> properties = new HashMap<>();
|
||||
private Map<String, String> testData = new HashMap<>();
|
||||
private Map<String, byte[]> testJars = new HashMap<>();
|
||||
private Map<String, byte[]> testResourceFiles = new HashMap<>();
|
||||
|
||||
public String getTestId() {
|
||||
return testId;
|
||||
|
@ -69,14 +68,6 @@ public class EngineContext {
|
|||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public Map<String, String> getTestData() {
|
||||
return testData;
|
||||
}
|
||||
|
||||
public void setTestData(Map<String, String> testData) {
|
||||
this.testData = testData;
|
||||
}
|
||||
|
||||
public String getResourcePoolId() {
|
||||
return resourcePoolId;
|
||||
}
|
||||
|
@ -111,11 +102,11 @@ public class EngineContext {
|
|||
}
|
||||
|
||||
|
||||
public Map<String, byte[]> getTestJars() {
|
||||
return testJars;
|
||||
public Map<String, byte[]> getTestResourceFiles() {
|
||||
return testResourceFiles;
|
||||
}
|
||||
|
||||
public void setTestJars(Map<String, byte[]> testJars) {
|
||||
this.testJars = testJars;
|
||||
public void setTestResourceFiles(Map<String, byte[]> testResourceFiles) {
|
||||
this.testResourceFiles = testResourceFiles;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import io.metersphere.service.FileService;
|
|||
import io.metersphere.service.KubernetesTestEngine;
|
||||
import io.metersphere.service.TestResourcePoolService;
|
||||
import org.apache.commons.beanutils.ConstructorUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.reflections8.Reflections;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -92,8 +93,7 @@ public class EngineFactory {
|
|||
}
|
||||
|
||||
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> jarFiles = fileMetadataList.stream().filter(f -> StringUtils.equalsIgnoreCase(f.getType(), FileType.JAR.name())).collect(Collectors.toList());
|
||||
List<FileMetadata> resourceFiles = ListUtils.subtract(fileMetadataList, jmxFiles);
|
||||
// 合并上传的jmx
|
||||
byte[] jmxBytes = mergeJmx(jmxFiles);
|
||||
final EngineContext engineContext = new EngineContext();
|
||||
|
@ -156,22 +156,13 @@ public class EngineFactory {
|
|||
MSException.throwException(e);
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(csvFiles)) {
|
||||
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)) {
|
||||
if (CollectionUtils.isNotEmpty(resourceFiles)) {
|
||||
Map<String, byte[]> data = new HashMap<>();
|
||||
jarFiles.forEach(jf -> {
|
||||
FileContent content = fileService.getFileContent(jf.getId());
|
||||
data.put(jf.getName(), content.getFile());
|
||||
resourceFiles.forEach(cf -> {
|
||||
FileContent csvContent = fileService.getFileContent(cf.getId());
|
||||
data.put(cf.getName(), csvContent.getFile());
|
||||
});
|
||||
engineContext.setTestJars(data);
|
||||
engineContext.setTestResourceFiles(data);
|
||||
}
|
||||
|
||||
return engineContext;
|
||||
|
|
|
@ -51,17 +51,9 @@ public class JmeterFileService {
|
|||
|
||||
// 每个测试生成一个文件夹
|
||||
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
|
||||
Map<String, byte[]> jarFiles = context.getTestJars();
|
||||
// 保存 byte[]
|
||||
Map<String, byte[]> jarFiles = context.getTestResourceFiles();
|
||||
if (!CollectionUtils.isEmpty(jarFiles)) {
|
||||
for (String k : jarFiles.keySet()) {
|
||||
byte[] v = jarFiles.get(k);
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -136,8 +136,8 @@ public class XmindCaseParser {
|
|||
data.setNodePath(nodePath);
|
||||
|
||||
|
||||
if (data.getName().length() > 50) {
|
||||
process.add(Translator.get("test_case") + Translator.get("test_track.length_less_than") + "50", nodePath + data.getName());
|
||||
if (data.getName().length() > 200) {
|
||||
process.add(Translator.get("test_case") + Translator.get("test_track.length_less_than") + "200", nodePath + data.getName());
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(nodePath)) {
|
||||
|
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.jmeter.assertions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.samplers.SampleResult;
|
||||
import org.apache.jmeter.testelement.AbstractTestElement;
|
||||
import org.apache.jmeter.testelement.ThreadListener;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.apache.oro.text.regex.Pattern;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This is main class for JSONPath Assertion which verifies assertion on
|
||||
* previous sample result using JSON path expression
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class JSONPathAssertion extends AbstractTestElement implements Serializable, Assertion, ThreadListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(JSONPathAssertion.class);
|
||||
private static final long serialVersionUID = 2L;
|
||||
public static final String JSONPATH = "JSON_PATH";
|
||||
public static final String EXPECTEDVALUE = "EXPECTED_VALUE";
|
||||
public static final String JSONVALIDATION = "JSONVALIDATION";
|
||||
public static final String EXPECT_NULL = "EXPECT_NULL";
|
||||
public static final String INVERT = "INVERT";
|
||||
public static final String ISREGEX = "ISREGEX";
|
||||
|
||||
private static ThreadLocal<DecimalFormat> decimalFormatter =
|
||||
ThreadLocal.withInitial(JSONPathAssertion::createDecimalFormat);
|
||||
|
||||
public String getOption() {
|
||||
return getPropertyAsString("ASS_OPTION");
|
||||
}
|
||||
|
||||
private static DecimalFormat createDecimalFormat() {
|
||||
DecimalFormat decimalFormatter = new DecimalFormat("#.#");
|
||||
decimalFormatter.setMaximumFractionDigits(340); // java.text.DecimalFormat.DOUBLE_FRACTION_DIGITS == 340
|
||||
decimalFormatter.setMinimumFractionDigits(1);
|
||||
return decimalFormatter;
|
||||
}
|
||||
|
||||
public String getJsonPath() {
|
||||
return getPropertyAsString(JSONPATH);
|
||||
}
|
||||
|
||||
public void setJsonPath(String jsonPath) {
|
||||
setProperty(JSONPATH, jsonPath);
|
||||
}
|
||||
|
||||
public String getExpectedValue() {
|
||||
return getPropertyAsString(EXPECTEDVALUE);
|
||||
}
|
||||
|
||||
public void setExpectedValue(String expectedValue) {
|
||||
setProperty(EXPECTEDVALUE, expectedValue);
|
||||
}
|
||||
|
||||
public void setJsonValidationBool(boolean jsonValidation) {
|
||||
setProperty(JSONVALIDATION, jsonValidation);
|
||||
}
|
||||
|
||||
public void setExpectNull(boolean val) {
|
||||
setProperty(EXPECT_NULL, val);
|
||||
}
|
||||
|
||||
public boolean isExpectNull() {
|
||||
return getPropertyAsBoolean(EXPECT_NULL);
|
||||
}
|
||||
|
||||
public boolean isJsonValidationBool() {
|
||||
return getPropertyAsBoolean(JSONVALIDATION);
|
||||
}
|
||||
|
||||
public void setInvert(boolean invert) {
|
||||
setProperty(INVERT, invert);
|
||||
}
|
||||
|
||||
public boolean isInvert() {
|
||||
return getPropertyAsBoolean(INVERT);
|
||||
}
|
||||
|
||||
public void setIsRegex(boolean flag) {
|
||||
setProperty(ISREGEX, flag);
|
||||
}
|
||||
|
||||
public boolean isUseRegex() {
|
||||
return getPropertyAsBoolean(ISREGEX, true);
|
||||
}
|
||||
|
||||
private void doAssert(String jsonString) {
|
||||
Object value = JsonPath.read(jsonString, getJsonPath());
|
||||
|
||||
if (!isJsonValidationBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value instanceof JSONArray) {
|
||||
if (arrayMatched((JSONArray) value)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ((isExpectNull() && value == null)
|
||||
|| isEquals(value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isExpectNull()) {
|
||||
throw new IllegalStateException(String.format("Value expected to be null, but found '%s'", value));
|
||||
} else {
|
||||
String msg;
|
||||
if (isUseRegex()) {
|
||||
msg = "Value expected to match regexp '%s', but it did not match: '%s'";
|
||||
} else {
|
||||
msg = "Value expected to be '%s', but found '%s'";
|
||||
}
|
||||
throw new IllegalStateException(String.format(msg, getExpectedValue(), objectToString(value)));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean arrayMatched(JSONArray value) {
|
||||
if (value.isEmpty() && "[]".equals(getExpectedValue())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Object subj : value.toArray()) {
|
||||
if ((subj == null && isExpectNull())
|
||||
|| isEquals(subj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return isEquals(value);
|
||||
}
|
||||
|
||||
private boolean isEquals(Object subj) {
|
||||
String str = objectToString(subj);
|
||||
if (isUseRegex()) {
|
||||
Pattern pattern = JMeterUtils.getPatternCache().getPattern(getExpectedValue());
|
||||
return JMeterUtils.getMatcher().matches(str, pattern);
|
||||
} else {
|
||||
if (StringUtils.isNotEmpty(getOption())) {
|
||||
boolean refFlag = false;
|
||||
switch (getOption()) {
|
||||
case "CONTAINS":
|
||||
refFlag = str.contains(getExpectedValue());
|
||||
break;
|
||||
case "NOT_CONTAINS":
|
||||
refFlag = !str.contains(getExpectedValue());
|
||||
break;
|
||||
case "EQUALS":
|
||||
refFlag = str.equals(getExpectedValue());
|
||||
break;
|
||||
case "NOT_EQUALS":
|
||||
refFlag = !str.contains(getExpectedValue());
|
||||
break;
|
||||
}
|
||||
return refFlag;
|
||||
}
|
||||
return str.equals(getExpectedValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssertionResult getResult(SampleResult samplerResult) {
|
||||
AssertionResult result = new AssertionResult(getName());
|
||||
String responseData = samplerResult.getResponseDataAsString();
|
||||
if (responseData.isEmpty()) {
|
||||
return result.setResultForNull();
|
||||
}
|
||||
|
||||
result.setFailure(false);
|
||||
result.setFailureMessage("");
|
||||
|
||||
if (!isInvert()) {
|
||||
try {
|
||||
doAssert(responseData);
|
||||
} catch (Exception e) {
|
||||
log.debug("Assertion failed", e);
|
||||
result.setFailure(true);
|
||||
result.setFailureMessage(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
doAssert(responseData);
|
||||
result.setFailure(true);
|
||||
if (isJsonValidationBool()) {
|
||||
if (isExpectNull()) {
|
||||
result.setFailureMessage("Failed that JSONPath " + getJsonPath() + " not matches null");
|
||||
} else {
|
||||
result.setFailureMessage("Failed that JSONPath " + getJsonPath() + " not matches " + getExpectedValue());
|
||||
}
|
||||
} else {
|
||||
result.setFailureMessage("Failed that JSONPath not exists: " + getJsonPath());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("Assertion failed, as expected", e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String objectToString(Object subj) {
|
||||
String str;
|
||||
if (subj == null) {
|
||||
str = "null";
|
||||
} else if (subj instanceof Map) {
|
||||
//noinspection unchecked
|
||||
str = new JSONObject((Map<String, Object>) subj).toJSONString();
|
||||
} else if (subj instanceof Double || subj instanceof Float) {
|
||||
str = decimalFormatter.get().format(subj);
|
||||
} else {
|
||||
str = subj.toString();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStarted() {
|
||||
// nothing to do on thread start
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadFinished() {
|
||||
decimalFormatter.remove();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -45,3 +45,9 @@ alter table test_plan_api_scenario change environment_id environment longtext nu
|
|||
|
||||
-- file add sort column
|
||||
alter table file_metadata add sort int default 0;
|
||||
|
||||
-- add Original state
|
||||
alter table api_definition add original_state varchar(64);
|
||||
alter table api_scenario add original_state varchar(64);
|
||||
update api_definition set original_state='Underway';
|
||||
update api_scenario set original_state='Underway';
|
|
@ -17,7 +17,6 @@
|
|||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.4.3",
|
||||
"default-passive-events": "^2.0.0",
|
||||
"diffable-html": "^4.0.0",
|
||||
"echarts": "^4.6.0",
|
||||
"el-table-infinite-scroll": "^1.0.10",
|
||||
|
|
|
@ -8,18 +8,20 @@
|
|||
<el-row id="header-top" type="flex" justify="space-between" align="middle">
|
||||
<el-col :span="12">
|
||||
<img :src="'/display/file/logo'" class="logo" alt="">
|
||||
<ms-top-menus/>
|
||||
<ms-top-menus :color="color"/>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" class="align-right">
|
||||
<!-- float right -->
|
||||
<ms-user/>
|
||||
<ms-language-switch/>
|
||||
<ms-header-org-ws/>
|
||||
<ms-language-switch :color="color"/>
|
||||
<ms-header-org-ws :color="color"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<ms-view/>
|
||||
|
||||
<theme/>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
|
@ -29,11 +31,14 @@ import MsView from "./components/common/router/View";
|
|||
import MsUser from "./components/common/head/HeaderUser";
|
||||
import MsHeaderOrgWs from "./components/common/head/HeaderOrgWs";
|
||||
import MsLanguageSwitch from "./components/common/head/LanguageSwitch";
|
||||
import {saveLocalStorage} from "@/common/js/utils";
|
||||
import {hasLicense, saveLocalStorage, setColor, setOriginColor} from "@/common/js/utils";
|
||||
import {registerRequestHeaders} from "@/common/js/ajax";
|
||||
import {ORIGIN_COLOR} from "@/common/js/constants";
|
||||
|
||||
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
|
||||
const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {};
|
||||
const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {};
|
||||
const theme = requireComponent.keys().length > 0 ? requireComponent("./display/Theme.vue") : {};
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
|
@ -44,9 +49,21 @@ export default {
|
|||
auth: false,
|
||||
header: {},
|
||||
logoId: '_blank',
|
||||
color: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
registerRequestHeaders();
|
||||
if (!hasLicense()) {
|
||||
setOriginColor()
|
||||
this.color = ORIGIN_COLOR;
|
||||
} else {
|
||||
//
|
||||
this.$get('/system/theme', res => {
|
||||
this.color = res.data ? res.data : ORIGIN_COLOR;
|
||||
setColor(this.color, this.color, this.color, this.color);
|
||||
})
|
||||
}
|
||||
if (localStorage.getItem("store")) {
|
||||
this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(localStorage.getItem("store"))))
|
||||
}
|
||||
|
@ -81,7 +98,8 @@ export default {
|
|||
MsView,
|
||||
MsTopMenus,
|
||||
MsHeaderOrgWs,
|
||||
"LicenseMessage": header.default
|
||||
"LicenseMessage": header.default,
|
||||
"Theme": theme.default
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -91,7 +109,7 @@ export default {
|
|||
#header-top {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
background-color: rgb(44, 42, 72);
|
||||
background-color: var(--color);
|
||||
color: rgb(245, 245, 245);
|
||||
font-size: 14px;
|
||||
height: 40px;
|
||||
|
|
|
@ -100,6 +100,19 @@
|
|||
active() {
|
||||
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() {
|
||||
this.init();
|
||||
if (this.reportId) {
|
||||
|
@ -113,7 +126,7 @@
|
|||
if (!this.content) {
|
||||
this.content = {scenarios: []};
|
||||
}
|
||||
this.$emit('refresh');
|
||||
this.formatResult(this.content);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
<!--测试计划-->
|
||||
<el-drawer :visible.sync="planVisible" :destroy-on-close="true" direction="ltr" :withHeader="false"
|
||||
:title="$t('test_track.plan_view.test_result')" :modal="false" size="90%">
|
||||
<ms-test-plan-list @addTestPlan="addTestPlan" @cancel="cancel"/>
|
||||
<ms-test-plan-list @addTestPlan="addTestPlan(arguments)" @cancel="cancel" ref="testPlanList" :row="selectRows"/>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</el-card>
|
||||
|
@ -276,7 +276,9 @@
|
|||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
|
||||
}
|
||||
},
|
||||
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
|
||||
|
||||
],
|
||||
isSelectAllDate: false,
|
||||
selectRows: new Set(),
|
||||
|
@ -318,7 +320,7 @@
|
|||
],
|
||||
principal: [],
|
||||
environmentId: [],
|
||||
projectEnv: []
|
||||
projectEnv: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -496,17 +498,31 @@
|
|||
cancel() {
|
||||
this.planVisible = false;
|
||||
},
|
||||
addTestPlan(plans) {
|
||||
let obj = {planIds: plans, scenarioIds: this.selection};
|
||||
addTestPlan(params) {
|
||||
let obj = {planIds: params[0], scenarioIds: this.selection};
|
||||
|
||||
obj.projectId = getCurrentProjectID();
|
||||
obj.selectAllDate = this.isSelectAllDate;
|
||||
obj.unSelectIds = this.unSelection;
|
||||
obj = Object.assign(obj, this.condition);
|
||||
// obj.projectId = getCurrentProjectID();
|
||||
// obj.selectAllDate = this.isSelectAllDate;
|
||||
// obj.unSelectIds = this.unSelection;
|
||||
// obj = Object.assign(obj, this.condition);
|
||||
|
||||
// todo 选取全部数据
|
||||
if (this.isSelectAllDate) {
|
||||
this.$warning("暂不支持批量添加所有场景到测试计划!");
|
||||
}
|
||||
|
||||
this.planVisible = false;
|
||||
|
||||
let map = new Map();
|
||||
this.selectRows.forEach(row => {
|
||||
map.set(row.id, row.projectIds);
|
||||
})
|
||||
obj.mapping = strMapToObj(map);
|
||||
obj.envMap = strMapToObj(params[1]);
|
||||
|
||||
this.$post("/api/automation/scenario/plan", obj, response => {
|
||||
this.$success(this.$t("commons.save_success"));
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
getReport() {
|
||||
|
@ -582,6 +598,29 @@
|
|||
this.search();
|
||||
})
|
||||
},
|
||||
handleDeleteBatch(row) {
|
||||
if (this.trashEnable) {
|
||||
let ids = Array.from(this.selectRows).map(row => row.id);
|
||||
this.$post('/api/automation/deleteBatch/', ids, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm') + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let ids = Array.from(this.selectRows).map(row => row.id);
|
||||
this.$post('/api/automation/removeToGc/', ids, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
execute(row) {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run";
|
||||
|
|
|
@ -184,7 +184,7 @@
|
|||
<!-- 调试结果 -->
|
||||
<el-drawer v-if="type!=='detail'" :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr"
|
||||
: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>
|
||||
|
||||
<!--场景公共参数-->
|
||||
|
@ -224,6 +224,7 @@
|
|||
import MsComponentConfig from "./component/ComponentConfig";
|
||||
import {handleCtrlSEvent} from "../../../../../common/js/utils";
|
||||
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
|
||||
|
||||
let jsonPath = require('jsonpath');
|
||||
export default {
|
||||
name: "EditApiScenario",
|
||||
|
@ -290,7 +291,8 @@
|
|||
response: {},
|
||||
projectIds: new Set,
|
||||
projectEnvMap: new Map,
|
||||
projectList: []
|
||||
projectList: [],
|
||||
debugResult: new Map,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -547,21 +549,32 @@
|
|||
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
|
||||
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() {
|
||||
for (let i in this.scenarioDefinition) {
|
||||
// 排序
|
||||
this.scenarioDefinition[i].index = Number(i) + 1;
|
||||
// 设置循环控制
|
||||
if (this.scenarioDefinition[i].type === ELEMENT_TYPE.LoopController && this.scenarioDefinition[i].hashTree
|
||||
&& this.scenarioDefinition[i].hashTree.length > 1) {
|
||||
this.scenarioDefinition[i].countController.proceed = true;
|
||||
}
|
||||
// 设置项目ID
|
||||
if (!this.scenarioDefinition[i].projectId) {
|
||||
this.scenarioDefinition.projectId = getCurrentProjectID();
|
||||
this.scenarioDefinition[i].projectId = getCurrentProjectID();
|
||||
}
|
||||
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
|
||||
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) {
|
||||
|
@ -575,6 +588,7 @@
|
|||
this.customizeRequest = {};
|
||||
this.sort();
|
||||
this.reload();
|
||||
this.initProjectIds();
|
||||
},
|
||||
addScenario(arr) {
|
||||
if (arr && arr.length > 0) {
|
||||
|
@ -1024,6 +1038,11 @@
|
|||
arr.forEach(a => this.projectIds.add(a));
|
||||
})
|
||||
})
|
||||
},
|
||||
detailRefresh(result) {
|
||||
// 把执行结果分发给各个请求
|
||||
this.debugResult = result;
|
||||
this.sort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,17 @@ export default {
|
|||
}
|
||||
})
|
||||
} else {
|
||||
// 如果有环境,检查环境
|
||||
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) {
|
||||
|
|
|
@ -7,7 +7,7 @@ export const ELEMENTS = new Map([
|
|||
['TCPSampler', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['OT_IMPORT', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['IfController', ["IfController", "scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['LoopController', ["IfController", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['LoopController', ["IfController", "scenario", "HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "OT_IMPORT", "ConstantTimer", "JSR223Processor", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract", "CustomizeReq"]],
|
||||
['ConstantTimer', []],
|
||||
['JSR223Processor', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
|
||||
['JSR223PreProcessor', []],
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.import_mode')">
|
||||
<el-form-item v-if="!isHar" :label="$t('commons.import_mode')">
|
||||
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
|
@ -118,6 +118,13 @@
|
|||
tip: this.$t('api_test.api_import.jmeter_tip'),
|
||||
exportTip: this.$t('api_test.api_import.jmeter_export_tip'),
|
||||
suffixes: new Set(['jmx'])
|
||||
},
|
||||
{
|
||||
name: 'Har',
|
||||
value: 'Har',
|
||||
tip: this.$t('api_test.api_import.har_tip'),
|
||||
exportTip: this.$t('api_test.api_import.har_export_tip'),
|
||||
suffixes: new Set(['har'])
|
||||
}
|
||||
],
|
||||
selectedPlatform: {},
|
||||
|
@ -150,6 +157,11 @@
|
|||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isHar() {
|
||||
return this.selectedPlatformValue === 'Har';
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
scheduleEdit() {
|
||||
if (!this.formData.swaggerUrl) {
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
<template v-slot:header>
|
||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition"
|
||||
@search="initTableData" :showCreate="false"
|
||||
:title="$t('test_track.plan.test_plan')"/>
|
||||
:title="$t('test_track.plan.test_plan')">
|
||||
</ms-table-header>
|
||||
</template>
|
||||
|
||||
<env-popover :env-map="projectEnvMap" :project-ids="projectIds" @setProjectEnvMap="setProjectEnvMap"
|
||||
:project-list="projectList" ref="envPopover" class="env-popover" style="float: right; margin-top: 4px;"/>
|
||||
<el-table
|
||||
border
|
||||
class="adjust-table"
|
||||
|
@ -142,7 +144,7 @@ import MsTableOperatorButton from "../../../../common/components/MsTableOperator
|
|||
import MsTableOperator from "../../../../common/components/MsTableOperator";
|
||||
import PlanStatusTableItem from "../../../../track/common/tableItems/plan/PlanStatusTableItem";
|
||||
import PlanStageTableItem from "../../../../track/common/tableItems/plan/PlanStageTableItem";
|
||||
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
|
||||
import {checkoutTestManagerOrTestUser, strMapToObj} from "@/common/js/utils";
|
||||
import TestReportTemplateList from "../../../../track/plan/view/comonents/TestReportTemplateList";
|
||||
import TestCaseReportView from "../../../../track/plan/view/comonents/report/TestCaseReportView";
|
||||
import MsDeleteConfirm from "../../../../common/components/MsDeleteConfirm";
|
||||
|
@ -150,6 +152,7 @@ import {TEST_PLAN_CONFIGS} from "../../../../common/components/search/search-com
|
|||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||
import {getCurrentProjectID} from "../../../../../../common/js/utils";
|
||||
import {_filter, _sort} from "@/common/js/tableUtils";
|
||||
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -160,7 +163,10 @@ export default {
|
|||
TestReportTemplateList,
|
||||
PlanStageTableItem,
|
||||
PlanStatusTableItem,
|
||||
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
|
||||
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination, EnvPopover
|
||||
},
|
||||
props: {
|
||||
row: Set
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -187,6 +193,9 @@ export default {
|
|||
{text: this.$t('test_track.plan.system_test'), value: 'system'},
|
||||
{text: this.$t('test_track.plan.regression_test'), value: 'regression'},
|
||||
],
|
||||
projectEnvMap: new Map(),
|
||||
projectList: [],
|
||||
projectIds: new Set()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -200,22 +209,37 @@ export default {
|
|||
this.projectId = this.$route.params.projectId;
|
||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
||||
this.initTableData();
|
||||
this.setScenarioSelectRows(this.row);
|
||||
this.getWsProjects();
|
||||
},
|
||||
methods: {
|
||||
confirm() {
|
||||
if (this.selection.length==0) {
|
||||
this.$warning(this.$t("api_test.definition.request.test_plan_select"));
|
||||
}else{
|
||||
this.$emit('addTestPlan', this.selection);
|
||||
const sign = this.checkEnv();
|
||||
if (!sign) {
|
||||
return false;
|
||||
}
|
||||
this.$emit('addTestPlan', this.selection, this.projectEnvMap);
|
||||
}
|
||||
},
|
||||
cancel(){
|
||||
this.$emit('cancel');
|
||||
},
|
||||
setProjectEnvMap(projectEnvMap) {
|
||||
this.projectEnvMap = projectEnvMap;
|
||||
},
|
||||
select(selection) {
|
||||
this.selection = selection.map(s => s.id);
|
||||
this.$emit('selection', selection);
|
||||
},
|
||||
setScenarioSelectRows(rows) {
|
||||
this.projectIds.clear();
|
||||
rows.forEach(row => {
|
||||
row.projectIds.forEach(id => this.projectIds.add(id));
|
||||
})
|
||||
},
|
||||
initTableData() {
|
||||
if (this.planId) {
|
||||
this.condition.planId = this.planId;
|
||||
|
@ -284,6 +308,14 @@ export default {
|
|||
this.$refs.testCaseReportView.open(planId, reportId);
|
||||
}
|
||||
},
|
||||
checkEnv() {
|
||||
return this.$refs.envPopover.checkEnv();
|
||||
},
|
||||
getWsProjects() {
|
||||
this.$get("/project/listAll", res => {
|
||||
this.projectList = res.data;
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,24 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-loading="loading">
|
||||
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
|
||||
<el-col>
|
||||
<el-input :disabled="isReadOnly" v-model="jsonPath.expression" maxlength="200" size="small" show-word-limit
|
||||
:placeholder="$t('api_test.request.extract.json_path_expression')"/>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-select v-model="jsonPath.option" class="ms-col-type" size="small" style="width:40%;margin-right: 10px" @change="reload">
|
||||
<el-option :label="$t('api_test.request.assertions.contains')" value="CONTAINS"/>
|
||||
<el-option :label="$t('api_test.request.assertions.not_contains')" value="NOT_CONTAINS"/>
|
||||
<el-option :label="$t('api_test.request.assertions.equals')" value="EQUALS"/>
|
||||
<el-option :label="$t('commons.adv_search.operators.not_equals')" value="NOT_EQUALS"/>
|
||||
<el-option label="正则匹配" value="REGEX"/>
|
||||
</el-select>
|
||||
<el-input :disabled="isReadOnly" v-model="jsonPath.expect" size="small" show-word-limit
|
||||
:placeholder="$t('api_test.request.assertions.expect')"/>
|
||||
:placeholder="$t('api_test.request.assertions.expect')" style="width: 50%"/>
|
||||
<el-tooltip placement="top" v-if="jsonPath.option === 'REGEX'">
|
||||
<div slot="content">特殊字符"$ ( ) * + . [ ] \ ^ { } |"需转义为"\ "+"特殊字符",如"\$"</div>
|
||||
<i class="el-icon-question" style="cursor: pointer"/>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col class="assertion-btn">
|
||||
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
||||
|
@ -44,8 +55,15 @@
|
|||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (!this.jsonPath.option) {
|
||||
this.jsonPath.option = "REGEX";
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -71,6 +89,12 @@
|
|||
jsonPath.description = jsonPath.expression + " expect: " + (jsonPath.expect ? jsonPath.expect : '');
|
||||
return jsonPath;
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
setJSONPathDescription() {
|
||||
this.jsonPath.description = this.jsonPath.expression + " expect: " + (this.jsonPath.expect ? this.jsonPath.expect : '');
|
||||
}
|
||||
|
|
|
@ -146,6 +146,11 @@ export default {
|
|||
jsonItem.expression = data.path;
|
||||
jsonItem.expect = data.value;
|
||||
jsonItem.setJSONPathDescription();
|
||||
let expect = jsonItem.expect.replaceAll('\\', "\\\\").replaceAll('(', "\\(").replaceAll(')', "\\)")
|
||||
.replaceAll('+', "\\+").replaceAll('.', "\\.").replaceAll('[', "\\[").replaceAll(']', "\\]")
|
||||
.replaceAll('?', "\\?").replaceAll('/', "\\/").replaceAll('*', "\\*")
|
||||
.replaceAll('^', "\\^").replaceAll('{', "\\{").replaceAll('}', "\\}").replaceAll('$', "\\$");
|
||||
jsonItem.expect = expect;
|
||||
this.assertions.jsonPath.push(jsonItem);
|
||||
},
|
||||
clearJson() {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!isScenarioModel" :label="$t('commons.import_mode')">
|
||||
<el-form-item v-if="!isScenarioModel&&!isHar" :label="$t('commons.import_mode')">
|
||||
<el-select size="small" v-model="formData.modeId" class="project-select" clearable>
|
||||
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
|
||||
</el-select>
|
||||
|
@ -146,7 +146,14 @@ export default {
|
|||
tip: this.$t('api_test.api_import.swagger_tip'),
|
||||
exportTip: this.$t('api_test.api_import.swagger_export_tip'),
|
||||
suffixes: new Set(['json'])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Har',
|
||||
value: 'Har',
|
||||
tip: this.$t('api_test.api_import.har_tip'),
|
||||
exportTip: this.$t('api_test.api_import.har_export_tip'),
|
||||
suffixes: new Set(['har'])
|
||||
},
|
||||
],
|
||||
selectedPlatform: {},
|
||||
selectedPlatformValue: 'Metersphere',
|
||||
|
@ -182,6 +189,9 @@ export default {
|
|||
isSwagger2() {
|
||||
return this.selectedPlatformValue === 'Swagger2';
|
||||
},
|
||||
isHar() {
|
||||
return this.selectedPlatformValue === 'Har';
|
||||
},
|
||||
isScenarioModel() {
|
||||
return this.model === 'scenario';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<el-card class="card-content" v-if="isShow">
|
||||
|
||||
<el-button-group v-if="isShowChangeButton">
|
||||
|
||||
<el-tooltip class="item" effect="dark" :content="$t('api_test.definition.api_title')" placement="left">
|
||||
<el-button plain style="width: 44px;height: 32px;padding: 5px 8px;" :class="{active: showApiList}" @click="changeTab('api')">API</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="item" effect="dark" :content="$t('api_test.definition.case_title')" placement="top">
|
||||
<el-button plain style="width: 44px;height: 32px;padding: 1px;" :class="{active: showTestCaseList}" @click="changeTab('testCase')">CASE</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="item" effect="dark" :content="$t('api_test.definition.doc_title')" placement="right">
|
||||
<el-button plain style="width: 44px;height: 32px;padding: 1px;" :class="{active: showDocList}" @click="changeTab('doc')">
|
||||
{{ $t('api_test.definition.doc_title') }}</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
</el-button-group>
|
||||
|
||||
<template v-slot:header>
|
||||
<slot name="header"></slot>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ApiListContainerWithDoc",
|
||||
data() {
|
||||
return {
|
||||
isShow: true,
|
||||
showApiList:false,
|
||||
showTestCaseList:false,
|
||||
showDocList:true,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
activeDom: String,
|
||||
isShowChangeButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.refreshButtonActiveClass(this.activeDom);
|
||||
},
|
||||
methods: {
|
||||
changeTab(tabType){
|
||||
this.refreshButtonActiveClass(tabType);
|
||||
this.$emit("activeDomChange",tabType);
|
||||
},
|
||||
refreshButtonActiveClass(tabType){
|
||||
if(tabType === "testCase"){
|
||||
this.showApiList = false;
|
||||
this.showTestCaseList = true;
|
||||
this.showDocList = false;
|
||||
}else if(tabType === "doc"){
|
||||
this.showApiList = false;
|
||||
this.showTestCaseList = false;
|
||||
this.showDocList = true;
|
||||
}else{
|
||||
this.showApiList = true;
|
||||
this.showTestCaseList = false;
|
||||
this.showDocList = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.active {
|
||||
border: solid 1px #6d317c;
|
||||
background-color: var(--color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.case-button {
|
||||
border-left: solid 1px var(--color);
|
||||
}
|
||||
|
||||
.item{
|
||||
border: solid 1px var(--color);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -151,7 +151,7 @@
|
|||
}
|
||||
this.reqMessages = this.$t('api_test.request.address') + ":\n" + this.response.url + "\n" +
|
||||
this.$t('api_test.scenario.headers') + ":\n" + this.response.headers + "\n" + "Cookies :\n" +
|
||||
this.response.cookies + "\n" + "Bpdy:" + "\n" + this.response.body;
|
||||
this.response.cookies + "\n" + "Body:" + "\n" + this.response.body;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
@ -147,7 +147,7 @@ export default {
|
|||
.count-number{
|
||||
font-family:'ArialMT', 'Arial', sans-serif;
|
||||
font-size:33px;
|
||||
color: #6C317C;
|
||||
color: var(--count_number);
|
||||
}
|
||||
|
||||
.main-number-show {
|
||||
|
@ -155,7 +155,7 @@ export default {
|
|||
height: 100px;
|
||||
border-style: solid;
|
||||
border-width: 7px;
|
||||
border-color: #CDB9D2;
|
||||
border-color: var(--count_number_shallow);
|
||||
border-radius:50%;
|
||||
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ export default {
|
|||
.count-number{
|
||||
font-family:'ArialMT', 'Arial', sans-serif;
|
||||
font-size:33px;
|
||||
color: #6C317C;
|
||||
color: var(--count_number);
|
||||
margin:20px auto;
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ export default {
|
|||
height: 100px;
|
||||
border-style: solid;
|
||||
border-width: 7px;
|
||||
border-color: #CDB9D2;
|
||||
border-color: var(--count_number_shallow);
|
||||
border-radius:50%;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ export default {
|
|||
.count-number{
|
||||
font-family:'ArialMT', 'Arial', sans-serif;
|
||||
font-size:33px;
|
||||
color: #6C317C;
|
||||
color: var(--count_number);
|
||||
}
|
||||
|
||||
.main-number-show {
|
||||
|
@ -132,7 +132,7 @@ export default {
|
|||
height: 100px;
|
||||
border-style: solid;
|
||||
border-width: 7px;
|
||||
border-color: #CDB9D2;
|
||||
border-color: var(--count_number_shallow);
|
||||
border-radius:50%;
|
||||
|
||||
}
|
||||
|
|
|
@ -173,7 +173,7 @@ export default {
|
|||
.count-number{
|
||||
font-family:'ArialMT', 'Arial', sans-serif;
|
||||
font-size:33px;
|
||||
color: #6C317C;
|
||||
color: var(--count_number);
|
||||
}
|
||||
|
||||
.main-number-show {
|
||||
|
@ -181,7 +181,7 @@ export default {
|
|||
height: 100px;
|
||||
border-style: solid;
|
||||
border-width: 7px;
|
||||
border-color: #CDB9D2;
|
||||
border-color: var(--count_number_shallow);
|
||||
border-radius:50%;
|
||||
|
||||
}
|
||||
|
|
|
@ -103,16 +103,16 @@ export default {
|
|||
|
||||
.active {
|
||||
border: solid 1px #6d317c;
|
||||
background-color: #7C3985;
|
||||
background-color: var(--color);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.case-button {
|
||||
border-left: solid 1px #6d317c;
|
||||
border-left: solid 1px var(--color);
|
||||
}
|
||||
|
||||
.item{
|
||||
border: solid 1px #6d317c;
|
||||
border: solid 1px var(--color);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-menu :unique-opened="true" mode="horizontal" router
|
||||
class="header-user-menu align-right"
|
||||
background-color="#2c2a48"
|
||||
:background-color="color"
|
||||
active-text-color="#fff"
|
||||
default-active="1"
|
||||
text-color="#fff">
|
||||
|
@ -80,7 +80,10 @@ export default {
|
|||
computed: {
|
||||
currentUser: () => {
|
||||
return getCurrentUser();
|
||||
}
|
||||
},
|
||||
},
|
||||
props: {
|
||||
color: String
|
||||
},
|
||||
watch: {
|
||||
searchOrg(val) {
|
||||
|
@ -189,7 +192,7 @@ export default {
|
|||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 1em;
|
||||
background-color: #595591;
|
||||
background-color: var(--color_shallow);
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
|
@ -207,12 +210,12 @@ export default {
|
|||
.search-input {
|
||||
padding: 0;
|
||||
margin-top: -4px;
|
||||
background-color: #595591;
|
||||
background-color: var(--color_shallow);
|
||||
}
|
||||
|
||||
.search-input >>> .el-input__inner {
|
||||
border-radius: 0;
|
||||
background-color: #2d2a49;
|
||||
background-color: var(--color);
|
||||
color: #d2ced8;
|
||||
border-color: #b4aebe;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<el-menu mode="horizontal" menu-trigger="click"
|
||||
background-color="#2c2a48"
|
||||
:background-color="color"
|
||||
class="header-top-menus"
|
||||
text-color="#F2F2F2"
|
||||
active-text-color="#fff"
|
||||
|
@ -18,7 +18,7 @@
|
|||
v-permission="['test_manager','test_user','test_viewer']">
|
||||
{{ $t('commons.performance') }}
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/reportForm" v-permission="['test_manager','test_user','test_viewer']" v-if="isReport">
|
||||
<el-menu-item index="/report" v-permission="['test_manager','test_user','test_viewer']" v-if="isReport">
|
||||
{{ $t('commons.report_statistics.title') }}
|
||||
</el-menu-item>
|
||||
|
||||
|
@ -40,6 +40,9 @@
|
|||
isReport: isReport
|
||||
}
|
||||
},
|
||||
props: {
|
||||
color: String
|
||||
},
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
if (to.matched.length > 0) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-menu :unique-opened="true" class="header-user-menu align-right"
|
||||
mode="horizontal"
|
||||
background-color="#2c2a48"
|
||||
:background-color="color"
|
||||
text-color="#fff"
|
||||
active-text-color="#fff"
|
||||
>
|
||||
|
@ -34,6 +34,9 @@
|
|||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
color: String
|
||||
},
|
||||
created() {
|
||||
let lang = this.currentUser().language;
|
||||
this.currentUserInfo = this.currentUser();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -22,7 +22,7 @@
|
|||
</el-form-item>
|
||||
<br>
|
||||
<el-form-item>
|
||||
<el-radio-group v-model="threadGroup.threadType">
|
||||
<el-radio-group v-model="threadGroup.threadType" :disabled="true">
|
||||
<el-radio label="DURATION">{{ $t('load_test.by_duration') }}</el-radio>
|
||||
<el-radio label="ITERATION">{{ $t('load_test.by_iteration') }}</el-radio>
|
||||
</el-radio-group>
|
||||
|
@ -348,7 +348,27 @@ export default {
|
|||
let threadInc2 = Math.ceil(tg.threadNumber / tg.step);
|
||||
let inc2count = tg.threadNumber - tg.step * threadInc1;
|
||||
for (let j = 0; j <= tg.duration; j++) {
|
||||
// x 轴
|
||||
let xAxis = handler.options.xAxis.data;
|
||||
if (xAxis.indexOf(j) < 0) {
|
||||
xAxis.push(j);
|
||||
}
|
||||
|
||||
if (tg.tgType === 'ThreadGroup') {
|
||||
seriesData.step = undefined;
|
||||
|
||||
if (j === 0) {
|
||||
seriesData.data.push([0, 0]);
|
||||
}
|
||||
if (j > tg.rampUpTime) {
|
||||
xAxis.push(tg.duration);
|
||||
|
||||
seriesData.data.push([j, tg.threadNumber]);
|
||||
seriesData.data.push([tg.duration, tg.threadNumber]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
seriesData.step = 'start';
|
||||
if (j > timePeriod) {
|
||||
timePeriod += timeInc;
|
||||
if (inc2count > 0) {
|
||||
|
@ -359,14 +379,14 @@ export default {
|
|||
}
|
||||
if (threadPeriod > tg.threadNumber) {
|
||||
threadPeriod = tg.threadNumber;
|
||||
// 预热结束
|
||||
xAxis.push(tg.duration);
|
||||
seriesData.data.push([tg.duration, threadPeriod]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// x 轴
|
||||
let xAxis = handler.options.xAxis.data;
|
||||
if (xAxis.indexOf(j) < 0) {
|
||||
xAxis.push(j);
|
||||
seriesData.data.push([j, threadPeriod]);
|
||||
}
|
||||
seriesData.data.push(threadPeriod);
|
||||
}
|
||||
handler.options.series.push(seriesData);
|
||||
}
|
||||
|
@ -445,6 +465,21 @@ export default {
|
|||
for (let i = 0; i <= handler.duration; i++) {
|
||||
// x 轴
|
||||
handler.options.xAxis.data.push(i);
|
||||
if (handler.tgType === 'ThreadGroup') {
|
||||
handler.options.series[0].step = undefined;
|
||||
|
||||
if (i === 0) {
|
||||
handler.options.series[0].data.push([0, 0]);
|
||||
}
|
||||
if (i > handler.rampUpTime) {
|
||||
handler.options.xAxis.data.push(handler.duration);
|
||||
|
||||
handler.options.series[0].data.push([i, handler.threadNumber]);
|
||||
handler.options.series[0].data.push([handler.duration, handler.threadNumber]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
handler.options.series[0].step = 'start';
|
||||
if (i > timePeriod) {
|
||||
timePeriod += timeInc;
|
||||
if (inc2count > 0) {
|
||||
|
@ -455,10 +490,14 @@ export default {
|
|||
}
|
||||
if (threadPeriod > handler.threadNumber) {
|
||||
threadPeriod = handler.threadNumber;
|
||||
handler.options.xAxis.data.push(handler.duration);
|
||||
handler.options.series[0].data.push([handler.duration, handler.threadNumber]);
|
||||
break;
|
||||
}
|
||||
handler.options.series[0].data.push(threadPeriod);
|
||||
handler.options.series[0].data.push([i, threadPeriod]);
|
||||
} else {
|
||||
handler.options.series[0].data.push(threadPeriod);
|
||||
handler.options.series[0].data.push([i, threadPeriod]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.calculateTotalChart();
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<el-tabs class="testplan-config" v-model="active" type="border-card" :stretch="true">
|
||||
<el-tab-pane :label="$t('load_test.basic_config')">
|
||||
<performance-basic-config :is-read-only="isReadOnly" :test="test" ref="basicConfig"
|
||||
@tgTypeChange="tgTypeChange"
|
||||
@fileChange="fileChange"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.pressure_config')">
|
||||
|
@ -347,6 +348,10 @@ export default {
|
|||
threadGroups.forEach(tg => {
|
||||
handler.calculateChart(tg);
|
||||
})
|
||||
},
|
||||
tgTypeChange(threadGroup) {
|
||||
let handler = this.$refs.pressureConfig;
|
||||
handler.calculateChart(threadGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,6 +117,36 @@
|
|||
</el-form>
|
||||
</el-col>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
|
@ -134,6 +164,18 @@ export default {
|
|||
domains: [],
|
||||
params: [],
|
||||
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: {
|
||||
|
@ -166,6 +208,7 @@ export default {
|
|||
this.statusCodeStr = this.statusCode.join(',');
|
||||
this.domains = data.domains || [];
|
||||
this.params = data.params || [];
|
||||
this.granularity = data.granularity;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -252,6 +295,7 @@ export default {
|
|||
statusCode: statusCode,
|
||||
params: this.params,
|
||||
domains: this.domains,
|
||||
granularity: this.granularity,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
@ -287,4 +331,8 @@ export default {
|
|||
align: center;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<el-table-column
|
||||
label="ThreadGroup">
|
||||
<template v-slot:default="{row}">
|
||||
<el-select v-model="row.tgType" :placeholder="$t('commons.please_select')" size="small">
|
||||
<el-select v-model="row.tgType" :placeholder="$t('commons.please_select')" size="small" @change="tgTypeChange(row)">
|
||||
<el-option v-for="tg in threadGroupForSelect" :key="tg.tagName" :label="tg.name"
|
||||
:value="tg.testclass"></el-option>
|
||||
</el-select>
|
||||
|
@ -70,7 +70,7 @@
|
|||
<el-row type="flex" justify="start" align="middle">
|
||||
<el-upload
|
||||
style="padding-right: 10px;"
|
||||
accept=".jar,.csv"
|
||||
accept=".jar,.csv,.json,.pdf,.jpg,.png,.jpeg,.doc,.docx,.xlsx"
|
||||
action=""
|
||||
:limit="fileNumLimit"
|
||||
multiple
|
||||
|
@ -276,7 +276,10 @@ export default {
|
|||
let self = this;
|
||||
let file = uploadResources.file;
|
||||
self.uploadList.push(file);
|
||||
|
||||
let type = file.name.substring(file.name.lastIndexOf(".") + 1);
|
||||
if (type.toLowerCase() !== 'jmx') {
|
||||
return;
|
||||
}
|
||||
let jmxReader = new FileReader();
|
||||
jmxReader.onload = (event) => {
|
||||
self.threadGroups = self.threadGroups.concat(findThreadGroup(event.target.result, file.name));
|
||||
|
@ -374,6 +377,9 @@ export default {
|
|||
threadGroupDisable(row) {
|
||||
return this.threadGroups.filter(tg => tg.enabled == 'true').length === 1 && row.enabled == 'true';
|
||||
},
|
||||
tgTypeChange(row) {
|
||||
this.$emit("tgTypeChange", row);
|
||||
},
|
||||
handleExceed() {
|
||||
this.$error(this.$t('load_test.file_size_limit'));
|
||||
},
|
||||
|
|
|
@ -42,10 +42,12 @@
|
|||
<br>
|
||||
<div v-if="threadGroup.threadType === 'DURATION'">
|
||||
<el-form-item :label="$t('load_test.duration')">
|
||||
<!-- 最多两天的测试时长 -->
|
||||
<el-input-number
|
||||
:disabled="isReadOnly"
|
||||
v-model="threadGroup.duration"
|
||||
:min="1"
|
||||
:max="172800"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
|
@ -349,7 +351,6 @@ export default {
|
|||
name: handler.threadGroups[i].attributes.testname,
|
||||
data: [],
|
||||
type: 'line',
|
||||
step: 'start',
|
||||
smooth: false,
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
|
@ -390,7 +391,26 @@ export default {
|
|||
let threadInc2 = Math.ceil(tg.threadNumber / tg.step);
|
||||
let inc2count = tg.threadNumber - tg.step * threadInc1;
|
||||
for (let j = 0; j <= tg.duration; j++) {
|
||||
// x 轴
|
||||
let xAxis = handler.options.xAxis.data;
|
||||
if (xAxis.indexOf(j) < 0) {
|
||||
xAxis.push(j);
|
||||
}
|
||||
if (tg.tgType === 'ThreadGroup') {
|
||||
seriesData.step = undefined;
|
||||
|
||||
if (j === 0) {
|
||||
seriesData.data.push([0, 0]);
|
||||
}
|
||||
if (j > tg.rampUpTime) {
|
||||
xAxis.push(tg.duration);
|
||||
|
||||
seriesData.data.push([j, tg.threadNumber]);
|
||||
seriesData.data.push([tg.duration, tg.threadNumber]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
seriesData.step = 'start';
|
||||
if (j > timePeriod) {
|
||||
timePeriod += timeInc;
|
||||
if (inc2count > 0) {
|
||||
|
@ -401,14 +421,14 @@ export default {
|
|||
}
|
||||
if (threadPeriod > tg.threadNumber) {
|
||||
threadPeriod = tg.threadNumber;
|
||||
// 预热结束
|
||||
xAxis.push(tg.duration);
|
||||
seriesData.data.push([tg.duration, threadPeriod]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// x 轴
|
||||
let xAxis = handler.options.xAxis.data;
|
||||
if (xAxis.indexOf(j) < 0) {
|
||||
xAxis.push(j);
|
||||
seriesData.data.push([j, threadPeriod]);
|
||||
}
|
||||
seriesData.data.push(threadPeriod);
|
||||
}
|
||||
handler.options.series.push(seriesData);
|
||||
}
|
||||
|
@ -492,6 +512,22 @@ export default {
|
|||
for (let i = 0; i <= handler.duration; i++) {
|
||||
// x 轴
|
||||
handler.options.xAxis.data.push(i);
|
||||
|
||||
if (handler.tgType === 'ThreadGroup') {
|
||||
handler.options.series[0].step = undefined;
|
||||
|
||||
if (i === 0) {
|
||||
handler.options.series[0].data.push([0, 0]);
|
||||
}
|
||||
if (i > handler.rampUpTime) {
|
||||
handler.options.xAxis.data.push(handler.duration);
|
||||
|
||||
handler.options.series[0].data.push([i, handler.threadNumber]);
|
||||
handler.options.series[0].data.push([handler.duration, handler.threadNumber]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
handler.options.series[0].step = 'start';
|
||||
if (i > timePeriod) {
|
||||
timePeriod += timeInc;
|
||||
if (inc2count > 0) {
|
||||
|
@ -502,10 +538,14 @@ export default {
|
|||
}
|
||||
if (threadPeriod > handler.threadNumber) {
|
||||
threadPeriod = handler.threadNumber;
|
||||
handler.options.xAxis.data.push(handler.duration);
|
||||
handler.options.series[0].data.push([handler.duration, handler.threadNumber]);
|
||||
break;
|
||||
}
|
||||
handler.options.series[0].data.push(threadPeriod);
|
||||
handler.options.series[0].data.push([i, threadPeriod]);
|
||||
} else {
|
||||
handler.options.series[0].data.push(threadPeriod);
|
||||
handler.options.series[0].data.push([i, threadPeriod]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.calculateTotalChart();
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 360d7214d15951ae11b3973add795305a5c3d035
|
||||
Subproject commit 17404980aab725889843ce8c65f5f5c00113ae21
|
|
@ -14,6 +14,12 @@ body {
|
|||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
/*--color: #2c2a48;*/
|
||||
/*--color_shallow: #595591;*/
|
||||
--color: '';
|
||||
--color_shallow: '';
|
||||
--count_number: '';
|
||||
--count_number_shallow: '';
|
||||
}
|
||||
|
||||
/* 解决 document.body.clientHeight 为0 */
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
.header-top-menus.el-menu--horizontal > li {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
|
@ -30,9 +29,9 @@
|
|||
}
|
||||
|
||||
.header-top-menus.el-menu--horizontal > li.is-active {
|
||||
background: #595591 !important;
|
||||
background: var(--color_shallow) !important;
|
||||
}
|
||||
|
||||
.el-menu.el-menu--horizontal {
|
||||
border-bottom: none;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
import {Message, MessageBox} from 'element-ui';
|
||||
import axios from "axios";
|
||||
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 {
|
||||
install(Vue) {
|
||||
|
|
|
@ -168,3 +168,8 @@ export const JMETER_FUNC = [
|
|||
{type: "String", name: "${__escapeHtml}", description: "Encode strings using HTML encoding"},
|
||||
{type: "String", name: "${__TestPlanName}", description: "Return name of current test plan"},
|
||||
]
|
||||
|
||||
export const ORIGIN_COLOR = '#2c2a48';
|
||||
export const ORIGIN_COLOR_SHALLOW = '#595591';
|
||||
export const COUNT_NUMBER = '#6C317C';
|
||||
export const COUNT_NUMBER_SHALLOW = '#CDB9D2';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
LicenseKey,
|
||||
COUNT_NUMBER, COUNT_NUMBER_SHALLOW,
|
||||
LicenseKey, ORIGIN_COLOR, ORIGIN_COLOR_SHALLOW,
|
||||
PROJECT_ID,
|
||||
REFRESH_SESSION_USER_URL,
|
||||
ROLE_ADMIN,
|
||||
|
@ -348,3 +349,18 @@ export function objToStrMap(obj) {
|
|||
}
|
||||
return strMap;
|
||||
}
|
||||
|
||||
export function getColor() {
|
||||
return localStorage.getItem('color');
|
||||
}
|
||||
|
||||
export function setColor(a, b, c, d) {
|
||||
document.body.style.setProperty('--color', a);
|
||||
document.body.style.setProperty('--color_shallow', b);
|
||||
document.body.style.setProperty('--count_number', c);
|
||||
document.body.style.setProperty('--count_number_shallow', d);
|
||||
}
|
||||
|
||||
export function setOriginColor() {
|
||||
setColor(ORIGIN_COLOR, ORIGIN_COLOR_SHALLOW, COUNT_NUMBER, COUNT_NUMBER_SHALLOW);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue