feat(接口自动化): 历史操作日志基本框架完成
This commit is contained in:
parent
4062f36cc2
commit
d23ca01c5d
|
@ -423,11 +423,6 @@
|
|||
<artifactId>json-path</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.flipkart.zjsonpatch</groupId>
|
||||
<artifactId>zjsonpatch</artifactId>
|
||||
<version>0.4.11</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -104,9 +104,8 @@ public class ApiDefinitionController {
|
|||
|
||||
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER}, logical = Logical.OR)
|
||||
public ApiDefinitionWithBLOBs update(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#request.id)", title = "#request.name", content = "#msClass.getLogDetails(#request.id)", msClass = ApiDefinitionService.class)
|
||||
public void update(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
|
||||
public ApiDefinitionWithBLOBs update(@RequestPart("request") SaveApiDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
|
||||
checkPermissionService.checkProjectOwner(request.getProjectId());
|
||||
return apiDefinitionService.update(request, bodyFiles);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,11 @@ import io.metersphere.api.dto.definition.ApiModuleDTO;
|
|||
import io.metersphere.api.dto.definition.DragModuleRequest;
|
||||
import io.metersphere.api.service.ApiModuleService;
|
||||
import io.metersphere.base.domain.ApiModule;
|
||||
import io.metersphere.commons.constants.OperLogConstants;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.ApiDefinitionDefaultApiTypeUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.log.annotation.MsAuditLog;
|
||||
import io.metersphere.service.CheckPermissionService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
|
@ -26,7 +28,7 @@ public class ApiModuleController {
|
|||
private CheckPermissionService checkPermissionService;
|
||||
|
||||
@GetMapping("/list/{projectId}/{protocol}")
|
||||
public List<ApiModuleDTO> getNodeByProjectId(@PathVariable String projectId,@PathVariable String protocol) {
|
||||
public List<ApiModuleDTO> getNodeByProjectId(@PathVariable String projectId, @PathVariable String protocol) {
|
||||
checkPermissionService.checkProjectOwner(projectId);
|
||||
String userId = SessionUtils.getUserId();
|
||||
ApiDefinitionDefaultApiTypeUtil.addUserSelectApiType(userId, protocol);
|
||||
|
@ -59,18 +61,21 @@ public class ApiModuleController {
|
|||
|
||||
@PostMapping("/add")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.CREATE, title = "#node.name", content = "#msClass.getLogDetails(#node)", msClass = ApiModuleService.class)
|
||||
public String addNode(@RequestBody ApiModule node) {
|
||||
return apiModuleService.addNode(node);
|
||||
}
|
||||
|
||||
@PostMapping("/edit")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#node)", title = "#node.name", content = "#msClass.getLogDetails(#node)", msClass = ApiModuleService.class)
|
||||
public int editNode(@RequestBody DragModuleRequest node) {
|
||||
return apiModuleService.editNode(node);
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.DELETE, beforeEvent = "#msClass.getLogDetails(#nodeIds)", msClass = ApiModuleService.class)
|
||||
public int deleteNode(@RequestBody List<String> nodeIds) {
|
||||
//nodeIds 包含删除节点ID及其所有子节点ID
|
||||
return apiModuleService.deleteNode(nodeIds);
|
||||
|
@ -78,6 +83,7 @@ public class ApiModuleController {
|
|||
|
||||
@PostMapping("/drag")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#node)", title = "#node.name", content = "#msClass.getLogDetails(#node)", msClass = ApiModuleService.class)
|
||||
public void dragNode(@RequestBody DragModuleRequest node) {
|
||||
apiModuleService.dragNode(node);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ import io.metersphere.api.dto.definition.*;
|
|||
import io.metersphere.api.service.ApiTestCaseService;
|
||||
import io.metersphere.base.domain.ApiTestCase;
|
||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.OperLogConstants;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.log.annotation.MsAuditLog;
|
||||
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
|
||||
import io.metersphere.track.service.TestPlanApiCaseService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
|
@ -31,6 +33,7 @@ public class ApiTestCaseController {
|
|||
private ApiTestCaseService apiTestCaseService;
|
||||
@Resource
|
||||
private TestPlanApiCaseService testPlanApiCaseService;
|
||||
|
||||
@PostMapping("/list")
|
||||
public List<ApiTestCaseResult> list(@RequestBody ApiTestCaseRequest request) {
|
||||
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
|
@ -38,20 +41,21 @@ public class ApiTestCaseController {
|
|||
}
|
||||
|
||||
@GetMapping("/findById/{id}")
|
||||
public ApiTestCaseResult single(@PathVariable String id ) {
|
||||
public ApiTestCaseResult single(@PathVariable String id) {
|
||||
ApiTestCaseRequest request = new ApiTestCaseRequest();
|
||||
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
request.setId(id);
|
||||
List<ApiTestCaseResult> list = apiTestCaseService.list(request);
|
||||
if(!list.isEmpty()){
|
||||
return list.get(0);
|
||||
}else {
|
||||
List<ApiTestCaseResult> list = apiTestCaseService.list(request);
|
||||
if (!list.isEmpty()) {
|
||||
return list.get(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/getStateByTestPlan/{id}")
|
||||
public String getStateByTestPlan(@PathVariable String id ) {
|
||||
String status=testPlanApiCaseService.getState(id);
|
||||
public String getStateByTestPlan(@PathVariable String id) {
|
||||
String status = testPlanApiCaseService.getState(id);
|
||||
return status;
|
||||
|
||||
}
|
||||
|
@ -86,21 +90,25 @@ public class ApiTestCaseController {
|
|||
}
|
||||
|
||||
@PostMapping(value = "/create", consumes = {"multipart/form-data"})
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.CREATE, title = "#request.name", content = "#msClass.getLogDetails(#request)", msClass = ApiTestCaseService.class)
|
||||
public ApiTestCase create(@RequestPart("request") SaveApiTestCaseRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
|
||||
return apiTestCaseService.create(request, bodyFiles);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/update", consumes = {"multipart/form-data"})
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#request)", title = "#request.name", content = "#msClass.getLogDetails(#request)", msClass = ApiTestCaseService.class)
|
||||
public ApiTestCase update(@RequestPart("request") SaveApiTestCaseRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
|
||||
return apiTestCaseService.update(request, bodyFiles);
|
||||
}
|
||||
|
||||
@GetMapping("/delete/{id}")
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.DELETE, beforeEvent = "#msClass.getLogDetails(#id)", msClass = ApiTestCaseService.class)
|
||||
public void delete(@PathVariable String id) {
|
||||
apiTestCaseService.delete(id);
|
||||
}
|
||||
|
||||
@PostMapping("/removeToGc")
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.GC, beforeEvent = "#msClass.getLogDetails(#ids)", msClass = ApiTestCaseService.class)
|
||||
public void removeToGc(@RequestBody List<String> ids) {
|
||||
apiTestCaseService.removeToGc(ids);
|
||||
}
|
||||
|
@ -118,16 +126,19 @@ public class ApiTestCaseController {
|
|||
|
||||
@PostMapping("/batch/editByParam")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.BATCH_UPDATE, beforeEvent = "#msClass.getLogDetails(#request.ids)", content = "#msClass.getLogDetails(#request.ids)", msClass = ApiTestCaseService.class)
|
||||
public void editApiBathByParam(@RequestBody ApiTestBatchRequest request) {
|
||||
apiTestCaseService.editApiBathByParam(request);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteBatch")
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.BATCH_DEL, beforeEvent = "#msClass.getLogDetails(#ids)", msClass = ApiTestCaseService.class)
|
||||
public void deleteBatch(@RequestBody List<String> ids) {
|
||||
apiTestCaseService.deleteBatch(ids);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteBatchByParam")
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.BATCH_DEL, beforeEvent = "#msClass.getLogDetails(#request.ids)", msClass = ApiTestCaseService.class)
|
||||
public void deleteBatchByParam(@RequestBody ApiTestBatchRequest request) {
|
||||
apiTestCaseService.deleteBatchByParam(request);
|
||||
}
|
||||
|
@ -136,18 +147,21 @@ public class ApiTestCaseController {
|
|||
public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) {
|
||||
apiTestCaseService.relevanceByCase(request);
|
||||
}
|
||||
|
||||
@PostMapping("/relevance/review")
|
||||
public void testCaseReviewRelevance(@RequestBody ApiCaseRelevanceRequest request){
|
||||
public void testCaseReviewRelevance(@RequestBody ApiCaseRelevanceRequest request) {
|
||||
apiTestCaseService.relevanceByApiByReview(request);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/jenkins/run")
|
||||
@MsAuditLog(module = "api_definition", type = OperLogConstants.EXECUTE, content = "#msClass.getLogDetails(#request.caseId)", msClass = ApiTestCaseService.class)
|
||||
public String jenkinsRun(@RequestBody RunCaseRequest request) {
|
||||
return apiTestCaseService.run(request);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/jenkins/exec/result/{id}")
|
||||
public String getExecResult(@PathVariable String id) {
|
||||
return apiTestCaseService.getExecResult(id);
|
||||
return apiTestCaseService.getExecResult(id);
|
||||
|
||||
}
|
||||
}
|
|
@ -324,6 +324,7 @@ public class ApiDefinitionService {
|
|||
test.setProtocol(request.getProtocol());
|
||||
test.setMethod(request.getMethod());
|
||||
test.setPath(request.getPath());
|
||||
test.setCreateUser(SessionUtils.getUserId());
|
||||
test.setProjectId(request.getProjectId());
|
||||
request.getRequest().setId(request.getId());
|
||||
test.setRequest(JSONObject.toJSONString(request.getRequest()));
|
||||
|
@ -1073,9 +1074,12 @@ public class ApiDefinitionService {
|
|||
|
||||
public String getLogDetails(String id) {
|
||||
ApiDefinitionWithBLOBs bloBs = apiDefinitionMapper.selectByPrimaryKey(id);
|
||||
List<DetailColumn> columns = ReflexObjectUtil.getColumns(bloBs, DefinitionReference.definitionColumns);
|
||||
OperatingLogDetails details = new OperatingLogDetails(id, bloBs.getProjectId(), columns);
|
||||
return JSON.toJSONString(details);
|
||||
if (bloBs != null) {
|
||||
List<DetailColumn> columns = ReflexObjectUtil.getColumns(bloBs, DefinitionReference.definitionColumns);
|
||||
OperatingLogDetails details = new OperatingLogDetails(id, bloBs.getProjectId(), bloBs.getCreateUser(), columns);
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLogDetails(List<String> ids) {
|
||||
|
@ -1084,7 +1088,7 @@ public class ApiDefinitionService {
|
|||
example.createCriteria().andIdIn(ids);
|
||||
List<ApiDefinition> definitions = apiDefinitionMapper.selectByExample(example);
|
||||
List<String> names = definitions.stream().map(ApiDefinition::getName).collect(Collectors.toList());
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(ids), definitions.get(0).getProjectId(), String.join(",", names), new LinkedList<>());
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(ids), definitions.get(0).getProjectId(), String.join(",", names), definitions.get(0).getCreateUser(), new LinkedList<>());
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
|
@ -1096,29 +1100,31 @@ public class ApiDefinitionService {
|
|||
ApiDefinitionExample example = new ApiDefinitionExample();
|
||||
example.createCriteria().andIdIn(request.getIds());
|
||||
List<ApiDefinition> definitions = apiDefinitionMapper.selectByExample(example);
|
||||
List<DetailColumn> columns = new LinkedList<>();
|
||||
if (StringUtils.isNotEmpty(request.getMethod())) {
|
||||
columns.clear();
|
||||
definitions.forEach(item -> {
|
||||
DetailColumn column = new DetailColumn(DefinitionReference.definitionColumns.get("method"), "method", item.getMethod(), null);
|
||||
columns.add(column);
|
||||
});
|
||||
} else if (StringUtils.isNotEmpty(request.getStatus())) {
|
||||
columns.clear();
|
||||
definitions.forEach(item -> {
|
||||
DetailColumn column = new DetailColumn(DefinitionReference.definitionColumns.get("status"), "status", item.getStatus(), null);
|
||||
columns.add(column);
|
||||
});
|
||||
} else if (StringUtils.isNotEmpty(request.getUserId())) {
|
||||
columns.clear();
|
||||
definitions.forEach(item -> {
|
||||
DetailColumn column = new DetailColumn(DefinitionReference.definitionColumns.get("userId"), "userId", item.getUserId(), null);
|
||||
columns.add(column);
|
||||
});
|
||||
if (CollectionUtils.isNotEmpty(definitions)) {
|
||||
List<DetailColumn> columns = new LinkedList<>();
|
||||
if (StringUtils.isNotEmpty(request.getMethod())) {
|
||||
columns.clear();
|
||||
definitions.forEach(item -> {
|
||||
DetailColumn column = new DetailColumn(DefinitionReference.definitionColumns.get("method"), "method", item.getMethod(), null);
|
||||
columns.add(column);
|
||||
});
|
||||
} else if (StringUtils.isNotEmpty(request.getStatus())) {
|
||||
columns.clear();
|
||||
definitions.forEach(item -> {
|
||||
DetailColumn column = new DetailColumn(DefinitionReference.definitionColumns.get("status"), "status", item.getStatus(), null);
|
||||
columns.add(column);
|
||||
});
|
||||
} else if (StringUtils.isNotEmpty(request.getUserId())) {
|
||||
columns.clear();
|
||||
definitions.forEach(item -> {
|
||||
DetailColumn column = new DetailColumn(DefinitionReference.definitionColumns.get("userId"), "userId", item.getUserId(), null);
|
||||
columns.add(column);
|
||||
});
|
||||
}
|
||||
List<String> names = definitions.stream().map(ApiDefinition::getName).collect(Collectors.toList());
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(request.getIds()), request.getProjectId(), String.join(",", names), definitions.get(0).getCreateUser(), columns);
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
List<String> names = definitions.stream().map(ApiDefinition::getName).collect(Collectors.toList());
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(request.getIds()), request.getProjectId(),String.join(",",names), columns);
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -13,18 +13,23 @@ import io.metersphere.base.mapper.ext.ExtApiDefinitionMapper;
|
|||
import io.metersphere.base.mapper.ext.ExtApiModuleMapper;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.log.utils.ReflexObjectUtil;
|
||||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.definition.DefinitionReference;
|
||||
import io.metersphere.service.NodeTreeService;
|
||||
import io.metersphere.service.ProjectService;
|
||||
import io.metersphere.track.service.TestPlanApiCaseService;
|
||||
import io.metersphere.track.service.TestPlanProjectService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
@ -108,6 +113,7 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
|
|||
node.setCreateTime(System.currentTimeMillis());
|
||||
node.setUpdateTime(System.currentTimeMillis());
|
||||
node.setId(UUID.randomUUID().toString());
|
||||
node.setCreateUser(SessionUtils.getUserId());
|
||||
double pos = getNextLevelPos(node.getProjectId(), node.getLevel(), node.getParentId());
|
||||
node.setPos(pos);
|
||||
apiModuleMapper.insertSelective(node);
|
||||
|
@ -376,4 +382,50 @@ public class ApiModuleService extends NodeTreeService<ApiModuleDTO> {
|
|||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
public String getLogDetails(List<String> ids) {
|
||||
ApiModuleExample example = new ApiModuleExample();
|
||||
ApiModuleExample.Criteria criteria = example.createCriteria();
|
||||
criteria.andIdIn(ids);
|
||||
List<ApiModule> nodes = apiModuleMapper.selectByExample(example);
|
||||
if (CollectionUtils.isNotEmpty(nodes)) {
|
||||
List<String> names = nodes.stream().map(ApiModule::getName).collect(Collectors.toList());
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(ids), nodes.get(0).getProjectId(), String.join(",", names), nodes.get(0).getCreateUser(), new LinkedList<>());
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLogDetails(ApiModule node) {
|
||||
ApiModule module = null;
|
||||
if (StringUtils.isNotEmpty(node.getId())) {
|
||||
module = apiModuleMapper.selectByPrimaryKey(node.getId());
|
||||
}
|
||||
if (module == null && StringUtils.isNotEmpty(node.getName())) {
|
||||
ApiModuleExample example = new ApiModuleExample();
|
||||
ApiModuleExample.Criteria criteria = example.createCriteria();
|
||||
criteria.andNameEqualTo(node.getName()).andProjectIdEqualTo(node.getProjectId());
|
||||
if (StringUtils.isNotEmpty(node.getProtocol())) {
|
||||
criteria.andProtocolEqualTo(node.getProtocol());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(node.getParentId())) {
|
||||
criteria.andParentIdEqualTo(node.getParentId());
|
||||
} else {
|
||||
criteria.andParentIdIsNull();
|
||||
}
|
||||
if (StringUtils.isNotEmpty(node.getId())) {
|
||||
criteria.andIdNotEqualTo(node.getId());
|
||||
}
|
||||
List<ApiModule> list = apiModuleMapper.selectByExample(example);
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
module = list.get(0);
|
||||
}
|
||||
}
|
||||
if (module != null) {
|
||||
List<DetailColumn> columns = ReflexObjectUtil.getColumns(module, DefinitionReference.moduleColumns);
|
||||
OperatingLogDetails details = new OperatingLogDetails(module.getId(), module.getProjectId(), module.getCreateUser(), columns);
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,10 @@ import io.metersphere.commons.constants.TestPlanStatus;
|
|||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.log.utils.ReflexObjectUtil;
|
||||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.definition.DefinitionReference;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.service.QuotaService;
|
||||
import io.metersphere.service.UserService;
|
||||
|
@ -705,4 +709,48 @@ public class ApiTestCaseService {
|
|||
|
||||
return list;
|
||||
}
|
||||
|
||||
public String getLogDetails(List<String> ids) {
|
||||
ApiTestCaseExample example = new ApiTestCaseExample();
|
||||
ApiTestCaseExample.Criteria criteria = example.createCriteria();
|
||||
criteria.andIdIn(ids);
|
||||
List<ApiTestCase> nodes = apiTestCaseMapper.selectByExample(example);
|
||||
if (CollectionUtils.isNotEmpty(nodes)) {
|
||||
List<String> names = nodes.stream().map(ApiTestCase::getName).collect(Collectors.toList());
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(ids), nodes.get(0).getProjectId(), String.join(",", names), nodes.get(0).getCreateUserId(), new LinkedList<>());
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLogDetails(String id) {
|
||||
ApiTestCaseWithBLOBs bloBs = apiTestCaseMapper.selectByPrimaryKey(id);
|
||||
if (bloBs != null) {
|
||||
OperatingLogDetails details = new OperatingLogDetails(id, bloBs.getProjectId(), bloBs.getName(), bloBs.getCreateUserId(), new LinkedList<>());
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLogDetails(SaveApiTestCaseRequest request) {
|
||||
ApiTestCaseWithBLOBs bloBs = null;
|
||||
if (StringUtils.isNotEmpty(request.getId())) {
|
||||
bloBs = apiTestCaseMapper.selectByPrimaryKey(request.getId());
|
||||
}
|
||||
if (bloBs == null && StringUtils.isNotEmpty(request.getName())) {
|
||||
ApiTestCaseExample example = new ApiTestCaseExample();
|
||||
ApiTestCaseExample.Criteria criteria = example.createCriteria();
|
||||
criteria.andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andApiDefinitionIdEqualTo(request.getApiDefinitionId());
|
||||
List<ApiTestCaseWithBLOBs> list = apiTestCaseMapper.selectByExampleWithBLOBs(example);
|
||||
if (CollectionUtils.isNotEmpty(list)) {
|
||||
bloBs = list.get(0);
|
||||
}
|
||||
}
|
||||
if (bloBs != null) {
|
||||
List<DetailColumn> columns = ReflexObjectUtil.getColumns(bloBs, DefinitionReference.caseColumns);
|
||||
OperatingLogDetails details = new OperatingLogDetails(bloBs.getId(), bloBs.getProjectId(), bloBs.getCreateUserId(), columns);
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,5 +39,7 @@ public class ApiDefinition implements Serializable {
|
|||
|
||||
private String originalState;
|
||||
|
||||
private String createUser;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -1263,6 +1263,76 @@ public class ApiDefinitionExample {
|
|||
addCriterion("original_state not between", value1, value2, "originalState");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIsNull() {
|
||||
addCriterion("create_user is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIsNotNull() {
|
||||
addCriterion("create_user is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserEqualTo(String value) {
|
||||
addCriterion("create_user =", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotEqualTo(String value) {
|
||||
addCriterion("create_user <>", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserGreaterThan(String value) {
|
||||
addCriterion("create_user >", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("create_user >=", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLessThan(String value) {
|
||||
addCriterion("create_user <", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLessThanOrEqualTo(String value) {
|
||||
addCriterion("create_user <=", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLike(String value) {
|
||||
addCriterion("create_user like", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotLike(String value) {
|
||||
addCriterion("create_user not like", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIn(List<String> values) {
|
||||
addCriterion("create_user in", values, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotIn(List<String> values) {
|
||||
addCriterion("create_user not in", values, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserBetween(String value1, String value2) {
|
||||
addCriterion("create_user between", value1, value2, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotBetween(String value1, String value2) {
|
||||
addCriterion("create_user not between", value1, value2, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -23,5 +23,7 @@ public class ApiModule implements Serializable {
|
|||
|
||||
private Double pos;
|
||||
|
||||
private String createUser;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -693,6 +693,76 @@ public class ApiModuleExample {
|
|||
addCriterion("pos not between", value1, value2, "pos");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIsNull() {
|
||||
addCriterion("create_user is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIsNotNull() {
|
||||
addCriterion("create_user is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserEqualTo(String value) {
|
||||
addCriterion("create_user =", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotEqualTo(String value) {
|
||||
addCriterion("create_user <>", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserGreaterThan(String value) {
|
||||
addCriterion("create_user >", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("create_user >=", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLessThan(String value) {
|
||||
addCriterion("create_user <", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLessThanOrEqualTo(String value) {
|
||||
addCriterion("create_user <=", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLike(String value) {
|
||||
addCriterion("create_user like", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotLike(String value) {
|
||||
addCriterion("create_user not like", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIn(List<String> values) {
|
||||
addCriterion("create_user in", values, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotIn(List<String> values) {
|
||||
addCriterion("create_user not in", values, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserBetween(String value1, String value2) {
|
||||
addCriterion("create_user between", value1, value2, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotBetween(String value1, String value2) {
|
||||
addCriterion("create_user not between", value1, value2, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Criteria extends GeneratedCriteria {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class OperatingLog implements Serializable {
|
||||
private String id;
|
||||
|
@ -11,6 +12,8 @@ public class OperatingLog implements Serializable {
|
|||
|
||||
private String operMethod;
|
||||
|
||||
private String createUser;
|
||||
|
||||
private String operUser;
|
||||
|
||||
private String sourceId;
|
||||
|
@ -21,9 +24,9 @@ public class OperatingLog implements Serializable {
|
|||
|
||||
private String operTitle;
|
||||
|
||||
private Long operTime;
|
||||
private String operPath;
|
||||
|
||||
private String operContent;
|
||||
private Long operTime;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -314,6 +314,76 @@ public class OperatingLogExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIsNull() {
|
||||
addCriterion("create_user is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIsNotNull() {
|
||||
addCriterion("create_user is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserEqualTo(String value) {
|
||||
addCriterion("create_user =", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotEqualTo(String value) {
|
||||
addCriterion("create_user <>", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserGreaterThan(String value) {
|
||||
addCriterion("create_user >", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("create_user >=", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLessThan(String value) {
|
||||
addCriterion("create_user <", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLessThanOrEqualTo(String value) {
|
||||
addCriterion("create_user <=", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserLike(String value) {
|
||||
addCriterion("create_user like", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotLike(String value) {
|
||||
addCriterion("create_user not like", value, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserIn(List<String> values) {
|
||||
addCriterion("create_user in", values, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotIn(List<String> values) {
|
||||
addCriterion("create_user not in", values, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserBetween(String value1, String value2) {
|
||||
addCriterion("create_user between", value1, value2, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andCreateUserNotBetween(String value1, String value2) {
|
||||
addCriterion("create_user not between", value1, value2, "createUser");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperUserIsNull() {
|
||||
addCriterion("oper_user is null");
|
||||
return (Criteria) this;
|
||||
|
@ -664,6 +734,76 @@ public class OperatingLogExample {
|
|||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathIsNull() {
|
||||
addCriterion("oper_path is null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathIsNotNull() {
|
||||
addCriterion("oper_path is not null");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathEqualTo(String value) {
|
||||
addCriterion("oper_path =", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathNotEqualTo(String value) {
|
||||
addCriterion("oper_path <>", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathGreaterThan(String value) {
|
||||
addCriterion("oper_path >", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathGreaterThanOrEqualTo(String value) {
|
||||
addCriterion("oper_path >=", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathLessThan(String value) {
|
||||
addCriterion("oper_path <", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathLessThanOrEqualTo(String value) {
|
||||
addCriterion("oper_path <=", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathLike(String value) {
|
||||
addCriterion("oper_path like", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathNotLike(String value) {
|
||||
addCriterion("oper_path not like", value, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathIn(List<String> values) {
|
||||
addCriterion("oper_path in", values, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathNotIn(List<String> values) {
|
||||
addCriterion("oper_path not in", values, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathBetween(String value1, String value2) {
|
||||
addCriterion("oper_path between", value1, value2, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperPathNotBetween(String value1, String value2) {
|
||||
addCriterion("oper_path not between", value1, value2, "operPath");
|
||||
return (Criteria) this;
|
||||
}
|
||||
|
||||
public Criteria andOperTimeIsNull() {
|
||||
addCriterion("oper_time is null");
|
||||
return (Criteria) this;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.base.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class OperatingLogWithBLOBs extends OperatingLog implements Serializable {
|
||||
private String operContent;
|
||||
|
||||
private String operParams;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -19,6 +19,7 @@
|
|||
<result column="num" jdbcType="INTEGER" property="num" />
|
||||
<result column="tags" jdbcType="VARCHAR" property="tags" />
|
||||
<result column="original_state" jdbcType="VARCHAR" property="originalState" />
|
||||
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
|
||||
<result column="description" jdbcType="LONGVARCHAR" property="description" />
|
||||
|
@ -85,7 +86,8 @@
|
|||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, project_id, `name`, `method`, module_path, environment_id, schedule, `status`,
|
||||
module_id, user_id, create_time, update_time, protocol, `path`, num, tags, original_state
|
||||
module_id, user_id, create_time, update_time, protocol, `path`, num, tags, original_state,
|
||||
create_user
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
description, request, response
|
||||
|
@ -144,15 +146,17 @@
|
|||
schedule, `status`, module_id,
|
||||
user_id, create_time, update_time,
|
||||
protocol, `path`, num,
|
||||
tags, original_state, description,
|
||||
request, response)
|
||||
tags, original_state, create_user,
|
||||
description, request, response
|
||||
)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{method,jdbcType=VARCHAR}, #{modulePath,jdbcType=VARCHAR}, #{environmentId,jdbcType=VARCHAR},
|
||||
#{schedule,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{moduleId,jdbcType=VARCHAR},
|
||||
#{userId,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
|
||||
#{protocol,jdbcType=VARCHAR}, #{path,jdbcType=VARCHAR}, #{num,jdbcType=INTEGER},
|
||||
#{tags,jdbcType=VARCHAR}, #{originalState,jdbcType=VARCHAR}, #{description,jdbcType=LONGVARCHAR},
|
||||
#{request,jdbcType=LONGVARCHAR}, #{response,jdbcType=LONGVARCHAR})
|
||||
#{tags,jdbcType=VARCHAR}, #{originalState,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
|
||||
#{description,jdbcType=LONGVARCHAR}, #{request,jdbcType=LONGVARCHAR}, #{response,jdbcType=LONGVARCHAR}
|
||||
)
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiDefinitionWithBLOBs">
|
||||
insert into api_definition
|
||||
|
@ -208,6 +212,9 @@
|
|||
<if test="originalState != null">
|
||||
original_state,
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
create_user,
|
||||
</if>
|
||||
<if test="description != null">
|
||||
description,
|
||||
</if>
|
||||
|
@ -270,6 +277,9 @@
|
|||
<if test="originalState != null">
|
||||
#{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
#{createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="description != null">
|
||||
#{description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -341,6 +351,9 @@
|
|||
<if test="record.originalState != null">
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.createUser != null">
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.description != null">
|
||||
description = #{record.description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -374,6 +387,7 @@
|
|||
num = #{record.num,jdbcType=INTEGER},
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
description = #{record.description,jdbcType=LONGVARCHAR},
|
||||
request = #{record.request,jdbcType=LONGVARCHAR},
|
||||
response = #{record.response,jdbcType=LONGVARCHAR}
|
||||
|
@ -399,7 +413,8 @@
|
|||
`path` = #{record.path,jdbcType=VARCHAR},
|
||||
num = #{record.num,jdbcType=INTEGER},
|
||||
tags = #{record.tags,jdbcType=VARCHAR},
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR}
|
||||
original_state = #{record.originalState,jdbcType=VARCHAR},
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -455,6 +470,9 @@
|
|||
<if test="originalState != null">
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="description != null">
|
||||
description = #{description,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
|
@ -485,6 +503,7 @@
|
|||
num = #{num,jdbcType=INTEGER},
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
description = #{description,jdbcType=LONGVARCHAR},
|
||||
request = #{request,jdbcType=LONGVARCHAR},
|
||||
response = #{response,jdbcType=LONGVARCHAR}
|
||||
|
@ -507,7 +526,8 @@
|
|||
`path` = #{path,jdbcType=VARCHAR},
|
||||
num = #{num,jdbcType=INTEGER},
|
||||
tags = #{tags,jdbcType=VARCHAR},
|
||||
original_state = #{originalState,jdbcType=VARCHAR}
|
||||
original_state = #{originalState,jdbcType=VARCHAR},
|
||||
create_user = #{createUser,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -11,6 +11,7 @@
|
|||
<result column="create_time" jdbcType="BIGINT" property="createTime" />
|
||||
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
|
||||
<result column="pos" jdbcType="DOUBLE" property="pos" />
|
||||
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
|
@ -71,7 +72,8 @@
|
|||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, project_id, `name`, protocol, parent_id, `level`, create_time, update_time, pos
|
||||
id, project_id, `name`, protocol, parent_id, `level`, create_time, update_time, pos,
|
||||
create_user
|
||||
</sql>
|
||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.ApiModuleExample" resultMap="BaseResultMap">
|
||||
select
|
||||
|
@ -106,12 +108,12 @@
|
|||
<insert id="insert" parameterType="io.metersphere.base.domain.ApiModule">
|
||||
insert into api_module (id, project_id, `name`,
|
||||
protocol, parent_id, `level`,
|
||||
create_time, update_time, pos
|
||||
)
|
||||
create_time, update_time, pos,
|
||||
create_user)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{protocol,jdbcType=VARCHAR}, #{parentId,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER},
|
||||
#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{pos,jdbcType=DOUBLE}
|
||||
)
|
||||
#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{pos,jdbcType=DOUBLE},
|
||||
#{createUser,jdbcType=VARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiModule">
|
||||
insert into api_module
|
||||
|
@ -143,6 +145,9 @@
|
|||
<if test="pos != null">
|
||||
pos,
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
create_user,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -172,6 +177,9 @@
|
|||
<if test="pos != null">
|
||||
#{pos,jdbcType=DOUBLE},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
#{createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.ApiModuleExample" resultType="java.lang.Long">
|
||||
|
@ -210,6 +218,9 @@
|
|||
<if test="record.pos != null">
|
||||
pos = #{record.pos,jdbcType=DOUBLE},
|
||||
</if>
|
||||
<if test="record.createUser != null">
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
|
@ -225,7 +236,8 @@
|
|||
`level` = #{record.level,jdbcType=INTEGER},
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
pos = #{record.pos,jdbcType=DOUBLE}
|
||||
pos = #{record.pos,jdbcType=DOUBLE},
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -257,6 +269,9 @@
|
|||
<if test="pos != null">
|
||||
pos = #{pos,jdbcType=DOUBLE},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
@ -269,7 +284,8 @@
|
|||
`level` = #{level,jdbcType=INTEGER},
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
pos = #{pos,jdbcType=DOUBLE}
|
||||
pos = #{pos,jdbcType=DOUBLE},
|
||||
create_user = #{createUser,jdbcType=VARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
</mapper>
|
|
@ -2,9 +2,11 @@ package io.metersphere.base.mapper;
|
|||
|
||||
import io.metersphere.base.domain.OperatingLog;
|
||||
import io.metersphere.base.domain.OperatingLogExample;
|
||||
import java.util.List;
|
||||
import io.metersphere.base.domain.OperatingLogWithBLOBs;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OperatingLogMapper {
|
||||
long countByExample(OperatingLogExample example);
|
||||
|
||||
|
@ -12,25 +14,25 @@ public interface OperatingLogMapper {
|
|||
|
||||
int deleteByPrimaryKey(String id);
|
||||
|
||||
int insert(OperatingLog record);
|
||||
int insert(OperatingLogWithBLOBs record);
|
||||
|
||||
int insertSelective(OperatingLog record);
|
||||
int insertSelective(OperatingLogWithBLOBs record);
|
||||
|
||||
List<OperatingLog> selectByExampleWithBLOBs(OperatingLogExample example);
|
||||
List<OperatingLogWithBLOBs> selectByExampleWithBLOBs(OperatingLogExample example);
|
||||
|
||||
List<OperatingLog> selectByExample(OperatingLogExample example);
|
||||
|
||||
OperatingLog selectByPrimaryKey(String id);
|
||||
OperatingLogWithBLOBs selectByPrimaryKey(String id);
|
||||
|
||||
int updateByExampleSelective(@Param("record") OperatingLog record, @Param("example") OperatingLogExample example);
|
||||
int updateByExampleSelective(@Param("record") OperatingLogWithBLOBs record, @Param("example") OperatingLogExample example);
|
||||
|
||||
int updateByExampleWithBLOBs(@Param("record") OperatingLog record, @Param("example") OperatingLogExample example);
|
||||
int updateByExampleWithBLOBs(@Param("record") OperatingLogWithBLOBs record, @Param("example") OperatingLogExample example);
|
||||
|
||||
int updateByExample(@Param("record") OperatingLog record, @Param("example") OperatingLogExample example);
|
||||
|
||||
int updateByPrimaryKeySelective(OperatingLog record);
|
||||
int updateByPrimaryKeySelective(OperatingLogWithBLOBs record);
|
||||
|
||||
int updateByPrimaryKeyWithBLOBs(OperatingLog record);
|
||||
int updateByPrimaryKeyWithBLOBs(OperatingLogWithBLOBs record);
|
||||
|
||||
int updateByPrimaryKey(OperatingLog record);
|
||||
}
|
|
@ -5,15 +5,18 @@
|
|||
<id column="id" jdbcType="VARCHAR" property="id" />
|
||||
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
|
||||
<result column="oper_method" jdbcType="VARCHAR" property="operMethod" />
|
||||
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
|
||||
<result column="oper_user" jdbcType="VARCHAR" property="operUser" />
|
||||
<result column="source_id" jdbcType="VARCHAR" property="sourceId" />
|
||||
<result column="oper_type" jdbcType="VARCHAR" property="operType" />
|
||||
<result column="oper_module" jdbcType="VARCHAR" property="operModule" />
|
||||
<result column="oper_title" jdbcType="VARCHAR" property="operTitle" />
|
||||
<result column="oper_path" jdbcType="VARCHAR" property="operPath" />
|
||||
<result column="oper_time" jdbcType="BIGINT" property="operTime" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.OperatingLog">
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.OperatingLogWithBLOBs">
|
||||
<result column="oper_content" jdbcType="LONGVARCHAR" property="operContent" />
|
||||
<result column="oper_params" jdbcType="LONGVARCHAR" property="operParams" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
|
@ -74,11 +77,11 @@
|
|||
</where>
|
||||
</sql>
|
||||
<sql id="Base_Column_List">
|
||||
id, project_id, oper_method, oper_user, source_id, oper_type, oper_module, oper_title,
|
||||
oper_time
|
||||
id, project_id, oper_method, create_user, oper_user, source_id, oper_type, oper_module,
|
||||
oper_title, oper_path, oper_time
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
oper_content
|
||||
oper_content, oper_params
|
||||
</sql>
|
||||
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.OperatingLogExample" resultMap="ResultMapWithBLOBs">
|
||||
select
|
||||
|
@ -128,17 +131,19 @@
|
|||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.OperatingLog">
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.OperatingLogWithBLOBs">
|
||||
insert into operating_log (id, project_id, oper_method,
|
||||
oper_user, source_id, oper_type,
|
||||
oper_module, oper_title, oper_time,
|
||||
oper_content)
|
||||
create_user, oper_user, source_id,
|
||||
oper_type, oper_module, oper_title,
|
||||
oper_path, oper_time, oper_content,
|
||||
oper_params)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{operMethod,jdbcType=VARCHAR},
|
||||
#{operUser,jdbcType=VARCHAR}, #{sourceId,jdbcType=VARCHAR}, #{operType,jdbcType=VARCHAR},
|
||||
#{operModule,jdbcType=VARCHAR}, #{operTitle,jdbcType=VARCHAR}, #{operTime,jdbcType=BIGINT},
|
||||
#{operContent,jdbcType=LONGVARCHAR})
|
||||
#{createUser,jdbcType=VARCHAR}, #{operUser,jdbcType=VARCHAR}, #{sourceId,jdbcType=VARCHAR},
|
||||
#{operType,jdbcType=VARCHAR}, #{operModule,jdbcType=VARCHAR}, #{operTitle,jdbcType=VARCHAR},
|
||||
#{operPath,jdbcType=VARCHAR}, #{operTime,jdbcType=BIGINT}, #{operContent,jdbcType=LONGVARCHAR},
|
||||
#{operParams,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.OperatingLog">
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.OperatingLogWithBLOBs">
|
||||
insert into operating_log
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -150,6 +155,9 @@
|
|||
<if test="operMethod != null">
|
||||
oper_method,
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
create_user,
|
||||
</if>
|
||||
<if test="operUser != null">
|
||||
oper_user,
|
||||
</if>
|
||||
|
@ -165,12 +173,18 @@
|
|||
<if test="operTitle != null">
|
||||
oper_title,
|
||||
</if>
|
||||
<if test="operPath != null">
|
||||
oper_path,
|
||||
</if>
|
||||
<if test="operTime != null">
|
||||
oper_time,
|
||||
</if>
|
||||
<if test="operContent != null">
|
||||
oper_content,
|
||||
</if>
|
||||
<if test="operParams != null">
|
||||
oper_params,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -182,6 +196,9 @@
|
|||
<if test="operMethod != null">
|
||||
#{operMethod,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
#{createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="operUser != null">
|
||||
#{operUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
|
@ -197,12 +214,18 @@
|
|||
<if test="operTitle != null">
|
||||
#{operTitle,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="operPath != null">
|
||||
#{operPath,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="operTime != null">
|
||||
#{operTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="operContent != null">
|
||||
#{operContent,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
<if test="operParams != null">
|
||||
#{operParams,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.OperatingLogExample" resultType="java.lang.Long">
|
||||
|
@ -223,6 +246,9 @@
|
|||
<if test="record.operMethod != null">
|
||||
oper_method = #{record.operMethod,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.createUser != null">
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.operUser != null">
|
||||
oper_user = #{record.operUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
|
@ -238,12 +264,18 @@
|
|||
<if test="record.operTitle != null">
|
||||
oper_title = #{record.operTitle,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.operPath != null">
|
||||
oper_path = #{record.operPath,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="record.operTime != null">
|
||||
oper_time = #{record.operTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="record.operContent != null">
|
||||
oper_content = #{record.operContent,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
<if test="record.operParams != null">
|
||||
oper_params = #{record.operParams,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
|
@ -254,13 +286,16 @@
|
|||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR},
|
||||
oper_method = #{record.operMethod,jdbcType=VARCHAR},
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
oper_user = #{record.operUser,jdbcType=VARCHAR},
|
||||
source_id = #{record.sourceId,jdbcType=VARCHAR},
|
||||
oper_type = #{record.operType,jdbcType=VARCHAR},
|
||||
oper_module = #{record.operModule,jdbcType=VARCHAR},
|
||||
oper_title = #{record.operTitle,jdbcType=VARCHAR},
|
||||
oper_path = #{record.operPath,jdbcType=VARCHAR},
|
||||
oper_time = #{record.operTime,jdbcType=BIGINT},
|
||||
oper_content = #{record.operContent,jdbcType=LONGVARCHAR}
|
||||
oper_content = #{record.operContent,jdbcType=LONGVARCHAR},
|
||||
oper_params = #{record.operParams,jdbcType=LONGVARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -270,17 +305,19 @@
|
|||
set id = #{record.id,jdbcType=VARCHAR},
|
||||
project_id = #{record.projectId,jdbcType=VARCHAR},
|
||||
oper_method = #{record.operMethod,jdbcType=VARCHAR},
|
||||
create_user = #{record.createUser,jdbcType=VARCHAR},
|
||||
oper_user = #{record.operUser,jdbcType=VARCHAR},
|
||||
source_id = #{record.sourceId,jdbcType=VARCHAR},
|
||||
oper_type = #{record.operType,jdbcType=VARCHAR},
|
||||
oper_module = #{record.operModule,jdbcType=VARCHAR},
|
||||
oper_title = #{record.operTitle,jdbcType=VARCHAR},
|
||||
oper_path = #{record.operPath,jdbcType=VARCHAR},
|
||||
oper_time = #{record.operTime,jdbcType=BIGINT}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.OperatingLog">
|
||||
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.OperatingLogWithBLOBs">
|
||||
update operating_log
|
||||
<set>
|
||||
<if test="projectId != null">
|
||||
|
@ -289,6 +326,9 @@
|
|||
<if test="operMethod != null">
|
||||
oper_method = #{operMethod,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="createUser != null">
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="operUser != null">
|
||||
oper_user = #{operUser,jdbcType=VARCHAR},
|
||||
</if>
|
||||
|
@ -304,37 +344,48 @@
|
|||
<if test="operTitle != null">
|
||||
oper_title = #{operTitle,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="operPath != null">
|
||||
oper_path = #{operPath,jdbcType=VARCHAR},
|
||||
</if>
|
||||
<if test="operTime != null">
|
||||
oper_time = #{operTime,jdbcType=BIGINT},
|
||||
</if>
|
||||
<if test="operContent != null">
|
||||
oper_content = #{operContent,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
<if test="operParams != null">
|
||||
oper_params = #{operParams,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.OperatingLog">
|
||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.OperatingLogWithBLOBs">
|
||||
update operating_log
|
||||
set project_id = #{projectId,jdbcType=VARCHAR},
|
||||
oper_method = #{operMethod,jdbcType=VARCHAR},
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
oper_user = #{operUser,jdbcType=VARCHAR},
|
||||
source_id = #{sourceId,jdbcType=VARCHAR},
|
||||
oper_type = #{operType,jdbcType=VARCHAR},
|
||||
oper_module = #{operModule,jdbcType=VARCHAR},
|
||||
oper_title = #{operTitle,jdbcType=VARCHAR},
|
||||
oper_path = #{operPath,jdbcType=VARCHAR},
|
||||
oper_time = #{operTime,jdbcType=BIGINT},
|
||||
oper_content = #{operContent,jdbcType=LONGVARCHAR}
|
||||
oper_content = #{operContent,jdbcType=LONGVARCHAR},
|
||||
oper_params = #{operParams,jdbcType=LONGVARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.OperatingLog">
|
||||
update operating_log
|
||||
set project_id = #{projectId,jdbcType=VARCHAR},
|
||||
oper_method = #{operMethod,jdbcType=VARCHAR},
|
||||
create_user = #{createUser,jdbcType=VARCHAR},
|
||||
oper_user = #{operUser,jdbcType=VARCHAR},
|
||||
source_id = #{sourceId,jdbcType=VARCHAR},
|
||||
oper_type = #{operType,jdbcType=VARCHAR},
|
||||
oper_module = #{operModule,jdbcType=VARCHAR},
|
||||
oper_title = #{operTitle,jdbcType=VARCHAR},
|
||||
oper_path = #{operPath,jdbcType=VARCHAR},
|
||||
oper_time = #{operTime,jdbcType=BIGINT}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.log.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.OperatingLog;
|
||||
import io.metersphere.base.domain.OperatingLogWithBLOBs;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.i18n.Translator;
|
||||
|
@ -26,8 +26,11 @@ import org.springframework.expression.ExpressionParser;
|
|||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
@ -116,7 +119,7 @@ public class MsLogAspect {
|
|||
MsAuditLog msLog = method.getAnnotation(MsAuditLog.class);
|
||||
if (msLog != null) {
|
||||
//保存日志
|
||||
OperatingLog msOperLog = new OperatingLog();
|
||||
OperatingLogWithBLOBs msOperLog = new OperatingLogWithBLOBs();
|
||||
|
||||
//保存获取的操作
|
||||
msOperLog.setId(UUID.randomUUID().toString());
|
||||
|
@ -164,19 +167,22 @@ public class MsLogAspect {
|
|||
try {
|
||||
if (StringUtils.isNotEmpty(content)) {
|
||||
OperatingLogDetails details = JSON.parseObject(content, OperatingLogDetails.class);
|
||||
msOperLog.setSourceId(details.getSourceId());
|
||||
if (StringUtils.isNotEmpty(details.getProjectId())) {
|
||||
msOperLog.setProjectId(details.getProjectId());
|
||||
}
|
||||
if (StringUtils.isEmpty(msLog.title())) {
|
||||
msOperLog.setOperTitle(details.getTitle());
|
||||
}
|
||||
msOperLog.setSourceId(details.getSourceId());
|
||||
msOperLog.setCreateUser(details.getCreateUser());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(content) && StringUtils.isNotEmpty(msLog.beforeValue())) {
|
||||
OperatingLogDetails details = JSON.parseObject(content, OperatingLogDetails.class);
|
||||
List<DetailColumn> columns = ReflexObjectUtil.compared(JSON.parseObject(msLog.beforeValue(), OperatingLogDetails.class), details);
|
||||
details.setColumns(columns);
|
||||
msOperLog.setOperContent(JSON.toJSONString(details));
|
||||
msOperLog.setSourceId(details.getSourceId());
|
||||
msOperLog.setCreateUser(details.getCreateUser());
|
||||
} else {
|
||||
msOperLog.setOperContent(content);
|
||||
}
|
||||
|
@ -191,10 +197,11 @@ public class MsLogAspect {
|
|||
if (StringUtils.isEmpty(msLog.title())) {
|
||||
msOperLog.setOperTitle(details.getTitle());
|
||||
}
|
||||
msOperLog.setSourceId(details.getSourceId());
|
||||
if (StringUtils.isNotEmpty(details.getProjectId())) {
|
||||
msOperLog.setProjectId(details.getProjectId());
|
||||
}
|
||||
msOperLog.setSourceId(details.getSourceId());
|
||||
msOperLog.setCreateUser(details.getCreateUser());
|
||||
}
|
||||
|
||||
//获取请求的类名
|
||||
|
@ -210,6 +217,11 @@ public class MsLogAspect {
|
|||
} else {
|
||||
msOperLog.setOperUser(SessionUtils.getUserId());
|
||||
}
|
||||
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||
|
||||
String path = request.getServletPath();
|
||||
msOperLog.setOperPath(path);
|
||||
|
||||
operatingLogService.create(msOperLog);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package io.metersphere.log.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.OperatingLog;
|
||||
import io.metersphere.base.domain.OperatingLogWithBLOBs;
|
||||
import io.metersphere.base.mapper.OperatingLogMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtOperatingLogMapper;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
|
@ -24,7 +24,7 @@ public class OperatingLogService {
|
|||
@Resource
|
||||
private ExtOperatingLogMapper extOperatingLogMapper;
|
||||
|
||||
public void create(OperatingLog log) {
|
||||
public void create(OperatingLogWithBLOBs log) {
|
||||
operatingLogMapper.insert(log);
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ public class OperatingLogService {
|
|||
}
|
||||
|
||||
public OperatingLogDTO get(String id) {
|
||||
OperatingLog log = operatingLogMapper.selectByPrimaryKey(id);
|
||||
OperatingLogWithBLOBs log = operatingLogMapper.selectByPrimaryKey(id);
|
||||
OperatingLogDTO dto = new OperatingLogDTO();
|
||||
BeanUtils.copyBean(dto, log);
|
||||
if (StringUtils.isNotEmpty(log.getOperContent())) {
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
package io.metersphere.log.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.flipkart.zjsonpatch.JsonDiff;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MyJSONCompartor {
|
||||
|
||||
public static void compareJSON(String src, String target) {
|
||||
JSONObject so = JSON.parseObject(src);
|
||||
JSONObject to = JSON.parseObject(target);
|
||||
for (String key : so.keySet()) {
|
||||
Object value = so.get(key);
|
||||
Object v2 = to.get(key);
|
||||
if (value instanceof Map) {
|
||||
compareJSON(JSON.toJSONString(value), JSON.toJSONString(v2));
|
||||
} else if (value instanceof List) {
|
||||
List list = (List<Map<String, Object>>) value;
|
||||
List list2 = (List<Map<String, Object>>) v2;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i) instanceof List || list.get(i) instanceof Map) {
|
||||
compareJSON(JSON.toJSONString(list.get(i)), JSON.toJSONString(list2.get(i)));
|
||||
} else {//如果是这种 LIst,值不是Map也不是List,就可以直接比较值 "phoneNumList": ["10086","10084"]
|
||||
if (!list.get(i).equals(list2.get(i))) {
|
||||
System.err.println("key " + key + "值不一样,期望" + list.get(i) + ",实际" + list2.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (null == value) {
|
||||
System.err.println("key " + key + "值是null!!!!!!!!!!!");
|
||||
} else if (!value.equals(v2)) {
|
||||
System.err.println("key " + key + "值不一样,期望" + value + ",实际" + v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
final String json1 = "{\"type\":\"HTTPSamplerProxy\",\"id\":\"9cf1ef83-099b-4a57-a229-2250e9b12372\",\"name\":\"测试添加\",\"label\":\"HTTPSamplerProxy\",\"active\":false,\"enable\":true,\"hashTree\":[],\"customizeReq\":false,\"mockEnvironment\":false,\"protocol\":\"HTTP\",\"method\":\"GET\",\"path\":\"/test\",\"connectTimeout\":\"6000\",\"responseTimeout\":\"6000\",\"headers\":[{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept\",\"required\":true,\"valid\":true,\"value\":\"a\"},{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept-Charset\",\"required\":true,\"valid\":true,\"value\":\"b\"},{\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"valid\":false}],\"body\":{\"binary\":[],\"json\":false,\"kV\":true,\"kvs\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@character\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@datetime\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"oldKV\":false,\"type\":\"Form Data\",\"valid\":true,\"xml\":false},\"rest\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"cx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"c1\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"ca\",\"required\":false,\"type\":\"text\",\"valid\":true,\"value\":\"c2\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"followRedirects\":true,\"doMultipartPost\":false,\"arguments\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"test\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"1\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"pa\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"2\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}]}\n";
|
||||
final String json2 = "{\"type\":\"HTTPSamplerProxy\",\"id\":\"9cf1ef83-099b-4a57-a229-2250e9b12372\",\"name\":\"测试添加\",\"label\":\"HTTPSamplerProxy\",\"active\":false,\"enable\":true,\"hashTree\":[],\"customizeReq\":false,\"mockEnvironment\":false,\"protocol\":\"HTTP\",\"method\":\"GET\",\"path\":\"/test\",\"connectTimeout\":\"6000\",\"responseTimeout\":\"6000\",\"headers\":[{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept\",\"required\":true,\"valid\":true,\"value\":\"a1\"},{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept-Charset\",\"required\":true,\"valid\":true,\"value\":\"b2\"},{\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"valid\":false}],\"body\":{\"binary\":[],\"json\":false,\"kV\":true,\"kvs\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@character\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@datetime\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"oldKV\":false,\"type\":\"Form Data\",\"valid\":true,\"xml\":false},\"rest\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"cx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"c13\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"ca\",\"required\":false,\"type\":\"text\",\"valid\":true,\"value\":\"c22\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"followRedirects\":true,\"doMultipartPost\":false,\"arguments\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"test\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"11\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"pa\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"22\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}]}\n";
|
||||
|
||||
String json4 ="{\"type\":\"HTTPSamplerProxy\",\"id\":\"9cf1ef83-099b-4a57-a229-2250e9b12372\",\"name\":\"测试添加\",\"label\":\"HTTPSamplerProxy\",\"active\":false,\"enable\":true,\"hashTree\":[],\"customizeReq\":false,\"mockEnvironment\":false,\"protocol\":\"HTTP\",\"method\":\"GET\",\"path\":\"/test\",\"connectTimeout\":\"6000\",\"responseTimeout\":\"6000\",\"headers\":[{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept\",\"required\":true,\"valid\":true,\"value\":\"a1\"},{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept-Charset\",\"required\":true,\"valid\":true,\"value\":\"b2\"},{\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"valid\":false}],\"body\":{\"binary\":[],\"json\":false,\"kV\":true,\"kvs\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@character\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@datetime\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"oldKV\":false,\"type\":\"Form Data\",\"valid\":true,\"xml\":false},\"rest\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"cx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"c13\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"ca\",\"required\":false,\"type\":\"text\",\"valid\":true,\"value\":\"c22\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"followRedirects\":true,\"doMultipartPost\":false,\"arguments\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"test\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"11\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"pa\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"22\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}]}\n";
|
||||
String json5 ="{\"type\":\"HTTPSamplerProxy\",\"id\":\"9cf1ef83-099b-4a57-a229-2250e9b12372\",\"name\":\"测试添加\",\"label\":\"HTTPSamplerProxy\",\"active\":false,\"enable\":true,\"hashTree\":[],\"customizeReq\":false,\"mockEnvironment\":false,\"protocol\":\"HTTP\",\"method\":\"GET\",\"path\":\"/test\",\"connectTimeout\":\"6000\",\"responseTimeout\":\"6000\",\"headers\":[{\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"Accept\",\"required\":true,\"valid\":true,\"value\":\"a1\"},{\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"valid\":false}],\"body\":{\"binary\":[],\"json\":false,\"kV\":true,\"kvs\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@character\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"axx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"@datetime\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"oldKV\":false,\"type\":\"Form Data\",\"valid\":true,\"xml\":false},\"rest\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"cx\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"c13\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"ca\",\"required\":false,\"type\":\"text\",\"valid\":true,\"value\":\"c22\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}],\"followRedirects\":true,\"doMultipartPost\":false,\"arguments\":[{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"test\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"11\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"pa\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"22\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"name\":\"cc\",\"required\":true,\"type\":\"text\",\"valid\":true,\"value\":\"cc\"},{\"contentType\":\"text/plain\",\"enable\":true,\"encode\":true,\"file\":false,\"required\":true,\"type\":\"text\",\"valid\":false}]}\n";
|
||||
|
||||
try {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode source = mapper.readTree(json1);
|
||||
JsonNode target = mapper.readTree(json2);
|
||||
|
||||
|
||||
JsonNode patch2 = JsonDiff.asJson( target, source);
|
||||
JsonNode patch = JsonDiff.asJson( source, target);
|
||||
|
||||
System.out.println( patch.toString());
|
||||
System.out.println( patch2.toString());
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
package io.metersphere.log.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.flipkart.zjsonpatch.JsonDiff;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.log.utils.dff.Diff;
|
||||
import io.metersphere.log.utils.dff.JsonDiff;
|
||||
import io.metersphere.log.utils.dff.Operation;
|
||||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -48,6 +52,11 @@ public class ReflexObjectUtil {
|
|||
DetailColumn column = new DetailColumn(columns.get(f.getName()), f.getName(), val, "");
|
||||
if (dffColumns.contains(f.getName())) {
|
||||
column.setDepthDff(true);
|
||||
if (val != null) {
|
||||
JSONObject object = JSONObject.parseObject(val.toString());
|
||||
String pretty = JSON.toJSONString(object, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteDateUseDateFormat);
|
||||
column.setOriginalValue(pretty);
|
||||
}
|
||||
}
|
||||
columnList.add(column);
|
||||
}
|
||||
|
@ -79,16 +88,45 @@ public class ReflexObjectUtil {
|
|||
List<DetailColumn> newColumns = newObj.getColumns();
|
||||
for (int i = 0; i < originalColumns.size(); i++) {
|
||||
if (!StringUtils.equals(JSON.toJSONString(originalColumns.get(i).getOriginalValue()), JSON.toJSONString(newColumns.get(i).getOriginalValue()))) {
|
||||
if (originalColumns.get(i).isDepthDff()) {
|
||||
// 深度对比
|
||||
if (originalColumns.get(i).isDepthDff() && originalColumns.get(i).getOriginalValue() != null && newColumns.get(i).getOriginalValue() != null) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
JsonNode source = mapper.readTree(JSON.toJSONString(originalColumns.get(i).getOriginalValue()));
|
||||
JsonNode target = mapper.readTree(JSON.toJSONString(newColumns.get(i).getOriginalValue()));
|
||||
JsonNode before = JsonDiff.asJson(target, source);
|
||||
JsonNode after = JsonDiff.asJson(source, target);
|
||||
JsonNode source = mapper.readTree(originalColumns.get(i).getOriginalValue().toString());
|
||||
JsonNode target = mapper.readTree(newColumns.get(i).getOriginalValue().toString());
|
||||
List<Diff> after = JsonDiff.jsonDiff(source, target);
|
||||
|
||||
StringBuilder addBuff = new StringBuilder();
|
||||
StringBuilder removeBuff = new StringBuilder();
|
||||
StringBuilder repBuff = new StringBuilder();
|
||||
StringBuilder oldValue = new StringBuilder();
|
||||
for (Diff item : after) {
|
||||
if (item.getOperation().equals(Operation.ADD)) {
|
||||
addBuff.append(item.getPath() + ":" + item.getValue()).append("/n");
|
||||
}
|
||||
if (item.getOperation().equals(Operation.REMOVE)) {
|
||||
removeBuff.append(item.getPath() + ":" + item.getValue()).append("/n");
|
||||
}
|
||||
if (item.getOperation().equals(Operation.REPLACE)) {
|
||||
repBuff.append(item.getPath() + ":" + item.getValue()).append("/n");
|
||||
oldValue.append(item.getPath() + ":" + item.getSrcValue()).append("/n");
|
||||
}
|
||||
}
|
||||
StringBuilder newValue = new StringBuilder();
|
||||
if (addBuff != null && addBuff.toString().length() > 0) {
|
||||
newValue.append("添加:").append(addBuff).append("/n");
|
||||
}
|
||||
if (removeBuff != null && removeBuff.toString().length() > 0) {
|
||||
newValue.append("移除:").append(removeBuff).append("/n");
|
||||
}
|
||||
if (repBuff != null && repBuff.toString().length() > 0) {
|
||||
newValue.append("修改:").append(repBuff).append("/n");
|
||||
}
|
||||
DetailColumn column = new DetailColumn();
|
||||
BeanUtils.copyBean(column, originalColumns.get(i));
|
||||
column.setOriginalValue(before.toString());
|
||||
column.setNewValue(after.toString());
|
||||
if (oldValue != null && oldValue.length() > 0) {
|
||||
column.setOriginalValue(oldValue);
|
||||
}
|
||||
column.setNewValue(newValue);
|
||||
comparedColumns.add(column);
|
||||
} else {
|
||||
DetailColumn column = new DetailColumn();
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Created by tomerga on 04/09/2016.
|
||||
*/
|
||||
public enum CompatibilityFlags {
|
||||
MISSING_VALUES_AS_NULLS,
|
||||
REMOVE_NONE_EXISTING_ARRAY_ELEMENT,
|
||||
ALLOW_MISSING_TARGET_OBJECT_ON_REPLACE;
|
||||
|
||||
public static EnumSet<CompatibilityFlags> defaults() {
|
||||
return EnumSet.noneOf(CompatibilityFlags.class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
/**
|
||||
* Created with IntelliJ IDEA.
|
||||
* User: gopi.vishwakarma
|
||||
* Date: 10/07/15
|
||||
* Time: 10:35 AM
|
||||
*/
|
||||
final class Constants {
|
||||
public static final String OP = "op";
|
||||
public static final String VALUE = "value";
|
||||
public static final String PATH = "path";
|
||||
public static final String FROM = "from";
|
||||
public static final String FROM_VALUE = "fromValue";
|
||||
|
||||
private Constants() {}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
class CopyingApplyProcessor extends InPlaceApplyProcessor {
|
||||
|
||||
CopyingApplyProcessor(JsonNode target) {
|
||||
this(target, CompatibilityFlags.defaults());
|
||||
}
|
||||
|
||||
CopyingApplyProcessor(JsonNode target, EnumSet<CompatibilityFlags> flags) {
|
||||
super(target.deepCopy(), flags);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
*/
|
||||
public class Diff {
|
||||
private final Operation operation;
|
||||
private final JsonPointer path;
|
||||
private final JsonNode value;
|
||||
private JsonPointer toPath;
|
||||
private final JsonNode srcValue;
|
||||
|
||||
Diff(Operation operation, JsonPointer path, JsonNode value) {
|
||||
this.operation = operation;
|
||||
this.path = path;
|
||||
this.value = value;
|
||||
this.srcValue = null;
|
||||
}
|
||||
|
||||
Diff(Operation operation, JsonPointer fromPath, JsonPointer toPath) {
|
||||
this.operation = operation;
|
||||
this.path = fromPath;
|
||||
this.toPath = toPath;
|
||||
this.value = null;
|
||||
this.srcValue = null;
|
||||
}
|
||||
|
||||
Diff(Operation operation, JsonPointer path, JsonNode srcValue, JsonNode value) {
|
||||
this.operation = operation;
|
||||
this.path = path;
|
||||
this.value = value;
|
||||
this.srcValue = srcValue;
|
||||
}
|
||||
|
||||
public Operation getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public JsonPointer getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public JsonNode getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Diff generateDiff(Operation replace, JsonPointer path, JsonNode target) {
|
||||
return new Diff(replace, path, target);
|
||||
}
|
||||
|
||||
public static Diff generateDiff(Operation replace, JsonPointer path, JsonNode source, JsonNode target) {
|
||||
return new Diff(replace, path, source, target);
|
||||
}
|
||||
|
||||
JsonPointer getToPath() {
|
||||
return toPath;
|
||||
}
|
||||
|
||||
public JsonNode getSrcValue() {
|
||||
return srcValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public enum DiffFlags {
|
||||
|
||||
/**
|
||||
* This flag omits the <i>value</i> field on remove operations.
|
||||
* This is a default flag.
|
||||
*/
|
||||
OMIT_VALUE_ON_REMOVE,
|
||||
|
||||
/**
|
||||
* This flag omits all {@link Operation#MOVE} operations, leaving only
|
||||
* {@link Operation#ADD}, {@link Operation#REMOVE}, {@link Operation#REPLACE}
|
||||
* and {@link Operation#COPY} operations. In other words, without this flag,
|
||||
* {@link Operation#ADD} and {@link Operation#REMOVE} operations are not normalized
|
||||
* into {@link Operation#MOVE} operations.
|
||||
*/
|
||||
OMIT_MOVE_OPERATION,
|
||||
|
||||
/**
|
||||
* This flag omits all {@link Operation#COPY} operations, leaving only
|
||||
* {@link Operation#ADD}, {@link Operation#REMOVE}, {@link Operation#REPLACE}
|
||||
* and {@link Operation#MOVE} operations. In other words, without this flag,
|
||||
* {@link Operation#ADD} operations are not normalized into {@link Operation#COPY}
|
||||
* operations.
|
||||
*/
|
||||
OMIT_COPY_OPERATION,
|
||||
|
||||
/**
|
||||
* This flag adds a <i>fromValue</i> field to all {@link Operation#REPLACE} operations.
|
||||
* <i>fromValue</i> represents the the value replaced by a {@link Operation#REPLACE}
|
||||
* operation, in other words, the original value. This can be useful for debugging
|
||||
* output or custom processing of the diffs by downstream systems.
|
||||
* Please note that this is a non-standard extension to RFC 6902 and will not affect
|
||||
* how patches produced by this library are processed by this or other libraries.
|
||||
*
|
||||
* @since 0.4.1
|
||||
*/
|
||||
ADD_ORIGINAL_VALUE_ON_REPLACE,
|
||||
|
||||
/**
|
||||
* This flag normalizes a {@link Operation#REPLACE} operation into its respective
|
||||
* {@link Operation#REMOVE} and {@link Operation#ADD} operations. Although it adds
|
||||
* a redundant step, this can be useful for auditing systems in which immutability
|
||||
* is a requirement.
|
||||
* <p>
|
||||
* For the flag to work, {@link DiffFlags#ADD_ORIGINAL_VALUE_ON_REPLACE} has to be
|
||||
* enabled as the new instructions in the patch need to grab the old <i>fromValue</i>
|
||||
* {@code "op": "replace", "fromValue": "F1", "value": "F2" }
|
||||
* The above instruction will be split into
|
||||
* {@code "op":"remove", "value":"F1" } and {@code "op":"add", "value":"F2"} respectively.
|
||||
* <p>
|
||||
* Please note that this is a non-standard extension to RFC 6902 and will not affect
|
||||
* how patches produced by this library are processed by this or other libraries.
|
||||
*
|
||||
* @since 0.4.11
|
||||
*/
|
||||
ADD_EXPLICIT_REMOVE_ADD_ON_REPLACE,
|
||||
|
||||
/**
|
||||
* This flag instructs the diff generator to emit {@link Operation#TEST} operations
|
||||
* that validate the state of the source document before each mutation. This can be
|
||||
* useful if you want to ensure data integrity prior to applying the patch.
|
||||
* The resulting patches are standard per RFC 6902 and should be processed correctly
|
||||
* by any compliant library; due to the associated space and performance costs,
|
||||
* however, this isn't default behavior.
|
||||
*
|
||||
* @since 0.4.8
|
||||
*/
|
||||
EMIT_TEST_OPERATIONS;
|
||||
|
||||
|
||||
public static EnumSet<DiffFlags> defaults() {
|
||||
return EnumSet.of(OMIT_VALUE_ON_REMOVE);
|
||||
}
|
||||
|
||||
public static EnumSet<DiffFlags> dontNormalizeOpIntoMoveAndCopy() {
|
||||
return EnumSet.of(OMIT_MOVE_OPERATION, OMIT_COPY_OPERATION);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
class InPlaceApplyProcessor implements JsonPatchProcessor {
|
||||
|
||||
private JsonNode target;
|
||||
private EnumSet<CompatibilityFlags> flags;
|
||||
|
||||
InPlaceApplyProcessor(JsonNode target) {
|
||||
this(target, CompatibilityFlags.defaults());
|
||||
}
|
||||
|
||||
InPlaceApplyProcessor(JsonNode target, EnumSet<CompatibilityFlags> flags) {
|
||||
this.target = target;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public JsonNode result() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(JsonPointer fromPath, JsonPointer toPath) throws JsonPointerEvaluationException {
|
||||
JsonNode valueNode = fromPath.evaluate(target);
|
||||
remove(fromPath);
|
||||
set(toPath, valueNode, Operation.MOVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(JsonPointer fromPath, JsonPointer toPath) throws JsonPointerEvaluationException {
|
||||
JsonNode valueNode = fromPath.evaluate(target);
|
||||
JsonNode valueToCopy = valueNode != null ? valueNode.deepCopy() : null;
|
||||
set(toPath, valueToCopy, Operation.COPY);
|
||||
}
|
||||
|
||||
private static String show(JsonNode value) {
|
||||
if (value == null || value.isNull())
|
||||
return "null";
|
||||
else if (value.isArray())
|
||||
return "array";
|
||||
else if (value.isObject())
|
||||
return "object";
|
||||
else
|
||||
return "value " + value.toString(); // Caveat: numeric may differ from source (e.g. trailing zeros)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void test(JsonPointer path, JsonNode value) throws JsonPointerEvaluationException {
|
||||
JsonNode valueNode = path.evaluate(target);
|
||||
if (!valueNode.equals(value))
|
||||
throw new JsonPatchApplicationException(
|
||||
"Expected " + show(value) + " but found " + show(valueNode), Operation.TEST, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(JsonPointer path, JsonNode value) throws JsonPointerEvaluationException {
|
||||
set(path, value, Operation.ADD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replace(JsonPointer path, JsonNode value) throws JsonPointerEvaluationException {
|
||||
if (path.isRoot()) {
|
||||
target = value;
|
||||
return;
|
||||
}
|
||||
|
||||
JsonNode parentNode = path.getParent().evaluate(target);
|
||||
JsonPointer.RefToken token = path.last();
|
||||
if (parentNode.isObject()) {
|
||||
if (!flags.contains(CompatibilityFlags.ALLOW_MISSING_TARGET_OBJECT_ON_REPLACE) &&
|
||||
!parentNode.has(token.getField()))
|
||||
throw new JsonPatchApplicationException(
|
||||
"Missing field \"" + token.getField() + "\"", Operation.REPLACE, path.getParent());
|
||||
((ObjectNode) parentNode).replace(token.getField(), value);
|
||||
} else if (parentNode.isArray()) {
|
||||
if (token.getIndex() >= parentNode.size())
|
||||
throw new JsonPatchApplicationException(
|
||||
"Array index " + token.getIndex() + " out of bounds", Operation.REPLACE, path.getParent());
|
||||
((ArrayNode) parentNode).set(token.getIndex(), value);
|
||||
} else {
|
||||
throw new JsonPatchApplicationException(
|
||||
"Can't reference past scalar value", Operation.REPLACE, path.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(JsonPointer path) throws JsonPointerEvaluationException {
|
||||
if (path.isRoot())
|
||||
throw new JsonPatchApplicationException("Cannot remove document root", Operation.REMOVE, path);
|
||||
|
||||
JsonNode parentNode = path.getParent().evaluate(target);
|
||||
JsonPointer.RefToken token = path.last();
|
||||
if (parentNode.isObject())
|
||||
((ObjectNode) parentNode).remove(token.getField());
|
||||
else if (parentNode.isArray()) {
|
||||
if (!flags.contains(CompatibilityFlags.REMOVE_NONE_EXISTING_ARRAY_ELEMENT) &&
|
||||
token.getIndex() >= parentNode.size())
|
||||
throw new JsonPatchApplicationException(
|
||||
"Array index " + token.getIndex() + " out of bounds", Operation.REPLACE, path.getParent());
|
||||
((ArrayNode) parentNode).remove(token.getIndex());
|
||||
} else {
|
||||
throw new JsonPatchApplicationException(
|
||||
"Cannot reference past scalar value", Operation.REPLACE, path.getParent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void set(JsonPointer path, JsonNode value, Operation forOp) throws JsonPointerEvaluationException {
|
||||
if (path.isRoot())
|
||||
target = value;
|
||||
else {
|
||||
JsonNode parentNode = path.getParent().evaluate(target);
|
||||
if (!parentNode.isContainerNode())
|
||||
throw new JsonPatchApplicationException("Cannot reference past scalar value", forOp, path.getParent());
|
||||
else if (parentNode.isArray())
|
||||
addToArray(path, value, parentNode);
|
||||
else
|
||||
addToObject(path, parentNode, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToObject(JsonPointer path, JsonNode node, JsonNode value) {
|
||||
final ObjectNode target = (ObjectNode) node;
|
||||
String key = path.last().getField();
|
||||
target.set(key, value);
|
||||
}
|
||||
|
||||
private void addToArray(JsonPointer path, JsonNode value, JsonNode parentNode) {
|
||||
final ArrayNode target = (ArrayNode) parentNode;
|
||||
int idx = path.last().getIndex();
|
||||
|
||||
if (idx == JsonPointer.LAST_INDEX) {
|
||||
// see http://tools.ietf.org/html/rfc6902#section-4.1
|
||||
target.add(value);
|
||||
} else {
|
||||
if (idx > target.size())
|
||||
throw new JsonPatchApplicationException(
|
||||
"Array index " + idx + " out of bounds", Operation.ADD, path.getParent());
|
||||
target.insert(idx, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
class InternalUtils {
|
||||
|
||||
static List<JsonNode> toList(ArrayNode input) {
|
||||
int size = input.size();
|
||||
List<JsonNode> toReturn = new ArrayList<JsonNode>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
toReturn.add(input.get(i));
|
||||
}
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
static List<JsonNode> longestCommonSubsequence(final List<JsonNode> a, final List<JsonNode> b) {
|
||||
if (a == null || b == null) {
|
||||
throw new NullPointerException("List must not be null for longestCommonSubsequence");
|
||||
}
|
||||
|
||||
List<JsonNode> toReturn = new LinkedList<JsonNode>();
|
||||
|
||||
int aSize = a.size();
|
||||
int bSize = b.size();
|
||||
int temp[][] = new int[aSize + 1][bSize + 1];
|
||||
|
||||
for (int i = 1; i <= aSize; i++) {
|
||||
for (int j = 1; j <= bSize; j++) {
|
||||
if (i == 0 || j == 0) {
|
||||
temp[i][j] = 0;
|
||||
} else if (a.get(i - 1).equals(b.get(j - 1))) {
|
||||
temp[i][j] = temp[i - 1][j - 1] + 1;
|
||||
} else {
|
||||
temp[i][j] = Math.max(temp[i][j - 1], temp[i - 1][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
int i = aSize, j = bSize;
|
||||
while (i > 0 && j > 0) {
|
||||
if (a.get(i - 1).equals(b.get(j - 1))) {
|
||||
toReturn.add(a.get(i - 1));
|
||||
i--;
|
||||
j--;
|
||||
} else if (temp[i - 1][j] > temp[i][j - 1])
|
||||
i--;
|
||||
else
|
||||
j--;
|
||||
}
|
||||
Collections.reverse(toReturn);
|
||||
return toReturn;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
/**
|
||||
* User: holograph
|
||||
* Date: 03/08/16
|
||||
*/
|
||||
public class InvalidJsonPatchException extends JsonPatchApplicationException {
|
||||
public InvalidJsonPatchException(String message) {
|
||||
super(message, null, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* User: gopi.vishwakarma
|
||||
* Date: 30/07/14
|
||||
*/
|
||||
public final class JsonDiff {
|
||||
|
||||
private final List<Diff> diffs = new ArrayList<Diff>();
|
||||
private final EnumSet<DiffFlags> flags;
|
||||
|
||||
private JsonDiff(EnumSet<DiffFlags> flags) {
|
||||
this.flags = flags.clone();
|
||||
}
|
||||
|
||||
public static JsonNode asJson(final JsonNode source, final JsonNode target) {
|
||||
return asJson(source, target, DiffFlags.defaults());
|
||||
}
|
||||
|
||||
public static JsonNode asJson(final JsonNode source, final JsonNode target, EnumSet<DiffFlags> flags) {
|
||||
JsonDiff diff = new JsonDiff(flags);
|
||||
if (source == null && target != null) {
|
||||
// return add node at root pointing to the target
|
||||
diff.diffs.add(Diff.generateDiff(Operation.ADD, JsonPointer.ROOT, target));
|
||||
}
|
||||
if (source != null && target == null) {
|
||||
// return remove node at root pointing to the source
|
||||
diff.diffs.add(Diff.generateDiff(Operation.REMOVE, JsonPointer.ROOT, source));
|
||||
}
|
||||
if (source != null && target != null) {
|
||||
diff.generateDiffs(JsonPointer.ROOT, source, target);
|
||||
|
||||
if (!flags.contains(DiffFlags.OMIT_MOVE_OPERATION))
|
||||
// Merging remove & add to move operation
|
||||
diff.introduceMoveOperation();
|
||||
|
||||
if (!flags.contains(DiffFlags.OMIT_COPY_OPERATION))
|
||||
// Introduce copy operation
|
||||
diff.introduceCopyOperation(source, target);
|
||||
|
||||
if (flags.contains(DiffFlags.ADD_EXPLICIT_REMOVE_ADD_ON_REPLACE))
|
||||
// Split replace into remove and add instructions
|
||||
diff.introduceExplicitRemoveAndAddOperation();
|
||||
}
|
||||
return diff.getJsonNodes();
|
||||
}
|
||||
|
||||
public static List<Diff> jsonDiff(final JsonNode source, final JsonNode target) {
|
||||
return diff(source, target);
|
||||
}
|
||||
|
||||
public static List<Diff> diff(final JsonNode source, final JsonNode target) {
|
||||
JsonDiff diff = new JsonDiff(DiffFlags.defaults());
|
||||
if (source == null && target != null) {
|
||||
// return add node at root pointing to the target
|
||||
diff.diffs.add(Diff.generateDiff(Operation.ADD, JsonPointer.ROOT, target));
|
||||
}
|
||||
if (source != null && target == null) {
|
||||
// return remove node at root pointing to the source
|
||||
diff.diffs.add(Diff.generateDiff(Operation.REMOVE, JsonPointer.ROOT, source));
|
||||
}
|
||||
if (source != null && target != null) {
|
||||
diff.generateDiffs(JsonPointer.ROOT, source, target);
|
||||
|
||||
if (!DiffFlags.defaults().contains(DiffFlags.OMIT_MOVE_OPERATION))
|
||||
// Merging remove & add to move operation
|
||||
diff.introduceMoveOperation();
|
||||
|
||||
if (!DiffFlags.defaults().contains(DiffFlags.OMIT_COPY_OPERATION))
|
||||
// Introduce copy operation
|
||||
diff.introduceCopyOperation(source, target);
|
||||
|
||||
if (DiffFlags.defaults().contains(DiffFlags.ADD_EXPLICIT_REMOVE_ADD_ON_REPLACE))
|
||||
// Split replace into remove and add instructions
|
||||
diff.introduceExplicitRemoveAndAddOperation();
|
||||
}
|
||||
return diff.getDiffs();
|
||||
}
|
||||
|
||||
private static JsonPointer getMatchingValuePath(Map<JsonNode, JsonPointer> unchangedValues, JsonNode value) {
|
||||
return unchangedValues.get(value);
|
||||
}
|
||||
|
||||
private void introduceCopyOperation(JsonNode source, JsonNode target) {
|
||||
Map<JsonNode, JsonPointer> unchangedValues = getUnchangedPart(source, target);
|
||||
|
||||
for (int i = 0; i < diffs.size(); i++) {
|
||||
Diff diff = diffs.get(i);
|
||||
if (Operation.ADD != diff.getOperation()) continue;
|
||||
|
||||
JsonPointer matchingValuePath = getMatchingValuePath(unchangedValues, diff.getValue());
|
||||
if (matchingValuePath != null && isAllowed(matchingValuePath, diff.getPath())) {
|
||||
// Matching value found; replace add with copy
|
||||
if (flags.contains(DiffFlags.EMIT_TEST_OPERATIONS)) {
|
||||
// Prepend test node
|
||||
diffs.add(i, new Diff(Operation.TEST, matchingValuePath, diff.getValue()));
|
||||
i++;
|
||||
}
|
||||
diffs.set(i, new Diff(Operation.COPY, matchingValuePath, diff.getPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNumber(String str) {
|
||||
int size = str.length();
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (!Character.isDigit(str.charAt(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
// TODO this is quite unclear and needs some serious documentation
|
||||
private static boolean isAllowed(JsonPointer source, JsonPointer destination) {
|
||||
boolean isSame = source.equals(destination);
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
// Hack to fix broken COPY operation, need better handling here
|
||||
while (i < source.size() && j < destination.size()) {
|
||||
JsonPointer.RefToken srcValue = source.get(i);
|
||||
JsonPointer.RefToken dstValue = destination.get(j);
|
||||
String srcStr = srcValue.toString();
|
||||
String dstStr = dstValue.toString();
|
||||
if (isNumber(srcStr) && isNumber(dstStr)) {
|
||||
|
||||
if (srcStr.compareTo(dstStr) > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
|
||||
}
|
||||
return !isSame;
|
||||
}
|
||||
|
||||
private static Map<JsonNode, JsonPointer> getUnchangedPart(JsonNode source, JsonNode target) {
|
||||
Map<JsonNode, JsonPointer> unchangedValues = new HashMap<JsonNode, JsonPointer>();
|
||||
computeUnchangedValues(unchangedValues, JsonPointer.ROOT, source, target);
|
||||
return unchangedValues;
|
||||
}
|
||||
|
||||
private static void computeUnchangedValues(Map<JsonNode, JsonPointer> unchangedValues, JsonPointer path, JsonNode source, JsonNode target) {
|
||||
if (source.equals(target)) {
|
||||
if (!unchangedValues.containsKey(target)) {
|
||||
unchangedValues.put(target, path);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final NodeType firstType = NodeType.getNodeType(source);
|
||||
final NodeType secondType = NodeType.getNodeType(target);
|
||||
|
||||
if (firstType == secondType) {
|
||||
switch (firstType) {
|
||||
case OBJECT:
|
||||
computeObject(unchangedValues, path, source, target);
|
||||
break;
|
||||
case ARRAY:
|
||||
computeArray(unchangedValues, path, source, target);
|
||||
break;
|
||||
default:
|
||||
/* nothing */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void computeArray(Map<JsonNode, JsonPointer> unchangedValues, JsonPointer path, JsonNode source, JsonNode target) {
|
||||
final int size = Math.min(source.size(), target.size());
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
JsonPointer currPath = path.append(i);
|
||||
computeUnchangedValues(unchangedValues, currPath, source.get(i), target.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
private static void computeObject(Map<JsonNode, JsonPointer> unchangedValues, JsonPointer path, JsonNode source, JsonNode target) {
|
||||
final Iterator<String> firstFields = source.fieldNames();
|
||||
while (firstFields.hasNext()) {
|
||||
String name = firstFields.next();
|
||||
if (target.has(name)) {
|
||||
JsonPointer currPath = path.append(name);
|
||||
computeUnchangedValues(unchangedValues, currPath, source.get(name), target.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merge 2 diffs ( remove then add, or vice versa ) with same value into one Move operation,
|
||||
* all the core logic resides here only
|
||||
*/
|
||||
private void introduceMoveOperation() {
|
||||
for (int i = 0; i < diffs.size(); i++) {
|
||||
Diff diff1 = diffs.get(i);
|
||||
|
||||
// if not remove OR add, move to next diff
|
||||
if (!(Operation.REMOVE == diff1.getOperation() ||
|
||||
Operation.ADD == diff1.getOperation())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int j = i + 1; j < diffs.size(); j++) {
|
||||
Diff diff2 = diffs.get(j);
|
||||
if (!diff1.getValue().equals(diff2.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Diff moveDiff = null;
|
||||
if (Operation.REMOVE == diff1.getOperation() &&
|
||||
Operation.ADD == diff2.getOperation()) {
|
||||
JsonPointer relativePath = computeRelativePath(diff2.getPath(), i + 1, j - 1, diffs);
|
||||
moveDiff = new Diff(Operation.MOVE, diff1.getPath(), relativePath);
|
||||
|
||||
} else if (Operation.ADD == diff1.getOperation() &&
|
||||
Operation.REMOVE == diff2.getOperation()) {
|
||||
JsonPointer relativePath = computeRelativePath(diff2.getPath(), i, j - 1, diffs); // diff1's add should also be considered
|
||||
moveDiff = new Diff(Operation.MOVE, relativePath, diff1.getPath());
|
||||
}
|
||||
if (moveDiff != null) {
|
||||
diffs.remove(j);
|
||||
diffs.set(i, moveDiff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method splits a {@link Operation#REPLACE} operation within a diff into a {@link Operation#REMOVE}
|
||||
* and {@link Operation#ADD} in order, respectively.
|
||||
* Does nothing if {@link Operation#REPLACE} op does not contain a from value
|
||||
*/
|
||||
private void introduceExplicitRemoveAndAddOperation() {
|
||||
List<Diff> updatedDiffs = new ArrayList<Diff>();
|
||||
for (Diff diff : diffs) {
|
||||
if (!diff.getOperation().equals(Operation.REPLACE) || diff.getSrcValue() == null) {
|
||||
updatedDiffs.add(diff);
|
||||
continue;
|
||||
}
|
||||
//Split into two #REMOVE and #ADD
|
||||
updatedDiffs.add(new Diff(Operation.REMOVE, diff.getPath(), diff.getSrcValue()));
|
||||
updatedDiffs.add(new Diff(Operation.ADD, diff.getPath(), diff.getValue()));
|
||||
}
|
||||
diffs.clear();
|
||||
diffs.addAll(updatedDiffs);
|
||||
}
|
||||
|
||||
//Note : only to be used for arrays
|
||||
//Finds the longest common Ancestor ending at Array
|
||||
private static JsonPointer computeRelativePath(JsonPointer path, int startIdx, int endIdx, List<Diff> diffs) {
|
||||
List<Integer> counters = new ArrayList<Integer>(path.size());
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
counters.add(0);
|
||||
}
|
||||
|
||||
for (int i = startIdx; i <= endIdx; i++) {
|
||||
Diff diff = diffs.get(i);
|
||||
//Adjust relative path according to #ADD and #Remove
|
||||
if (Operation.ADD == diff.getOperation() || Operation.REMOVE == diff.getOperation()) {
|
||||
updatePath(path, diff, counters);
|
||||
}
|
||||
}
|
||||
return updatePathWithCounters(counters, path);
|
||||
}
|
||||
|
||||
private static JsonPointer updatePathWithCounters(List<Integer> counters, JsonPointer path) {
|
||||
List<JsonPointer.RefToken> tokens = path.decompose();
|
||||
for (int i = 0; i < counters.size(); i++) {
|
||||
int value = counters.get(i);
|
||||
if (value != 0) {
|
||||
int currValue = tokens.get(i).getIndex();
|
||||
tokens.set(i, new JsonPointer.RefToken(Integer.toString(currValue + value)));
|
||||
}
|
||||
}
|
||||
return new JsonPointer(tokens);
|
||||
}
|
||||
|
||||
private static void updatePath(JsonPointer path, Diff pseudo, List<Integer> counters) {
|
||||
//find longest common prefix of both the paths
|
||||
|
||||
if (pseudo.getPath().size() <= path.size()) {
|
||||
int idx = -1;
|
||||
for (int i = 0; i < pseudo.getPath().size() - 1; i++) {
|
||||
if (pseudo.getPath().get(i).equals(path.get(i))) {
|
||||
idx = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx == pseudo.getPath().size() - 2) {
|
||||
if (pseudo.getPath().get(pseudo.getPath().size() - 1).isArrayIndex()) {
|
||||
updateCounters(pseudo, pseudo.getPath().size() - 1, counters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateCounters(Diff pseudo, int idx, List<Integer> counters) {
|
||||
if (Operation.ADD == pseudo.getOperation()) {
|
||||
counters.set(idx, counters.get(idx) - 1);
|
||||
} else {
|
||||
if (Operation.REMOVE == pseudo.getOperation()) {
|
||||
counters.set(idx, counters.get(idx) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayNode getJsonNodes() {
|
||||
JsonNodeFactory FACTORY = JsonNodeFactory.instance;
|
||||
final ArrayNode patch = FACTORY.arrayNode();
|
||||
for (Diff diff : diffs) {
|
||||
ObjectNode jsonNode = getJsonNode(FACTORY, diff, flags);
|
||||
patch.add(jsonNode);
|
||||
}
|
||||
return patch;
|
||||
}
|
||||
private List<Diff> getDiffs() {
|
||||
return diffs;
|
||||
}
|
||||
|
||||
private static ObjectNode getJsonNode(JsonNodeFactory FACTORY, Diff diff, EnumSet<DiffFlags> flags) {
|
||||
ObjectNode jsonNode = FACTORY.objectNode();
|
||||
jsonNode.put(Constants.OP, diff.getOperation().rfcName());
|
||||
|
||||
switch (diff.getOperation()) {
|
||||
case MOVE:
|
||||
case COPY:
|
||||
jsonNode.put(Constants.FROM, diff.getPath().toString()); // required {from} only in case of Move Operation
|
||||
jsonNode.put(Constants.PATH, diff.getToPath().toString()); // destination Path
|
||||
break;
|
||||
|
||||
case REMOVE:
|
||||
jsonNode.put(Constants.PATH, diff.getPath().toString());
|
||||
if (!flags.contains(DiffFlags.OMIT_VALUE_ON_REMOVE))
|
||||
jsonNode.set(Constants.VALUE, diff.getValue());
|
||||
break;
|
||||
|
||||
case REPLACE:
|
||||
if (flags.contains(DiffFlags.ADD_ORIGINAL_VALUE_ON_REPLACE)) {
|
||||
jsonNode.set(Constants.FROM_VALUE, diff.getSrcValue());
|
||||
}
|
||||
case ADD:
|
||||
case TEST:
|
||||
jsonNode.put(Constants.PATH, diff.getPath().toString());
|
||||
jsonNode.set(Constants.VALUE, diff.getValue());
|
||||
break;
|
||||
|
||||
default:
|
||||
// Safety net
|
||||
throw new IllegalArgumentException("Unknown operation specified:" + diff.getOperation());
|
||||
}
|
||||
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
private void generateDiffs(JsonPointer path, JsonNode source, JsonNode target) {
|
||||
if (!source.equals(target)) {
|
||||
final NodeType sourceType = NodeType.getNodeType(source);
|
||||
final NodeType targetType = NodeType.getNodeType(target);
|
||||
|
||||
if (sourceType == NodeType.ARRAY && targetType == NodeType.ARRAY) {
|
||||
//both are arrays
|
||||
compareArray(path, source, target);
|
||||
} else if (sourceType == NodeType.OBJECT && targetType == NodeType.OBJECT) {
|
||||
//both are json
|
||||
compareObjects(path, source, target);
|
||||
} else {
|
||||
//can be replaced
|
||||
if (flags.contains(DiffFlags.EMIT_TEST_OPERATIONS))
|
||||
diffs.add(new Diff(Operation.TEST, path, source));
|
||||
diffs.add(Diff.generateDiff(Operation.REPLACE, path, source, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void compareArray(JsonPointer path, JsonNode source, JsonNode target) {
|
||||
List<JsonNode> lcs = getLCS(source, target);
|
||||
int srcIdx = 0;
|
||||
int targetIdx = 0;
|
||||
int lcsIdx = 0;
|
||||
int srcSize = source.size();
|
||||
int targetSize = target.size();
|
||||
int lcsSize = lcs.size();
|
||||
|
||||
int pos = 0;
|
||||
while (lcsIdx < lcsSize) {
|
||||
JsonNode lcsNode = lcs.get(lcsIdx);
|
||||
JsonNode srcNode = source.get(srcIdx);
|
||||
JsonNode targetNode = target.get(targetIdx);
|
||||
|
||||
|
||||
if (lcsNode.equals(srcNode) && lcsNode.equals(targetNode)) { // Both are same as lcs node, nothing to do here
|
||||
srcIdx++;
|
||||
targetIdx++;
|
||||
lcsIdx++;
|
||||
pos++;
|
||||
} else {
|
||||
if (lcsNode.equals(srcNode)) { // src node is same as lcs, but not targetNode
|
||||
//addition
|
||||
JsonPointer currPath = path.append(pos);
|
||||
diffs.add(Diff.generateDiff(Operation.ADD, currPath, targetNode));
|
||||
pos++;
|
||||
targetIdx++;
|
||||
} else if (lcsNode.equals(targetNode)) { //targetNode node is same as lcs, but not src
|
||||
//removal,
|
||||
JsonPointer currPath = path.append(pos);
|
||||
if (flags.contains(DiffFlags.EMIT_TEST_OPERATIONS))
|
||||
diffs.add(new Diff(Operation.TEST, currPath, srcNode));
|
||||
diffs.add(Diff.generateDiff(Operation.REMOVE, currPath, srcNode));
|
||||
srcIdx++;
|
||||
} else {
|
||||
JsonPointer currPath = path.append(pos);
|
||||
//both are unequal to lcs node
|
||||
generateDiffs(currPath, srcNode, targetNode);
|
||||
srcIdx++;
|
||||
targetIdx++;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while ((srcIdx < srcSize) && (targetIdx < targetSize)) {
|
||||
JsonNode srcNode = source.get(srcIdx);
|
||||
JsonNode targetNode = target.get(targetIdx);
|
||||
JsonPointer currPath = path.append(pos);
|
||||
generateDiffs(currPath, srcNode, targetNode);
|
||||
srcIdx++;
|
||||
targetIdx++;
|
||||
pos++;
|
||||
}
|
||||
pos = addRemaining(path, target, pos, targetIdx, targetSize);
|
||||
removeRemaining(path, pos, srcIdx, srcSize, source);
|
||||
}
|
||||
|
||||
private void removeRemaining(JsonPointer path, int pos, int srcIdx, int srcSize, JsonNode source) {
|
||||
while (srcIdx < srcSize) {
|
||||
JsonPointer currPath = path.append(pos);
|
||||
if (flags.contains(DiffFlags.EMIT_TEST_OPERATIONS))
|
||||
diffs.add(new Diff(Operation.TEST, currPath, source.get(srcIdx)));
|
||||
diffs.add(Diff.generateDiff(Operation.REMOVE, currPath, source.get(srcIdx)));
|
||||
srcIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
private int addRemaining(JsonPointer path, JsonNode target, int pos, int targetIdx, int targetSize) {
|
||||
while (targetIdx < targetSize) {
|
||||
JsonNode jsonNode = target.get(targetIdx);
|
||||
JsonPointer currPath = path.append(pos);
|
||||
diffs.add(Diff.generateDiff(Operation.ADD, currPath, jsonNode.deepCopy()));
|
||||
pos++;
|
||||
targetIdx++;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
private void compareObjects(JsonPointer path, JsonNode source, JsonNode target) {
|
||||
Iterator<String> keysFromSrc = source.fieldNames();
|
||||
while (keysFromSrc.hasNext()) {
|
||||
String key = keysFromSrc.next();
|
||||
if (!target.has(key)) {
|
||||
//remove case
|
||||
JsonPointer currPath = path.append(key);
|
||||
if (flags.contains(DiffFlags.EMIT_TEST_OPERATIONS))
|
||||
diffs.add(new Diff(Operation.TEST, currPath, source.get(key)));
|
||||
diffs.add(Diff.generateDiff(Operation.REMOVE, currPath, source.get(key)));
|
||||
continue;
|
||||
}
|
||||
JsonPointer currPath = path.append(key);
|
||||
generateDiffs(currPath, source.get(key), target.get(key));
|
||||
}
|
||||
Iterator<String> keysFromTarget = target.fieldNames();
|
||||
while (keysFromTarget.hasNext()) {
|
||||
String key = keysFromTarget.next();
|
||||
if (!source.has(key)) {
|
||||
//add case
|
||||
JsonPointer currPath = path.append(key);
|
||||
diffs.add(Diff.generateDiff(Operation.ADD, currPath, target.get(key)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<JsonNode> getLCS(final JsonNode first, final JsonNode second) {
|
||||
return ListUtils.longestCommonSubsequence(InternalUtils.toList((ArrayNode) first), InternalUtils.toList((ArrayNode) second));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.NullNode;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* User: gopi.vishwakarma
|
||||
* Date: 31/07/14
|
||||
*/
|
||||
public final class JsonPatch {
|
||||
|
||||
private JsonPatch() {
|
||||
}
|
||||
|
||||
private static JsonNode getPatchAttr(JsonNode jsonNode, String attr) {
|
||||
JsonNode child = jsonNode.get(attr);
|
||||
if (child == null)
|
||||
throw new InvalidJsonPatchException("Invalid JSON Patch payload (missing '" + attr + "' field)");
|
||||
return child;
|
||||
}
|
||||
|
||||
private static JsonNode getPatchAttrWithDefault(JsonNode jsonNode, String attr, JsonNode defaultValue) {
|
||||
JsonNode child = jsonNode.get(attr);
|
||||
if (child == null)
|
||||
return defaultValue;
|
||||
else
|
||||
return child;
|
||||
}
|
||||
|
||||
private static void process(JsonNode patch, JsonPatchProcessor processor, EnumSet<CompatibilityFlags> flags)
|
||||
throws InvalidJsonPatchException {
|
||||
|
||||
if (!patch.isArray())
|
||||
throw new InvalidJsonPatchException("Invalid JSON Patch payload (not an array)");
|
||||
Iterator<JsonNode> operations = patch.iterator();
|
||||
while (operations.hasNext()) {
|
||||
JsonNode jsonNode = operations.next();
|
||||
if (!jsonNode.isObject()) throw new InvalidJsonPatchException("Invalid JSON Patch payload (not an object)");
|
||||
Operation operation = Operation.fromRfcName(getPatchAttr(jsonNode, Constants.OP).textValue());
|
||||
JsonPointer path = JsonPointer.parse(getPatchAttr(jsonNode, Constants.PATH).textValue());
|
||||
|
||||
try {
|
||||
switch (operation) {
|
||||
case REMOVE: {
|
||||
processor.remove(path);
|
||||
break;
|
||||
}
|
||||
|
||||
case ADD: {
|
||||
JsonNode value;
|
||||
if (!flags.contains(CompatibilityFlags.MISSING_VALUES_AS_NULLS))
|
||||
value = getPatchAttr(jsonNode, Constants.VALUE);
|
||||
else
|
||||
value = getPatchAttrWithDefault(jsonNode, Constants.VALUE, NullNode.getInstance());
|
||||
processor.add(path, value.deepCopy());
|
||||
break;
|
||||
}
|
||||
|
||||
case REPLACE: {
|
||||
JsonNode value;
|
||||
if (!flags.contains(CompatibilityFlags.MISSING_VALUES_AS_NULLS))
|
||||
value = getPatchAttr(jsonNode, Constants.VALUE);
|
||||
else
|
||||
value = getPatchAttrWithDefault(jsonNode, Constants.VALUE, NullNode.getInstance());
|
||||
processor.replace(path, value.deepCopy());
|
||||
break;
|
||||
}
|
||||
|
||||
case MOVE: {
|
||||
JsonPointer fromPath = JsonPointer.parse(getPatchAttr(jsonNode, Constants.FROM).textValue());
|
||||
processor.move(fromPath, path);
|
||||
break;
|
||||
}
|
||||
|
||||
case COPY: {
|
||||
JsonPointer fromPath = JsonPointer.parse(getPatchAttr(jsonNode, Constants.FROM).textValue());
|
||||
processor.copy(fromPath, path);
|
||||
break;
|
||||
}
|
||||
|
||||
case TEST: {
|
||||
JsonNode value;
|
||||
if (!flags.contains(CompatibilityFlags.MISSING_VALUES_AS_NULLS))
|
||||
value = getPatchAttr(jsonNode, Constants.VALUE);
|
||||
else
|
||||
value = getPatchAttrWithDefault(jsonNode, Constants.VALUE, NullNode.getInstance());
|
||||
processor.test(path, value.deepCopy());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JsonPointerEvaluationException e) {
|
||||
throw new JsonPatchApplicationException(e.getMessage(), operation, e.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void validate(JsonNode patch, EnumSet<CompatibilityFlags> flags) throws InvalidJsonPatchException {
|
||||
process(patch, NoopProcessor.INSTANCE, flags);
|
||||
}
|
||||
|
||||
public static void validate(JsonNode patch) throws InvalidJsonPatchException {
|
||||
validate(patch, CompatibilityFlags.defaults());
|
||||
}
|
||||
|
||||
public static JsonNode apply(JsonNode patch, JsonNode source, EnumSet<CompatibilityFlags> flags) throws JsonPatchApplicationException {
|
||||
CopyingApplyProcessor processor = new CopyingApplyProcessor(source, flags);
|
||||
process(patch, processor, flags);
|
||||
return processor.result();
|
||||
}
|
||||
|
||||
public static JsonNode apply(JsonNode patch, JsonNode source) throws JsonPatchApplicationException {
|
||||
return apply(patch, source, CompatibilityFlags.defaults());
|
||||
}
|
||||
|
||||
public static void applyInPlace(JsonNode patch, JsonNode source) {
|
||||
applyInPlace(patch, source, CompatibilityFlags.defaults());
|
||||
}
|
||||
|
||||
public static void applyInPlace(JsonNode patch, JsonNode source, EnumSet<CompatibilityFlags> flags) {
|
||||
InPlaceApplyProcessor processor = new InPlaceApplyProcessor(source, flags);
|
||||
process(patch, processor, flags);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
/**
|
||||
* User: holograph
|
||||
* Date: 03/08/16
|
||||
*/
|
||||
public class JsonPatchApplicationException extends RuntimeException {
|
||||
Operation operation;
|
||||
JsonPointer path;
|
||||
|
||||
public JsonPatchApplicationException(String message, Operation operation, JsonPointer path) {
|
||||
super(message);
|
||||
this.operation = operation;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (operation != null) sb.append('[').append(operation).append(" Operation] ");
|
||||
sb.append(getMessage());
|
||||
if (path != null) sb.append(" at ").append(path.isRoot() ? "root" : path);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
interface JsonPatchProcessor {
|
||||
void remove(JsonPointer path) throws JsonPointerEvaluationException;
|
||||
void replace(JsonPointer path, JsonNode value) throws JsonPointerEvaluationException;
|
||||
void add(JsonPointer path, JsonNode value) throws JsonPointerEvaluationException;
|
||||
void move(JsonPointer fromPath, JsonPointer toPath) throws JsonPointerEvaluationException;
|
||||
void copy(JsonPointer fromPath, JsonPointer toPath) throws JsonPointerEvaluationException;
|
||||
void test(JsonPointer path, JsonNode value) throws JsonPointerEvaluationException;
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
package io.metersphere.log.utils.dff;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Implements RFC 6901 (JSON Pointer)
|
||||
*
|
||||
* <p>For full details, please refer to <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>.
|
||||
*
|
||||
* <p></p>Generally, a JSON Pointer is a string representation of a path into a JSON document.
|
||||
* This class implements the RFC as closely as possible, and offers several helpers and
|
||||
* utility methods on top of it:
|
||||
*
|
||||
* <pre>
|
||||
* // Parse, build or render a JSON pointer
|
||||
* String path = "/a/0/b/1";
|
||||
* JsonPointer ptr1 = JsonPointer.{@link #parse}(path);
|
||||
* JsonPointer ptr2 = JsonPointer.{@link #ROOT}.append("a").append(0).append("b").append(1);
|
||||
* assert(ptr1.equals(ptr2));
|
||||
* assert(path.equals(ptr1.toString()));
|
||||
* assert(path.equals(ptr2.toString()));
|
||||
*
|
||||
* // Evaluate a JSON pointer against a live document
|
||||
* ObjectMapper om = new ObjectMapper();
|
||||
* JsonNode doc = om.readTree("{\"foo\":[\"bar\", \"baz\"]}");
|
||||
* JsonNode baz = JsonPointer.parse("/foo/1").{@link #evaluate(JsonNode) evaluate}(doc);
|
||||
* assert(baz.textValue().equals("baz"));
|
||||
* </pre>
|
||||
*
|
||||
* <p>Instances of {@link JsonPointer} and its constituent {@link RefToken}s are <b>immutable</b>.
|
||||
*
|
||||
* @since 0.4.8
|
||||
*/
|
||||
class JsonPointer {
|
||||
private final RefToken[] tokens;
|
||||
|
||||
/** A JSON pointer representing the root node of a JSON document */
|
||||
public final static JsonPointer ROOT = new JsonPointer(new RefToken[] {});
|
||||
|
||||
private JsonPointer(RefToken[] tokens) {
|
||||
this.tokens = tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new pointer from a list of reference tokens.
|
||||
*
|
||||
* @param tokens The list of reference tokens from which to construct the new pointer. This list is not modified.
|
||||
*/
|
||||
public JsonPointer(List<RefToken> tokens) {
|
||||
this.tokens = tokens.toArray(new RefToken[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a valid string representation of a JSON Pointer.
|
||||
*
|
||||
* @param path The string representation to be parsed.
|
||||
* @return An instance of {@link JsonPointer} conforming to the specified string representation.
|
||||
* @throws IllegalArgumentException The specified JSON Pointer is invalid.
|
||||
*/
|
||||
public static JsonPointer parse(String path) throws IllegalArgumentException {
|
||||
StringBuilder reftoken = null;
|
||||
List<RefToken> result = new ArrayList<RefToken>();
|
||||
|
||||
for (int i = 0; i < path.length(); ++i) {
|
||||
char c = path.charAt(i);
|
||||
|
||||
// Require leading slash
|
||||
if (i == 0) {
|
||||
if (c != '/') throw new IllegalArgumentException("Missing leading slash");
|
||||
reftoken = new StringBuilder();
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
// Escape sequences
|
||||
case '~':
|
||||
switch (path.charAt(++i)) {
|
||||
case '0': reftoken.append('~'); break;
|
||||
case '1': reftoken.append('/'); break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid escape sequence ~" + path.charAt(i) + " at index " + i);
|
||||
}
|
||||
break;
|
||||
|
||||
// New reftoken
|
||||
case '/':
|
||||
result.add(new RefToken(reftoken.toString()));
|
||||
reftoken.setLength(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
reftoken.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reftoken == null)
|
||||
return ROOT;
|
||||
|
||||
result.add(RefToken.parse(reftoken.toString()));
|
||||
return new JsonPointer(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not this instance points to the root of a JSON document.
|
||||
* @return {@code true} if this pointer represents the root node, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isRoot() {
|
||||
return tokens.length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JSON pointer to the specified field of the object referenced by this instance.
|
||||
*
|
||||
* @param field The desired field name, or any valid JSON Pointer reference token
|
||||
* @return The new {@link JsonPointer} instance.
|
||||
*/
|
||||
JsonPointer append(String field) {
|
||||
RefToken[] newTokens = Arrays.copyOf(tokens, tokens.length + 1);
|
||||
newTokens[tokens.length] = new RefToken(field);
|
||||
return new JsonPointer(newTokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new JSON pointer to an indexed element of the array referenced by this instance.
|
||||
*
|
||||
* @param index The desired index, or {@link #LAST_INDEX} to point past the end of the array.
|
||||
* @return The new {@link JsonPointer} instance.
|
||||
*/
|
||||
JsonPointer append(int index) {
|
||||
return append(Integer.toString(index));
|
||||
}
|
||||
|
||||
/** Returns the number of reference tokens comprising this instance. */
|
||||
int size() {
|
||||
return tokens.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this instance
|
||||
*
|
||||
* @return
|
||||
* An <a href="https://tools.ietf.org/html/rfc6901#section-5">RFC 6901 compliant</a> string
|
||||
* representation of this JSON pointer.
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (RefToken token : tokens) {
|
||||
sb.append('/');
|
||||
sb.append(token);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decomposes this JSON pointer into its reference tokens.
|
||||
*
|
||||
* @return A list of {@link RefToken}s. Modifications to this list do not affect this instance.
|
||||
*/
|
||||
public List<RefToken> decompose() {
|
||||
return Arrays.asList(tokens.clone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the reference token at the specified index.
|
||||
*
|
||||
* @param index The desired reference token index.
|
||||
* @return The specified instance of {@link RefToken}.
|
||||
* @throws IndexOutOfBoundsException The specified index is illegal.
|
||||
*/
|
||||
public RefToken get(int index) throws IndexOutOfBoundsException {
|
||||
if (index < 0 || index >= tokens.length) throw new IndexOutOfBoundsException("Illegal index: " + index);
|
||||
return tokens[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the last reference token for this JSON pointer.
|
||||
*
|
||||
* @return The last {@link RefToken} comprising this instance.
|
||||
* @throws IllegalStateException Last cannot be called on {@link #ROOT root} pointers.
|
||||
*/
|
||||
public RefToken last() {
|
||||
if (isRoot()) throw new IllegalStateException("Root pointers contain no reference tokens");
|
||||
return tokens[tokens.length - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON pointer to the parent of the node represented by this instance.
|
||||
*
|
||||
* The parent of the {@link #ROOT root} pointer is the root pointer itself.
|
||||
*
|
||||
* @return A {@link JsonPointer} to the parent node.
|
||||
*/
|
||||
public JsonPointer getParent() {
|
||||
return isRoot() ? this : new JsonPointer(Arrays.copyOf(tokens, tokens.length - 1));
|
||||
}
|
||||
|
||||
private void error(int atToken, String message, JsonNode document) throws JsonPointerEvaluationException {
|
||||
throw new JsonPointerEvaluationException(
|
||||
message,
|
||||
new JsonPointer(Arrays.copyOf(tokens, atToken)),
|
||||
document);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a target document and resolves the node represented by this instance.
|
||||
*
|
||||
* The evaluation semantics are described in
|
||||
* <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901 sectino 4</a>.
|
||||
*
|
||||
* @param document The target document against which to evaluate the JSON pointer.
|
||||
* @return The {@link JsonNode} resolved by evaluating this JSON pointer.
|
||||
* @throws JsonPointerEvaluationException The pointer could not be evaluated.
|
||||
*/
|
||||
public JsonNode evaluate(final JsonNode document) throws JsonPointerEvaluationException {
|
||||
JsonNode current = document;
|
||||
|
||||
for (int idx = 0; idx < tokens.length; ++idx) {
|
||||
final RefToken token = tokens[idx];
|
||||
|
||||
if (current.isArray()) {
|
||||
if (!token.isArrayIndex())
|
||||
error(idx, "Can't reference field \"" + token.getField() + "\" on array", document);
|
||||
if (token.getIndex() == LAST_INDEX || token.getIndex() >= current.size())
|
||||
error(idx, "Array index " + token.toString() + " is out of bounds", document);
|
||||
current = current.get(token.getIndex());
|
||||
}
|
||||
else if (current.isObject()) {
|
||||
if (!current.has(token.getField()))
|
||||
error(idx,"Missing field \"" + token.getField() + "\"", document);
|
||||
current = current.get(token.getField());
|
||||
}
|
||||
else
|
||||
error(idx, "Can't reference past scalar value", document);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
JsonPointer that = (JsonPointer) o;
|
||||
|
||||
// Probably incorrect - comparing Object[] arrays with Arrays.equals
|
||||
return Arrays.equals(tokens, that.tokens);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(tokens);
|
||||
}
|
||||
|
||||
/** Represents a single JSON Pointer reference token. */
|
||||
static class RefToken {
|
||||
private String decodedToken;
|
||||
transient private Integer index = null;
|
||||
|
||||
public RefToken(String decodedToken) {
|
||||
if (decodedToken == null) throw new IllegalArgumentException("Token can't be null");
|
||||
this.decodedToken = decodedToken;
|
||||
}
|
||||
|
||||
private static final Pattern DECODED_TILDA_PATTERN = Pattern.compile("~0");
|
||||
private static final Pattern DECODED_SLASH_PATTERN = Pattern.compile("~1");
|
||||
|
||||
private static String decodePath(Object object) {
|
||||
String path = object.toString(); // see http://tools.ietf.org/html/rfc6901#section-4
|
||||
path = DECODED_SLASH_PATTERN.matcher(path).replaceAll("/");
|
||||
return DECODED_TILDA_PATTERN.matcher(path).replaceAll("~");
|
||||
}
|
||||
|
||||
private static final Pattern ENCODED_TILDA_PATTERN = Pattern.compile("~");
|
||||
private static final Pattern ENCODED_SLASH_PATTERN = Pattern.compile("/");
|
||||
|
||||
private static String encodePath(Object object) {
|
||||
String path = object.toString(); // see http://tools.ietf.org/html/rfc6901#section-4
|
||||
path = ENCODED_TILDA_PATTERN.matcher(path).replaceAll("~0");
|
||||
return ENCODED_SLASH_PATTERN.matcher(path).replaceAll("~1");
|
||||
}
|
||||
|
||||
private static final Pattern VALID_ARRAY_IND = Pattern.compile("-|0|(?:[1-9][0-9]*)");
|
||||
|
||||
public static RefToken parse(String rawToken) {
|
||||
if (rawToken == null) throw new IllegalArgumentException("Token can't be null");
|
||||
return new RefToken(decodePath(rawToken));
|
||||
}
|
||||
|
||||
public boolean isArrayIndex() {
|
||||
if (index != null) return true;
|
||||
Matcher matcher = VALID_ARRAY_IND.matcher(decodedToken);
|
||||
if (matcher.matches()) {
|
||||
index = matcher.group().equals("-") ? LAST_INDEX : Integer.parseInt(matcher.group());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
if (!isArrayIndex()) throw new IllegalStateException("Object operation on array target");
|
||||
return index;
|
||||
}
|
||||
|
||||
public String getField() {
|
||||
return decodedToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return encodePath(decodedToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
RefToken refToken = (RefToken) o;
|
||||
|
||||
return decodedToken.equals(refToken.decodedToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return decodedToken.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an array index pointing past the end of the array.
|
||||
*
|
||||
* Such an index is represented by the JSON pointer reference token "{@code -}"; see
|
||||
* <a href="https://tools.ietf.org/html/rfc6901#section-4">RFC 6901 section 4</a> for
|
||||
* more details.
|
||||
*/
|
||||
final static int LAST_INDEX = Integer.MIN_VALUE;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public class JsonPointerEvaluationException extends Exception {
|
||||
private final JsonPointer path;
|
||||
private final JsonNode target;
|
||||
|
||||
public JsonPointerEvaluationException(String message, JsonPointer path, JsonNode target) {
|
||||
super(message);
|
||||
this.path = path;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public JsonPointer getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public JsonNode getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
enum NodeType {
|
||||
/**
|
||||
* Array nodes
|
||||
*/
|
||||
ARRAY("array"),
|
||||
/**
|
||||
* Boolean nodes
|
||||
*/
|
||||
BOOLEAN("boolean"),
|
||||
/**
|
||||
* Integer nodes
|
||||
*/
|
||||
INTEGER("integer"),
|
||||
/**
|
||||
* Number nodes (ie, decimal numbers)
|
||||
*/
|
||||
NULL("null"),
|
||||
/**
|
||||
* Object nodes
|
||||
*/
|
||||
NUMBER("number"),
|
||||
/**
|
||||
* Null nodes
|
||||
*/
|
||||
OBJECT("object"),
|
||||
/**
|
||||
* String nodes
|
||||
*/
|
||||
STRING("string");
|
||||
|
||||
/**
|
||||
* The name for this type, as encountered in a JSON schema
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
private static final Map<JsonToken, NodeType> TOKEN_MAP
|
||||
= new EnumMap<JsonToken, NodeType>(JsonToken.class);
|
||||
|
||||
static {
|
||||
TOKEN_MAP.put(JsonToken.START_ARRAY, ARRAY);
|
||||
TOKEN_MAP.put(JsonToken.VALUE_TRUE, BOOLEAN);
|
||||
TOKEN_MAP.put(JsonToken.VALUE_FALSE, BOOLEAN);
|
||||
TOKEN_MAP.put(JsonToken.VALUE_NUMBER_INT, INTEGER);
|
||||
TOKEN_MAP.put(JsonToken.VALUE_NUMBER_FLOAT, NUMBER);
|
||||
TOKEN_MAP.put(JsonToken.VALUE_NULL, NULL);
|
||||
TOKEN_MAP.put(JsonToken.START_OBJECT, OBJECT);
|
||||
TOKEN_MAP.put(JsonToken.VALUE_STRING, STRING);
|
||||
|
||||
}
|
||||
|
||||
NodeType(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static NodeType getNodeType(final JsonNode node) {
|
||||
final JsonToken token = node.asToken();
|
||||
final NodeType ret = TOKEN_MAP.get(token);
|
||||
if (ret == null) throw new NullPointerException("unhandled token type " + token);
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
/**
|
||||
* A JSON patch processor that does nothing, intended for testing and validation.
|
||||
*/
|
||||
public class NoopProcessor implements JsonPatchProcessor {
|
||||
static final NoopProcessor INSTANCE;
|
||||
static {
|
||||
INSTANCE = new NoopProcessor();
|
||||
}
|
||||
|
||||
@Override public void remove(JsonPointer path) {}
|
||||
@Override public void replace(JsonPointer path, JsonNode value) {}
|
||||
@Override public void add(JsonPointer path, JsonNode value) {}
|
||||
@Override public void move(JsonPointer fromPath, JsonPointer toPath) {}
|
||||
@Override public void copy(JsonPointer fromPath, JsonPointer toPath) {}
|
||||
@Override public void test(JsonPointer path, JsonNode value) {}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2016 flipkart.com zjsonpatch.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.metersphere.log.utils.dff;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* User: gopi.vishwakarma
|
||||
* Date: 30/07/14
|
||||
*/
|
||||
public enum Operation {
|
||||
ADD("添加"),
|
||||
REMOVE("移除"),
|
||||
REPLACE("修改"),
|
||||
MOVE("移动"),
|
||||
COPY("复制"),
|
||||
TEST("测试");
|
||||
|
||||
private final static Map<String, Operation> OPS = createImmutableMap();
|
||||
|
||||
private static Map<String, Operation> createImmutableMap() {
|
||||
Map<String, Operation> map = new HashMap<String, Operation>();
|
||||
map.put(ADD.rfcName, ADD);
|
||||
map.put(REMOVE.rfcName, REMOVE);
|
||||
map.put(REPLACE.rfcName, REPLACE);
|
||||
map.put(MOVE.rfcName, MOVE);
|
||||
map.put(COPY.rfcName, COPY);
|
||||
map.put(TEST.rfcName, TEST);
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
private String rfcName;
|
||||
|
||||
Operation(String rfcName) {
|
||||
this.rfcName = rfcName;
|
||||
}
|
||||
|
||||
public static Operation fromRfcName(String rfcName) throws InvalidJsonPatchException {
|
||||
if (rfcName == null) throw new InvalidJsonPatchException("rfcName cannot be null");
|
||||
Operation op = OPS.get(rfcName.toLowerCase());
|
||||
if (op == null) throw new InvalidJsonPatchException("unknown / unsupported operation " + rfcName);
|
||||
return op;
|
||||
}
|
||||
|
||||
public String rfcName() {
|
||||
return this.rfcName;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -24,6 +24,8 @@ public class OperatingLogDTO implements Serializable {
|
|||
|
||||
private String operTitle;
|
||||
|
||||
private String createUser;
|
||||
|
||||
private Long operTime;
|
||||
|
||||
private OperatingLogDetails details;
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.List;
|
|||
public class OperatingLogDetails {
|
||||
private String sourceId;
|
||||
private String projectId;
|
||||
private String createUser;
|
||||
private String title;
|
||||
private List<DetailColumn> columns;
|
||||
|
||||
|
@ -15,15 +16,17 @@ public class OperatingLogDetails {
|
|||
|
||||
}
|
||||
|
||||
public OperatingLogDetails(String sourceId, String projectId, List<DetailColumn> columns) {
|
||||
public OperatingLogDetails(String sourceId, String projectId, String createUser, List<DetailColumn> columns) {
|
||||
this.sourceId = sourceId;
|
||||
this.projectId = projectId;
|
||||
this.createUser = createUser;
|
||||
this.columns = columns;
|
||||
}
|
||||
|
||||
public OperatingLogDetails(String sourceId, String projectId, String title, List<DetailColumn> columns) {
|
||||
public OperatingLogDetails(String sourceId, String projectId, String title, String createUser, List<DetailColumn> columns) {
|
||||
this.sourceId = sourceId;
|
||||
this.projectId = projectId;
|
||||
this.createUser = createUser;
|
||||
this.title = title;
|
||||
this.columns = columns;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,15 @@ import java.util.Map;
|
|||
|
||||
public class DefinitionReference {
|
||||
public static Map<String, String> definitionColumns = new LinkedHashMap<>();
|
||||
public static Map<String, String> moduleColumns = new LinkedHashMap<>();
|
||||
public static Map<String, String> caseColumns = new LinkedHashMap<>();
|
||||
|
||||
static {
|
||||
definitionColumns.clear();
|
||||
moduleColumns.clear();
|
||||
caseColumns.clear();
|
||||
definitionColumns.put("name", "接口名称");
|
||||
definitionColumns.put("createUser", "创建人");
|
||||
definitionColumns.put("method", "请求类型");
|
||||
definitionColumns.put("modulePath", "模块");
|
||||
definitionColumns.put("status", "接口状态");
|
||||
|
@ -19,6 +25,23 @@ public class DefinitionReference {
|
|||
definitionColumns.put("response", "返回参数");
|
||||
definitionColumns.put("description", "描述");
|
||||
// 需要深度对比的字段,可以支持多个req1,req2
|
||||
definitionColumns.put("ms-dff-col", "request");
|
||||
definitionColumns.put("ms-dff-col", "request,response");
|
||||
|
||||
// 模块列数据
|
||||
moduleColumns.put("name", "模块名称");
|
||||
moduleColumns.put("createUser", "创建人");
|
||||
moduleColumns.put("protocol", "协议");
|
||||
moduleColumns.put("level", "模块级别");
|
||||
// 用例列数据
|
||||
caseColumns.put("name", "用例名称");
|
||||
caseColumns.put("priority", "用例级别");
|
||||
caseColumns.put("createUserId", "创建人");
|
||||
caseColumns.put("updateUserId", "编辑人");
|
||||
caseColumns.put("tags", "标签");
|
||||
caseColumns.put("description", "描述");
|
||||
caseColumns.put("request", "请求参数");
|
||||
// 深度对比字段
|
||||
caseColumns.put("ms-dff-col", "request");
|
||||
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit 9a95a444a4f0a477427f94bd107571430d85e766
|
||||
Subproject commit e435fe1d01a2495481efa58daf3875be07b2ac9b
|
|
@ -35,3 +35,23 @@ CREATE TABLE IF NOT EXISTS api_environment_running_param (
|
|||
alter table schedule
|
||||
add config VARCHAR(500) null;
|
||||
|
||||
CREATE TABLE `operating_log` (
|
||||
`id` varchar(50) NOT NULL COMMENT 'ID',
|
||||
`project_id` varchar(50) NOT NULL COMMENT 'Project ID',
|
||||
`oper_method` varchar(500) DEFAULT NULL COMMENT 'operating method',
|
||||
`create_user` varchar(100) DEFAULT NULL COMMENT 'source create u',
|
||||
`oper_user` varchar(50) DEFAULT NULL COMMENT 'operating user id',
|
||||
`source_id` varchar(2000) DEFAULT NULL COMMENT 'operating source id',
|
||||
`oper_type` varchar(100) DEFAULT NULL COMMENT 'operating type',
|
||||
`oper_module` varchar(64) DEFAULT NULL COMMENT 'operating module',
|
||||
`oper_title` varchar(2000) DEFAULT NULL COMMENT 'operating title',
|
||||
`oper_path` varchar(500) DEFAULT NULL COMMENT 'operating path',
|
||||
`oper_content` longtext COMMENT 'operating content',
|
||||
`oper_params` longtext COMMENT 'operating parrams',
|
||||
`oper_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- add all table create_user
|
||||
ALTER TABLE api_definition ADD create_user VARCHAR(100) NULL;
|
||||
ALTER TABLE api_module ADD create_user VARCHAR(100) NULL;
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
<el-dialog :close-on-click-modal="false" :title="getType(detail.operType)+title" :visible.sync="infoVisible" width="60%" :destroy-on-close="true"
|
||||
@close="handleClose">
|
||||
<div v-if="detail.createUser">
|
||||
<p class="tip">{{ this.$t('report.user_name') }} :{{detail.createUser}}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="tip">{{ this.$t('operating_log.user') }} :{{detail.operUser}}</p>
|
||||
</div>
|
||||
|
@ -26,7 +29,7 @@
|
|||
</div>
|
||||
<div v-else>
|
||||
<div v-if="detail && detail.details && detail.details.columns" style="margin-left: 20px">
|
||||
<p v-for="n in detail.details.columns" :key="n.id">{{n.columnTitle}}:{{n.originalValue}}</p>
|
||||
<pre style="overflow: auto" v-for="n in detail.details.columns" :key="n.id">{{n.columnTitle}}:{{n.originalValue}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 097d3a5a00beaa660d34525765d93b35e63f52c0
|
||||
Subproject commit e50f0463826ac4d7837ea3a237333827774a1b19
|
Loading…
Reference in New Issue