From a326c7fd77bda0a1b665ae2edc04ddc03052727c Mon Sep 17 00:00:00 2001 From: song-tianyang Date: Tue, 17 Aug 2021 18:51:01 +0800 Subject: [PATCH] =?UTF-8?q?feat(TCPMock=E6=9C=8D=E5=8A=A1):=20=E5=8F=82?= =?UTF-8?q?=E8=80=83HTTP=20MOCK=E5=8A=9F=E8=83=BD=E6=96=B0=E5=A2=9ETCP=20M?= =?UTF-8?q?OCK=E5=8A=9F=E8=83=BD#1001753?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://www.tapd.cn/55049933/prong/stories/view/1155049933001001753# --- .../controller/ApiDefinitionController.java | 7 +- .../ApiTestEnvironmentController.java | 4 + .../api/dto/automation/EsbDataStruct.java | 19 + .../automation/TcpTreeTableDataStruct.java | 21 +- .../service/ApiTestEnvironmentService.java | 85 ++++- .../api/service/MockConfigService.java | 298 ++++++++++++--- .../java/io/metersphere/api/tcp/TCPPool.java | 87 +++++ .../metersphere/api/tcp/server/TCPServer.java | 60 +++ .../api/tcp/server/TCPServicer.java | 82 ++++ .../io/metersphere/base/domain/Project.java | 4 + .../base/domain/ProjectExample.java | 120 ++++++ .../metersphere/base/mapper/ProjectMapper.xml | 42 +- .../mapper/ext/ExtMockExpectConfigMapper.java | 10 + .../mapper/ext/ExtMockExpectConfigMapper.xml | 10 + .../base/mapper/ext/ExtProjectMapper.xml | 2 +- .../commons/utils/JsonPathUtils.java | 113 ------ .../commons/utils/JsonStructUtils.java | 236 ++++++++++++ .../controller/ProjectController.java | 9 +- .../listener/AppStartListener.java | 6 + .../metersphere/service/ProjectService.java | 87 +++++ .../db/migration/V80__v1.9.0_release.sql | 2 +- .../components/EditCompleteContainer.vue | 13 +- .../complete/EditCompleteTCPApi.vue | 32 +- .../components/mock/TcpMockConfig.vue | 361 ++++++++++++++++++ .../components/request/tcp/TcpParams.vue | 289 ++++++++++++++ .../settings/project/ProjectList.vue | 5 +- 26 files changed, 1799 insertions(+), 205 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/tcp/TCPPool.java create mode 100644 backend/src/main/java/io/metersphere/api/tcp/server/TCPServer.java create mode 100644 backend/src/main/java/io/metersphere/api/tcp/server/TCPServicer.java create mode 100644 backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.java create mode 100644 backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.xml create mode 100644 backend/src/main/java/io/metersphere/commons/utils/JsonStructUtils.java create mode 100644 frontend/src/business/components/api/definition/components/mock/TcpMockConfig.vue create mode 100644 frontend/src/business/components/api/definition/components/request/tcp/TcpParams.vue diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java index dabc366535..128bc61840 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiDefinitionController.java @@ -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); } } diff --git a/backend/src/main/java/io/metersphere/api/controller/ApiTestEnvironmentController.java b/backend/src/main/java/io/metersphere/api/controller/ApiTestEnvironmentController.java index 72ae0700dc..7acf0f4084 100644 --- a/backend/src/main/java/io/metersphere/api/controller/ApiTestEnvironmentController.java +++ b/backend/src/main/java/io/metersphere/api/controller/ApiTestEnvironmentController.java @@ -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); + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/EsbDataStruct.java b/backend/src/main/java/io/metersphere/api/dto/automation/EsbDataStruct.java index 295cd9fdd0..b9aab28d66 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/EsbDataStruct.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/EsbDataStruct.java @@ -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 getNameDeep() { + List returnList = new ArrayList<>(); + if(StringUtils.isNotEmpty(this.name)){ + returnList.add(this.name); + } + if(CollectionUtils.isNotEmpty(this.children)){ + for (EsbDataStruct child :this.children) { + List itemNameList = child.getNameDeep(); + for (String itemName :itemNameList) { + if(!returnList.contains(itemName)){ + returnList.add(itemName); + } + } + } + } + return returnList; + } } diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/TcpTreeTableDataStruct.java b/backend/src/main/java/io/metersphere/api/dto/automation/TcpTreeTableDataStruct.java index ba20dfb0e8..7d4ddf1db9 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/TcpTreeTableDataStruct.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/TcpTreeTableDataStruct.java @@ -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 getNameDeep() { + List returnList = new ArrayList<>(); + if(StringUtils.isNotEmpty(this.name)){ + returnList.add(this.name); + } + if(CollectionUtils.isNotEmpty(this.children)){ + for (TcpTreeTableDataStruct child :this.children) { + List itemNameList = child.getNameDeep(); + for (String itemName :itemNameList) { + if(!returnList.contains(itemName)){ + returnList.add(itemName); + } + } + } + } + return returnList; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java index 14b0ba4bfb..02b2fd61ad 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java @@ -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; + } } diff --git a/backend/src/main/java/io/metersphere/api/service/MockConfigService.java b/backend/src/main/java/io/metersphere/api/service/MockConfigService.java index 13ab227b63..c724572940 100644 --- a/backend/src/main/java/io/metersphere/api/service/MockConfigService.java +++ b/backend/src/main/java/io/metersphere/api/service/MockConfigService.java @@ -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 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 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 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 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> 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> getTCPApiParams(ApiDefinitionWithBLOBs apiModel) { + List> list = new ArrayList<>(); + List 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 map = new HashMap<>(); + map.put("value", param); + list.add(map); + } + return list; + } + + private List> getHTTPApiParams(ApiDefinitionWithBLOBs apiModel) { List> list = new ArrayList<>(); List 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 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 parseByJsonDataStruct(String dataString) { + List returnList = new ArrayList<>(); + try { + JSONValidator validator = JSONValidator.from(dataString); + Map 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 parseByTcpTreeDataStruct(String dataString) { + List list = JSONArray.parseArray(dataString, TcpTreeTableDataStruct.class); + List returnList = new ArrayList<>(); + for (TcpTreeTableDataStruct dataStruct : list) { + List nameList = dataStruct.getNameDeep(); + for (String name : nameList) { + if (!returnList.contains(nameList)) { + returnList.add(name); + } + } + } + return returnList; + } + + private List parseByESBDataStruct(String dataString) { + List list = JSONArray.parseArray(dataString, EsbDataStruct.class); + List returnList = new ArrayList<>(); + for (EsbDataStruct dataStruct : list) { + List 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 projectList = projectMapper.selectByExample(projectExample); + + boolean isJsonMessage = this.checkMessageIsJson(message); + boolean isXMLMessage = this.checkMessageIsXml(message); + + List structResult = new ArrayList<>(); + List rawResult = new ArrayList<>(); + + for (Project project : projectList) { + String projectId = project.getId(); + List 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 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; + } } diff --git a/backend/src/main/java/io/metersphere/api/tcp/TCPPool.java b/backend/src/main/java/io/metersphere/api/tcp/TCPPool.java new file mode 100644 index 0000000000..28af94c2c9 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/tcp/TCPPool.java @@ -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 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 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; + } + } +} diff --git a/backend/src/main/java/io/metersphere/api/tcp/server/TCPServer.java b/backend/src/main/java/io/metersphere/api/tcp/server/TCPServer.java new file mode 100644 index 0000000000..e77d48e5c8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/tcp/server/TCPServer.java @@ -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) { + } + } +} diff --git a/backend/src/main/java/io/metersphere/api/tcp/server/TCPServicer.java b/backend/src/main/java/io/metersphere/api/tcp/server/TCPServicer.java new file mode 100644 index 0000000000..6997893212 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/tcp/server/TCPServicer.java @@ -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 { + + } + } + } + } + +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/Project.java b/backend/src/main/java/io/metersphere/base/domain/Project.java index 2e1470180b..d434edb2b5 100644 --- a/backend/src/main/java/io/metersphere/base/domain/Project.java +++ b/backend/src/main/java/io/metersphere/base/domain/Project.java @@ -39,5 +39,9 @@ public class Project implements Serializable { private String systemId; + private Integer mockTcpPort; + + private Boolean isMockTcpOpen; + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java b/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java index abf3cc1b01..33b5c69343 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/ProjectExample.java @@ -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 values) { + addCriterion("mock_tcp_port in", values, "mockTcpPort"); + return (Criteria) this; + } + + public Criteria andMockTcpPortNotIn(List 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 values) { + addCriterion("is_mock_tcp_open in", values, "isMockTcpOpen"); + return (Criteria) this; + } + + public Criteria andIsMockTcpOpenNotIn(List 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 { diff --git a/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml index f99aa860a8..55baf5e074 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ProjectMapper.xml @@ -19,6 +19,8 @@ + + @@ -81,7 +83,7 @@ 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 @@ -296,6 +312,12 @@ system_id = #{record.systemId,jdbcType=VARCHAR}, + + mock_tcp_port = #{record.mockTcpPort,jdbcType=INTEGER}, + + + is_mock_tcp_open = #{record.isMockTcpOpen,jdbcType=BIT}, + @@ -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} @@ -375,6 +399,12 @@ system_id = #{systemId,jdbcType=VARCHAR}, + + mock_tcp_port = #{mockTcpPort,jdbcType=INTEGER}, + + + is_mock_tcp_open = #{isMockTcpOpen,jdbcType=BIT}, + where id = #{id,jdbcType=VARCHAR} @@ -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} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.java new file mode 100644 index 0000000000..18d2a08f40 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.java @@ -0,0 +1,10 @@ +package io.metersphere.base.mapper.ext; + +import io.metersphere.base.domain.MockExpectConfigWithBLOBs; + +import java.util.List; + +public interface ExtMockExpectConfigMapper { + + List selectByProjectIdAndStatusIsOpen(String projectId); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.xml new file mode 100644 index 0000000000..84cfc506f4 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtMockExpectConfigMapper.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml index 7303768450..8bba5ecabe 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtProjectMapper.xml @@ -34,7 +34,7 @@