feat(接口定义): 断言-文档结构校验基本功能完成
This commit is contained in:
parent
3286ba3b67
commit
316394076b
|
@ -457,6 +457,12 @@
|
|||
<artifactId>generex</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sf.json-lib</groupId>
|
||||
<artifactId>json-lib</artifactId>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -8,6 +8,8 @@ import io.metersphere.api.dto.automation.ApiScenarioRequest;
|
|||
import io.metersphere.api.dto.automation.ReferenceDTO;
|
||||
import io.metersphere.api.dto.definition.*;
|
||||
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.DocumentElement;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult;
|
||||
import io.metersphere.api.dto.swaggerurl.SwaggerUrlRequest;
|
||||
import io.metersphere.api.service.ApiDefinitionService;
|
||||
|
@ -21,6 +23,7 @@ import io.metersphere.commons.constants.NoticeConstants;
|
|||
import io.metersphere.commons.constants.OperLogConstants;
|
||||
import io.metersphere.commons.constants.PermissionConstants;
|
||||
import io.metersphere.commons.json.JSONSchemaGenerator;
|
||||
import io.metersphere.commons.json.JSONToDocumentUtils;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.controller.request.ResetOrderRequest;
|
||||
|
@ -61,6 +64,7 @@ public class ApiDefinitionController {
|
|||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
return PageUtils.setPageInfo(page, apiDefinitionService.list(request));
|
||||
}
|
||||
|
||||
@PostMapping("/week/list/{goPage}/{pageSize}")
|
||||
@RequiresPermissions("PROJECT_API_DEFINITION:READ")
|
||||
public Pager<List<ApiDefinitionResult>> weekList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {
|
||||
|
@ -319,7 +323,7 @@ public class ApiDefinitionController {
|
|||
}
|
||||
|
||||
@PostMapping("/relationship/relate/{goPage}/{pageSize}")
|
||||
public Pager< List<ApiDefinitionResult>> getRelationshipRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {
|
||||
public Pager<List<ApiDefinitionResult>> getRelationshipRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {
|
||||
return apiDefinitionService.getRelationshipRelateList(request, goPage, pageSize);
|
||||
}
|
||||
|
||||
|
@ -327,4 +331,15 @@ public class ApiDefinitionController {
|
|||
public List<String> getFollows(@PathVariable String definitionId) {
|
||||
return apiDefinitionService.getFollows(definitionId);
|
||||
}
|
||||
|
||||
@GetMapping("/getDocument/{id}/{type}")
|
||||
public List<DocumentElement> getDocument(@PathVariable String id,@PathVariable String type) {
|
||||
return apiDefinitionService.getDocument(id,type);
|
||||
}
|
||||
|
||||
@PostMapping("/jsonGenerator")
|
||||
public List<DocumentElement> jsonGenerator(@RequestBody Body body) {
|
||||
return JSONToDocumentUtils.getDocument(body.getRaw(),body.getType());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package io.metersphere.api.dto.definition.request.assertions;
|
|||
|
||||
import com.alibaba.fastjson.annotation.JSONType;
|
||||
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.MsAssertionDocument;
|
||||
import io.metersphere.api.service.ApiDefinitionService;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.plugin.core.MsParameter;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import lombok.Data;
|
||||
|
@ -27,6 +30,7 @@ public class MsAssertions extends MsTestElement {
|
|||
private List<MsAssertionXPath2> xpath2;
|
||||
private MsAssertionDuration duration;
|
||||
private String type = "Assertions";
|
||||
private MsAssertionDocument document;
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, MsParameter msParameter) {
|
||||
|
@ -39,6 +43,27 @@ public class MsAssertions extends MsTestElement {
|
|||
}
|
||||
|
||||
private void addAssertions(HashTree hashTree) {
|
||||
// 增加JSON文档结构校验
|
||||
if (this.getDocument() != null && this.getDocument().getType().equals("JSON")) {
|
||||
if (StringUtils.isNotEmpty(this.getDocument().getData().getJsonFollowAPI())) {
|
||||
ApiDefinitionService apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class);
|
||||
this.getDocument().getData().setJson(apiDefinitionService.getDocument(this.getDocument().getData().getJsonFollowAPI(), "JSON"));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(this.getDocument().getData().getJson())) {
|
||||
this.getDocument().getData().parseJson(hashTree, this.getName());
|
||||
}
|
||||
}
|
||||
// 增加XML文档结构校验
|
||||
if (this.getDocument() != null && this.getDocument().getType().equals("XML") && CollectionUtils.isNotEmpty(this.getDocument().getData().getXml())) {
|
||||
if (StringUtils.isNotEmpty(this.getDocument().getData().getXmlFollowAPI())) {
|
||||
ApiDefinitionService apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class);
|
||||
this.getDocument().getData().setXml(apiDefinitionService.getDocument(this.getDocument().getData().getXmlFollowAPI(), "XML"));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(this.getDocument().getData().getXml())) {
|
||||
this.getDocument().getData().parseXml(hashTree, this.getName());
|
||||
}
|
||||
}
|
||||
|
||||
if (CollectionUtils.isNotEmpty(this.getRegex())) {
|
||||
this.getRegex().stream().filter(MsAssertionRegex::isValid).forEach(assertion ->
|
||||
hashTree.add(responseAssertion(assertion))
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.api.dto.definition.request.assertions.document;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Condition {
|
||||
private String key;
|
||||
private Object value;
|
||||
|
||||
public Condition() {
|
||||
|
||||
}
|
||||
|
||||
public Condition(String key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package io.metersphere.api.dto.definition.request.assertions.document;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.assertions.JSONPathAssertion;
|
||||
import org.apache.jmeter.assertions.XMLAssertion;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class Document {
|
||||
private String jsonFollowAPI;
|
||||
private String xmlFollowAPI;
|
||||
private List<DocumentElement> json;
|
||||
private List<DocumentElement> xml;
|
||||
private String assertionName;
|
||||
|
||||
public void parseJson(HashTree hashTree, String name) {
|
||||
this.assertionName = name;
|
||||
// 提取出合并的权限
|
||||
Map<String, ElementCondition> conditionMap = new HashMap<>();
|
||||
conditions(this.getJson(), conditionMap);
|
||||
// 数据处理生成断言条件
|
||||
List<JSONPathAssertion> list = new LinkedList<>();
|
||||
formatting(this.getJson(), list, null, conditionMap);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
hashTree.add(list);
|
||||
}
|
||||
}
|
||||
|
||||
public void parseXml(HashTree hashTree, String name) {
|
||||
this.assertionName = name;
|
||||
// 提取出合并的权限
|
||||
Map<String, ElementCondition> conditionMap = new HashMap<>();
|
||||
conditions(this.getXml(), conditionMap);
|
||||
// 数据处理生成断言条件
|
||||
List<XMLAssertion> list = new LinkedList<>();
|
||||
xmlFormatting(this.getXml(), list, null, conditionMap);
|
||||
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
hashTree.add(list);
|
||||
}
|
||||
}
|
||||
|
||||
private void conditions(List<DocumentElement> dataList, Map<String, ElementCondition> conditionMap) {
|
||||
dataList.forEach(item -> {
|
||||
if (StringUtils.isEmpty(item.getGroupId())) {
|
||||
conditionMap.put(item.getId(),
|
||||
new ElementCondition(item.isInclude(), item.isTypeVerification(), item.isArrayVerification(),
|
||||
new LinkedList<Condition>() {{
|
||||
this.add(new Condition(item.getContentVerification(), item.getExpectedOutcome()));
|
||||
}}));
|
||||
} else {
|
||||
if (conditionMap.containsKey(item.getGroupId())) {
|
||||
conditionMap.get(item.getGroupId()).getConditions().add(new Condition(item.getContentVerification(), item.getExpectedOutcome()));
|
||||
} else {
|
||||
conditionMap.put(item.getGroupId(),
|
||||
new ElementCondition(item.isInclude(), item.isTypeVerification(), item.isArrayVerification(),
|
||||
new LinkedList<Condition>() {{
|
||||
this.add(new Condition(item.getContentVerification(), item.getExpectedOutcome()));
|
||||
}}));
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(item.getChildren())) {
|
||||
conditions(item.getChildren(), conditionMap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void formatting(List<DocumentElement> dataList, List<JSONPathAssertion> list, DocumentElement parentNode, Map<String, ElementCondition> conditionMap) {
|
||||
for (DocumentElement item : dataList) {
|
||||
if (StringUtils.isEmpty(item.getGroupId())) {
|
||||
if (!item.getId().equals("root")) {
|
||||
if (parentNode != null) {
|
||||
item.setJsonPath(parentNode.getJsonPath() + "." + item.getName());
|
||||
} else {
|
||||
item.setJsonPath("$." + item.getName());
|
||||
}
|
||||
if (!StringUtils.equalsAny(item.getContentVerification(), "none", null) || item.isInclude()) {
|
||||
list.add(newJSONPathAssertion(item, conditionMap.get(item.getId())));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(item.getChildren())) {
|
||||
formatting(item.getChildren(), list, item, conditionMap);
|
||||
}
|
||||
} else {
|
||||
if (CollectionUtils.isNotEmpty(item.getChildren())) {
|
||||
formatting(item.getChildren(), list, null, conditionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void xmlFormatting(List<DocumentElement> dataList, List<XMLAssertion> list, DocumentElement parentNode, Map<String, ElementCondition> conditionMap) {
|
||||
for (DocumentElement item : dataList) {
|
||||
if (StringUtils.isEmpty(item.getGroupId())) {
|
||||
if (parentNode != null) {
|
||||
item.setJsonPath(parentNode.getJsonPath() + "." + item.getName());
|
||||
} else {
|
||||
item.setJsonPath("$." + item.getName());
|
||||
}
|
||||
if (!StringUtils.equalsAny(item.getContentVerification(), "none", null) || item.isInclude()) {
|
||||
list.add(newXMLAssertion(item, conditionMap.get(item.getId())));
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(item.getChildren())) {
|
||||
xmlFormatting(item.getChildren(), list, item, conditionMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getConditionStr(DocumentElement item, ElementCondition elementCondition) {
|
||||
StringBuilder conditionStr = new StringBuilder();
|
||||
if (elementCondition != null && CollectionUtils.isNotEmpty(elementCondition.getConditions())) {
|
||||
elementCondition.getConditions().forEach(condition -> {
|
||||
conditionStr.append(item.getLabel(item.getContentVerification()).replace("'%'", (item.getExpectedOutcome() != null ? item.getExpectedOutcome().toString() : "")));
|
||||
conditionStr.append(" and ");
|
||||
});
|
||||
}
|
||||
String label = "";
|
||||
if (StringUtils.isNotEmpty(conditionStr.toString())) {
|
||||
label = conditionStr.toString().substring(0, conditionStr.toString().length() - 4);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
private JSONPathAssertion newJSONPathAssertion(DocumentElement item, ElementCondition elementCondition) {
|
||||
JSONPathAssertion assertion = new JSONPathAssertion();
|
||||
assertion.setEnabled(true);
|
||||
assertion.setJsonValidationBool(true);
|
||||
assertion.setExpectNull(false);
|
||||
assertion.setInvert(false);
|
||||
|
||||
assertion.setName((StringUtils.isNotEmpty(assertionName) ? assertionName : "DocumentAssertion") + ("==" + item.getJsonPath() + " " + getConditionStr(item, elementCondition)));
|
||||
assertion.setProperty(TestElement.TEST_CLASS, JSONPathAssertion.class.getName());
|
||||
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("JSONPathAssertionGui"));
|
||||
assertion.setJsonPath(item.getJsonPath());
|
||||
assertion.setExpectedValue(item.getExpectedOutcome() != null ? item.getExpectedOutcome().toString() : "");
|
||||
assertion.setProperty("ASS_OPTION", "DOCUMENT");
|
||||
assertion.setProperty("ElementCondition", JSON.toJSONString(elementCondition));
|
||||
|
||||
if (StringUtils.isEmpty(item.getContentVerification()) || "regular".equals(item.getContentVerification())) {
|
||||
assertion.setIsRegex(true);
|
||||
} else {
|
||||
assertion.setIsRegex(false);
|
||||
}
|
||||
return assertion;
|
||||
}
|
||||
|
||||
private XMLAssertion newXMLAssertion(DocumentElement item, ElementCondition elementCondition) {
|
||||
XMLAssertion assertion = new XMLAssertion();
|
||||
assertion.setEnabled(true);
|
||||
assertion.setName((StringUtils.isNotEmpty(assertionName) ? assertionName : "XMLAssertion") + "==" + (item.getJsonPath() + " " + getConditionStr(item, elementCondition)));
|
||||
assertion.setProperty(TestElement.TEST_CLASS, XMLAssertion.class.getName());
|
||||
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("XMLAssertionGui"));
|
||||
assertion.setProperty("XML_PATH", item.getJsonPath());
|
||||
assertion.setProperty("EXPECTED_VALUE", item.getExpectedOutcome() != null ? item.getExpectedOutcome().toString() : "");
|
||||
assertion.setProperty("ElementCondition", JSON.toJSONString(elementCondition));
|
||||
return assertion;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package io.metersphere.api.dto.definition.request.assertions.document;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class DocumentElement {
|
||||
private String id;
|
||||
private String name;
|
||||
private boolean include;
|
||||
private String status;
|
||||
private boolean typeVerification;
|
||||
private String type;
|
||||
private String groupId;
|
||||
private int rowspan;
|
||||
private boolean arrayVerification;
|
||||
private String contentVerification;
|
||||
private Object expectedOutcome;
|
||||
|
||||
private List<DocumentElement> children;
|
||||
|
||||
// 候补两个属性,在执行时组装数据用
|
||||
private String jsonPath;
|
||||
List<String> conditions;
|
||||
|
||||
public DocumentElement() {
|
||||
|
||||
}
|
||||
|
||||
public DocumentElement(String name, String type, Object expectedOutcome, List<DocumentElement> children) {
|
||||
this.id = UUID.randomUUID().toString();
|
||||
this.name = name;
|
||||
this.expectedOutcome = expectedOutcome;
|
||||
this.type = type;
|
||||
this.children = children == null ? this.children = new LinkedList<>() : children;
|
||||
this.rowspan = 1;
|
||||
this.contentVerification = "value_eq";
|
||||
if (StringUtils.equalsAny(type, "object", "array")) {
|
||||
this.contentVerification = "none";
|
||||
} else if (expectedOutcome == null || StringUtils.isEmpty(expectedOutcome.toString())) {
|
||||
this.contentVerification = "none";
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentElement(String id, String name, String type, Object expectedOutcome, List<DocumentElement> children) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.expectedOutcome = expectedOutcome;
|
||||
this.type = type;
|
||||
this.children = children == null ? this.children = new LinkedList<>() : children;
|
||||
this.rowspan = 1;
|
||||
this.contentVerification = "value_eq";
|
||||
if (StringUtils.equalsAny(type, "object", "array")) {
|
||||
this.contentVerification = "none";
|
||||
} else if (expectedOutcome == null || StringUtils.isEmpty(expectedOutcome.toString())) {
|
||||
this.contentVerification = "none";
|
||||
}
|
||||
}
|
||||
|
||||
public DocumentElement newRoot(String type, List<DocumentElement> children) {
|
||||
return new DocumentElement("root", "root", type, "", children);
|
||||
}
|
||||
|
||||
public String getLabel(String value) {
|
||||
String label = "";
|
||||
switch (value) {
|
||||
case "value_eq":
|
||||
label = "值-等于[value='%']";
|
||||
break;
|
||||
case "value_not_eq":
|
||||
label = "值-不等于[value!='%']";
|
||||
break;
|
||||
case "value_in":
|
||||
label = "值-包含[include='%']";
|
||||
break;
|
||||
case "length_eq":
|
||||
label = "长度-等于[length='%']";
|
||||
break;
|
||||
case "length_not_eq":
|
||||
label = "长度-不等于[length!='%']";
|
||||
break;
|
||||
case "length_gt":
|
||||
label = "长度-大于[length>'%']";
|
||||
break;
|
||||
case "length_lt":
|
||||
label = "长度-小于[length<'%']";
|
||||
break;
|
||||
case "regular":
|
||||
label = "正则匹配";
|
||||
break;
|
||||
default:
|
||||
label = "不校验[]";
|
||||
break;
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package io.metersphere.api.dto.definition.request.assertions.document;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ElementCondition {
|
||||
private boolean include;
|
||||
private boolean typeVerification;
|
||||
private boolean arrayVerification;
|
||||
List<Condition> conditions;
|
||||
|
||||
public ElementCondition() {
|
||||
|
||||
}
|
||||
|
||||
public ElementCondition(boolean include, boolean typeVerification, boolean arrayVerification, List<Condition> conditions) {
|
||||
this.include = include;
|
||||
this.typeVerification = typeVerification;
|
||||
this.arrayVerification = arrayVerification;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package io.metersphere.api.dto.definition.request.assertions.document;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsAssertionDocument {
|
||||
private String type;
|
||||
private Document data;
|
||||
}
|
|
@ -14,6 +14,7 @@ import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
|
|||
import io.metersphere.api.dto.definition.parse.ApiDefinitionImportParserFactory;
|
||||
import io.metersphere.api.dto.definition.parse.Swagger3Parser;
|
||||
import io.metersphere.api.dto.definition.request.ParameterConfig;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.DocumentElement;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
|
||||
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
|
||||
import io.metersphere.api.dto.mockconfig.MockConfigImportDTO;
|
||||
|
@ -32,6 +33,8 @@ import io.metersphere.base.mapper.*;
|
|||
import io.metersphere.base.mapper.ext.*;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.json.JSONSchemaToDocumentUtils;
|
||||
import io.metersphere.commons.json.JSONToDocumentUtils;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.controller.request.ResetOrderRequest;
|
||||
import io.metersphere.controller.request.ScheduleRequest;
|
||||
|
@ -725,11 +728,11 @@ public class ApiDefinitionService {
|
|||
private void reSetImportMocksApiId(List<MockConfigImportDTO> mocks, String originId, String newId, int apiNum) {
|
||||
if (CollectionUtils.isNotEmpty(mocks)) {
|
||||
int index = 1;
|
||||
for(MockConfigImportDTO item : mocks){
|
||||
for (MockConfigImportDTO item : mocks) {
|
||||
if (StringUtils.equals(item.getApiId(), originId)) {
|
||||
item.setApiId(newId);
|
||||
}
|
||||
item.setExpectNum(apiNum+"_"+index);
|
||||
item.setExpectNum(apiNum + "_" + index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
@ -901,7 +904,7 @@ public class ApiDefinitionService {
|
|||
if (request.getConfig() != null && StringUtils.isNotBlank(request.getConfig().getResourcePoolId())) {
|
||||
jMeterService.runTest(request.getId(), request.getId(), runMode, null, request.getConfig());
|
||||
} else {
|
||||
jMeterService.runLocal(request.getId(),request.getConfig(), hashTree, request.getReportId(), runMode);
|
||||
jMeterService.runLocal(request.getId(), request.getConfig(), hashTree, request.getReportId(), runMode);
|
||||
}
|
||||
return request.getId();
|
||||
}
|
||||
|
@ -1015,10 +1018,10 @@ public class ApiDefinitionService {
|
|||
apiImport = (ApiDefinitionImport) Objects.requireNonNull(apiImportParser).parse(file == null ? null : file.getInputStream(), request);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
String returnThrowException = e.getMessage();
|
||||
if(StringUtils.contains(returnThrowException,"模块树最大深度为")){
|
||||
String returnThrowException = e.getMessage();
|
||||
if (StringUtils.contains(returnThrowException, "模块树最大深度为")) {
|
||||
MSException.throwException(returnThrowException);
|
||||
}else {
|
||||
} else {
|
||||
MSException.throwException(Translator.get("parse_data_error"));
|
||||
}
|
||||
// 发送通知
|
||||
|
@ -1207,8 +1210,8 @@ public class ApiDefinitionService {
|
|||
}
|
||||
|
||||
public List<ApiDefinition> selectApiDefinitionBydIds(List<String> ids) {
|
||||
if(CollectionUtils.isEmpty(ids)){
|
||||
return new ArrayList<>();
|
||||
if (CollectionUtils.isEmpty(ids)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||
example.createCriteria().andIdIn(ids);
|
||||
|
@ -1493,7 +1496,7 @@ public class ApiDefinitionService {
|
|||
}
|
||||
for (ApiDefinition api : apiList) {
|
||||
String path = api.getPath();
|
||||
if(StringUtils.isEmpty(path)){
|
||||
if (StringUtils.isEmpty(path)) {
|
||||
continue;
|
||||
}
|
||||
if (path.startsWith("/")) {
|
||||
|
@ -1729,4 +1732,38 @@ public class ApiDefinitionService {
|
|||
List<ApiDefinitionFollow> follows = apiDefinitionFollowMapper.selectByExample(example);
|
||||
return follows.stream().map(ApiDefinitionFollow::getFollowId).distinct().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<DocumentElement> getDocument(String id, String type) {
|
||||
ApiDefinitionWithBLOBs bloBs = apiDefinitionMapper.selectByPrimaryKey(id);
|
||||
List<DocumentElement> list = new LinkedList<>();
|
||||
if (bloBs != null && StringUtils.isNotEmpty(bloBs.getResponse())) {
|
||||
JSONObject object = JSON.parseObject(bloBs.getResponse());
|
||||
JSONObject body = (JSONObject) object.get("body");
|
||||
if (body != null) {
|
||||
if (StringUtils.equals(type, "JSON")) {
|
||||
String jsonSchema = body.getString("jsonSchema");
|
||||
String dataType = body.getString("type");
|
||||
if (StringUtils.equalsAny(dataType, "JSON", "JSON-SCHEMA") && StringUtils.isNotEmpty(jsonSchema)) {
|
||||
JSONObject obj = (JSONObject) body.get("jsonSchema");
|
||||
if (StringUtils.equals(obj.getString("type"), "array")) {
|
||||
list.add(new DocumentElement().newRoot("array", JSONSchemaToDocumentUtils.getDocument(jsonSchema)));
|
||||
} else {
|
||||
list.add(new DocumentElement().newRoot("object", JSONSchemaToDocumentUtils.getDocument(jsonSchema)));
|
||||
}
|
||||
} else {
|
||||
list.add(new DocumentElement().newRoot("object", null));
|
||||
}
|
||||
} else {
|
||||
String xml = body.getString("raw");
|
||||
String dataType = body.getString("type");
|
||||
if (StringUtils.equals(dataType, "XML") && StringUtils.isNotEmpty(xml)) {
|
||||
list = JSONToDocumentUtils.getDocument(xml, type);
|
||||
} else {
|
||||
list.add(new DocumentElement().newRoot("root", null));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
package io.metersphere.commons.json;
|
||||
|
||||
import com.google.gson.*;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.DocumentElement;
|
||||
import io.metersphere.jmeter.utils.ScriptEngineUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class JSONSchemaToDocumentUtils {
|
||||
|
||||
private static void analyzeRootSchemaElement(JsonObject rootElement, List<DocumentElement> roots) {
|
||||
if (rootElement.has("type") || rootElement.has("allOf")) {
|
||||
analyzeObject(rootElement, roots);
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeObject(JsonObject object, List<DocumentElement> roots) {
|
||||
if (object.has("allOf")) {
|
||||
JsonArray allOfArray = object.get("allOf").getAsJsonArray();
|
||||
for (JsonElement allOfElement : allOfArray) {
|
||||
JsonObject allOfElementObj = allOfElement.getAsJsonObject();
|
||||
if (allOfElementObj.has("properties")) {
|
||||
// Properties elements will become the attributes/references of the element
|
||||
JsonObject propertiesObj = allOfElementObj.get("properties").getAsJsonObject();
|
||||
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
|
||||
String propertyKey = entry.getKey();
|
||||
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
|
||||
analyzeProperty(roots, propertyKey, propertyObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (object.has("properties")) {
|
||||
JsonObject propertiesObj = object.get("properties").getAsJsonObject();
|
||||
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
|
||||
String propertyKey = entry.getKey();
|
||||
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
|
||||
analyzeProperty(roots, propertyKey, propertyObj);
|
||||
}
|
||||
} else if (object.has("type") && object.get("type").getAsString().equals("array")) {
|
||||
analyzeProperty(roots, "MS-OBJECT", object);
|
||||
} else if (object.has("type") && !object.get("type").getAsString().equals("object")) {
|
||||
analyzeProperty(roots, object.getAsString(), object);
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeProperty(List<DocumentElement> concept,
|
||||
String propertyName, JsonObject object) {
|
||||
if (object.has("type")) {
|
||||
String propertyObjType = null;
|
||||
if (object.get("type") instanceof JsonPrimitive) {
|
||||
propertyObjType = object.get("type").getAsString();
|
||||
} else if (object.get("type") instanceof JsonArray) {
|
||||
JsonArray typeArray = (JsonArray) object.get("type").getAsJsonArray();
|
||||
propertyObjType = typeArray.get(0).getAsString();
|
||||
}
|
||||
if (object.has("default")) {
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, object.get("default") != null ? object.get("default").toString() : "", null));
|
||||
} else if (object.has("enum")) {
|
||||
try {
|
||||
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
Object value = object.get("mock").getAsJsonObject().get("mock");
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, value.toString(), null));
|
||||
} else {
|
||||
List<Object> list = analyzeEnumProperty(object);
|
||||
if (list.size() > 0) {
|
||||
int index = (int) (Math.random() * list.size());
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, list.get(index).toString(), null));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, "", null));
|
||||
}
|
||||
} else if (propertyObjType.equals("string")) {
|
||||
// 先设置空值
|
||||
String value = "";
|
||||
if (object.has("default")) {
|
||||
value = object.get("default") != null ? object.get("default").toString() : "";
|
||||
}
|
||||
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString()) && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(object.get("mock").getAsJsonObject().get("mock").getAsString());
|
||||
}
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, value, null));
|
||||
} else if (propertyObjType.equals("integer")) {
|
||||
Object value = null;
|
||||
if (object.has("default")) {
|
||||
value = object.get("default");
|
||||
}
|
||||
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
try {
|
||||
value = object.get("mock").getAsJsonObject().get("mock").getAsInt();
|
||||
} catch (Exception e) {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(object.get("mock").getAsJsonObject().get("mock").getAsString());
|
||||
}
|
||||
}
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, value, null));
|
||||
} else if (propertyObjType.equals("number")) {
|
||||
Object value = null;
|
||||
if (object.has("default")) {
|
||||
value = object.get("default");
|
||||
}
|
||||
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
try {
|
||||
value = object.get("mock").getAsJsonObject().get("mock").getAsNumber();
|
||||
} catch (Exception e) {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(object.get("mock").getAsJsonObject().get("mock").getAsString());
|
||||
}
|
||||
}
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, value, null));
|
||||
} else if (propertyObjType.equals("boolean")) {
|
||||
Object value = null;
|
||||
if (object.has("default")) {
|
||||
value = object.get("default");
|
||||
}
|
||||
if (object.has("mock") && object.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(object.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
try {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(object.get("mock").getAsJsonObject().get("mock").toString());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, value, null));
|
||||
} else if (propertyObjType.equals("array")) {
|
||||
analyzeArray(propertyObjType, propertyName, object);
|
||||
} else if (propertyObjType.equals("object")) {
|
||||
List<DocumentElement> list = new LinkedList<>();
|
||||
concept.add(new DocumentElement(propertyName, propertyObjType, "", list));
|
||||
analyzeObject(object, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void analyzeArray(String propertyObjType, String propertyName, JsonObject object) {
|
||||
// 先设置空值
|
||||
List<DocumentElement> array = new LinkedList<>();
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
if (object.has("items") && object.get("items").isJsonArray()) {
|
||||
jsonArray = object.get("items").getAsJsonArray();
|
||||
} else {
|
||||
JsonObject itemsObject = object.get("items").getAsJsonObject();
|
||||
array.add(new DocumentElement(propertyName, propertyObjType, itemsObject, null));
|
||||
}
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
JsonObject itemsObject = jsonArray.get(i).getAsJsonObject();
|
||||
if (object.has("items")) {
|
||||
Object value = null;
|
||||
if (itemsObject.has("mock") && itemsObject.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(itemsObject.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
try {
|
||||
value = itemsObject.get("mock").getAsJsonObject().get("mock").getAsInt();
|
||||
} catch (Exception e) {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(itemsObject.get("mock").getAsJsonObject().get("mock").getAsString());
|
||||
}
|
||||
} else if (itemsObject.has("type") && itemsObject.get("type").getAsString().equals("string")) {
|
||||
if (itemsObject.has("default")) {
|
||||
value = itemsObject.get("default");
|
||||
} else if (itemsObject.has("mock") && itemsObject.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(itemsObject.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(itemsObject.get("mock").getAsJsonObject().get("mock").getAsString());
|
||||
}
|
||||
} else if (itemsObject.has("type") && itemsObject.get("type").getAsString().equals("number")) {
|
||||
if (itemsObject.has("default")) {
|
||||
value = itemsObject.get("default");
|
||||
} else if (itemsObject.has("mock") && itemsObject.get("mock").getAsJsonObject() != null && StringUtils.isNotEmpty(itemsObject.get("mock").getAsJsonObject().get("mock").getAsString())) {
|
||||
value = ScriptEngineUtils.buildFunctionCallString(itemsObject.get("mock").getAsJsonObject().get("mock").getAsString());
|
||||
}
|
||||
} else if (itemsObject.has("properties")) {
|
||||
List<DocumentElement> propertyConcept = new LinkedList<>();
|
||||
JsonObject propertiesObj = itemsObject.get("properties").getAsJsonObject();
|
||||
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
|
||||
String propertyKey = entry.getKey();
|
||||
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
|
||||
analyzeProperty(propertyConcept, propertyKey, propertyObj);
|
||||
}
|
||||
}
|
||||
array.add(new DocumentElement(propertyName, itemsObject.get("type").getAsString(), value, null));
|
||||
} else if (object.has("items") && object.get("items").isJsonArray()) {
|
||||
JsonArray itemsObjectArray = object.get("items").getAsJsonArray();
|
||||
array.add(new DocumentElement(propertyName, itemsObject.get("type").getAsString(), itemsObjectArray, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Object> analyzeEnumProperty(JsonObject object) {
|
||||
List<Object> list = new LinkedList<>();
|
||||
String jsonStr = null;
|
||||
try {
|
||||
JsonArray enumValues = object.get("enum").getAsJsonArray();
|
||||
for (JsonElement enumValueElem : enumValues) {
|
||||
String enumValue = enumValueElem.getAsString();
|
||||
list.add(enumValue);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
jsonStr = object.get("enum").getAsString();
|
||||
}
|
||||
if (jsonStr != null && list.isEmpty()) {
|
||||
String[] arrays = jsonStr.split("\n");
|
||||
for (String str : arrays) {
|
||||
list.add(str);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<DocumentElement> getDocument(String jsonSchema) {
|
||||
Gson gson = new Gson();
|
||||
JsonElement element = gson.fromJson(jsonSchema, JsonElement.class);
|
||||
JsonObject rootElement = element.getAsJsonObject();
|
||||
List<DocumentElement> roots = new LinkedList<>();
|
||||
analyzeRootSchemaElement(rootElement, roots);
|
||||
return roots;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package io.metersphere.commons.json;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.DocumentElement;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.SAXReader;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class JSONToDocumentUtils {
|
||||
|
||||
public static void jsonDataFormatting(JSONArray array, List<DocumentElement> children) {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
JSONObject element = array.getJSONObject(i);
|
||||
jsonDataFormatting(element, children);
|
||||
}
|
||||
}
|
||||
|
||||
public static void jsonDataFormatting(JSONObject object, List<DocumentElement> children) {
|
||||
for (String key : object.keySet()) {
|
||||
Object value = object.get(key);
|
||||
if (value instanceof JSONObject) {
|
||||
List<DocumentElement> childrenElements = new LinkedList<>();
|
||||
children.add(new DocumentElement(key, "object", "", childrenElements));
|
||||
jsonDataFormatting((JSONObject) value, childrenElements);
|
||||
} else if (value instanceof JSONArray) {
|
||||
List<DocumentElement> childrenElements = new LinkedList<>();
|
||||
children.add(new DocumentElement(key, "array", "", childrenElements));
|
||||
jsonDataFormatting((JSONArray) value, childrenElements);
|
||||
} else {
|
||||
children.add(new DocumentElement(key, "string", value, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static List<DocumentElement> getDocument(String json, String type) {
|
||||
try {
|
||||
if (StringUtils.equals(type, "JSON")) {
|
||||
List<DocumentElement> roots = new LinkedList<>();
|
||||
List<DocumentElement> children = new LinkedList<>();
|
||||
roots.add(new DocumentElement("root", "root", "object", "", children));
|
||||
JSONObject object = JSON.parseObject(json);
|
||||
jsonDataFormatting(object, children);
|
||||
return roots;
|
||||
} else {
|
||||
return getXmlDocument(json);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
return new LinkedList<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从指定节点开始,递归遍历所有子节点
|
||||
*/
|
||||
public static void getNodes(Element node, List<DocumentElement> children) {
|
||||
//递归遍历当前节点所有的子节点
|
||||
List<Element> listElement = node.elements();
|
||||
if (listElement.isEmpty()) {
|
||||
children.add(new DocumentElement(node.getName(), "string", node.getTextTrim(), null));
|
||||
}
|
||||
for (Element element : listElement) {//遍历所有一级子节点
|
||||
List<Element> elementNodes = element.elements();
|
||||
if (elementNodes.size() > 0) {
|
||||
List<DocumentElement> elements = new LinkedList<>();
|
||||
children.add(new DocumentElement(element.getName(), "object", element.getTextTrim(), elements));
|
||||
getNodes(element, elements);//递归
|
||||
} else {
|
||||
getNodes(element, children);//递归
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<DocumentElement> getXmlDocument(String xml) {
|
||||
List<DocumentElement> roots = new LinkedList<>();
|
||||
try {
|
||||
InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
|
||||
// 创建saxReader对象
|
||||
SAXReader reader = new SAXReader();
|
||||
// 通过read方法读取一个文件 转换成Document对象
|
||||
Document document = reader.read(is);
|
||||
//获取根节点元素对象
|
||||
getNodes(document.getRootElement(), roots);
|
||||
// 未能处理root补偿root 节点
|
||||
if (roots.size() > 1) {
|
||||
Element node = document.getRootElement();
|
||||
List<DocumentElement> newRoots = new LinkedList<>();
|
||||
newRoots.add(new DocumentElement("root", node.getName(), "object", node.getTextTrim(), roots));
|
||||
return newRoots;
|
||||
} else if (roots.size() == 1) {
|
||||
roots.get(0).setId("root");
|
||||
}
|
||||
return roots;
|
||||
} catch (Exception ex) {
|
||||
LogUtil.error(ex);
|
||||
return roots;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,14 @@
|
|||
|
||||
package org.apache.jmeter.assertions;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.google.gson.Gson;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.Predicate;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.Condition;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.ElementCondition;
|
||||
import net.minidev.json.JSONArray;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.samplers.SampleResult;
|
||||
import org.apache.jmeter.testelement.AbstractTestElement;
|
||||
|
@ -47,6 +51,10 @@ public class JSONPathAssertion extends AbstractTestElement implements Serializab
|
|||
return getPropertyAsString("ASS_OPTION");
|
||||
}
|
||||
|
||||
public String getElementCondition() {
|
||||
return getPropertyAsString("ElementCondition");
|
||||
}
|
||||
|
||||
public String getJsonPath() {
|
||||
return this.getPropertyAsString("JSON_PATH");
|
||||
}
|
||||
|
@ -132,6 +140,9 @@ public class JSONPathAssertion extends AbstractTestElement implements Serializab
|
|||
case "LT":
|
||||
msg = "Value < '%s', but found '%s'";
|
||||
break;
|
||||
case "DOCUMENT":
|
||||
msg = (StringUtils.isNotEmpty(this.getName()) ? this.getName().split("==")[1] : "") + "校验失败,返回数据:" + (value != null ? value.toString() : "");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
msg = "Value expected to be '%s', but found '%s'";
|
||||
|
@ -202,13 +213,70 @@ public class JSONPathAssertion extends AbstractTestElement implements Serializab
|
|||
case "LT":
|
||||
refFlag = isLt(str, getExpectedValue());
|
||||
break;
|
||||
|
||||
case "DOCUMENT":
|
||||
refFlag = isDocument(str);
|
||||
break;
|
||||
}
|
||||
return refFlag;
|
||||
}
|
||||
return str.equals(this.getExpectedValue());
|
||||
}
|
||||
}
|
||||
private String ifValue(Object value) {
|
||||
if (value != null) {
|
||||
return value.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isDocument(String resValue) {
|
||||
String condition = this.getElementCondition();
|
||||
if (StringUtils.isNotEmpty(condition)) {
|
||||
ElementCondition elementCondition = JSON.parseObject(condition, ElementCondition.class);
|
||||
boolean isTrue = true;
|
||||
if (CollectionUtils.isNotEmpty(elementCondition.getConditions())) {
|
||||
for (Condition item : elementCondition.getConditions()) {
|
||||
String expectedValue = ifValue(item.getValue());
|
||||
switch (item.getKey()) {
|
||||
case "value_eq":
|
||||
isTrue = StringUtils.equals(resValue, expectedValue);
|
||||
break;
|
||||
case "value_not_eq":
|
||||
isTrue = !StringUtils.equals(resValue, expectedValue);
|
||||
break;
|
||||
case "value_in":
|
||||
isTrue = StringUtils.contains(resValue, expectedValue);
|
||||
break;
|
||||
case "length_eq":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() == expectedValue.length())
|
||||
|| (StringUtils.isEmpty(resValue) && StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "length_not_eq":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() != expectedValue.length())
|
||||
|| (StringUtils.isEmpty(resValue) || StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "length_gt":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() > expectedValue.length())
|
||||
|| (StringUtils.isNotEmpty(resValue) && StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "length_lt":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() < expectedValue.length())
|
||||
|| (StringUtils.isEmpty(resValue) || StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "regular":
|
||||
Pattern pattern = JMeterUtils.getPatternCache().getPattern(this.getExpectedValue());
|
||||
isTrue = JMeterUtils.getMatcher().matches(resValue, pattern);
|
||||
break;
|
||||
}
|
||||
if (!isTrue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isTrue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public AssertionResult getResult(SampleResult samplerResult) {
|
||||
AssertionResult result = new AssertionResult(this.getName());
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* 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 com.alibaba.fastjson.JSON;
|
||||
import com.google.gson.Gson;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.Predicate;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.Condition;
|
||||
import io.metersphere.api.dto.definition.request.assertions.document.ElementCondition;
|
||||
import net.minidev.json.JSONArray;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
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.json.JSONObject;
|
||||
import org.json.XML;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.XMLReader;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Checks if the result is a well-formed XML content using {@link XMLReader}
|
||||
*/
|
||||
public class XMLAssertion extends AbstractTestElement implements Serializable, Assertion, ThreadListener {
|
||||
private static final Logger log = LoggerFactory.getLogger(XMLAssertion.class);
|
||||
private static ThreadLocal<DecimalFormat> decimalFormatter = ThreadLocal.withInitial(XMLAssertion::createDecimalFormat);
|
||||
|
||||
private static final long serialVersionUID = 242L;
|
||||
|
||||
public String getXmlPath() {
|
||||
return this.getPropertyAsString("XML_PATH");
|
||||
}
|
||||
|
||||
public String getExpectedValue() {
|
||||
return this.getPropertyAsString("EXPECTED_VALUE");
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return getPropertyAsString("ElementCondition");
|
||||
}
|
||||
|
||||
private static DecimalFormat createDecimalFormat() {
|
||||
DecimalFormat decimalFormatter = new DecimalFormat("#.#");
|
||||
decimalFormatter.setMaximumFractionDigits(340);
|
||||
decimalFormatter.setMinimumFractionDigits(1);
|
||||
return decimalFormatter;
|
||||
}
|
||||
|
||||
// one builder for all requests in a thread
|
||||
private static final ThreadLocal<XMLReader> XML_READER = new ThreadLocal<XMLReader>() {
|
||||
@Override
|
||||
protected XMLReader initialValue() {
|
||||
try {
|
||||
XMLReader reader = SAXParserFactory.newInstance()
|
||||
.newSAXParser()
|
||||
.getXMLReader();
|
||||
reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
return reader;
|
||||
} catch (SAXException | ParserConfigurationException e) {
|
||||
log.error("Error initializing XMLReader in XMLAssertion", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the result of the Assertion.
|
||||
* Here it checks whether the Sample data is XML.
|
||||
* If so an AssertionResult containing a FailureMessage will be returned.
|
||||
* Otherwise the returned AssertionResult will reflect the success of the Sample.
|
||||
*/
|
||||
@Override
|
||||
public AssertionResult getResult(SampleResult response) {
|
||||
// no error as default
|
||||
AssertionResult result = new AssertionResult(getName());
|
||||
String resultData = response.getResponseDataAsString();
|
||||
if (resultData.length() == 0) {
|
||||
return result.setResultForNull();
|
||||
}
|
||||
result.setFailure(false);
|
||||
XMLReader builder = XML_READER.get();
|
||||
if (builder != null) {
|
||||
try {
|
||||
builder.setErrorHandler(new LogErrorHandler());
|
||||
builder.parse(new InputSource(new StringReader(resultData)));
|
||||
try {
|
||||
JSONObject xmlJSONObj = XML.toJSONObject(resultData);
|
||||
String jsonPrettyPrintString = xmlJSONObj.toString(4);
|
||||
doAssert(jsonPrettyPrintString);
|
||||
} catch (Exception e) {
|
||||
result.setError(true);
|
||||
result.setFailure(true);
|
||||
result.setFailureMessage(e.getMessage());
|
||||
}
|
||||
} catch (SAXException | IOException e) {
|
||||
result.setError(true);
|
||||
result.setFailure(true);
|
||||
result.setFailureMessage(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
result.setError(true);
|
||||
result.setFailureMessage("Cannot initialize XMLReader in element:" + getName() + ", check jmeter.log file");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private void doAssert(String jsonString) {
|
||||
Object value = JsonPath.read(jsonString, this.getXmlPath(), new Predicate[0]);
|
||||
if (value instanceof JSONArray) {
|
||||
if (this.arrayMatched((JSONArray) value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!this.isEquals(value)) {
|
||||
String msg = (StringUtils.isNotEmpty(this.getName()) ? this.getName().split("==")[1] : "") + "校验失败,返回数据:" + (value != null ? value.toString() : "");
|
||||
throw new IllegalStateException(String.format(msg, this.getExpectedValue(), objectToString(value)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isEquals(Object subj) {
|
||||
String str = objectToString(subj);
|
||||
return isDocument(str);
|
||||
}
|
||||
|
||||
private boolean arrayMatched(JSONArray value) {
|
||||
if (value.isEmpty() && "[]".equals(this.getExpectedValue())) {
|
||||
return true;
|
||||
} else {
|
||||
Object[] var2 = value.toArray();
|
||||
int var3 = var2.length;
|
||||
|
||||
for (int var4 = 0; var4 < var3; ++var4) {
|
||||
Object subj = var2[var4];
|
||||
if (subj == null || this.isEquals(subj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.isEquals(value);
|
||||
}
|
||||
}
|
||||
|
||||
private String ifValue(Object value) {
|
||||
if (value != null) {
|
||||
return value.toString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private boolean isDocument(String resValue) {
|
||||
String condition = this.getCondition();
|
||||
if (StringUtils.isNotEmpty(condition)) {
|
||||
ElementCondition elementCondition = JSON.parseObject(condition, ElementCondition.class);
|
||||
boolean isTrue = true;
|
||||
if (CollectionUtils.isNotEmpty(elementCondition.getConditions())) {
|
||||
for (Condition item : elementCondition.getConditions()) {
|
||||
String expectedValue = ifValue(item.getValue());
|
||||
switch (item.getKey()) {
|
||||
case "value_eq":
|
||||
isTrue = StringUtils.equals(resValue, expectedValue);
|
||||
break;
|
||||
case "value_not_eq":
|
||||
isTrue = !StringUtils.equals(resValue, expectedValue);
|
||||
break;
|
||||
case "value_in":
|
||||
isTrue = StringUtils.contains(resValue, expectedValue);
|
||||
break;
|
||||
case "length_eq":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() == expectedValue.length())
|
||||
|| (StringUtils.isEmpty(resValue) && StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "length_not_eq":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() != expectedValue.length())
|
||||
|| (StringUtils.isEmpty(resValue) || StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "length_gt":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() > expectedValue.length())
|
||||
|| (StringUtils.isNotEmpty(resValue) && StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "length_lt":
|
||||
isTrue = (StringUtils.isNotEmpty(resValue) && StringUtils.isNotEmpty(expectedValue) && resValue.length() < expectedValue.length())
|
||||
|| (StringUtils.isEmpty(resValue) || StringUtils.isEmpty(expectedValue));
|
||||
break;
|
||||
case "regular":
|
||||
Pattern pattern = JMeterUtils.getPatternCache().getPattern(this.getExpectedValue());
|
||||
isTrue = JMeterUtils.getMatcher().matches(resValue, pattern);
|
||||
break;
|
||||
}
|
||||
if (!isTrue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isTrue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String objectToString(Object subj) {
|
||||
String str;
|
||||
if (subj == null) {
|
||||
str = "null";
|
||||
} else if (subj instanceof Map) {
|
||||
str = new Gson().toJson(subj);
|
||||
} else if (!(subj instanceof Double) && !(subj instanceof Float)) {
|
||||
str = subj.toString();
|
||||
} else {
|
||||
str = ((DecimalFormat) decimalFormatter.get()).format(subj);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadStarted() {
|
||||
// nothing to do on thread start
|
||||
}
|
||||
|
||||
@Override
|
||||
public void threadFinished() {
|
||||
XML_READER.remove();
|
||||
}
|
||||
}
|
|
@ -15,31 +15,61 @@
|
|||
<div class="assertion-add" :draggable="draggable">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="4">
|
||||
<el-select :disabled="isReadOnly" class="assertion-item" v-model="type"
|
||||
:placeholder="$t('api_test.request.assertions.select_type')"
|
||||
size="small">
|
||||
<el-select :disabled="isReadOnly" class="assertion-item" v-model="type" :placeholder="$t('api_test.request.assertions.select_type')" size="small">
|
||||
<el-option :label="$t('api_test.request.assertions.text')" :value="options.TEXT"/>
|
||||
<el-option :label="$t('api_test.request.assertions.regex')" :value="options.REGEX"/>
|
||||
<el-option :label="'JSONPath'" :value="options.JSON_PATH"/>
|
||||
<el-option :label="'XPath'" :value="options.XPATH2"/>
|
||||
<el-option :label="$t('api_test.request.assertions.response_time')" :value="options.DURATION"/>
|
||||
<el-option :label="$t('api_test.request.assertions.jsr223')" :value="options.JSR223"/>
|
||||
<el-option :label="$t('api_test.definition.request.document_structure')" :value="options.DOCUMENT"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<ms-api-assertion-text :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.TEXT"
|
||||
:callback="after"/>
|
||||
|
||||
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex" v-if="type === options.REGEX"
|
||||
:callback="after"/>
|
||||
<ms-api-assertion-json-path :is-read-only="isReadOnly" :list="assertions.jsonPath"
|
||||
v-if="type === options.JSON_PATH" :callback="after"/>
|
||||
<ms-api-assertion-x-path2 :is-read-only="isReadOnly" :list="assertions.xpath2" v-if="type === options.XPATH2"
|
||||
:callback="after"/>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="time" :duration="assertions.duration"
|
||||
v-if="type === options.DURATION" :callback="after"/>
|
||||
<ms-api-assertion-jsr223 :is-read-only="isReadOnly" :list="assertions.jsr223" v-if="type === options.JSR223"
|
||||
:callback="after"/>
|
||||
<ms-api-assertion-text
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.regex"
|
||||
:callback="after"
|
||||
v-if="type === options.TEXT"
|
||||
/>
|
||||
<ms-api-assertion-regex
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.regex"
|
||||
:callback="after"
|
||||
v-if="type === options.REGEX"
|
||||
/>
|
||||
<ms-api-assertion-json-path
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.jsonPath"
|
||||
:callback="after"
|
||||
v-if="type === options.JSON_PATH"
|
||||
/>
|
||||
<ms-api-assertion-x-path2
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.xpath2"
|
||||
:callback="after"
|
||||
v-if="type === options.XPATH2"
|
||||
/>
|
||||
<ms-api-assertion-duration
|
||||
v-model="time"
|
||||
:is-read-only="isReadOnly"
|
||||
:duration="assertions.duration"
|
||||
:callback="after"
|
||||
v-if="type === options.DURATION"
|
||||
/>
|
||||
<ms-api-assertion-jsr223
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.jsr223"
|
||||
:callback="after"
|
||||
v-if="type === options.JSR223"
|
||||
/>
|
||||
<ms-api-assertion-document
|
||||
:is-read-only="isReadOnly"
|
||||
v-model="time"
|
||||
:document="assertions.document"
|
||||
:callback="after"
|
||||
v-if="type === options.DOCUMENT"
|
||||
/>
|
||||
<el-button v-if="!type" :disabled="true" type="primary" size="small">
|
||||
{{ $t('api_test.request.assertions.add') }}
|
||||
</el-button>
|
||||
|
@ -47,12 +77,23 @@
|
|||
</el-row>
|
||||
</div>
|
||||
|
||||
<api-json-path-suggest-button :open-tip="$t('api_test.request.assertions.json_path_suggest')"
|
||||
:clear-tip="$t('api_test.request.assertions.json_path_clear')" @open="suggestJsonOpen" @clear="clearJson"/>
|
||||
<api-json-path-suggest-button
|
||||
:open-tip="$t('api_test.request.assertions.json_path_suggest')"
|
||||
:clear-tip="$t('api_test.request.assertions.json_path_clear')"
|
||||
@open="suggestJsonOpen"
|
||||
@clear="clearJson"/>
|
||||
|
||||
<ms-api-assertions-edit :is-read-only="isReadOnly" :assertions="assertions" :reloadData="reloadData" style="margin-bottom: 20px"/>
|
||||
<ms-api-assertions-edit
|
||||
:is-read-only="isReadOnly"
|
||||
:assertions="assertions"
|
||||
:apiId="apiId"
|
||||
:reloadData="reloadData"
|
||||
style="margin-bottom: 20px"/>
|
||||
|
||||
<ms-api-jsonpath-suggest :tip="$t('api_test.request.extract.suggest_tip')" @addSuggest="addJsonPathSuggest" ref="jsonpathSuggest"/>
|
||||
<ms-api-jsonpath-suggest
|
||||
:tip="$t('api_test.request.extract.suggest_tip')"
|
||||
@addSuggest="addJsonPathSuggest"
|
||||
ref="jsonpathSuggest"/>
|
||||
|
||||
</api-base-component>
|
||||
</template>
|
||||
|
@ -71,6 +112,7 @@ import {getUUID} from "@/common/js/utils";
|
|||
import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton";
|
||||
import MsApiJsonpathSuggest from "./ApiJsonpathSuggest";
|
||||
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
|
||||
import MsApiAssertionDocument from "./document/DocumentHeader";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertions",
|
||||
|
@ -82,7 +124,11 @@ export default {
|
|||
MsApiAssertionJsr223,
|
||||
MsApiJsonpathSuggestList,
|
||||
MsApiAssertionJsonPath,
|
||||
MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText
|
||||
MsApiAssertionsEdit,
|
||||
MsApiAssertionDuration,
|
||||
MsApiAssertionRegex,
|
||||
MsApiAssertionText,
|
||||
MsApiAssertionDocument,
|
||||
},
|
||||
props: {
|
||||
draggable: {
|
||||
|
@ -100,6 +146,7 @@ export default {
|
|||
assertions: {},
|
||||
node: {},
|
||||
request: {},
|
||||
apiId: String,
|
||||
response: {},
|
||||
customizeStyle: {
|
||||
type: String,
|
||||
|
|
|
@ -1,135 +1,156 @@
|
|||
<template>
|
||||
<div v-loading="loading">
|
||||
<div class="assertion-item-editing regex" v-if="assertions.regex.length > 0">
|
||||
<div>
|
||||
{{ $t("api_test.request.assertions.regex") }}
|
||||
</div>
|
||||
<div> {{ $t("api_test.request.assertions.regex") }}</div>
|
||||
<div class="regex-item" v-for="(regex, index) in assertions.regex" :key="index">
|
||||
<ms-api-assertion-regex :is-read-only="isReadOnly" :list="assertions.regex"
|
||||
:regex="regex" :edit="true" :index="index"/>
|
||||
<ms-api-assertion-regex
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.regex"
|
||||
:regex="regex"
|
||||
:edit="true"
|
||||
:index="index"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assertion-item-editing json_path" v-if="assertions.jsonPath.length > 0">
|
||||
<div>
|
||||
{{ 'JSONPath' }}
|
||||
</div>
|
||||
<div> {{ 'JSONPath' }}</div>
|
||||
<div class="regex-item" v-for="(jsonPath, index) in assertions.jsonPath" :key="index">
|
||||
<ms-api-assertion-json-path :is-read-only="isReadOnly" :list="assertions.jsonPath"
|
||||
:json-path="jsonPath" :edit="true" :index="index"/>
|
||||
<ms-api-assertion-json-path
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.jsonPath"
|
||||
:json-path="jsonPath"
|
||||
:edit="true"
|
||||
:index="index"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assertion-item-editing x_path" v-if="assertions.xpath2.length > 0">
|
||||
<div>
|
||||
{{ 'XPath' }}
|
||||
</div>
|
||||
<div> {{ 'XPath' }}</div>
|
||||
<div class="regex-item" v-for="(xPath, index) in assertions.xpath2" :key="index">
|
||||
<ms-api-assertion-x-path2 :is-read-only="isReadOnly" :list="assertions.xpath2"
|
||||
:x-path2="xPath" :edit="true" :index="index"/>
|
||||
<ms-api-assertion-x-path2
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.xpath2"
|
||||
:x-path2="xPath"
|
||||
:edit="true"
|
||||
:index="index"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assertion-item-editing jsr223" v-if="assertions.jsr223.length > 0">
|
||||
<div>
|
||||
{{ $t("api_test.request.assertions.script") }}
|
||||
</div>
|
||||
<div> {{ $t("api_test.request.assertions.script") }}</div>
|
||||
<div class="regex-item" v-for="(assertion, index) in assertions.jsr223" :key="index">
|
||||
<ms-api-assertion-jsr223 :is-read-only="isReadOnly" :list="assertions.jsr223"
|
||||
:assertion="assertion" :edit="true" :index="index"/>
|
||||
<ms-api-assertion-jsr223
|
||||
:is-read-only="isReadOnly"
|
||||
:list="assertions.jsr223"
|
||||
:assertion="assertion"
|
||||
:edit="true"
|
||||
:index="index"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assertion-item-editing response-time" v-if="isShow">
|
||||
<div>
|
||||
{{ $t("api_test.request.assertions.response_time") }}
|
||||
</div>
|
||||
<ms-api-assertion-duration :is-read-only="isReadOnly" v-model="assertions.duration.value"
|
||||
:duration="assertions.duration" :edit="true"/>
|
||||
<div> {{ $t("api_test.request.assertions.response_time") }}</div>
|
||||
<ms-api-assertion-duration
|
||||
:is-read-only="isReadOnly"
|
||||
v-model="assertions.duration.value"
|
||||
:duration="assertions.duration"
|
||||
:edit="true"/>
|
||||
</div>
|
||||
<div class="assertion-item-editing response-time" v-if="isDocument">
|
||||
<div> {{ assertions.document.type }}-{{ $t("api_test.definition.request.document_structure") }}</div>
|
||||
<ms-document-body :document="assertions.document" :apiId="apiId"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
|
||||
import MsApiAssertionJsr223 from "./ApiAssertionJsr223";
|
||||
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
|
||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||
import MsApiAssertionDuration from "./ApiAssertionDuration";
|
||||
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
|
||||
import MsApiAssertionJsr223 from "./ApiAssertionJsr223";
|
||||
import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertionsEdit",
|
||||
export default {
|
||||
name: "MsApiAssertionsEdit",
|
||||
|
||||
components: {
|
||||
MsApiAssertionXPath2,
|
||||
MsApiAssertionJsr223, MsApiAssertionJsonPath, MsApiAssertionDuration, MsApiAssertionRegex
|
||||
},
|
||||
components: {
|
||||
MsApiAssertionXPath2,
|
||||
MsApiAssertionJsr223,
|
||||
MsApiAssertionJsonPath,
|
||||
MsApiAssertionDuration,
|
||||
MsApiAssertionRegex,
|
||||
MsDocumentBody: () => import("./document/DocumentBody"),
|
||||
},
|
||||
|
||||
props: {
|
||||
assertions: {},
|
||||
reloadData: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isShow() {
|
||||
let rt = this.assertions.duration;
|
||||
return rt.value !== undefined && rt.value !== 0;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
reloadData() {
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
props: {
|
||||
assertions: {},
|
||||
reloadData: String,
|
||||
apiId: String,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isShow() {
|
||||
let rt = this.assertions.duration;
|
||||
return rt.value !== undefined && rt.value !== 0;
|
||||
},
|
||||
isDocument() {
|
||||
return this.assertions.document.data && (this.assertions.document.data.json.length > 0 || this.assertions.document.data.xml.length > 0);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
reloadData() {
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.assertion-item-editing {
|
||||
padding-left: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.assertion-item-editing {
|
||||
padding-left: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.assertion-item-editing.regex {
|
||||
border-left: 2px solid #7B0274;
|
||||
}
|
||||
.assertion-item-editing.regex {
|
||||
border-left: 2px solid #7B0274;
|
||||
}
|
||||
|
||||
.assertion-item-editing.json_path {
|
||||
border-left: 2px solid #44B3D2;
|
||||
}
|
||||
.assertion-item-editing.json_path {
|
||||
border-left: 2px solid #44B3D2;
|
||||
}
|
||||
|
||||
.assertion-item-editing.response-time {
|
||||
border-left: 2px solid #DD0240;
|
||||
}
|
||||
.assertion-item-editing.response-time {
|
||||
border-left: 2px solid #DD0240;
|
||||
}
|
||||
|
||||
.assertion-item-editing.jsr223 {
|
||||
border-left: 2px solid #1FDD02;
|
||||
}
|
||||
.assertion-item-editing.jsr223 {
|
||||
border-left: 2px solid #1FDD02;
|
||||
}
|
||||
|
||||
.assertion-item-editing.x_path {
|
||||
border-left: 2px solid #fca130;
|
||||
}
|
||||
.assertion-item-editing.x_path {
|
||||
border-left: 2px solid #fca130;
|
||||
}
|
||||
|
||||
.regex-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.regex-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,461 @@
|
|||
<template>
|
||||
<div class="ms-border">
|
||||
<div style="margin-bottom: 10px">
|
||||
<span class="ms-import" @click="importData">
|
||||
<i class="el-icon-edit-outline" style="font-size: 16px"/>
|
||||
{{ $t('commons.import') }}
|
||||
</span>
|
||||
<el-checkbox v-model="checked" @change="checkedAPI">跟随API定义</el-checkbox>
|
||||
</div>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:span-method="objectSpanMethod"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
@cell-mouse-enter="editRow"
|
||||
@cell-mouse-leave="editLeave"
|
||||
row-key="id"
|
||||
default-expand-all
|
||||
v-loading="loading">
|
||||
|
||||
<el-table-column prop="name" :label="$t('api_test.definition.request.esb_table.name')" width="230">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="scope.row.status && scope.column.fixed && scope.row.id!=='root'" v-model="scope.row.name" style="width: 140px" size="mini"/>
|
||||
<span v-else>{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="include" width="78" label="必含" :render-header="renderHeader">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.include" @change="handleCheckOneChange" :disabled="checked"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="typeVerification" width="100" label="类型校验" :render-header="renderHeaderType">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.typeVerification" @change="handleCheckOneChange" :disabled="checked"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="type" :label="$t('api_test.definition.request.esb_table.type')" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.type" :placeholder="$t('commons.please_select')" size="mini" @change="changeType(scope.row)" :disabled="checked">
|
||||
<el-option v-for="item in typeSelectOptions " :key="item.value" :label="item.label" :value="item.value"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="arrayVerification" width="140" label="校验数组内元素" :render-header="renderHeaderArray">
|
||||
<template slot-scope="scope">
|
||||
<el-checkbox v-model="scope.row.arrayVerification" @change="handleCheckOneChange" v-if="scope.row.type==='array'" :disabled="checked"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="contentVerification" label="内容校验" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.contentVerification" :placeholder="$t('commons.please_select')" size="mini" :disabled="checked">
|
||||
<el-option v-for="item in verificationOptions " :key="item.value" :label="item.label" :value="item.value"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="expectedOutcome" label="预期结果" min-width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-if="scope.row.status && scope.column.fixed" v-model="scope.row.expectedOutcome" size="mini"/>
|
||||
<span v-else>{{ scope.row.expectedOutcome }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('commons.operating')" width="200" fixed="right">
|
||||
<template v-slot:default="scope">
|
||||
<span>
|
||||
<el-button size="mini" type="text" circle @click="addVerification(scope.row)" :disabled="scope.row.type ==='object' || checked || scope.row.id==='root'">添加校验</el-button>
|
||||
<el-button size="mini" type="text" @click="addRow(scope.row)" :disabled="(scope.row.type !=='object' && scope.row.type !=='array') || checked">添加子字段</el-button>
|
||||
<el-button size="mini" type="text" @click="remove(scope.row)" :disabled="checked || scope.row.id==='root'">{{ $t('commons.remove') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ms-document-import :document="document" @setJSONData="setJSONData" ref="import"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "MsDocumentBody",
|
||||
components: {
|
||||
MsDocumentImport: () => import("./DocumentImport"),
|
||||
},
|
||||
props: {
|
||||
document: {},
|
||||
apiId: String,
|
||||
showOptionsButton: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
verificationOptions: [
|
||||
{value: 'none', label: '不校验[]'},
|
||||
{value: 'value_eq', label: '值-等于[value=]'},
|
||||
{value: 'value_not_eq', label: '值-不等于[value!=]'},
|
||||
{value: 'value_in', label: '值-包含[include=]'},
|
||||
{value: 'length_eq', label: '长度-等于[length=]'},
|
||||
{value: 'length_not_eq', label: '长度-不等于[length!=]'},
|
||||
{value: 'length_gt', label: '长度-大于[length>]'},
|
||||
{value: 'length_lt', label: '长度-小于[length<]'},
|
||||
{value: 'regular', label: '正则匹配'}
|
||||
],
|
||||
typeSelectOptions: [
|
||||
{value: 'object', label: 'object'},
|
||||
{value: 'array', label: 'array'},
|
||||
{value: 'string', label: 'string'},
|
||||
{value: 'int', label: 'int'},
|
||||
{value: 'number', label: 'number'},
|
||||
|
||||
],
|
||||
requiredSelectOptions: [
|
||||
{value: true, label: '必填'},
|
||||
{value: false, label: '非必填'},
|
||||
],
|
||||
checked: false,
|
||||
tableData: Array,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.document.type === "JSON") {
|
||||
this.checked = this.document.data.jsonFollowAPI ? true : false;
|
||||
} else if (this.document.type === "XML") {
|
||||
this.checked = this.document.data.xmlFollowAPI ? true : false;
|
||||
}
|
||||
this.changeData();
|
||||
},
|
||||
watch: {
|
||||
'document.type'() {
|
||||
if (this.document.type === "JSON") {
|
||||
this.checked = this.document.data.jsonFollowAPI ? true : false;
|
||||
} else if (this.document.type === "XML") {
|
||||
this.checked = this.document.data.xmlFollowAPI ? true : false;
|
||||
}
|
||||
this.changeData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setJSONData(data) {
|
||||
this.checked = false;
|
||||
this.tableData = data;
|
||||
if (this.document.type === "JSON") {
|
||||
this.document.data.json = this.tableData;
|
||||
this.document.data.jsonFollowAPI = this.apiId;
|
||||
} else if (this.document.type === "XML") {
|
||||
this.document.data.xml = this.tableData;
|
||||
this.document.data.xmlFollowAPI = this.apiId;
|
||||
}
|
||||
},
|
||||
checkedAPI() {
|
||||
this.changeData();
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
getAPI() {
|
||||
let url = "/api/definition/getDocument/" + this.apiId + "/" + this.document.type;
|
||||
this.$get(url, response => {
|
||||
if (response.data) {
|
||||
this.tableData = response.data;
|
||||
}
|
||||
});
|
||||
},
|
||||
changeData() {
|
||||
if (this.document.data) {
|
||||
this.tableData = [];
|
||||
if (this.document.type === "JSON") {
|
||||
this.document.data.jsonFollowAPI = this.checked ? this.apiId : "";
|
||||
if (this.document.data.jsonFollowAPI) {
|
||||
this.getAPI();
|
||||
} else {
|
||||
this.tableData = this.document.data.json;
|
||||
}
|
||||
} else if (this.document.type === "XML") {
|
||||
this.document.data.xmlFollowAPI = this.checked ? this.apiId : "";
|
||||
if (this.document.data.xmlFollowAPI) {
|
||||
this.getAPI();
|
||||
} else {
|
||||
this.tableData = this.document.data.xml;
|
||||
}
|
||||
}
|
||||
this.reload();
|
||||
}
|
||||
if (this.tableData && this.tableData.length > 0) {
|
||||
this.tableData.forEach(row => {
|
||||
if (row.name == null || row.name === "") {
|
||||
this.remove(row);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
objectSpanMethod({row, column, rowIndex, columnIndex}) {
|
||||
if (columnIndex === 0 || columnIndex === 1 || columnIndex === 2 || columnIndex === 3 || columnIndex === 4) {
|
||||
return {
|
||||
rowspan: row.rowspan,
|
||||
colspan: row.rowspan > 0 ? 1 : 0
|
||||
};
|
||||
}
|
||||
},
|
||||
changeType(row) {
|
||||
row.children = [];
|
||||
},
|
||||
getValue(key) {
|
||||
let value = "";
|
||||
this.verificationOptions.forEach(item => {
|
||||
if (key === item.value) {
|
||||
value = item.label;
|
||||
}
|
||||
})
|
||||
return value;
|
||||
},
|
||||
renderHeader(h, {column}) {
|
||||
return h(
|
||||
'span', [
|
||||
h('el-checkbox', {
|
||||
style: 'margin-right:5px;',
|
||||
on: {
|
||||
change: this.handleCheckAllChange
|
||||
}
|
||||
}),
|
||||
h('span', column.label)
|
||||
]
|
||||
)
|
||||
},
|
||||
renderHeaderType(h, {column}) {
|
||||
return h(
|
||||
'span', [
|
||||
h('el-checkbox', {
|
||||
style: 'margin-right:5px;',
|
||||
on: {
|
||||
change: this.handleType
|
||||
}
|
||||
}),
|
||||
h('span', column.label)
|
||||
]
|
||||
)
|
||||
},
|
||||
renderHeaderArray(h, {column}) {
|
||||
return h(
|
||||
'span', [
|
||||
h('el-checkbox', {
|
||||
style: 'margin-right:5px;',
|
||||
on: {
|
||||
change: this.handleArray
|
||||
}
|
||||
}),
|
||||
h('span', column.label)
|
||||
]
|
||||
)
|
||||
},
|
||||
childrenChecked(arr, type, val) {
|
||||
if (arr && arr.length > 0) {
|
||||
arr.forEach(item => {
|
||||
if (type === 1) {
|
||||
item.include = val
|
||||
}
|
||||
if (type === 2) {
|
||||
item.typeVerification = val
|
||||
}
|
||||
if (type === 3) {
|
||||
item.arrayVerification = val
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
this.childrenChecked(item.children, type, val);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
handleCheckAllChange(val) {
|
||||
this.tableData.forEach(item => {
|
||||
item.include = val;
|
||||
this.childrenChecked(item.children, 1, val);
|
||||
})
|
||||
},
|
||||
handleType(val) {
|
||||
this.tableData.forEach(item => {
|
||||
item.typeVerification = val;
|
||||
this.childrenChecked(item.children, 2, val);
|
||||
})
|
||||
},
|
||||
handleArray(val) {
|
||||
this.tableData.forEach(item => {
|
||||
item.arrayVerification = val;
|
||||
this.childrenChecked(item.children, 3, val);
|
||||
})
|
||||
},
|
||||
handleCheckOneChange(val) {
|
||||
},
|
||||
importData() {
|
||||
this.$refs.import.openOneClickOperation();
|
||||
},
|
||||
remove(row) {
|
||||
this.removeTableRow(row);
|
||||
},
|
||||
addRow(row) {
|
||||
//首先保存当前行内容
|
||||
row.type = "object";
|
||||
let newRow = this.getNewRow();
|
||||
row.children.push(newRow);
|
||||
},
|
||||
verSet(arr, row) {
|
||||
// 第三条
|
||||
if (row.groupId) {
|
||||
const index = arr.findIndex(d => d.id === row.groupId);
|
||||
if (index !== -1) {
|
||||
arr[index].rowspan = arr[index].rowspan + 1;
|
||||
}
|
||||
} else if (row.rowspan > 0) {
|
||||
const index = arr.findIndex(d => d.id === row.id);
|
||||
if (index !== -1) {
|
||||
arr[index].rowspan = arr[index].rowspan + 1;
|
||||
}
|
||||
} else {
|
||||
row.rowspan = 2;
|
||||
}
|
||||
arr.forEach(item => {
|
||||
// 找到当前行的位置
|
||||
if (item.id === row.id) {
|
||||
let newRow = JSON.parse(JSON.stringify(row));
|
||||
newRow.id = getUUID();
|
||||
newRow.groupId = !row.groupId ? row.id : row.groupId;
|
||||
newRow.rowspan = 0;
|
||||
if (row.type !== "object" || row.type !== "array") {
|
||||
newRow.children = [];
|
||||
}
|
||||
const index = arr.findIndex(d => d.id === item.id);
|
||||
if (index !== -1) {
|
||||
arr.splice(index + 1, 0, newRow);
|
||||
} else {
|
||||
arr.push(newRow);
|
||||
}
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
this.verSet(item.children, row);
|
||||
}
|
||||
})
|
||||
},
|
||||
addVerification(row) {
|
||||
if (!row.groupId && row.rowspan == 0) {
|
||||
row.rowspan = 2;
|
||||
}
|
||||
this.verSet(this.tableData, row);
|
||||
},
|
||||
confirm(row) {
|
||||
this.validateRowData(row) ? row.status = '' : row.status;
|
||||
if (row.status === "") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
getNewRow() {
|
||||
let row = {
|
||||
id: getUUID(),
|
||||
name: "",
|
||||
include: false,
|
||||
status: true,
|
||||
typeVerification: false,
|
||||
type: "string",
|
||||
groupId: "",
|
||||
rowspan: 1,
|
||||
arrayVerification: false,
|
||||
contentVerification: "none",
|
||||
expectedOutcome: "",
|
||||
children: []
|
||||
};
|
||||
return row;
|
||||
},
|
||||
validateRowData(row) {
|
||||
if (row.name == null || row.name === '') {
|
||||
this.$warning("参数名" + "不能为空,且不能包含英文小数点[.]");
|
||||
return false;
|
||||
} else if (row.name.indexOf(".") > 0) {
|
||||
this.$warning("参数名[" + row.name + "]不合法,不能包含英文小数点\".\"!");
|
||||
return false;
|
||||
} else if (row.type == null || row.type === '') {
|
||||
this.$warning("类型" + "不能为空!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
editRow(row, column, cell) {
|
||||
if (this.checked) {
|
||||
return;
|
||||
}
|
||||
column.fixed = true;
|
||||
row.status = true;
|
||||
},
|
||||
editLeave(row, column, cell, event) {
|
||||
column.fixed = false;
|
||||
row.status = false;
|
||||
},
|
||||
removeVerSet(arr, row) {
|
||||
// 第三条
|
||||
if (!row.groupId) {
|
||||
const index = arr.findIndex(d => d.groupId === row.id);
|
||||
if (index !== -1) {
|
||||
// 把合并权限让出去
|
||||
arr[index].rowspan = row.rowspan - 1;
|
||||
arr[index].id = row.id;
|
||||
arr[index].groupId = "";
|
||||
}
|
||||
} else if (row.groupId) {
|
||||
const index = arr.findIndex(d => d.id === row.groupId);
|
||||
if (index !== -1) {
|
||||
arr[index].rowspan = arr[index].rowspan - 1;
|
||||
}
|
||||
} else if (row.rowspan > 1) {
|
||||
const index = arr.findIndex(d => d.id === row.id);
|
||||
if (index !== -1) {
|
||||
arr[index].rowspan = arr[index].rowspan - 1;
|
||||
}
|
||||
} else {
|
||||
row.rowspan = 1;
|
||||
}
|
||||
arr.forEach(item => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
this.removeVerSet(item.children, row);
|
||||
}
|
||||
})
|
||||
},
|
||||
removeTableRow(row) {
|
||||
this.removeVerSet(this.tableData, row);
|
||||
this.removeFromDataStruct(this.tableData, row);
|
||||
},
|
||||
removeFromDataStruct(dataStruct, row) {
|
||||
if (dataStruct == null || dataStruct.length === 0) {
|
||||
return;
|
||||
}
|
||||
let rowIndex = dataStruct.indexOf(row);
|
||||
if (rowIndex >= 0) {
|
||||
dataStruct.splice(rowIndex, 1);
|
||||
} else {
|
||||
dataStruct.forEach(itemData => {
|
||||
if (itemData.children != null && itemData.children.length > 0) {
|
||||
this.removeFromDataStruct(itemData.children, row);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ms-import {
|
||||
margin: 10px
|
||||
}
|
||||
|
||||
.ms-import:hover {
|
||||
cursor: pointer;
|
||||
border-color: #783887;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
|
||||
<el-col>
|
||||
<el-select v-model="document.type" size="small">
|
||||
<el-option label="JSON" value="JSON"/>
|
||||
<el-option label="XML" value="XML"/>
|
||||
</el-select>
|
||||
</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"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add" v-else>
|
||||
{{ $t('api_test.request.assertions.add') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "DocumentHeader",
|
||||
|
||||
props: {
|
||||
value: [Number, String],
|
||||
document: {},
|
||||
edit: Boolean,
|
||||
callback: Function,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
if (this.document.type === "JSON" && this.document.data.json.length === 0) {
|
||||
let obj = {
|
||||
id: "root",
|
||||
name: "root",
|
||||
status: true,
|
||||
groupId: "",
|
||||
rowspan: 1,
|
||||
include: false,
|
||||
typeVerification: false,
|
||||
type: "object",
|
||||
arrayVerification: false,
|
||||
contentVerifications: "none",
|
||||
expectedOutcome: "",
|
||||
children: []
|
||||
};
|
||||
this.document.data.json.push(obj);
|
||||
}
|
||||
if (this.document.type === "XML" && this.document.data.xml.length === 0) {
|
||||
let obj = {
|
||||
id: getUUID(),
|
||||
name: "root",
|
||||
status: true,
|
||||
groupId: "",
|
||||
rowspan: 1,
|
||||
include: false,
|
||||
typeVerification: false,
|
||||
type: "object",
|
||||
arrayVerification: false,
|
||||
contentVerifications: "none",
|
||||
expectedOutcome: "",
|
||||
children: []
|
||||
};
|
||||
this.document.data.xml.push(obj);
|
||||
}
|
||||
this.callback();
|
||||
},
|
||||
remove() {
|
||||
},
|
||||
change(value) {
|
||||
|
||||
},
|
||||
input(value) {
|
||||
|
||||
},
|
||||
validate() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.assertion-btn {
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('commons.import') + document.type"
|
||||
:visible.sync="importVisible"
|
||||
width="50%"
|
||||
append-to-body
|
||||
show-close
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleClose">
|
||||
<div style="height: 400px">
|
||||
<ms-code-edit
|
||||
:mode="mode"
|
||||
:data.sync="json" theme="eclipse" :modes="[]"
|
||||
ref="codeEdit"/>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<ms-dialog-footer
|
||||
@cancel="importVisible = false"
|
||||
@confirm="saveConfirm"/>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsDialogFooter from '../../../../../common/components/MsDialogFooter'
|
||||
import MsCodeEdit from "../../../../../common/components/MsCodeEdit";
|
||||
import json5 from 'json5';
|
||||
import MsConvert from "@/business/components/common/json-schema/convert/convert";
|
||||
|
||||
export default {
|
||||
name: "MsDocumentImport",
|
||||
components: {MsDialogFooter, MsCodeEdit},
|
||||
data() {
|
||||
return {
|
||||
importVisible: false,
|
||||
activeName: "JSON",
|
||||
mode: "json",
|
||||
json: "",
|
||||
};
|
||||
},
|
||||
watch: {},
|
||||
props: {
|
||||
document: {}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
openOneClickOperation() {
|
||||
this.mode = this.document.type.toLowerCase();
|
||||
this.importVisible = true;
|
||||
},
|
||||
checkIsJson(json) {
|
||||
try {
|
||||
json5.parse(json);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
/*
|
||||
* 验证xml格式的正确性
|
||||
*/
|
||||
validateXML(xmlContent) {
|
||||
//errorCode 0是xml正确,1是xml错误,2是无法验证
|
||||
let xmlDoc, errorMessage, errorCode = 0;
|
||||
if (document.implementation.createDocument) {
|
||||
let parser = new DOMParser();
|
||||
xmlDoc = parser.parseFromString(xmlContent, "text/xml");
|
||||
let error = xmlDoc.getElementsByTagName("parsererror");
|
||||
if (error.length > 0) {
|
||||
if (xmlDoc.documentElement.nodeName == "parsererror") {
|
||||
errorCode = 1;
|
||||
errorMessage = xmlDoc.documentElement.childNodes[0].nodeValue;
|
||||
} else {
|
||||
errorCode = 1;
|
||||
errorMessage = xmlDoc.getElementsByTagName("parsererror")[0].innerHTML;
|
||||
}
|
||||
} else {
|
||||
errorMessage = "格式正确";
|
||||
}
|
||||
} else {
|
||||
errorCode = 2;
|
||||
errorMessage = "浏览器不支持验证,无法验证xml正确性";
|
||||
}
|
||||
return {
|
||||
"msg": errorMessage,
|
||||
"error_code": errorCode
|
||||
};
|
||||
},
|
||||
saveConfirm() {
|
||||
if (this.document.type === "JSON" && !this.checkIsJson(this.json)) {
|
||||
this.$error("导入的数据非JSON格式");
|
||||
return;
|
||||
}
|
||||
if (this.document.type === "XML" && this.validateXML(this.json).error_code > 0) {
|
||||
this.$error("导入的数据非XML格式");
|
||||
return;
|
||||
}
|
||||
let url = "/api/definition/jsonGenerator";
|
||||
this.$post(url, {raw: this.json, type: this.document.type}, response => {
|
||||
if (response.data) {
|
||||
console.log(response.data)
|
||||
this.$emit('setJSONData', response.data);
|
||||
}
|
||||
});
|
||||
this.importVisible = false;
|
||||
},
|
||||
handleClose() {
|
||||
this.importVisible = false;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -137,6 +137,8 @@ export default {
|
|||
let data = MsConvert.format(JSON.parse(this.body.raw));
|
||||
if (this.body.jsonSchema) {
|
||||
this.body.jsonSchema = this.deepAssign(this.body.jsonSchema, data);
|
||||
}else{
|
||||
this.body.jsonSchema = data;
|
||||
}
|
||||
} catch (ex) {
|
||||
this.body.jsonSchema = "";
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<ms-jmx-step :request="api.request" :response="responseData"/>
|
||||
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
|
||||
</el-card>
|
||||
|
||||
<!-- 加载用例 -->
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
<ms-request-result-tail :response="responseData" ref="runResult"/>
|
||||
</div>
|
||||
|
||||
<ms-jmx-step :request="api.request" :response="responseData"/>
|
||||
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
|
||||
|
||||
</el-card>
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<ms-request-result-tail :response="responseData" :currentProtocol="currentProtocol" ref="runResult"/>
|
||||
</div>
|
||||
|
||||
<ms-jmx-step :request="api.request" :response="responseData"/>
|
||||
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
|
||||
|
||||
</el-card>
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
</el-form>
|
||||
|
||||
<ms-jmx-step :request="api.request" :response="responseData"/>
|
||||
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
|
||||
|
||||
<div v-if="api.method=='ESB'">
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}}</p>
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
@copyRow="copyRow"
|
||||
@remove="remove"
|
||||
:response="response"
|
||||
:apiId="apiId"
|
||||
:is-read-only="isReadOnly"
|
||||
:assertions="row"/>
|
||||
</div>
|
||||
|
@ -97,6 +98,7 @@ export default {
|
|||
props: {
|
||||
request: {},
|
||||
response: {},
|
||||
apiId: String,
|
||||
showScript: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
|
|
|
@ -103,6 +103,7 @@ export const ASSERTION_TYPE = {
|
|||
DURATION: "Duration",
|
||||
JSR223: "JSR223",
|
||||
XPATH2: "XPath2",
|
||||
DOCUMENT: "Document",
|
||||
}
|
||||
|
||||
export const ASSERTION_REGEX_SUBJECT = {
|
||||
|
@ -126,8 +127,7 @@ export class BaseConfig {
|
|||
if (!(this[name] instanceof Array)) {
|
||||
if (notUndefined === true) {
|
||||
this[name] = options[name] === undefined ? this[name] : options[name];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this[name] = options[name];
|
||||
}
|
||||
}
|
||||
|
@ -782,6 +782,7 @@ export class Assertions extends BaseConfig {
|
|||
this.xpath2 = [];
|
||||
this.duration = undefined;
|
||||
this.enable = true;
|
||||
this.document = {type: "JSON", data: {xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: []}};
|
||||
this.set(options);
|
||||
this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options);
|
||||
}
|
||||
|
@ -846,7 +847,7 @@ export class JSR223Processor extends BaseConfig {
|
|||
this.resourceId = uuid();
|
||||
this.active = false;
|
||||
this.type = "JSR223Processor";
|
||||
this.label="";
|
||||
this.label = "";
|
||||
this.script = undefined;
|
||||
this.scriptLanguage = "beanshell";
|
||||
this.enable = true;
|
||||
|
@ -1115,7 +1116,7 @@ export class TransactionController extends Controller {
|
|||
label() {
|
||||
if (this.isValid()) {
|
||||
let label = this.$t('api_test.automation.transcation_controller');
|
||||
if(this.name != null && this.name !== ""){
|
||||
if (this.name != null && this.name !== "") {
|
||||
label = this.name;
|
||||
}
|
||||
return label;
|
||||
|
|
|
@ -955,6 +955,7 @@ export default {
|
|||
add_data: "Add Data"
|
||||
},
|
||||
request: {
|
||||
document_structure: "Document structure verification",
|
||||
grade_info: "Filter by rank",
|
||||
grade_order_asc: "from low to high by use case level",
|
||||
grade_order_desc: "from high to low by use case level",
|
||||
|
|
|
@ -963,6 +963,7 @@ export default {
|
|||
add_data: "去添加"
|
||||
},
|
||||
request: {
|
||||
document_structure: "文档结构校验",
|
||||
grade_info: "按等级筛选",
|
||||
grade_order_asc: "按用例等级从低到高",
|
||||
grade_order_desc: "按用例等级从高到低",
|
||||
|
|
|
@ -961,6 +961,7 @@ export default {
|
|||
add_data: "去添加"
|
||||
},
|
||||
request: {
|
||||
document_structure: "文檔結構校驗",
|
||||
grade_info: "按等級篩選",
|
||||
grade_order_asc: "按用例等級從低到高",
|
||||
grade_order_desc: "按用例等級從高到低",
|
||||
|
|
Loading…
Reference in New Issue