@@ -1,150 +0,0 @@
|
||||
package com.codechecker.api.analyzer;
|
||||
|
||||
import com.codechecker.analyzer.DtoNestIndex;
|
||||
import com.codechecker.api.model.ApiChangeKind;
|
||||
import com.codechecker.api.model.EndpointChangeReport;
|
||||
import com.codechecker.api.model.EndpointSnapshot;
|
||||
import com.codechecker.api.model.ParameterChange;
|
||||
import com.codechecker.api.parser.EndpointSnapshotParser;
|
||||
import com.codechecker.config.AppConfig;
|
||||
import com.codechecker.git.GitChangeScanner;
|
||||
import com.codechecker.model.ApiEndpoint;
|
||||
import com.codechecker.model.ClassChangeReport;
|
||||
import com.codechecker.model.ClassType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 类变更(Dto/Vo 嵌套字段)后,对受影响的 Controller 继续 API 参数 diff,产出 PARAM_CHANGED 报告。
|
||||
*/
|
||||
public class DtoImpactedApiAnalyzer {
|
||||
private final GitChangeScanner gitScanner;
|
||||
|
||||
public DtoImpactedApiAnalyzer(GitChangeScanner gitScanner) {
|
||||
this.gitScanner = gitScanner;
|
||||
}
|
||||
|
||||
public List<EndpointChangeReport> analyze(Path repoRoot, AppConfig config,
|
||||
String oldSha, String newSha,
|
||||
List<ClassChangeReport> classReports,
|
||||
Set<String> alreadyScannedFiles,
|
||||
DtoNestIndex nestIndex) throws IOException {
|
||||
Map<String, Set<String>> controllerToDtos = collectImpactedControllers(classReports, alreadyScannedFiles,
|
||||
nestIndex);
|
||||
if (controllerToDtos.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
EndpointSnapshotParser parser = new EndpointSnapshotParser(config.isApiExcludeFrameworkParams());
|
||||
ParameterDiffEngine parameterDiffEngine = new ParameterDiffEngine(
|
||||
repoRoot, buildSearchDirs(config), gitScanner, oldSha, newSha, config.getNestMaxDepth());
|
||||
EndpointDiffEngine endpointDiffEngine = new EndpointDiffEngine(parameterDiffEngine);
|
||||
|
||||
List<EndpointSnapshot> oldSnapshots = new ArrayList<>();
|
||||
List<EndpointSnapshot> newSnapshots = new ArrayList<>();
|
||||
for (String path : controllerToDtos.keySet()) {
|
||||
boolean feign = isFeignPath(path, config);
|
||||
String oldSource = gitScanner.readFileAtCommit(oldSha, path);
|
||||
String newSource = gitScanner.readFileAtCommit(newSha, path);
|
||||
oldSnapshots.addAll(parser.parseSource(oldSource, path, feign));
|
||||
newSnapshots.addAll(parser.parseSource(newSource, path, feign));
|
||||
}
|
||||
|
||||
List<EndpointChangeReport> reports = new ArrayList<>();
|
||||
for (EndpointChangeReport report : endpointDiffEngine.diff(oldSnapshots, newSnapshots)) {
|
||||
if (report.getChangeKind() != ApiChangeKind.PARAM_CHANGED || !report.hasParameterChanges()) {
|
||||
continue;
|
||||
}
|
||||
String relatedDto = findRelatedDto(report, controllerToDtos);
|
||||
if (relatedDto == null) {
|
||||
continue;
|
||||
}
|
||||
reports.add(EndpointChangeReport.dtoFollowUp(report, relatedDto));
|
||||
}
|
||||
return reports;
|
||||
}
|
||||
|
||||
private Map<String, Set<String>> collectImpactedControllers(List<ClassChangeReport> classReports,
|
||||
Set<String> alreadyScannedFiles,
|
||||
DtoNestIndex nestIndex) {
|
||||
Map<String, Set<String>> controllerToDtos = new LinkedHashMap<>();
|
||||
for (ClassChangeReport report : classReports) {
|
||||
if (report.getFieldChanges().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (report.getClassType() != ClassType.DTO && report.getClassType() != ClassType.VO) {
|
||||
continue;
|
||||
}
|
||||
Set<String> bodyRoots = resolveBodyRoots(report, nestIndex);
|
||||
if (bodyRoots.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (ApiEndpoint endpoint : report.getInputImpactEndpoints()) {
|
||||
String controllerFile = endpoint.getSourceFile();
|
||||
if (alreadyScannedFiles.contains(controllerFile)) {
|
||||
continue;
|
||||
}
|
||||
controllerToDtos.computeIfAbsent(controllerFile, k -> new LinkedHashSet<>()).addAll(bodyRoots);
|
||||
}
|
||||
}
|
||||
return controllerToDtos;
|
||||
}
|
||||
|
||||
private Set<String> resolveBodyRoots(ClassChangeReport report, DtoNestIndex nestIndex) {
|
||||
if (nestIndex == null) {
|
||||
Set<String> names = new LinkedHashSet<>();
|
||||
if (report.getClassName().endsWith("Dto")) {
|
||||
names.add(report.getClassName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
Set<String> roots = new LinkedHashSet<>();
|
||||
roots.addAll(nestIndex.findRequestBodyRoots(report.getClassName()));
|
||||
if (report.getOldClassName() != null && !report.getOldClassName().isBlank()) {
|
||||
roots.addAll(nestIndex.findRequestBodyRoots(report.getOldClassName()));
|
||||
}
|
||||
return roots;
|
||||
}
|
||||
|
||||
private String findRelatedDto(EndpointChangeReport report, Map<String, Set<String>> controllerToDtos) {
|
||||
Set<String> impactedDtos = controllerToDtos.getOrDefault(report.getSourceFile(), Set.of());
|
||||
for (ParameterChange change : report.getParameterChanges()) {
|
||||
if (!"body".equals(change.getSource())) {
|
||||
continue;
|
||||
}
|
||||
String parentDto = change.getParentDto();
|
||||
if (parentDto != null && impactedDtos.contains(parentDto)) {
|
||||
return parentDto;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<String> buildSearchDirs(AppConfig config) {
|
||||
List<String> dirs = new ArrayList<>();
|
||||
dirs.addAll(config.getModelDirs());
|
||||
dirs.addAll(config.getAllApiScanDirs());
|
||||
return dirs;
|
||||
}
|
||||
|
||||
private boolean isFeignPath(String path, AppConfig config) {
|
||||
String normalized = path.replace('\\', '/');
|
||||
for (String dir : config.getApiFeignScanDirs()) {
|
||||
String prefix = dir.replace('\\', '/');
|
||||
if (!prefix.endsWith("/")) {
|
||||
prefix = prefix + "/";
|
||||
}
|
||||
if (normalized.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user