feat(接口自动化): 历史操作日志基本框架完成

This commit is contained in:
fit2-zhao 2021-05-17 14:26:49 +08:00 committed by fit2-zhao
parent 4062f36cc2
commit d23ca01c5d
46 changed files with 2499 additions and 178 deletions

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -39,5 +39,7 @@ public class ApiDefinition implements Serializable {
private String originalState;
private String createUser;
private static final long serialVersionUID = 1L;
}

View File

@ -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 {

View File

@ -23,5 +23,7 @@ public class ApiModule implements Serializable {
private Double pos;
private String createUser;
private static final long serialVersionUID = 1L;
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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>

View File

@ -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>

View File

@ -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);
}

View File

@ -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>

View File

@ -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) {

View File

@ -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())) {

View File

@ -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) {
}
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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() {}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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) {}
}

View File

@ -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;
}
}

View File

@ -24,6 +24,8 @@ public class OperatingLogDTO implements Serializable {
private String operTitle;
private String createUser;
private Long operTime;
private OperatingLogDetails details;

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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