This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
package com.codechecker.notify;
|
||||
|
||||
import com.codechecker.api.model.ApiChangeKind;
|
||||
import com.codechecker.api.model.EndpointChangeReport;
|
||||
import com.codechecker.api.model.ParameterChange;
|
||||
import com.codechecker.config.DtoOverlapMode;
|
||||
import com.codechecker.model.ApiEndpoint;
|
||||
import com.codechecker.model.ClassChangeReport;
|
||||
import com.codechecker.model.ClassType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 按配置过滤 Dto 类变更与 API 参数变更的重叠通知。
|
||||
*/
|
||||
public class OverlapNotificationFilter {
|
||||
|
||||
public static final class FilterResult {
|
||||
private final List<ClassChangeReport> classReports;
|
||||
private final List<EndpointChangeReport> apiReports;
|
||||
|
||||
public FilterResult(List<ClassChangeReport> classReports, List<EndpointChangeReport> apiReports) {
|
||||
this.classReports = classReports;
|
||||
this.apiReports = apiReports;
|
||||
}
|
||||
|
||||
public List<ClassChangeReport> classReports() {
|
||||
return classReports;
|
||||
}
|
||||
|
||||
public List<EndpointChangeReport> apiReports() {
|
||||
return apiReports;
|
||||
}
|
||||
}
|
||||
|
||||
public static FilterResult apply(List<ClassChangeReport> classReports,
|
||||
List<EndpointChangeReport> apiReports,
|
||||
DtoOverlapMode mode) {
|
||||
if (mode == DtoOverlapMode.BOTH) {
|
||||
return new FilterResult(classReports, apiReports);
|
||||
}
|
||||
Set<OverlapKey> overlapKeys = buildOverlapKeys(classReports);
|
||||
if (overlapKeys.isEmpty()) {
|
||||
return new FilterResult(classReports, apiReports);
|
||||
}
|
||||
if (mode == DtoOverlapMode.CLASS_ONLY) {
|
||||
return new FilterResult(classReports, filterApiReports(apiReports, overlapKeys));
|
||||
}
|
||||
Set<OverlapKey> apiOverlapKeys = buildApiOverlapKeys(apiReports);
|
||||
return new FilterResult(filterClassReportsForApiOnly(classReports, apiOverlapKeys), apiReports);
|
||||
}
|
||||
|
||||
private static Set<OverlapKey> buildOverlapKeys(List<ClassChangeReport> classReports) {
|
||||
Set<OverlapKey> keys = new LinkedHashSet<>();
|
||||
for (ClassChangeReport report : classReports) {
|
||||
if (report.getClassType() != ClassType.DTO) {
|
||||
continue;
|
||||
}
|
||||
if (!hasDtoFieldChanges(report)) {
|
||||
continue;
|
||||
}
|
||||
Set<String> dtoNames = dtoNames(report);
|
||||
for (ApiEndpoint endpoint : report.getInputImpactEndpoints()) {
|
||||
for (String dtoName : dtoNames) {
|
||||
keys.add(new OverlapKey(dtoName, endpoint.endpointKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static List<EndpointChangeReport> filterApiReports(List<EndpointChangeReport> apiReports,
|
||||
Set<OverlapKey> overlapKeys) {
|
||||
List<EndpointChangeReport> kept = new ArrayList<>();
|
||||
for (EndpointChangeReport report : apiReports) {
|
||||
if (!matchesOverlap(report, overlapKeys)) {
|
||||
kept.add(report);
|
||||
}
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
private static List<ClassChangeReport> filterClassReportsForApiOnly(List<ClassChangeReport> classReports,
|
||||
Set<OverlapKey> apiOverlapKeys) {
|
||||
List<ClassChangeReport> kept = new ArrayList<>();
|
||||
for (ClassChangeReport report : classReports) {
|
||||
if (!shouldSuppressClassForApiOnly(report, apiOverlapKeys)) {
|
||||
kept.add(report);
|
||||
}
|
||||
}
|
||||
return kept;
|
||||
}
|
||||
|
||||
private static Set<OverlapKey> buildApiOverlapKeys(List<EndpointChangeReport> apiReports) {
|
||||
Set<OverlapKey> keys = new LinkedHashSet<>();
|
||||
for (EndpointChangeReport report : apiReports) {
|
||||
if (report.getChangeKind() != ApiChangeKind.PARAM_CHANGED) {
|
||||
continue;
|
||||
}
|
||||
String endpointKey = report.getHttpMethod() + " " + report.getUri();
|
||||
for (ParameterChange change : report.getParameterChanges()) {
|
||||
if (!"body".equals(change.getSource())) {
|
||||
continue;
|
||||
}
|
||||
String parentDto = change.getParentDto();
|
||||
if (parentDto != null && !parentDto.isBlank()) {
|
||||
keys.add(new OverlapKey(parentDto, endpointKey));
|
||||
}
|
||||
}
|
||||
if (report.isDtoFollowUp()) {
|
||||
String relatedDto = report.getRelatedDtoClassName();
|
||||
if (relatedDto != null && !relatedDto.isBlank()) {
|
||||
keys.add(new OverlapKey(relatedDto, endpointKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static boolean shouldSuppressClassForApiOnly(ClassChangeReport report,
|
||||
Set<OverlapKey> apiOverlapKeys) {
|
||||
if (report.getClassType() != ClassType.DTO || !hasDtoFieldChanges(report)) {
|
||||
return false;
|
||||
}
|
||||
for (ApiEndpoint endpoint : report.getInputImpactEndpoints()) {
|
||||
for (String dtoName : dtoNames(report)) {
|
||||
if (apiOverlapKeys.contains(new OverlapKey(dtoName, endpoint.endpointKey()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean matchesOverlap(EndpointChangeReport report, Set<OverlapKey> overlapKeys) {
|
||||
if (report.getChangeKind() != ApiChangeKind.PARAM_CHANGED) {
|
||||
return false;
|
||||
}
|
||||
String endpointKey = report.getHttpMethod() + " " + report.getUri();
|
||||
for (ParameterChange change : report.getParameterChanges()) {
|
||||
if (!"body".equals(change.getSource())) {
|
||||
continue;
|
||||
}
|
||||
String parentDto = change.getParentDto();
|
||||
if (parentDto == null || parentDto.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if (overlapKeys.contains(new OverlapKey(parentDto, endpointKey))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (report.isDtoFollowUp()) {
|
||||
String relatedDto = report.getRelatedDtoClassName();
|
||||
if (relatedDto != null && overlapKeys.contains(new OverlapKey(relatedDto, endpointKey))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasDtoFieldChanges(ClassChangeReport report) {
|
||||
return !report.getFieldChanges().isEmpty();
|
||||
}
|
||||
|
||||
private static Set<String> dtoNames(ClassChangeReport report) {
|
||||
Set<String> names = new LinkedHashSet<>();
|
||||
names.add(report.getClassName());
|
||||
if (report.getOldClassName() != null && !report.getOldClassName().isBlank()) {
|
||||
names.add(report.getOldClassName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private static final class OverlapKey {
|
||||
private final String dtoClassName;
|
||||
private final String endpointKey;
|
||||
|
||||
private OverlapKey(String dtoClassName, String endpointKey) {
|
||||
this.dtoClassName = dtoClassName;
|
||||
this.endpointKey = endpointKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof OverlapKey)) {
|
||||
return false;
|
||||
}
|
||||
OverlapKey other = (OverlapKey) obj;
|
||||
return dtoClassName.equals(other.dtoClassName) && endpointKey.equals(other.endpointKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return dtoClassName.hashCode() * 31 + endpointKey.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user