feat(TCPMock服务): 参考HTTP MOCK功能新增TCP MOCK功能#1001753

https://www.tapd.cn/55049933/prong/stories/view/1155049933001001753#
This commit is contained in:
song-tianyang 2021-08-17 18:51:01 +08:00 committed by 刘瑞斌
parent 54ba05d9f4
commit a326c7fd77
26 changed files with 1799 additions and 205 deletions

View File

@ -292,12 +292,7 @@ public class ApiDefinitionController {
@GetMapping("/getMockEnvironment/{projectId}/{protocal}")
public ApiTestEnvironmentWithBLOBs getMockEnvironment(@PathVariable String projectId, @PathVariable String protocal, HttpServletRequest request) {
String requestUrl = request.getRequestURL().toString();
String baseUrl = "";
if (requestUrl.contains("/api/definition")) {
baseUrl = requestUrl.split("/api/definition")[0];
}
return apiTestEnvironmentService.getMockEnvironmentByProjectId(projectId, protocal, baseUrl);
return apiTestEnvironmentService.getMockEnvironmentByProjectId(projectId);
}
}

View File

@ -86,4 +86,8 @@ public class ApiTestEnvironmentController {
apiTestEnvironmentService.delete(id);
}
@GetMapping("/getTcpMockInfo/{projectId}")
public String getMockInfo(@PathVariable(value = "projectId") String projectId) {
return apiTestEnvironmentService.getMockInfo(projectId);
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.automation;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
@ -177,4 +178,22 @@ public class EsbDataStruct {
}
return element;
}
public List<String> getNameDeep() {
List<String> returnList = new ArrayList<>();
if(StringUtils.isNotEmpty(this.name)){
returnList.add(this.name);
}
if(CollectionUtils.isNotEmpty(this.children)){
for (EsbDataStruct child :this.children) {
List<String> itemNameList = child.getNameDeep();
for (String itemName :itemNameList) {
if(!returnList.contains(itemName)){
returnList.add(itemName);
}
}
}
}
return returnList;
}
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.automation;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
@ -11,7 +12,7 @@ import java.util.List;
import java.util.UUID;
/**
* //ESB数据格式
* 树形表格数据格式
*
* @author song.tianyang
* @Date 2021/3/15 4:37 下午
@ -181,4 +182,22 @@ public class TcpTreeTableDataStruct {
}
return element;
}
public List<String> getNameDeep() {
List<String> returnList = new ArrayList<>();
if(StringUtils.isNotEmpty(this.name)){
returnList.add(this.name);
}
if(CollectionUtils.isNotEmpty(this.children)){
for (TcpTreeTableDataStruct child :this.children) {
List<String> itemNameList = child.getNameDeep();
for (String itemName :itemNameList) {
if(!returnList.contains(itemName)){
returnList.add(itemName);
}
}
}
}
return returnList;
}
}

View File

@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.ApiTestEnvironmentDTO;
import io.metersphere.api.dto.mockconfig.MockConfigStaticData;
import io.metersphere.api.tcp.TCPPool;
import io.metersphere.base.domain.ApiTestEnvironmentExample;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.base.domain.Project;
@ -138,10 +139,12 @@ public class ApiTestEnvironmentService {
* @param projectId
* @return
*/
public synchronized ApiTestEnvironmentWithBLOBs getMockEnvironmentByProjectId(String projectId, String protocal, String baseUrl) {
//创建的时候检查当前站点
public synchronized ApiTestEnvironmentWithBLOBs getMockEnvironmentByProjectId(String projectId) {
SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class);
BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo();
String baseUrl = baseSystemConfigDTO.getUrl();
String protocal = "http";
if (baseSystemConfigDTO != null && StringUtils.isNotEmpty(baseSystemConfigDTO.getUrl())) {
baseUrl = baseSystemConfigDTO.getUrl();
if (baseUrl.startsWith("http:")) {
@ -170,6 +173,8 @@ public class ApiTestEnvironmentService {
private ApiTestEnvironmentWithBLOBs checkMockEvnIsRightful(ApiTestEnvironmentWithBLOBs returnModel, String protocal, String projectId,String projectNumber, String name, String url) {
boolean needUpdate = false;
ProjectService projectService = CommonBeanFactory.getBean(ProjectService.class);
Project project = projectService.getProjectById(projectId);
if (returnModel.getConfig() != null) {
try {
JSONObject configObj = JSONObject.parseObject(returnModel.getConfig());
@ -207,6 +212,26 @@ public class ApiTestEnvironmentService {
}
}
}
if(project.getMockTcpPort() != null && project.getMockTcpPort().intValue() != 0){
if(configObj.containsKey("tcpConfig")){
if(configObj.containsKey("port")){
if(configObj.getInteger("port").intValue() != project.getMockTcpPort().intValue()){
needUpdate = true;
}
}else {
needUpdate = true;
}
if(configObj.containsKey("server")){
if(!StringUtils.equals(configObj.getString("server"),url)){
needUpdate = true;
}
}else {
needUpdate = true;
}
}
}
} catch (Exception e) {
needUpdate = true;
e.printStackTrace();
@ -214,21 +239,35 @@ public class ApiTestEnvironmentService {
}
if (needUpdate) {
String id = returnModel.getId();
returnModel = this.genHttpApiTestEnvironmentByUrl(projectId,projectNumber, protocal, name, url);
returnModel = this.genHttpApiTestEnvironmentByUrl(project,projectNumber, protocal, name, url);
returnModel.setId(id);
apiTestEnvironmentMapper.updateByPrimaryKeyWithBLOBs(returnModel);
}
return returnModel;
}
private ApiTestEnvironmentWithBLOBs genHttpApiTestEnvironmentByUrl(String projectId,String projectNumber, String protocal, String name, String url) {
private ApiTestEnvironmentWithBLOBs genHttpApiTestEnvironmentByUrl(String projectId,String projectNumber, String protocal, String name, String baseUrl) {
ProjectService projectService = CommonBeanFactory.getBean(ProjectService.class);
Project project = projectService.getProjectById(projectId);
if(project != null){
return this.genHttpApiTestEnvironmentByUrl(project,projectNumber, protocal, name, baseUrl);
}
return null;
}
private ApiTestEnvironmentWithBLOBs genHttpApiTestEnvironmentByUrl(Project project,String projectNumber, String protocal, String name, String baseUrl) {
String socket = "";
String url = baseUrl;
if (url.startsWith("http://")) {
url = url.substring(7);
} else if (url.startsWith("https://")) {
url = url.substring(8);
}
socket = url;
String tcpSocket = socket;
if(StringUtils.isNotEmpty(tcpSocket) && tcpSocket.contains(":")){
tcpSocket = socket.split(":")[0];
}
String portStr = "";
String ipStr = url;
@ -301,6 +340,10 @@ public class ApiTestEnvironmentService {
tcpConfigObj.put("reUseConnection", false);
tcpConfigObj.put("nodelay", false);
tcpConfigObj.put("closeConnection", false);
if(project.getMockTcpPort() != null && project.getMockTcpPort().intValue() != 0){
tcpConfigObj.put("server", tcpSocket);
tcpConfigObj.put("port", 12138);
}
JSONObject object = new JSONObject();
object.put("commonConfig", commonConfigObj);
@ -309,7 +352,7 @@ public class ApiTestEnvironmentService {
object.put("tcpConfig", tcpConfigObj);
ApiTestEnvironmentWithBLOBs blobs = new ApiTestEnvironmentWithBLOBs();
blobs.setProjectId(projectId);
blobs.setProjectId(project.getId());
blobs.setName(name);
blobs.setConfig(object.toString());
@ -353,4 +396,36 @@ public class ApiTestEnvironmentService {
}
return null;
}
public String getMockInfo(String projectId) {
String returnStr = "";
ApiTestEnvironmentWithBLOBs mockEnv = this.getMockEnvironmentByProjectId(projectId);
if (mockEnv != null && mockEnv.getConfig() != null) {
try {
JSONObject configObj = JSONObject.parseObject(mockEnv.getConfig());
if(configObj.containsKey("tcpConfig")){
JSONObject tcpConfigObj = configObj.getJSONObject("tcpConfig");
int tcpPort = 0;
if(tcpConfigObj.containsKey("port")){
tcpPort = tcpConfigObj.getInteger("port").intValue();
if(tcpPort == 0 || !TCPPool.isTcpOpen(tcpPort)){
return returnStr;
}
}else {
return returnStr;
}
if(tcpConfigObj.containsKey("server")){
String server = tcpConfigObj.getString("server");
returnStr = server +":"+ tcpPort;
}else {
return returnStr;
}
}
}catch (Exception e){
e.printStackTrace();
}
}
return returnStr;
}
}

View File

@ -4,6 +4,9 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import io.metersphere.api.dto.automation.EsbDataStruct;
import io.metersphere.api.dto.automation.TcpTreeTableDataStruct;
import io.metersphere.api.dto.automation.parse.TcpTreeTableDataParser;
import io.metersphere.api.dto.mockconfig.MockConfigRequest;
import io.metersphere.api.dto.mockconfig.MockExpectConfigRequest;
import io.metersphere.api.dto.mockconfig.response.JsonSchemaReturnObj;
@ -12,22 +15,28 @@ import io.metersphere.api.dto.mockconfig.response.MockExpectConfigResponse;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.MockConfigMapper;
import io.metersphere.base.mapper.MockExpectConfigMapper;
import io.metersphere.base.mapper.ProjectMapper;
import io.metersphere.base.mapper.ext.ExtMockExpectConfigMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.JsonPathUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.jmeter.utils.ScriptEngineUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.XML;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.xml.sax.InputSource;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Service
@ -39,7 +48,11 @@ public class MockConfigService {
@Resource
private MockExpectConfigMapper mockExpectConfigMapper;
@Resource
private ExtMockExpectConfigMapper extMockExpectConfigMapper;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ProjectMapper projectMapper;
public MockConfigResponse findByApiIdList(List<String> apiIdList) {
if (apiIdList.isEmpty()) {
@ -182,7 +195,7 @@ public class MockConfigService {
JSONObject requestObj = model.getRequest();
boolean isJsonParam = requestObj.getBoolean("jsonParam");
if (isJsonParam) {
if(StringUtils.isEmpty(requestObj.getString("jsonData"))){
if (StringUtils.isEmpty(requestObj.getString("jsonData"))) {
return model;
}
} else {
@ -219,13 +232,13 @@ public class MockConfigService {
if (isJsonParam) {
String jsonParams = requestObj.getString("jsonData");
JSONValidator jsonValidator = JSONValidator.from(jsonParams);
if(StringUtils.equalsIgnoreCase("Array",jsonValidator.getType().name())){
if (StringUtils.equalsIgnoreCase("Array", jsonValidator.getType().name())) {
JSONArray mockExpectArr = JSONArray.parseArray(jsonParams);
for(int expectIndex = 0;expectIndex < mockExpectArr.size(); expectIndex ++){
for (int expectIndex = 0; expectIndex < mockExpectArr.size(); expectIndex++) {
JSONObject itemObj = mockExpectArr.getJSONObject(expectIndex);
mockExpectJson = itemObj;
}
}else if(StringUtils.equalsIgnoreCase("Object",jsonValidator.getType().name())){
} else if (StringUtils.equalsIgnoreCase("Object", jsonValidator.getType().name())) {
JSONObject mockExpectJsonItem = JSONObject.parseObject(jsonParams);
mockExpectJson = mockExpectJsonItem;
}
@ -247,7 +260,7 @@ public class MockConfigService {
}
}
boolean mathing = JsonPathUtils.checkJsonObjCompliance(reqJsonObj, mockExpectJson);
boolean mathing = JsonStructUtils.checkJsonObjCompliance(reqJsonObj, mockExpectJson);
if (mathing) {
returnModel = model;
break;
@ -271,7 +284,7 @@ public class MockConfigService {
boolean isJsonParam = requestObj.getBoolean("jsonParam");
if (isJsonParam) {
if(StringUtils.isEmpty(requestObj.getString("jsonData"))){
if (StringUtils.isEmpty(requestObj.getString("jsonData"))) {
return model;
}
} else {
@ -308,18 +321,18 @@ public class MockConfigService {
if (isJsonParam) {
String jsonParams = requestObj.getString("jsonData");
JSONValidator jsonValidator = JSONValidator.from(jsonParams);
if(StringUtils.equalsIgnoreCase("Array",jsonValidator.getType().name())){
if (StringUtils.equalsIgnoreCase("Array", jsonValidator.getType().name())) {
JSONArray mockExpectArr = JSONArray.parseArray(jsonParams);
for(int expectIndex = 0;expectIndex < mockExpectArr.size(); expectIndex ++){
for (int expectIndex = 0; expectIndex < mockExpectArr.size(); expectIndex++) {
JSONObject itemObj = mockExpectArr.getJSONObject(expectIndex);
mathing = JsonPathUtils.checkJsonArrayCompliance(reqJsonArray, itemObj);
if(!mathing){
mathing = JsonStructUtils.checkJsonArrayCompliance(reqJsonArray, itemObj);
if (!mathing) {
break;
}
}
}else if(StringUtils.equalsIgnoreCase("Object",jsonValidator.getType().name())){
} else if (StringUtils.equalsIgnoreCase("Object", jsonValidator.getType().name())) {
JSONObject mockExpectJson = JSONObject.parseObject(jsonParams);
mathing = JsonPathUtils.checkJsonArrayCompliance(reqJsonArray, mockExpectJson);
mathing = JsonStructUtils.checkJsonArrayCompliance(reqJsonArray, mockExpectJson);
}
} else {
@ -339,7 +352,7 @@ public class MockConfigService {
mockExpectJson.put(name, value);
}
}
mathing = JsonPathUtils.checkJsonArrayCompliance(reqJsonArray, mockExpectJson);
mathing = JsonStructUtils.checkJsonArrayCompliance(reqJsonArray, mockExpectJson);
}
if (mathing) {
returnModel = model;
@ -393,9 +406,9 @@ public class MockConfigService {
public String updateHttpServletResponse(List<ApiDefinitionWithBLOBs> apis, HttpServletResponse response) {
String returnStr = "";
try {
if(CollectionUtils.isEmpty(apis)){
if (CollectionUtils.isEmpty(apis)) {
response.setStatus(404);
}else {
} else {
for (ApiDefinitionWithBLOBs api : apis) {
int status = 404;
if (api.getResponse() != null) {
@ -509,7 +522,7 @@ public class MockConfigService {
}
}
}
if(StringUtils.isNotEmpty(returnStr) && status == 404){
if (StringUtils.isNotEmpty(returnStr) && status == 404) {
status = 200;
}
response.setStatus(status);
@ -524,8 +537,8 @@ public class MockConfigService {
private JSONObject parseJsonSchema(JSONObject bodyReturnObj) {
JSONObject returnObj = new JSONObject();
if(bodyReturnObj == null){
return returnObj;
if (bodyReturnObj == null) {
return returnObj;
}
Set<String> keySet = bodyReturnObj.keySet();
@ -556,7 +569,7 @@ public class MockConfigService {
}
}
}
}else {
} else {
String values = obj.getMockValue();
if (StringUtils.isEmpty(values)) {
values = "";
@ -612,9 +625,9 @@ public class MockConfigService {
try {
String param = this.getRequestPostStr(request);
JSONValidator jsonValidator = JSONValidator.from(param);
if(StringUtils.equalsIgnoreCase("Array",jsonValidator.getType().name())){
if (StringUtils.equalsIgnoreCase("Array", jsonValidator.getType().name())) {
returnJson = JSONArray.parseArray(param);
}else if(StringUtils.equalsIgnoreCase("Object",jsonValidator.getType().name())){
} else if (StringUtils.equalsIgnoreCase("Object", jsonValidator.getType().name())) {
returnJson = JSONObject.parseObject(param);
}
} catch (Exception e) {
@ -732,39 +745,51 @@ public class MockConfigService {
}
}
// List<String> sendParams = new ArrayList<>();
// for (String param : pathArr) {
// if (param.startsWith("{") && param.endsWith("}")) {
// param = param.substring(1, param.length() - 1);
// sendParams.add(param);
// }
// }
// try {
// JSONObject requestJson = JSONObject.parseObject(api.getRequest());
// if (requestJson.containsKey("rest")) {
// JSONArray jsonArray = requestJson.getJSONArray("rest");
// for (int i = 0; i < jsonArray.size(); i++) {
// JSONObject object = jsonArray.getJSONObject(i);
// if (object.containsKey("name") && object.containsKey("enable") && object.getBoolean("enable")) {
// String name = object.getString("name");
// if (sendParams.contains(name)) {
// String value = "";
// if (object.containsKey("value")) {
// value = object.getString("value");
// }
// returnJson.put(name, value);
// }
// }
// }
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
}
return returnJson;
}
public List<Map<String, String>> getApiParamsByApiDefinitionBLOBs(ApiDefinitionWithBLOBs apiModel) {
if (apiModel == null) {
return new ArrayList<>();
} else if (StringUtils.equalsIgnoreCase("tcp", apiModel.getMethod())) {
return this.getTCPApiParams(apiModel);
} else {
return this.getHTTPApiParams(apiModel);
}
}
private List<Map<String, String>> getTCPApiParams(ApiDefinitionWithBLOBs apiModel) {
List<Map<String, String>> list = new ArrayList<>();
List<String> paramNameList = new ArrayList<>();
if (apiModel != null) {
if (apiModel.getRequest() != null) {
JSONObject requestObj = this.genJSONObject(apiModel.getRequest());
if (requestObj != null && requestObj.containsKey("reportType")) {
String reportType = requestObj.getString("reportType");
if (StringUtils.equalsIgnoreCase(reportType, "xml") && requestObj.containsKey("xmlDataStruct")) {
paramNameList = this.parseByTcpTreeDataStruct(requestObj.getString("xmlDataStruct"));
} else if (StringUtils.equalsIgnoreCase(reportType, "json") && requestObj.containsKey("jsonDataStruct")) {
paramNameList = this.parseByJsonDataStruct(requestObj.getString("jsonDataStruct"));
} else if (requestObj.containsKey("protocol")) {
String protocol = requestObj.getString("protocol");
if (StringUtils.equalsIgnoreCase("ESB", protocol) && requestObj.containsKey("esbDataStruct")) {
paramNameList = this.parseByESBDataStruct(requestObj.getString("esbDataStruct"));
}
}
}
}
}
for (String param : paramNameList) {
Map<String, String> map = new HashMap<>();
map.put("value", param);
list.add(map);
}
return list;
}
private List<Map<String, String>> getHTTPApiParams(ApiDefinitionWithBLOBs apiModel) {
List<Map<String, String>> list = new ArrayList<>();
List<String> paramNameList = new ArrayList<>();
if (apiModel != null) {
@ -893,15 +918,15 @@ public class MockConfigService {
if (mockConfigData != null && mockConfigData.getMockExpectConfigList() != null) {
JSON paramJson = this.getPostParamMap(request);
if(paramJson instanceof JSONObject){
JSONObject paramMap = (JSONObject)paramJson;
if (paramJson instanceof JSONObject) {
JSONObject paramMap = (JSONObject) paramJson;
MockExpectConfigResponse finalExpectConfig = this.findExpectConfig(mockConfigData.getMockExpectConfigList(), paramMap);
if (finalExpectConfig != null) {
isMatch = true;
returnStr = this.updateHttpServletResponse(finalExpectConfig, response);
}
}else if(paramJson instanceof JSONArray){
JSONArray paramArray = (JSONArray)paramJson;
} else if (paramJson instanceof JSONArray) {
JSONArray paramArray = (JSONArray) paramJson;
MockExpectConfigResponse finalExpectConfig = this.findExpectConfig(mockConfigData.getMockExpectConfigList(), paramArray);
if (finalExpectConfig != null) {
isMatch = true;
@ -922,7 +947,7 @@ public class MockConfigService {
String returnStr = "";
boolean isMatch = false;
List<ApiDefinitionWithBLOBs> aualifiedApiList = new ArrayList<>();
if(project != null){
if (project != null) {
String urlSuffix = this.getUrlSuffix(project.getSystemId(), request);
aualifiedApiList = apiDefinitionService.preparedUrl(project.getId(), method, null, urlSuffix);
@ -1001,4 +1026,165 @@ public class MockConfigService {
}
return new String(buffer, charEncoding);
}
private List<String> parseByJsonDataStruct(String dataString) {
List<String> returnList = new ArrayList<>();
try {
JSONValidator validator = JSONValidator.from(dataString);
Map<String, String> keyValueMap = new HashMap<>();
if (StringUtils.equalsIgnoreCase(validator.getType().name(), "Object")) {
JsonStructUtils.deepParseKeyByJsonObject(JSONObject.parseObject(dataString), returnList);
} else if (StringUtils.equalsIgnoreCase(validator.getType().name(), "Array")) {
JsonStructUtils.deepParseKeyByJsonArray(JSONArray.parseArray(dataString), returnList);
}
} catch (Exception e) {
LogUtil.error(e.getMessage());
}
return returnList;
}
private List<String> parseByTcpTreeDataStruct(String dataString) {
List<TcpTreeTableDataStruct> list = JSONArray.parseArray(dataString, TcpTreeTableDataStruct.class);
List<String> returnList = new ArrayList<>();
for (TcpTreeTableDataStruct dataStruct : list) {
List<String> nameList = dataStruct.getNameDeep();
for (String name : nameList) {
if (!returnList.contains(nameList)) {
returnList.add(name);
}
}
}
return returnList;
}
private List<String> parseByESBDataStruct(String dataString) {
List<EsbDataStruct> list = JSONArray.parseArray(dataString, EsbDataStruct.class);
List<String> returnList = new ArrayList<>();
for (EsbDataStruct dataStruct : list) {
List<String> nameList = dataStruct.getNameDeep();
for (String name : nameList) {
if (!returnList.contains(nameList)) {
returnList.add(name);
}
}
}
return returnList;
}
public MockExpectConfigWithBLOBs matchTcpMockExpect(String message, int port) {
ProjectExample projectExample = new ProjectExample();
projectExample.createCriteria().andMockTcpPortEqualTo(port).andIsMockTcpOpenEqualTo(true);
List<Project> projectList = projectMapper.selectByExample(projectExample);
boolean isJsonMessage = this.checkMessageIsJson(message);
boolean isXMLMessage = this.checkMessageIsXml(message);
List<MockExpectConfigWithBLOBs> structResult = new ArrayList<>();
List<MockExpectConfigWithBLOBs> rawResult = new ArrayList<>();
for (Project project : projectList) {
String projectId = project.getId();
List<MockExpectConfigWithBLOBs> mockExpectConfigList = extMockExpectConfigMapper.selectByProjectIdAndStatusIsOpen(projectId);
for (MockExpectConfigWithBLOBs expectConfig : mockExpectConfigList) {
String requestStr = expectConfig.getRequest();
String responseStr = expectConfig.getResponse();
if (StringUtils.isEmpty(responseStr)) {
continue;
}
try {
JSONObject requestJson = JSONObject.parseObject(requestStr);
if (requestJson.containsKey("reportType")) {
boolean isMatch = false;
boolean isRaw = false;
String reportType = requestJson.getString("reportType");
if (isJsonMessage && StringUtils.equalsIgnoreCase(reportType, "json")) {
if (requestJson.containsKey("jsonDataStruct")) {
isMatch = JsonStructUtils.checkJsonCompliance(message, requestJson.getString("jsonDataStruct"));
}
} else if (isXMLMessage && StringUtils.equalsIgnoreCase(reportType, "xml")) {
if (requestJson.containsKey("xmlDataStruct")) {
JSONObject sourceObj = XMLUtils.XmlToJson(message);
String xmlStr = "";
try {
List<TcpTreeTableDataStruct> tcpDataList = JSONArray.parseArray(requestJson.getString("xmlDataStruct"),TcpTreeTableDataStruct.class);
xmlStr = TcpTreeTableDataParser.treeTableData2Xml(tcpDataList);
}catch (Exception e){
}
JSONObject matchObj = XMLUtils.XmlToJson(xmlStr);
isMatch = JsonStructUtils.checkJsonObjCompliance(sourceObj, matchObj);
}
} else if (StringUtils.equalsIgnoreCase(reportType, "raw")) {
if (requestJson.containsKey("rawDataStruct")) {
String rawDataStruct = requestJson.getString("rawDataStruct");
if (StringUtils.contains(message, rawDataStruct)) {
isMatch = true;
isRaw = true;
} else {
Pattern pattern = Pattern.compile(rawDataStruct);
Matcher matcher = pattern.matcher(message);
if (matcher.find()) {
isMatch = true;
isRaw = true;
}
}
}
}
if (isMatch) {
JSONObject responseObj = JSONObject.parseObject(responseStr);
if (responseObj.containsKey("body")) {
if (isRaw) {
rawResult.add(expectConfig);
} else {
structResult.add(expectConfig);
}
}
}
}
} catch (Exception e) {
}
}
}
//优先返回结构匹配的数据
if(!structResult.isEmpty()){
return structResult.get(0);
}else {
if(!rawResult.isEmpty()){
return rawResult.get(0);
}else {
return null;
}
}
}
private boolean checkMessageIsXml(String message) {
boolean isXml = false;
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
// builder.parse(message);
builder.parse(new InputSource(new ByteArrayInputStream(message.getBytes("utf-8"))));
isXml = true;
} catch (Exception e) {
e.printStackTrace();
}
return isXml;
}
private boolean checkMessageIsJson(String message) {
boolean isJson = false;
try {
JSONValidator validator = JSONValidator.from(message);
String type = validator.getType().name();
if (!StringUtils.equalsIgnoreCase("value", type)) {
isJson = true;
}
} catch (Exception e) {
}
return isJson;
}
}

View File

@ -0,0 +1,87 @@
package io.metersphere.api.tcp;
import io.metersphere.api.tcp.server.TCPServer;
import io.metersphere.commons.exception.MSException;
import java.util.HashMap;
import java.util.Map;
/**
* @author song.tianyang
* @Date 2021/8/10 3:04 下午
*/
public class TCPPool {
private static HashMap<Integer, TCPServer> serverSockedMap = new HashMap<>();
private TCPPool(){}
public static String createTcp(int port){
String returnString = "";
TCPServer tcpServer = null;
if(serverSockedMap.containsKey(port)){
tcpServer = serverSockedMap.get(port);
}else {
tcpServer = new TCPServer(port);
serverSockedMap.put(port,tcpServer);
}
try {
if(!tcpServer.isSocketOpen()){
Thread t = new Thread(tcpServer);
t.start();
}
returnString = "OK";
}catch (Exception e){
returnString = e.getMessage();
e.printStackTrace();
MSException.throwException(e.getMessage());
}
return returnString;
}
public static boolean isTcpOpen(int port){
TCPServer server = serverSockedMap.get(port);
if(server != null ){
return server.isSocketOpen();
}
return false;
}
public static String getTcpStatus() {
if(serverSockedMap.isEmpty()){
return "null";
}else {
StringBuffer stringBuffer = new StringBuffer();
for (Map.Entry<Integer, TCPServer> entry:serverSockedMap.entrySet()) {
int port = entry.getKey();
TCPServer tcpServer = entry.getValue();
if(tcpServer == null){
stringBuffer.append("Port is "+port + ";");
stringBuffer.append("Server is null;");
}else {
stringBuffer.append("Port is "+port + ";");
stringBuffer.append("Server is open: "+ tcpServer.isSocketOpen());
}
}
return stringBuffer.toString();
}
}
public static String closeTcp(int portNum) {
TCPServer server = serverSockedMap.get(portNum);
if(server == null){
return "Tcp Is not create!";
}else {
String returnMsg = null;
try {
server.closeSocket();
returnMsg = "OK";
}catch (Exception e){
returnMsg = e.getMessage();
e.printStackTrace();
}
return returnMsg;
}
}
}

View File

@ -0,0 +1,60 @@
package io.metersphere.api.tcp.server;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author song.tianyang
* @Date 2021/8/11 10:35 上午
*/
public class TCPServer implements Runnable {
private int port;
private ServerSocket serverSocket;
private TCPServicer servicer;
public TCPServer(int port){
this.port = port;
}
public void openSocket() throws Exception {
this.serverSocket = new ServerSocket(this.port);
int connectIndex = 0;
while (true) {
if (!this.serverSocket.isClosed()) {
Socket socket = this.serverSocket.accept();
servicer = new TCPServicer(socket,port);
servicer.run();
}
if (this.serverSocket.isClosed()) {
break;
}
}
}
public boolean isSocketOpen(){
if (this.serverSocket != null && !this.serverSocket.isClosed()) {
return true;
}else {
return false;
}
}
public void closeSocket() throws Exception {
if (this.serverSocket != null && !this.serverSocket.isClosed()) {
if(servicer != null){
servicer.close();
}
this.serverSocket.close();
}
}
@Override
public void run() {
try {
this.openSocket();
} catch (Exception e) {
}
}
}

View File

@ -0,0 +1,82 @@
package io.metersphere.api.tcp.server;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.service.MockConfigService;
import io.metersphere.base.domain.MockExpectConfigWithBLOBs;
import io.metersphere.commons.utils.CommonBeanFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TCPServicer {
private Socket s;
private InputStream is;
private OutputStream os;
private int port;
public TCPServicer(Socket s, int port) {
this.s = s;
this.port = port;
}
public void run() {
byte[] b = new byte[1024];
String returnMsg = "";
String message = "";
try {
is = s.getInputStream();
os = s.getOutputStream();
int len = is.read(b);
message = new String(b,0,len);
// } catch (Exception e) {
// e.printStackTrace();
// }
returnMsg = this.getReturnMsg(message);
// try {
os.write(returnMsg.getBytes());
} catch (Exception e) {
e.printStackTrace();
}finally {
this.close();
}
//关闭资源
// this.close();
}
private String getReturnMsg(String message) {
MockConfigService mockConfigService = CommonBeanFactory.getBean(MockConfigService.class);
MockExpectConfigWithBLOBs matchdMockExpect = mockConfigService.matchTcpMockExpect(message,this.port);
String response = matchdMockExpect.getResponse();
JSONObject responseObj = JSONObject.parseObject(response);
try {
int delayed = responseObj.getInteger("delayed");
Thread.sleep(delayed);
} catch (InterruptedException e) {
e.printStackTrace();
}
String returnMsg = responseObj.getString("body");
return returnMsg;
}
public void close() {
//关闭资源
try{
is.close();
}catch (Exception e){}finally {
try{
os.close();
}catch (Exception e){}finally {
try{
s.close();
}catch (Exception e){}finally {
}
}
}
}
}

View File

@ -39,5 +39,9 @@ public class Project implements Serializable {
private String systemId;
private Integer mockTcpPort;
private Boolean isMockTcpOpen;
private static final long serialVersionUID = 1L;
}

View File

@ -1243,6 +1243,126 @@ public class ProjectExample {
addCriterion("system_id not between", value1, value2, "systemId");
return (Criteria) this;
}
public Criteria andMockTcpPortIsNull() {
addCriterion("mock_tcp_port is null");
return (Criteria) this;
}
public Criteria andMockTcpPortIsNotNull() {
addCriterion("mock_tcp_port is not null");
return (Criteria) this;
}
public Criteria andMockTcpPortEqualTo(Integer value) {
addCriterion("mock_tcp_port =", value, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortNotEqualTo(Integer value) {
addCriterion("mock_tcp_port <>", value, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortGreaterThan(Integer value) {
addCriterion("mock_tcp_port >", value, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortGreaterThanOrEqualTo(Integer value) {
addCriterion("mock_tcp_port >=", value, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortLessThan(Integer value) {
addCriterion("mock_tcp_port <", value, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortLessThanOrEqualTo(Integer value) {
addCriterion("mock_tcp_port <=", value, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortIn(List<Integer> values) {
addCriterion("mock_tcp_port in", values, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortNotIn(List<Integer> values) {
addCriterion("mock_tcp_port not in", values, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortBetween(Integer value1, Integer value2) {
addCriterion("mock_tcp_port between", value1, value2, "mockTcpPort");
return (Criteria) this;
}
public Criteria andMockTcpPortNotBetween(Integer value1, Integer value2) {
addCriterion("mock_tcp_port not between", value1, value2, "mockTcpPort");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenIsNull() {
addCriterion("is_mock_tcp_open is null");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenIsNotNull() {
addCriterion("is_mock_tcp_open is not null");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenEqualTo(Boolean value) {
addCriterion("is_mock_tcp_open =", value, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenNotEqualTo(Boolean value) {
addCriterion("is_mock_tcp_open <>", value, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenGreaterThan(Boolean value) {
addCriterion("is_mock_tcp_open >", value, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenGreaterThanOrEqualTo(Boolean value) {
addCriterion("is_mock_tcp_open >=", value, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenLessThan(Boolean value) {
addCriterion("is_mock_tcp_open <", value, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenLessThanOrEqualTo(Boolean value) {
addCriterion("is_mock_tcp_open <=", value, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenIn(List<Boolean> values) {
addCriterion("is_mock_tcp_open in", values, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenNotIn(List<Boolean> values) {
addCriterion("is_mock_tcp_open not in", values, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenBetween(Boolean value1, Boolean value2) {
addCriterion("is_mock_tcp_open between", value1, value2, "isMockTcpOpen");
return (Criteria) this;
}
public Criteria andIsMockTcpOpenNotBetween(Boolean value1, Boolean value2) {
addCriterion("is_mock_tcp_open not between", value1, value2, "isMockTcpOpen");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -19,6 +19,8 @@
<result column="scenario_custom_num" jdbcType="BIT" property="scenarioCustomNum" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="system_id" jdbcType="VARCHAR" property="systemId" />
<result column="mock_tcp_port" jdbcType="INTEGER" property="mockTcpPort" />
<result column="is_mock_tcp_open" jdbcType="BIT" property="isMockTcpOpen" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -81,7 +83,7 @@
<sql id="Base_Column_List">
id, workspace_id, `name`, description, create_time, update_time, tapd_id, jira_key,
zentao_id, azure_devops_id, `repeatable`, case_template_id, issue_template_id, custom_num,
scenario_custom_num, create_user, system_id
scenario_custom_num, create_user, system_id, mock_tcp_port, is_mock_tcp_open
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultMap="BaseResultMap">
select
@ -119,13 +121,15 @@
tapd_id, jira_key, zentao_id,
azure_devops_id, `repeatable`, case_template_id,
issue_template_id, custom_num, scenario_custom_num,
create_user, system_id)
create_user, system_id, mock_tcp_port,
is_mock_tcp_open)
values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{tapdId,jdbcType=VARCHAR}, #{jiraKey,jdbcType=VARCHAR}, #{zentaoId,jdbcType=VARCHAR},
#{azureDevopsId,jdbcType=VARCHAR}, #{repeatable,jdbcType=BIT}, #{caseTemplateId,jdbcType=VARCHAR},
#{issueTemplateId,jdbcType=VARCHAR}, #{customNum,jdbcType=BIT}, #{scenarioCustomNum,jdbcType=BIT},
#{createUser,jdbcType=VARCHAR}, #{systemId,jdbcType=VARCHAR})
#{createUser,jdbcType=VARCHAR}, #{systemId,jdbcType=VARCHAR}, #{mockTcpPort,jdbcType=INTEGER},
#{isMockTcpOpen,jdbcType=BIT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.Project">
insert into project
@ -181,6 +185,12 @@
<if test="systemId != null">
system_id,
</if>
<if test="mockTcpPort != null">
mock_tcp_port,
</if>
<if test="isMockTcpOpen != null">
is_mock_tcp_open,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -234,6 +244,12 @@
<if test="systemId != null">
#{systemId,jdbcType=VARCHAR},
</if>
<if test="mockTcpPort != null">
#{mockTcpPort,jdbcType=INTEGER},
</if>
<if test="isMockTcpOpen != null">
#{isMockTcpOpen,jdbcType=BIT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.ProjectExample" resultType="java.lang.Long">
@ -296,6 +312,12 @@
<if test="record.systemId != null">
system_id = #{record.systemId,jdbcType=VARCHAR},
</if>
<if test="record.mockTcpPort != null">
mock_tcp_port = #{record.mockTcpPort,jdbcType=INTEGER},
</if>
<if test="record.isMockTcpOpen != null">
is_mock_tcp_open = #{record.isMockTcpOpen,jdbcType=BIT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -319,7 +341,9 @@
custom_num = #{record.customNum,jdbcType=BIT},
scenario_custom_num = #{record.scenarioCustomNum,jdbcType=BIT},
create_user = #{record.createUser,jdbcType=VARCHAR},
system_id = #{record.systemId,jdbcType=VARCHAR}
system_id = #{record.systemId,jdbcType=VARCHAR},
mock_tcp_port = #{record.mockTcpPort,jdbcType=INTEGER},
is_mock_tcp_open = #{record.isMockTcpOpen,jdbcType=BIT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -375,6 +399,12 @@
<if test="systemId != null">
system_id = #{systemId,jdbcType=VARCHAR},
</if>
<if test="mockTcpPort != null">
mock_tcp_port = #{mockTcpPort,jdbcType=INTEGER},
</if>
<if test="isMockTcpOpen != null">
is_mock_tcp_open = #{isMockTcpOpen,jdbcType=BIT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -395,7 +425,9 @@
custom_num = #{customNum,jdbcType=BIT},
scenario_custom_num = #{scenarioCustomNum,jdbcType=BIT},
create_user = #{createUser,jdbcType=VARCHAR},
system_id = #{systemId,jdbcType=VARCHAR}
system_id = #{systemId,jdbcType=VARCHAR},
mock_tcp_port = #{mockTcpPort,jdbcType=INTEGER},
is_mock_tcp_open = #{isMockTcpOpen,jdbcType=BIT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -0,0 +1,10 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.MockExpectConfigWithBLOBs;
import java.util.List;
public interface ExtMockExpectConfigMapper {
List<MockExpectConfigWithBLOBs> selectByProjectIdAndStatusIsOpen(String projectId);
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="io.metersphere.base.mapper.ext.ExtMockExpectConfigMapper">
<select id="selectByProjectIdAndStatusIsOpen" resultType="io.metersphere.base.domain.MockExpectConfigWithBLOBs">
SELECT * FROM mock_expect_config WHERE status = 'true' AND mock_config_id IN
(SELECT id FROM mock_config WHERE api_path IS NULL AND project_id = #{0} )
</select>
</mapper>

View File

@ -34,7 +34,7 @@
<select id="getProjectWithWorkspace" resultType="io.metersphere.dto.ProjectDTO">
select p.id, p.workspace_id, p.name, p.description, p.update_time, p.issue_template_id, p.case_template_id,
p.create_time, w.id as workspaceId, w.name as workspaceName, p.tapd_id, p.jira_key, p.zentao_id,p.azure_devops_id,p.repeatable, p.custom_num,
user.name as createUserName,
user.name as createUserName,p.mock_tcp_port AS mockTcpPort,p.is_mock_tcp_open AS isMockTcpOpen,
p.scenario_custom_num
from project p
join workspace w on p.workspace_id = w.id

View File

@ -1,7 +1,6 @@
package io.metersphere.commons.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import org.apache.commons.lang3.StringUtils;
@ -131,116 +130,4 @@ public class JsonPathUtils {
}
return ret;
}
/**
* 检查一个JSON对象的数据是否被另一个对象匹配包含
*
* @param sourceObj 目标JSON
* @param matchObj 要进行匹配的JSON
* @return
*/
public static boolean checkJsonObjCompliance(JSONObject sourceObj, JSONObject matchObj) {
if (sourceObj == null && matchObj == null) {
return true;
} else if (sourceObj != null && matchObj != null) {
boolean isMatch = false;
try {
Set<String> matchKeys = matchObj.keySet();
for (String key : matchKeys) {
if (sourceObj.containsKey(key)) {
Object sourceObjItem = sourceObj.get(key);
Object matchObjItem = matchObj.get(key);
isMatch = checkObjCompliance(sourceObjItem, matchObjItem);
} else {
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isMatch;
} else {
return false;
}
}
/**
* 检查一个JSON对象的数据集合是否包含另一个对象包含
* @param sourceArray
* @param matchObj
* @return
*/
public static boolean checkJsonArrayCompliance(JSONArray sourceArray, JSONObject matchObj) {
if (sourceArray == null && matchObj == null) {
return true;
} else if (sourceArray != null && matchObj != null) {
boolean isMatch = false;
try {
Set<String> matchKeys = matchObj.keySet();
for(int sourceIndex = 0;sourceIndex < sourceArray.size();sourceIndex ++){
JSONObject sourceObj = sourceArray.getJSONObject(sourceIndex);
for (String key : matchKeys) {
if (sourceObj.containsKey(key)) {
Object sourceObjItem = sourceObj.get(key);
Object matchObjItem = matchObj.get(key);
isMatch = checkObjCompliance(sourceObjItem, matchObjItem);
if(!isMatch){
break;
}
} else {
isMatch = false;
break;
}
}
if(isMatch){
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isMatch;
} else {
return false;
}
}
private static boolean checkObjCompliance(Object sourceObjItem, Object matchObjItem) {
if (matchObjItem instanceof JSONObject) {
if (sourceObjItem instanceof JSONObject) {
return checkJsonObjCompliance((JSONObject) sourceObjItem, (JSONObject) matchObjItem);
} else {
return false;
}
} else if (matchObjItem instanceof JSONArray) {
if (sourceObjItem instanceof JSONArray) {
JSONArray sourceArr = (JSONArray) sourceObjItem;
JSONArray matchArr = (JSONArray) matchObjItem;
//同是arr 可能顺序存在不同 所以需要循环匹配
if (matchArr.size() > sourceArr.size()) {
return false;
} else {
for (int i = 0; i < matchArr.size(); i++) {
for (int j = i; j < sourceArr.size(); j++) {
Object matchItemObj = matchArr.get(i);
Object sourceItemObj = sourceArr.get(j);
boolean check = checkObjCompliance(sourceItemObj, matchItemObj);
if (!check) {
return check;
}
}
}
return true;
}
} else {
return false;
}
} else {
String sourceValues = String.valueOf(sourceObjItem);
String matchValues = String.valueOf(matchObjItem);
return StringUtils.equals(sourceValues, matchValues);
}
}
}

View File

@ -0,0 +1,236 @@
package io.metersphere.commons.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Set;
/**
* JSON数据结构相关的工具类
* @author song.tianyang
* @Date 2021/8/16 3:50 下午
*/
public class JsonStructUtils {
/**
* 检查一个JSON对象的数据是否被另一个对象匹配包含
*
* @param sourceObj 目标JSON
* @param matchObj 要进行匹配的JSON
* @return
*/
public static boolean checkJsonObjCompliance(JSONObject sourceObj, JSONObject matchObj) {
if (sourceObj == null && matchObj == null) {
return true;
} else if (sourceObj != null && matchObj != null) {
boolean isMatch = false;
try {
Set<String> matchKeys = matchObj.keySet();
for (String key : matchKeys) {
if (sourceObj.containsKey(key)) {
Object sourceObjItem = sourceObj.get(key);
Object matchObjItem = matchObj.get(key);
isMatch = checkObjCompliance(sourceObjItem, matchObjItem);
} else {
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isMatch;
} else {
return false;
}
}
public static boolean checkJsonArrayCompliance(JSONArray sourceArray, JSONArray matchArray) {
if (sourceArray == null && matchArray == null) {
return true;
} else if (sourceArray != null && matchArray != null && sourceArray.size() > matchArray.size()) {
try {
for (int i = 0; i < matchArray.size(); i ++) {
Object obj = matchArray.get(i);
if(!sourceArray.contains(obj)){
return false;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
} else {
return false;
}
}
public static boolean checkJsonArrayContainsObj(JSONArray sourceArray, JSONObject matchObj) {
if (sourceArray == null && matchObj == null) {
return true;
} else if (sourceArray != null && matchObj != null ) {
try {
for (int i = 0; i < sourceArray.size(); i ++) {
Object obj = sourceArray.get(i);
if(obj instanceof JSONObject){
boolean isMatch = checkJsonObjCompliance((JSONObject) obj,matchObj);
if(isMatch){
return isMatch;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
} else {
return false;
}
}
/**
* 检查一个JSON对象的数据集合是否包含另一个对象包含
* @param sourceArray
* @param matchObj
* @return
*/
public static boolean checkJsonArrayCompliance(JSONArray sourceArray, JSONObject matchObj) {
if (sourceArray == null && matchObj == null) {
return true;
} else if (sourceArray != null && matchObj != null) {
boolean isMatch = false;
try {
Set<String> matchKeys = matchObj.keySet();
for(int sourceIndex = 0;sourceIndex < sourceArray.size();sourceIndex ++){
JSONObject sourceObj = sourceArray.getJSONObject(sourceIndex);
for (String key : matchKeys) {
if (sourceObj.containsKey(key)) {
Object sourceObjItem = sourceObj.get(key);
Object matchObjItem = matchObj.get(key);
isMatch = checkObjCompliance(sourceObjItem, matchObjItem);
if(!isMatch){
break;
}
} else {
isMatch = false;
break;
}
}
if(isMatch){
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return isMatch;
} else {
return false;
}
}
private static boolean checkObjCompliance(Object sourceObjItem, Object matchObjItem) {
if (matchObjItem instanceof JSONObject) {
if (sourceObjItem instanceof JSONObject) {
return checkJsonObjCompliance((JSONObject) sourceObjItem, (JSONObject) matchObjItem);
} else {
return false;
}
} else if (matchObjItem instanceof JSONArray) {
if (sourceObjItem instanceof JSONArray) {
JSONArray sourceArr = (JSONArray) sourceObjItem;
JSONArray matchArr = (JSONArray) matchObjItem;
//同是arr 可能顺序存在不同 所以需要循环匹配
if (matchArr.size() > sourceArr.size()) {
return false;
} else {
for (int i = 0; i < matchArr.size(); i++) {
for (int j = i; j < sourceArr.size(); j++) {
Object matchItemObj = matchArr.get(i);
Object sourceItemObj = sourceArr.get(j);
boolean check = checkObjCompliance(sourceItemObj, matchItemObj);
if (!check) {
return check;
}
}
}
return true;
}
} else {
return false;
}
} else {
String sourceValues = String.valueOf(sourceObjItem);
String matchValues = String.valueOf(matchObjItem);
return StringUtils.equals(sourceValues, matchValues);
}
}
public static void deepParseKeyByJsonObject(JSONObject jsonObject, List<String> keyList) {
for (String key : jsonObject.keySet()) {
Object obj = jsonObject.get(key);
if(obj instanceof JSONArray) {
deepParseKeyByJsonArray((JSONArray) obj, keyList);
}else if(obj instanceof JSONObject){
deepParseKeyByJsonObject((JSONObject) obj,keyList);
}else {
if(!keyList.contains(key)){
keyList.add(key);
}
}
}
}
public static void deepParseKeyByJsonArray(JSONArray jsonArray, List<String> keyList) {
for (int i = 0; i < jsonArray.size(); i ++) {
Object itemObj = jsonArray.get(i);
if(itemObj instanceof JSONObject){
deepParseKeyByJsonObject((JSONObject)itemObj,keyList);
}
}
}
public static boolean checkJsonCompliance(String sourceJson, String matchJson) {
boolean isMatch = false;
try {
boolean isSourceJsonIsArray = false;
boolean isMatchJsonIsArray = false;
JSONValidator sourceValidator = JSONValidator.from(sourceJson);
JSONValidator matchValidator = JSONValidator.from(matchJson);
String sourceType = sourceValidator.getType().name();
String matchType = matchValidator.getType().name();
if(StringUtils.equalsIgnoreCase(sourceType,"array")&&StringUtils.equalsIgnoreCase(matchType,"array")){
isSourceJsonIsArray = true;
isMatchJsonIsArray = true;
}else if(StringUtils.equalsIgnoreCase(sourceType,"array")){
isSourceJsonIsArray = true;
}else if(StringUtils.equalsIgnoreCase(matchType,"array")){
isMatchJsonIsArray = true;
}
if(isSourceJsonIsArray && isMatchJsonIsArray){
JSONArray sourceArr = JSONArray.parseArray(sourceJson);
JSONArray compArr = JSONArray.parseArray(matchJson);
isMatch = checkJsonArrayCompliance(sourceArr,compArr);
}else if(isSourceJsonIsArray && !isMatchJsonIsArray){
JSONArray sourceArr = JSONArray.parseArray(sourceJson);
JSONObject compObj = JSONObject.parseObject(matchJson);
isMatch = checkJsonArrayContainsObj(sourceArr,compObj);
}else if(!isSourceJsonIsArray && !isMatchJsonIsArray){
JSONObject sourceObj = JSONObject.parseObject(sourceJson);
JSONObject compObj = JSONObject.parseObject(matchJson);
isMatch = checkJsonObjCompliance(sourceObj,compObj);
}else {
isMatch = false;
}
}catch (Exception e){
}
return isMatch;
}
}

View File

@ -73,15 +73,8 @@ public class ProjectController {
@MsAuditLog(module = "project_project_manager", type = OperLogConstants.CREATE, content = "#msClass.getLogDetails(#project.id)", msClass = ProjectService.class)
public Project addProject(@RequestBody AddProjectRequest project, HttpServletRequest request) {
Project returnModel = projectService.addProject(project);
//创建项目的时候默认增加Mock环境
String requestUrl = request.getRequestURL().toString();
String baseUrl = "";
if (requestUrl.contains("/project/add")) {
baseUrl = requestUrl.split("/project/add")[0];
}
apiTestEnvironmentService.getMockEnvironmentByProjectId(returnModel.getId(), project.getProtocal(), baseUrl);
apiTestEnvironmentService.getMockEnvironmentByProjectId(returnModel.getId());
return returnModel;
}

View File

@ -8,6 +8,7 @@ import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.RunInterface;
import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.service.JarConfigService;
import io.metersphere.service.ProjectService;
import io.metersphere.service.ScheduleService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.track.service.IssuesService;
@ -38,6 +39,8 @@ public class AppStartListener implements ApplicationListener<ApplicationReadyEve
@Resource
private IssuesService issuesService;
@Resource
private ProjectService projectService;
@Resource
private PerformanceTestService performanceTestService;
@Value("${jmeter.home}")
private String jmeterHome;
@ -53,6 +56,8 @@ public class AppStartListener implements ApplicationListener<ApplicationReadyEve
initPythonEnv();
projectService.initMockTcpService();
initOperate(apiAutomationService::checkApiScenarioUseUrl, "init.scenario.url");
initOperate(apiAutomationService::checkApiScenarioReferenceId, "init.scenario.referenceId");
initOperate(issuesService::syncThirdPartyIssues, "init.issue");
@ -66,6 +71,7 @@ public class AppStartListener implements ApplicationListener<ApplicationReadyEve
}
scheduleService.startEnableSchedules();
}

View File

@ -5,6 +5,8 @@ import io.metersphere.api.dto.DeleteAPITestRequest;
import io.metersphere.api.dto.QueryAPITestRequest;
import io.metersphere.api.service.APITestService;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.api.service.ApiTestEnvironmentService;
import io.metersphere.api.tcp.TCPPool;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtOrganizationMapper;
@ -13,6 +15,7 @@ import io.metersphere.base.mapper.ext.ExtUserGroupMapper;
import io.metersphere.base.mapper.ext.ExtUserMapper;
import io.metersphere.commons.constants.UserGroupConstants;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.ProjectRequest;
@ -186,6 +189,9 @@ public class ProjectService {
// User Group
deleteProjectUserGroup(projectId);
//关闭TCP
this.closeMockTcp(projectId);
// delete project
projectMapper.deleteByPrimaryKey(projectId);
@ -260,6 +266,15 @@ public class ProjectService {
}
public void updateProject(Project project) {
//查询之前的TCP端口用于检查是否需要开启/关闭 TCP接口
int lastTcpNum = 0;
Project oldData = projectMapper.selectByPrimaryKey(project.getId());
if(oldData!=null && oldData.getMockTcpPort() != null){
lastTcpNum = oldData.getMockTcpPort().intValue();
}
this.checkProjectTcpPort(project);
project.setCreateTime(null);
project.setUpdateTime(System.currentTimeMillis());
checkProjectExist(project);
@ -267,6 +282,28 @@ public class ProjectService {
testCaseService.updateTestCaseCustomNumByProjectId(project.getId());
}
projectMapper.updateByPrimaryKeySelective(project);
//检查Mock环境是否需要同步更新
ApiTestEnvironmentService apiTestEnvironmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
apiTestEnvironmentService.getMockEnvironmentByProjectId(project.getId());
//开启tcp mock
if(project.getIsMockTcpOpen()){
this.reloadMockTcp(project,lastTcpNum);
}else {
this.closeMockTcp(project);
}
}
private void checkProjectTcpPort(Project project) {
//判断端口是否重复
if(project.getMockTcpPort() != null && project.getMockTcpPort().intValue() != 0){
ProjectExample example = new ProjectExample();
example.createCriteria().andMockTcpPortEqualTo(project.getMockTcpPort());
long countResult = projectMapper.countByExample(example);
if(countResult > 0){
MSException.throwException("TCP Port is not unique");
}
}
}
private void checkProjectExist(Project project) {
@ -483,4 +520,54 @@ public class ProjectService {
public Map<String, Project> queryNameByIds(List<String> ids) {
return extProjectMapper.queryNameByIds(ids);
}
public void openMockTcp(Project project){
if(project == null){
MSException.throwException("Project not found!");
}else {
if(project.getMockTcpPort() == null){
MSException.throwException("Mock tcp port is not Found!");
}else {
TCPPool.createTcp(project.getMockTcpPort());
}
}
}
public void reloadMockTcp(Project project,int oldPort){
this.closeMockTcp(oldPort);
this.openMockTcp(project);
}
public void closeMockTcp(String projectId){
Project project = projectMapper.selectByPrimaryKey(projectId);
this.closeMockTcp(project);
}
public void closeMockTcp(Project project){
if(project == null){
MSException.throwException("Project not found!");
}else {
if(project.getMockTcpPort() == null){
MSException.throwException("Mock tcp port is not Found!");
}else {
this.closeMockTcp(project.getMockTcpPort().intValue());
}
}
}
public void closeMockTcp(int tcpPort){
if(tcpPort != 0){
TCPPool.closeTcp(tcpPort);
}
}
public void initMockTcpService() {
ProjectExample example = new ProjectExample();
Integer portInteger = new Integer(0);
Boolean statusBoolean = new Boolean(true);
example.createCriteria().andIsMockTcpOpenEqualTo(statusBoolean).andMockTcpPortNotEqualTo(portInteger);
List<Project> projectList = projectMapper.selectByExample(example);
for (Project p :projectList) {
this.openMockTcp(p);
}
}
}

View File

@ -174,7 +174,7 @@ CREATE TABLE IF NOT EXISTS `mock_expect_config`
`tags` varchar(1000) null,
request longtext null,
response longtext null,
STATUS VARCHAR(10) null,
status VARCHAR(10) null,
create_time bigint(13) null,
update_time bigint(13) null,
create_user_id VARCHAR(64) null,

View File

@ -11,7 +11,7 @@
<el-button plain :class="{active: showTestCaseList}" @click="changeTab('testCase')" size="small">CASE</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="Mock设置" placement="right" v-if="currentProtocol==='HTTP'">
<el-tooltip class="item" effect="dark" content="Mock设置" placement="right" v-if="currentProtocol === 'HTTP' || currentProtocol === 'TCP'">
<el-button plain :class="{active: showMock}" @click="changeTab('mock')" size="small"> Mock</el-button>
</el-tooltip>
@ -72,8 +72,11 @@
/>
</div>
<div v-if="showMock" class="ms-api-div">
<mock-config :base-mock-config-data="baseMockConfigData"/>
<div v-if="showMock && (currentProtocol === 'HTTP')" class="ms-api-div">
<mock-config :base-mock-config-data="baseMockConfigData" type="http"/>
</div>
<div v-if="showMock && (currentProtocol === 'TCP')" class="ms-api-div">
<tcp-mock-config :base-mock-config-data="baseMockConfigData" type="tcp"/>
</div>
<div v-if="showTestCaseList">
<!--测试用例列表-->
@ -96,6 +99,7 @@ import MsRunTestTcpPage from "./runtest/RunTestTCPPage";
import MsRunTestSqlPage from "./runtest/RunTestSQLPage";
import MsRunTestDubboPage from "./runtest/RunTestDubboPage";
import MockConfig from "@/business/components/api/definition/components/mock/MockConfig";
import TcpMockConfig from "@/business/components/api/definition/components/mock/TcpMockConfig";
import ApiCaseSimpleList from "./list/ApiCaseSimpleList";
export default {
@ -107,6 +111,7 @@ export default {
MsRunTestSqlPage,
MsRunTestDubboPage,
MockConfig,
TcpMockConfig,
ApiCaseSimpleList
},
data() {
@ -139,7 +144,7 @@ export default {
},
created() {
this.refreshButtonActiveClass(this.activeDom);
if (this.currentApi.id !== null && this.currentProtocol === "HTTP") {
if (this.currentApi.id !== null && (this.currentProtocol === "HTTP" || this.currentProtocol === "TCP")) {
this.mockSetting();
}
},

View File

@ -21,6 +21,21 @@
@changeApiProtocol="changeApiProtocol" @callback="callback"/>
</el-col>
</el-row>
<!-- MOCK信息 -->
<p class="tip">{{ $t('test_track.plan_view.mock_info') }} </p>
<div class="mock-info">
<el-row>
<el-col :span="24">
Mock地址
<el-link v-if="this.mockInfo !== '' " target="_blank" style="color: black"
type="primary">{{ this.mockInfo }}
</el-link>
<el-link v-else target="_blank" style="color: darkred"
type="primary">当前项目未开启Mock服务
</el-link>
</el-col>
</el-row>
</div>
<!-- 请求参数 -->
<div v-if="apiProtocol=='TCP'">
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
@ -45,7 +60,7 @@
import MsTcpBasicApi from "./TCPBasicApi";
import MsTcpFormatParameters from "../request/tcp/TcpFormatParameters";
import MsChangeHistory from "../../../../history/ChangeHistory";
import {hasLicense} from "@/common/js/utils";
import {hasLicense,getCurrentProjectID} from "@/common/js/utils";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const esbDefinition = (requireComponent!=null&&requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinition.vue") : {};
@ -69,6 +84,7 @@ export default {
return {
validated: false,
apiProtocol: "TCP",
mockInfo: "",
methodTypes:[
{
'key':"TCP",
@ -96,8 +112,8 @@ export default {
this.methodTypes.push(esbMethodType);
}
}
}
this.getMockInfo();
},
watch: {
syncTabs() {
@ -191,11 +207,19 @@ export default {
},
changeApiProtocol(protocol){
this.apiProtocol = protocol;
}
},
getMockInfo(){
let projectId = getCurrentProjectID();
this.$get("/api/environment/getTcpMockInfo/" + projectId, response => {
this.mockInfo = response.data;
});
},
},
}
</script>
<style scoped>
.mock-info {
margin: 20px 45px;
}
</style>

View File

@ -0,0 +1,361 @@
<template>
<div class="card-container">
<el-card class="card-content" v-loading="mockConfigData.loading">
<p class="tip">期望列表</p>
<div class="card">
<el-input :placeholder="$t('commons.search_by_name')" class="search-input" size="small"
:clearable="serchInputClearable"
v-model="tableSearch"/>
<el-table ref="table" border
:data="mockConfigData.mockExpectConfigList.filter(data=>!tableSearch || data.name.toLowerCase().includes(tableSearch.toLowerCase()))"
@row-click="clickRow"
row-key="id" class="test-content" :height="screenHeight">
<el-table-column :label="$t('api_test.mock.table.name')" min-width="160px" prop="name"></el-table-column>
<el-table-column :label="$t('api_test.mock.table.tag')" min-width="200px" prop="tags">
<template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:show-tooltip="true" :content="itemName"
style="margin-left: 0px; margin-right: 2px"/>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.mock.table.creator')" min-width="160px"
prop="createUserId"></el-table-column>
<el-table-column :label="$t('api_test.mock.table.status')" min-width="80px" prop="status">
<template v-slot:default="scope">
<div>
<el-switch
v-model="scope.row.status"
class="captcha-img"
@change="changeStatus(scope.row)"
></el-switch>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.mock.table.update_time')" min-width="160px" prop="updateTime">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" min-width="100" align="center" :label="$t('commons.operating')">
<template v-slot:default="scope">
<div>
<ms-table-operator-button :tip="$t('commons.copy')" icon="el-icon-copy-document"
@exec="copyExpect(scope.row)"
/>
<ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete"
@exec="removeExpect(scope.row)"
/>
</div>
</template>
</el-table-column>
</el-table>
</div>
<!-- 期望详情 -->
<p class="tip">{{ $t('api_test.mock.expect_detail') }}</p>
<el-form :model="mockExpectConfig" :rules="rule" ref="mockExpectForm" label-width="80px" label-position="right">
<div class="card">
<div class="base-info">
<el-row>
<el-col>{{ $t('api_test.mock.base_info') }}</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input class="ms-http-input" size="small" v-model="mockExpectConfig.name"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('commons.tag')" prop="tag">
<ms-input-tag :currentScenario="mockExpectConfig" v-if="showHeadTable" ref="tag"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>{{ $t('api_test.mock.req_param') }}</el-col>
</el-row>
<el-row>
<tcp-params :request="mockExpectConfig.request" style="margin: 10px 10px;" ref="tcpParam"></tcp-params>
</el-row>
<el-row style="margin-top: 10px;">
<el-col>{{ $t('api_test.mock.rsp_param') }}</el-col>
</el-row>
<el-row>
<el-form-item label="延时 (ms)" prop="response.delayed">
<el-input-number v-model="mockExpectConfig.response.delayed" :min="0">
<template slot="append">ms</template>
</el-input-number>
</el-form-item>
</el-row>
<el-row style="margin-top: 10px;">
<el-form-item label="Body:" label-width="50px">
<ms-code-edit height="200px" :mode="'txt'" ref="codeEdit" :data.sync="mockExpectConfig.response.body"
style="margin-top: 10px;"/>
</el-form-item>
</el-row>
<el-row>
<div style="float: right;margin-right: 20px">
<el-button type="primary" size="small" @click="saveMockExpectConfig" title="ctrl + s">{{
$t('commons.add')
}}
</el-button>
<el-button type="primary" size="small" @click="cleanMockExpectConfig">{{
$t('commons.clear')
}}
</el-button>
<el-button type="primary" size="small" v-if="mockExpectConfig.id != '' && mockExpectConfig.id != null"
@click="updateMockExpectConfig">{{ $t('commons.update') }}
</el-button>
</div>
</el-row>
</div>
</div>
</el-form>
</el-card>
</div>
</template>
<script>
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
import MockRowVariables from "@/business/components/api/definition/components/mock/MockRowVariables";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsCodeEdit from "@/business/components/api/definition/components/MsCodeEdit";
import MsApiVariableAdvance from "@/business/components/api/test/components/ApiVariableAdvance";
import MsTag from "@/business/components/common/components/MsTag";
import {REQUEST_HEADERS} from "@/common/js/constants";
import TcpParams from "@/business/components/api/definition/components/request/tcp/TcpParams";
import {getCurrentProjectID} from "@/common/js/utils";
export default {
name: "TcpMockConfig",
components: {
MockRowVariables,
MsTableOperatorButton,
MsInputTag,
MsCodeEdit,
MsApiVariableAdvance,
TcpParams,
MsTag,
},
data() {
return {
screenHeight: 300,
tableSearch: "",
showHeadTable: true,
serchInputClearable: true,
mockConfigData: {},
apiParams: [],
headerSuggestions: REQUEST_HEADERS,
mockExpectConfig: {
id: "",
name: "",
mockConfigId: "",
request: {
reportType:'raw',
xmlDataStruct:[],
},
response: {
httpCode: "",
delayed: "",
httpHeads: [],
body: "",
},
},
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
],
response: {
httpCode: [{required: true, message: this.$t('api_test.mock.rule.input_code'), trigger: 'blur'},],
delayed: [{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},],
},
},
};
},
props: {
baseMockConfigData: {},
type:String
},
created() {
this.mockConfigData = this.baseMockConfigData;
this.searchApiParams(this.mockConfigData.mockConfig.apiId);
},
methods: {
searchApiParams(apiId) {
let selectUrl = "/mockConfig/getApiParams/" + apiId;
this.$get(selectUrl, response => {
this.apiParams = response.data;
});
},
copyExpect(row) {
let selectUrl = "/mockConfig/mockExpectConfig/" + row.id;
this.$get(selectUrl, response => {
let data = response.data;
this.showHeadTable = false;
this.mockExpectConfig = data;
this.mockExpectConfig.id = "";
this.mockExpectConfig.name = this.mockExpectConfig.name + "_copy";
if (this.mockExpectConfig.request == null) {
this.mockExpectConfig.request = {
jsonParam: false,
variables: [],
jsonData: "{}",
};
}
if (this.mockExpectConfig.response == null) {
this.mockExpectConfig.response = {
httpCode: "",
delayed: "",
httpHeads: [],
body: "",
};
}
this.$nextTick(function () {
this.showHeadTable = true;
this.saveMockExpectConfig();
});
});
},
removeExpect(row) {
this.$confirm(this.$t('api_test.mock.delete_mock_expect'), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
let mockInfoId = row.mockConfigId;
let selectUrl = "/mockConfig/deleteMockExpectConfig/" + row.id;
this.$get(selectUrl, response => {
this.cleanMockExpectConfig();
this.refreshMockInfo(mockInfoId);
this.$message({
type: 'success',
message: this.$t('commons.delete_success'),
});
});
}).catch(() => {
});
},
saveMockExpectConfig() {
let mockConfigId = this.mockConfigData.mockConfig.id;
this.mockExpectConfig.mockConfigId = mockConfigId;
this.mockExpectConfig.id = "";
let formCheckResult = this.checkMockExpectForm("mockExpectForm", true);
},
cleanMockExpectConfig() {
this.showHeadTable = false;
this.mockExpectConfig = {
id: "",
name: "",
mockConfigId: "",
request: {
reportType:'raw',
xmlDataStruct:[],
},
response: {
httpCode: "",
delayed: "",
httpHeads: [],
body: "",
},
};
this.$nextTick(function () {
this.showHeadTable = true;
});
},
updateMockExpectConfig() {
this.checkMockExpectForm("mockExpectForm");
},
uploadMockExpectConfig(clearForm) {
let url = "/mockConfig/updateMockExpectConfig";
let param = this.mockExpectConfig;
this.$post(url, param, response => {
let returnData = response.data;
this.mockExpectConfig.id = returnData.id;
this.refreshMockInfo(param.mockConfigId);
if (clearForm) {
this.cleanMockExpectConfig();
}
this.$message({
type: 'success',
message: this.$t('commons.save_success'),
});
});
},
refreshMockInfo(mockConfigId) {
let mockParam = {};
mockParam.id = mockConfigId;
this.$post('/mockConfig/genMockConfig', mockParam, response => {
this.mockConfigData = response.data;
});
},
checkMockExpectForm(formName, clearForm) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.uploadMockExpectConfig(clearForm);
return true;
} else {
return false;
}
});
},
changeStatus(row) {
let mockParam = {};
mockParam.id = row.id;
mockParam.status = row.status;
this.$post('/mockConfig/updateMockExpectConfig', mockParam, response => {
});
},
clickRow(row, column, event) {
let selectUrl = "/mockConfig/mockExpectConfig/" + row.id;
this.$get(selectUrl, response => {
let data = response.data;
this.showHeadTable = false;
this.mockExpectConfig = data;
if (this.mockExpectConfig.request == null) {
this.mockExpectConfig.request = {
reportType:'raw',
xmlDataStruct:[],
};
}
if (this.mockExpectConfig.response == null) {
this.mockExpectConfig.response = {
httpCode: "",
delayed: "",
httpHeads: [],
body: "",
};
}
this.$nextTick(function () {
this.showHeadTable = true;
});
});
}
}
};
</script>
<style scoped>
.search-input {
float: right;
width: 300px;
margin-right: 10px;
margin-bottom: 10px;
}
.base-info .el-form-item {
width: 100%;
}
.base-info .el-form-item >>> .el-form-item__content {
width: 80%;
}
.base-info .ms-http-select {
width: 100%;
}
</style>

View File

@ -0,0 +1,289 @@
<template>
<div>
<el-row>
<el-radio-group v-model="request.reportType" size="mini" style="margin: 10px 0px;">
<el-radio :disabled="isReadOnly" label="json" @change="changeReportType">
json
</el-radio>
<el-radio :disabled="isReadOnly" label="xml" @change="changeReportType">
xml
</el-radio>
<el-radio :disabled="isReadOnly" label="raw" @change="changeReportType">
raw
</el-radio>
</el-radio-group>
<div style="min-width: 1200px;" v-if="request.reportType === 'xml'">
<tcp-xml-table :table-data="request.xmlDataStruct" :show-options-button="true"
@xmlTablePushRow="xmlTablePushRow"
@initXmlTableData="initXmlTableData"
@saveTableData="saveXmlTableData" ref="treeTable"></tcp-xml-table>
</div>
<div style="min-width: 1200px;" v-if="request.reportType === 'json'">
<div class="send-request">
<ms-code-edit mode="json" :read-only="isReadOnly" :data.sync="request.jsonDataStruct" :modes="['text', 'json', 'xml', 'html']" theme="eclipse"/>
</div>
</div>
<div style="min-width: 1200px;" v-if="request.reportType === 'raw'">
<div class="send-request">
<ms-code-edit mode="text" :read-only="isReadOnly" :data.sync="request.rawDataStruct" :modes="['text', 'json', 'xml', 'html']" theme="eclipse"/>
</div>
</div>
</el-row>
</div>
</template>
<script>
import MsApiKeyValue from "../../ApiKeyValue";
import MsApiAssertions from "../../assertion/ApiAssertions";
import MsApiExtract from "../../extract/ApiExtract";
import ApiRequestMethodSelect from "../../collapse/ApiRequestMethodSelect";
import MsCodeEdit from "../../../../../common/components/MsCodeEdit";
import MsApiScenarioVariables from "../../ApiScenarioVariables";
import {createComponent} from "../../jmeter/components";
import {Assertions, Extract} from "../../../model/ApiTestModel";
import {parseEnvironment} from "../../../model/EnvironmentModel";
import ApiEnvironmentConfig from "../../environment/ApiEnvironmentConfig";
import {API_STATUS} from "../../../model/JsonData";
import TCPSampler from "../../jmeter/components/sampler/tcp-sampler";
import {getCurrentProjectID, getUUID} from "@/common/js/utils";
import MsApiVariable from "../../ApiVariable";
import MsInstructionsIcon from "../../../../../common/components/MsInstructionsIcon";
import Jsr233ProcessorContent from "../../../../automation/scenario/common/Jsr233ProcessorContent";
import JSR223PreProcessor from "../../jmeter/components/pre-processors/jsr223-pre-processor";
import ApiDefinitionStepButton from "../components/ApiDefinitionStepButton";
import TcpXmlTable from "@/business/components/api/definition/components/complete/table/TcpXmlTable";
export default {
name: "MsTcpParams",
components: {
TcpXmlTable,
ApiDefinitionStepButton,
Jsr233ProcessorContent,
MsInstructionsIcon,
MsApiVariable,
MsApiScenarioVariables,
MsCodeEdit,
ApiRequestMethodSelect, MsApiExtract, MsApiAssertions, MsApiKeyValue, ApiEnvironmentConfig
},
props: {
request: {},
basisData: {},
moduleOptions: Array,
isReadOnly: {
type: Boolean,
default: false
},
showScript: {
type: Boolean,
default: true,
},
referenced: {
type: Boolean,
default: false,
},
},
data() {
return {
reportType:"raw",
isReloadData: false,
refreshedXmlTable:true,
currentProjectId: "",
}
},
watch:{
reportType(){
this.request.reportType = this.reportType;
}
},
created() {
this.currentProjectId = getCurrentProjectID();
if (!this.request.parameters) {
this.$set(this.request, 'parameters', []);
this.request.parameters = [];
}
if(!this.request.connectEncoding){
this.request.connectEncoding = "UTF-8";
}
if(!this.request.reportType){
this.request.reportType = 'raw';
}
if(!this.request.jsonDataStruct){
this.request.jsonDataStruct = "";
}
if(!this.request.rawDataStruct){
this.request.rawDataStruct = "";
}
if(!this.request.xmlDataStruct){
this.request.xmlDataStruct = [];
}
},
methods: {
reload() {
this.isReloadData = true
this.$nextTick(() => {
this.isReloadData = false
})
},
runTest() {
},
changeReportType(){
},
getParmas(){
return this.request;
},
//xml
updateXmlTableData(dataStruct){
this.request.xmlDataStruct = dataStruct;
},
saveXmlTableData(dataStruct){
let valedataResult = this.validateXmlDataStruct(dataStruct);
if(valedataResult){
this.request.xmlDataStruct = dataStruct;
this.refreshXmlTable();
}
},
validateXmlDataStruct(){
if(this.request.xmlDataStruct){
this.refreshXmlTableDataStruct(this.request.xmlDataStruct);
let result = this.checkXmlTableDataStructData(this.request.xmlDataStruct);
return result;
}
},
refreshXmlTableDataStruct(dataStruct){
if(dataStruct && dataStruct.length > 0){
dataStruct.forEach( row => {
row.status = "";
if(row.children == null || row.children.length === 0){
row.children = [];
}else if(row.children.length>0){
this.refreshXmlTableDataStruct(row.children);
}
});
}
},
checkXmlTableDataStructData(dataStruct){
let allCheckResult = true;
if(dataStruct && dataStruct.length > 0){
for(let i = 0;i<dataStruct.length;i++){
let row = dataStruct[i];
allCheckResult = this.$refs.treeTable.validateRowData(row);
if(allCheckResult){
if(row.children != null && row.children.length > 0){
allCheckResult = this.checkXmlTableDataStructData(row.children);
if(!allCheckResult){
return false;
}
}
}else{
return false;
}
}
}
return allCheckResult;
},
initXmlTableData(){
if(this.request){
this.request.xmlDataStruct = [];
this.refreshXmlTable();
}
},
xmlTableDataPushRow(newRow){
if(this.request){
if(!this.request.xmlDataStruct){
this.request.xmlDataStruct = [];
}
this.request.xmlDataStruct.push(newRow);
this.refreshXmlTable();
}
},
xmlTableDataRemoveRow(row){
if(this.request){
if(this.request.xmlDataStruct){
this.removeFromDataStruct(this.request.xmlDataStruct,row);
this.refreshXmlTable();
}
}
},
removeFromDataStruct(dataStruct,row){
if(!dataStruct || dataStruct.length === 0){
return;
}
let rowIndex = dataStruct.indexOf(row);
if(rowIndex >= 0){
dataStruct.splice(rowIndex,1);
}else {
dataStruct.forEach( itemData => {
if(!itemData.children && itemData.children.length > 0){
this.removeFromDataStruct(itemData.children,row);
}
});
}
},
refreshXmlTable(){
this.refreshedXmlTable = true
this.$nextTick(() => {
this.refreshedXmlTable = false
})
},
xmlTablePushRow(row){
this.request.xmlDataStruct.push(row);
}
}
}
</script>
<style scoped>
.tcp >>> .el-input-number {
width: 100%;
}
.send-request {
padding: 0px 0;
height: 300px;
border: 1px #DCDFE6 solid;
border-radius: 4px;
width: 100%;
}
.ms-left-cell {
margin-top: 40px;
}
.ms-left-buttion {
margin: 6px 0px 8px 30px;
}
/deep/ .el-form-item {
margin-bottom: 15px;
}
.ms-left-cell {
margin-top: 40px;
}
.ms-left-buttion {
margin: 6px 0px 8px 30px;
}
/deep/ .el-form-item {
margin-bottom: 15px;
}
/deep/ .instructions-icon {
font-size: 14px !important;
}
.request-tabs {
margin: 20px;
min-height: 200px;
}
.other-config {
padding: 15px;
}
</style>

View File

@ -88,7 +88,6 @@
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label-width="labelWidth" :label="$t('用例模板')" prop="caseTemplateId">
<template-select :data="form" scene="API_CASE" prop="caseTemplateId" ref="caseTemplate"/>
</el-form-item>
@ -96,6 +95,10 @@
<template-select :data="form" scene="ISSUE" prop="issueTemplateId" ref="issueTemplate"/>
</el-form-item>
<el-form-item :label-width="labelWidth" label="TCP Mock Port">
<el-input-number v-model="form.mockTcpPort" :controls="false" style="width: 30%;margin-right: 30px"></el-input-number>
<el-switch v-model="form.isMockTcpOpen"></el-switch>
</el-form-item>
<el-form-item :label-width="labelWidth" :label="$t('commons.description')" prop="description">
<el-input :autosize="{ minRows: 2, maxRows: 4}" type="textarea" v-model="form.description"></el-input>