From 8a572f67707395c91d281efb904eb583410caaa4 Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:53:20 +0200 Subject: [PATCH 1/7] Follow-ups Markus and allow REST calls that start with "/" to be assigned to the correct endpoints anyway --- .../analysis-of-endpoint-connections.yml | 4 +- .../endpointanalysis/EndpointAnalyzer.java | 48 ++++++++++--------- .../endpointanalysis/EndpointInformation.java | 4 +- .../cit/endpointanalysis/EndpointParser.java | 44 ++++++++--------- .../endpointanalysis/RestCallAnalyzer.java | 48 ++++++++++--------- .../RestCallFileInformation.java | 4 +- .../endpointanalysis/RestCallInformation.java | 6 ++- .../src/main/typeScript/Postprocessor.ts | 20 ++++---- 8 files changed, 94 insertions(+), 84 deletions(-) diff --git a/.github/workflows/analysis-of-endpoint-connections.yml b/.github/workflows/analysis-of-endpoint-connections.yml index 2116605bea0a..73d5109f56a9 100644 --- a/.github/workflows/analysis-of-endpoint-connections.yml +++ b/.github/workflows/analysis-of-endpoint-connections.yml @@ -20,7 +20,7 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK 21 + - name: Set up JDK uses: actions/setup-java@v4 with: java-version: '${{ env.java }}' @@ -59,7 +59,7 @@ jobs: with: fetch-depth: 0 - - name: Set up JDK 21 + - name: Set up JDK uses: actions/setup-java@v4 with: distribution: 'temurin' diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java index 250966bfd432..a1e25b1d9ec7 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java @@ -15,9 +15,9 @@ public class EndpointAnalyzer { - private static String EndpointAnalysisResultPath = "endpointAnalysisResult.json"; + private static String ENDPOINT_ANALYSIS_RESULT_PATH = "endpointAnalysisResult.json"; - private static final Logger logger = LoggerFactory.getLogger(EndpointAnalyzer.class); + private static final Logger log = LoggerFactory.getLogger(EndpointAnalyzer.class); public static void main(String[] args) { analyzeEndpoints(); @@ -37,11 +37,11 @@ private static void analyzeEndpoints() { try { List endpointClasses = mapper.readValue(new File(EndpointParser.ENDPOINT_PARSING_RESULT_PATH), - new TypeReference>() { - }); + new TypeReference>() { + }); List restCallFiles = mapper.readValue(new File(EndpointParser.REST_CALL_PARSING_RESULT_PATH), - new TypeReference>() { - }); + new TypeReference>() { + }); List endpointsAndMatchingRestCalls = new ArrayList<>(); List unusedEndpoints = new ArrayList<>(); @@ -60,7 +60,9 @@ private static void analyzeEndpoints() { for (EndpointInformation endpoint : endpointClass.endpoints()) { String endpointURI = endpoint.buildComparableEndpointUri(); - List matchingRestCalls = restCallMap.getOrDefault(endpointURI, new ArrayList<>()); + List restCallsWithMatchingURI = restCallMap.getOrDefault(endpointURI, new ArrayList<>()); + List matchingRestCalls = restCallsWithMatchingURI.stream() + .filter(restCall -> restCall.method().toLowerCase().equals(endpoint.getHttpMethod().toLowerCase())).toList(); // Check for wildcard endpoints if no exact match is found checkForWildcardEndpoints(endpoint, matchingRestCalls, endpointURI, restCallMap); @@ -75,10 +77,10 @@ private static void analyzeEndpoints() { } EndpointAnalysis endpointAnalysis = new EndpointAnalysis(endpointsAndMatchingRestCalls, unusedEndpoints); - mapper.writeValue(new File(EndpointAnalysisResultPath), endpointAnalysis); + mapper.writeValue(new File(ENDPOINT_ANALYSIS_RESULT_PATH), endpointAnalysis); } catch (IOException e) { - logger.error("Failed to analyze endpoints", e); + log.error("Failed to analyze endpoints", e); } } @@ -96,11 +98,11 @@ private static void analyzeEndpoints() { * @param restCallMap The map of rest call URIs to their corresponding information. */ private static void checkForWildcardEndpoints(EndpointInformation endpoint, List matchingRestCalls, String endpointURI, - Map> restCallMap) { + Map> restCallMap) { if (matchingRestCalls.isEmpty() && endpointURI.endsWith("*")) { for (String uri : restCallMap.keySet()) { if (uri.startsWith(endpoint.buildComparableEndpointUri().substring(0, endpoint.buildComparableEndpointUri().length() - 1)) - && endpoint.getHttpMethod().toLowerCase().equals(restCallMap.get(uri).get(0).method().toLowerCase())) { + && endpoint.getHttpMethod().toLowerCase().equals(restCallMap.get(uri).get(0).method().toLowerCase())) { matchingRestCalls.addAll(restCallMap.get(uri)); } } @@ -119,26 +121,26 @@ private static void printEndpointAnalysisResult() { ObjectMapper mapper = new ObjectMapper(); EndpointAnalysis endpointsAndMatchingRestCalls = null; try { - endpointsAndMatchingRestCalls = mapper.readValue(new File(EndpointAnalysisResultPath), new TypeReference() { + endpointsAndMatchingRestCalls = mapper.readValue(new File(ENDPOINT_ANALYSIS_RESULT_PATH), new TypeReference() { }); } catch (IOException e) { - logger.error("Failed to deserialize endpoint analysis result", e); + log.error("Failed to deserialize endpoint analysis result", e); return; } endpointsAndMatchingRestCalls.unusedEndpoints().stream().forEach(endpoint -> { - logger.info("============================================="); - logger.info("Endpoint URI: {}", endpoint.buildCompleteEndpointURI()); - logger.info("HTTP method: {}", endpoint.httpMethodAnnotation()); - logger.info("File path: {}", endpoint.className()); - logger.info("Line: {}", endpoint.line()); - logger.info("============================================="); - logger.info("No matching REST call found for endpoint: {}", endpoint.buildCompleteEndpointURI()); - logger.info("---------------------------------------------"); - logger.info(""); + log.info("============================================="); + log.info("Endpoint URI: {}", endpoint.buildCompleteEndpointURI()); + log.info("HTTP method: {}", endpoint.httpMethodAnnotation()); + log.info("File path: {}", endpoint.className()); + log.info("Line: {}", endpoint.line()); + log.info("============================================="); + log.info("No matching REST call found for endpoint: {}", endpoint.buildCompleteEndpointURI()); + log.info("---------------------------------------------"); + log.info(""); }); - logger.info("Number of endpoints without matching REST calls: {}", endpointsAndMatchingRestCalls.unusedEndpoints().size()); + log.info("Number of endpoints without matching REST calls: {}", endpointsAndMatchingRestCalls.unusedEndpoints().size()); } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java index 19b32dc8881b..2bdc8f17927a 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; -public record EndpointInformation(String requestMapping, String endpoint, String httpMethodAnnotation, String URI, String className, int line, List otherAnnotations) { +public record EndpointInformation(String requestMapping, String endpoint, String httpMethodAnnotation, String uri, String className, int line, List otherAnnotations) { public String buildCompleteEndpointURI() { StringBuilder result = new StringBuilder(); @@ -13,7 +13,7 @@ public String buildCompleteEndpointURI() { result.append(this.requestMapping.replace("\"", "")); } // Remove quotes from the URI as they are used to define the String in the source code but are not part of the URI - result.append(this.URI.replace("\"", "")); + result.append(this.uri.replace("\"", "")); return result.toString(); } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java index b0ab2cdb1f0b..29082e2d8320 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java @@ -36,7 +36,7 @@ public class EndpointParser { static final String REST_CALL_PARSING_RESULT_PATH = "restCalls.json"; - private static final Logger logger = LoggerFactory.getLogger(EndpointParser.class); + private static final Logger log = LoggerFactory.getLogger(EndpointParser.class); public static void main(String[] args) { final Path absoluteDirectoryPath = Path.of("../../src/main/java").toAbsolutePath().normalize(); @@ -48,7 +48,7 @@ public static void main(String[] args) { filesToParse = paths.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".java")).map(Path::toString).toArray(String[]::new); } catch (IOException e) { - logger.error("Error reading files from directory: {}", absoluteDirectoryPath, e); + log.error("Error reading files from directory: {}", absoluteDirectoryPath, e); } parseServerEndpoints(filesToParse); @@ -66,7 +66,7 @@ public static void main(String[] args) { private static void parseServerEndpoints(String[] filePaths) { List endpointClasses = new ArrayList<>(); final Set httpMethodClasses = Set.of(GetMapping.class.getSimpleName(), PostMapping.class.getSimpleName(), PutMapping.class.getSimpleName(), - DeleteMapping.class.getSimpleName(), PatchMapping.class.getSimpleName(), RequestMapping.class.getSimpleName()); + DeleteMapping.class.getSimpleName(), PatchMapping.class.getSimpleName(), RequestMapping.class.getSimpleName()); List filesFailedToParse = new ArrayList<>(); for (String filePath : filePaths) { @@ -111,11 +111,11 @@ private static void parseServerEndpoints(String[] filePaths) { */ private static List extractAnnotationPathValues(ClassOrInterfaceDeclaration javaClass, Set httpMethodClasses, String classRequestMappingString) { return javaClass.getMethods().stream() - .flatMap(method -> method.getAnnotations().stream().filter(annotation -> httpMethodClasses.contains(annotation.getNameAsString())) - .flatMap(annotation -> extractPathsFromAnnotation(annotation).stream() - .map(path -> new EndpointInformation(classRequestMappingString, method.getNameAsString(), annotation.getNameAsString(), path, - javaClass.getNameAsString(), method.getBegin().get().line, method.getAnnotations().stream().map(AnnotationExpr::toString).toList())))) - .toList(); + .flatMap(method -> method.getAnnotations().stream().filter(annotation -> httpMethodClasses.contains(annotation.getNameAsString())) + .flatMap(annotation -> extractPathsFromAnnotation(annotation).stream() + .map(path -> new EndpointInformation(classRequestMappingString, method.getNameAsString(), annotation.getNameAsString(), path, + javaClass.getNameAsString(), method.getBegin().get().line, method.getAnnotations().stream().map(AnnotationExpr::toString).toList())))) + .toList(); } /** @@ -158,23 +158,23 @@ else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { */ private static String extractClassRequestMapping(ClassOrInterfaceDeclaration javaClass, Set httpMethodClasses) { boolean hasEndpoint = javaClass.getMethods().stream().flatMap(method -> method.getAnnotations().stream()) - .anyMatch(annotation -> httpMethodClasses.contains(annotation.getNameAsString())); + .anyMatch(annotation -> httpMethodClasses.contains(annotation.getNameAsString())); if (!hasEndpoint) { return ""; } String classRequestMapping = javaClass.getAnnotations().stream().filter(annotation -> annotation.getNameAsString().equals(RequestMapping.class.getSimpleName())).findFirst() - .map(annotation -> { - if (annotation instanceof SingleMemberAnnotationExpr singleMemberAnnotationExpr) { - return singleMemberAnnotationExpr.getMemberValue().toString(); - } - else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { - return normalAnnotationExpr.getPairs().stream().filter(pair -> "path".equals(pair.getNameAsString())).map(pair -> pair.getValue().toString()).findFirst() - .orElse(""); - } - return ""; - }).orElse(""); + .map(annotation -> { + if (annotation instanceof SingleMemberAnnotationExpr singleMemberAnnotationExpr) { + return singleMemberAnnotationExpr.getMemberValue().toString(); + } + else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { + return normalAnnotationExpr.getPairs().stream().filter(pair -> "path".equals(pair.getNameAsString())).map(pair -> pair.getValue().toString()).findFirst() + .orElse(""); + } + return ""; + }).orElse(""); return classRequestMapping; } @@ -190,9 +190,9 @@ else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { */ private static void printFilesFailedToParse(List filesFailedToParse) { if (!filesFailedToParse.isEmpty()) { - logger.warn("Files failed to parse:", filesFailedToParse); + log.warn("Files failed to parse:", filesFailedToParse); for (String file : filesFailedToParse) { - logger.warn(file); + log.warn(file); } } } @@ -211,7 +211,7 @@ private static void writeEndpointsToFile(List endpoint new ObjectMapper().writeValue(new File(ENDPOINT_PARSING_RESULT_PATH), endpointClasses); } catch (IOException e) { - logger.error("Failed to write endpoint information to file", e); + log.error("Failed to write endpoint information to file", e); } } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java index aac71d6573bf..cb0d781d521a 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java @@ -17,7 +17,7 @@ public class RestCallAnalyzer { private static final String REST_CALL_ANALYSIS_RESULT_PATH = "restCallAnalysisResult.json"; - private static final Logger logger = LoggerFactory.getLogger(RestCallAnalyzer.class); + private static final Logger log = LoggerFactory.getLogger(RestCallAnalyzer.class); public static void main(String[] args) { analyzeRestCalls(); @@ -37,8 +37,8 @@ private static void analyzeRestCalls() { try { List endpointClasses = mapper.readValue(new File(EndpointParser.ENDPOINT_PARSING_RESULT_PATH), - new TypeReference>() { - }); + new TypeReference>() { + }); List restCalls = mapper.readValue(new File(EndpointParser.REST_CALL_PARSING_RESULT_PATH), new TypeReference>() { }); @@ -58,16 +58,19 @@ private static void analyzeRestCalls() { for (RestCallFileInformation restCallFile : restCalls) { for (RestCallInformation restCall : restCallFile.restCalls()) { String restCallURI = restCall.buildComparableRestCallUri(); - List matchingEndpoints = endpointMap.getOrDefault(restCallURI, new ArrayList<>()); + List endpointsWithMatchingUri = endpointMap.getOrDefault(restCallURI, new ArrayList<>()); - checkForWildcardMatches(restCall, matchingEndpoints, restCallURI, endpointMap); + checkForWildcardMatches(restCall, endpointsWithMatchingUri, restCallURI, endpointMap); - if (matchingEndpoints.isEmpty()) { + List endpointsWithMatchingHttpMethod = endpointsWithMatchingUri.stream() + .filter(endpoint -> endpoint.getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())).toList(); + + if (endpointsWithMatchingHttpMethod.isEmpty()) { restCallsWithoutMatchingEndpoint.add(restCall); } else { - for (EndpointInformation endpoint : matchingEndpoints) { - restCallsWithMatchingEndpoint.add(new RestCallWithMatchingEndpoint(endpoint, restCall, restCall.fileName())); + for (EndpointInformation endpoint : endpointsWithMatchingHttpMethod) { + restCallsWithMatchingEndpoint.add(new RestCallWithMatchingEndpoint(endpoint, restCall, restCall.filePath())); } } } @@ -77,7 +80,7 @@ private static void analyzeRestCalls() { mapper.writeValue(new File(REST_CALL_ANALYSIS_RESULT_PATH), restCallAnalysis); } catch (IOException e) { - logger.error("Failed to analyze REST calls", e); + log.error("Failed to analyze REST calls", e); } } @@ -95,11 +98,11 @@ private static void analyzeRestCalls() { * @param endpointMap The map of endpoint URIs to their corresponding information. */ private static void checkForWildcardMatches(RestCallInformation restCall, List matchingEndpoints, String restCallURI, - Map> endpointMap) { + Map> endpointMap) { if (matchingEndpoints.isEmpty() && restCallURI.endsWith("*")) { for (String uri : endpointMap.keySet()) { if (uri.startsWith(restCallURI.substring(0, restCallURI.length() - 1)) - && endpointMap.get(uri).get(0).getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())) { + && endpointMap.get(uri).get(0).getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())) { matchingEndpoints.addAll(endpointMap.get(uri)); } } @@ -124,21 +127,22 @@ private static void printRestCallAnalysisResult() { }); } catch (IOException e) { - logger.error("Failed to deserialize rest call analysis results", e); + log.error("Failed to deserialize rest call analysis results", e); + return; } restCallsAndMatchingEndpoints.restCallsWithoutMatchingEndpoints().stream().forEach(endpoint -> { - logger.info("============================================="); - logger.info("REST call URI: {}", endpoint.buildCompleteRestCallURI()); - logger.info("HTTP method: {}", endpoint.method()); - logger.info("File path: {}", endpoint.fileName()); - logger.info("Line: {}", endpoint.line()); - logger.info("============================================="); - logger.info("No matching endpoint found for REST call: {}", endpoint.buildCompleteRestCallURI()); - logger.info("---------------------------------------------"); - logger.info(""); + log.info("============================================="); + log.info("REST call URI: {}", endpoint.buildCompleteRestCallURI()); + log.info("HTTP method: {}", endpoint.method()); + log.info("File path: {}", endpoint.filePath()); + log.info("Line: {}", endpoint.line()); + log.info("============================================="); + log.info("No matching endpoint found for REST call: {}", endpoint.buildCompleteRestCallURI()); + log.info("---------------------------------------------"); + log.info(""); }); - logger.info("Number of REST calls without matching endpoints: {}", restCallsAndMatchingEndpoints.restCallsWithoutMatchingEndpoints().size()); + log.info("Number of REST calls without matching endpoints: {}", restCallsAndMatchingEndpoints.restCallsWithoutMatchingEndpoints().size()); } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java index 847ec03b1561..2582f57443cc 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallFileInformation.java @@ -1,4 +1,6 @@ package de.tum.cit.endpointanalysis; -public record RestCallFileInformation(String fileName, RestCallInformation[] restCalls) { +import java.util.List; + +public record RestCallFileInformation(String filePath, List restCalls) { } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java index fb1e44f92f2a..b7ba39e66f0d 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java @@ -1,17 +1,19 @@ package de.tum.cit.endpointanalysis; -public record RestCallInformation(String method, String url, int line, String fileName) { +public record RestCallInformation(String method, String url, String filePath, int line) { public String buildCompleteRestCallURI() { return this.url.replace("`", ""); } - public String buildComparableRestCallUri() { // Replace arguments with placeholder String result = this.buildCompleteRestCallURI().replaceAll("\\$\\{.*?\\}", ":param:"); // Remove query parameters result = result.split("\\?")[0]; + if (result.startsWith("/")) { + result = result.substring(1); + } return result; } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts index 18b54a5f0ac4..67bc380e89d7 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts @@ -5,7 +5,7 @@ interface RestCall { method: string; url: string; line: number; - fileName: string; + filePath: string; } enum ParsingResultType { @@ -38,17 +38,17 @@ class ParsingResult { } export class Postprocessor { - static filesWithRestCalls: { fileName: string, restCalls: RestCall[] }[] = []; + static filesWithRestCalls: { filePath: string, restCalls: RestCall[] }[] = []; private readonly restCalls: RestCall[] = []; - private readonly fileName: string; + private readonly filePath: string; private readonly ast: TSESTree.Program; /** - * @param fileName - The name of the file being processed. + * @param filePath - The name of the file being processed. * @param ast - The abstract syntax tree (AST) of the processed file. */ - constructor(fileName: string, ast: TSESTree.Program) { - this.fileName = fileName; + constructor(filePath: string, ast: TSESTree.Program) { + this.filePath = filePath; this.ast = ast; } @@ -61,7 +61,7 @@ export class Postprocessor { } }); if (this.restCalls.length > 0) { - Postprocessor.filesWithRestCalls.push( {fileName: this.fileName, restCalls: this.restCalls} ); + Postprocessor.filesWithRestCalls.push( {filePath: this.filePath, restCalls: this.restCalls} ); } } @@ -108,10 +108,10 @@ export class Postprocessor { urlEvaluationResult = this.evaluateUrl(node.arguments[0], methodDefinition, node, classBody); } - const fileName = this.fileName; + const filePath = this.filePath; if (urlEvaluationResult.resultType === ParsingResultType.EVALUATE_URL_SUCCESS) { for (let url of urlEvaluationResult.result) { - this.restCalls.push({ method, url, line, fileName }); + this.restCalls.push({ method, url, line, filePath: filePath }); } } } @@ -619,7 +619,7 @@ export class Postprocessor { for (let i = 0; i < superConstructorCallArguments.arguments.length; i++) { let constructorArgument = constructorArguments[i]; if (superConstructorCallArguments.arguments[i] !== '' && constructorArgument.type === 'TSParameterProperty' - && constructorArgument.parameter.type === 'Identifier' && constructorArgument.parameter.name === memberExprKey) { + && constructorArgument.parameter.type === 'Identifier' && constructorArgument.parameter.name === memberExprKey) { memberExpressionResult.push(superConstructorCallArguments.arguments[i]); resultType = ParsingResultType.EVALUATE_MEMBER_EXPRESSION_SUCCESS; } From 8bf638f6afc6e19002ca76851ac9d6ac5fcb01a0 Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Tue, 20 Aug 2024 17:56:37 +0200 Subject: [PATCH 2/7] Also, Make action only run on PRs --- .github/workflows/analysis-of-endpoint-connections.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/analysis-of-endpoint-connections.yml b/.github/workflows/analysis-of-endpoint-connections.yml index 73d5109f56a9..f5f2dd4edb71 100644 --- a/.github/workflows/analysis-of-endpoint-connections.yml +++ b/.github/workflows/analysis-of-endpoint-connections.yml @@ -2,7 +2,13 @@ name: Analysis of Endpoint Connections on: workflow_dispatch: - push: + pull_request: + types: + - opened + - synchronize + paths: + - 'src/main/java/**' + - 'src/main/webapp/**' # Keep in sync with build.yml and test.yml and codeql-analysis.yml env: From 23fa5c5bb4739848c9aceac9465be4642c476ee2 Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:00:11 +0200 Subject: [PATCH 3/7] spotless --- .../endpointanalysis/EndpointAnalyzer.java | 14 ++++---- .../cit/endpointanalysis/EndpointParser.java | 34 +++++++++---------- .../endpointanalysis/RestCallAnalyzer.java | 10 +++--- .../endpointanalysis/RestCallInformation.java | 1 + 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java index a1e25b1d9ec7..9007ccba9126 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java @@ -37,11 +37,11 @@ private static void analyzeEndpoints() { try { List endpointClasses = mapper.readValue(new File(EndpointParser.ENDPOINT_PARSING_RESULT_PATH), - new TypeReference>() { - }); + new TypeReference>() { + }); List restCallFiles = mapper.readValue(new File(EndpointParser.REST_CALL_PARSING_RESULT_PATH), - new TypeReference>() { - }); + new TypeReference>() { + }); List endpointsAndMatchingRestCalls = new ArrayList<>(); List unusedEndpoints = new ArrayList<>(); @@ -62,7 +62,7 @@ private static void analyzeEndpoints() { String endpointURI = endpoint.buildComparableEndpointUri(); List restCallsWithMatchingURI = restCallMap.getOrDefault(endpointURI, new ArrayList<>()); List matchingRestCalls = restCallsWithMatchingURI.stream() - .filter(restCall -> restCall.method().toLowerCase().equals(endpoint.getHttpMethod().toLowerCase())).toList(); + .filter(restCall -> restCall.method().toLowerCase().equals(endpoint.getHttpMethod().toLowerCase())).toList(); // Check for wildcard endpoints if no exact match is found checkForWildcardEndpoints(endpoint, matchingRestCalls, endpointURI, restCallMap); @@ -98,11 +98,11 @@ private static void analyzeEndpoints() { * @param restCallMap The map of rest call URIs to their corresponding information. */ private static void checkForWildcardEndpoints(EndpointInformation endpoint, List matchingRestCalls, String endpointURI, - Map> restCallMap) { + Map> restCallMap) { if (matchingRestCalls.isEmpty() && endpointURI.endsWith("*")) { for (String uri : restCallMap.keySet()) { if (uri.startsWith(endpoint.buildComparableEndpointUri().substring(0, endpoint.buildComparableEndpointUri().length() - 1)) - && endpoint.getHttpMethod().toLowerCase().equals(restCallMap.get(uri).get(0).method().toLowerCase())) { + && endpoint.getHttpMethod().toLowerCase().equals(restCallMap.get(uri).get(0).method().toLowerCase())) { matchingRestCalls.addAll(restCallMap.get(uri)); } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java index 29082e2d8320..9fb5a05b72da 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java @@ -66,7 +66,7 @@ public static void main(String[] args) { private static void parseServerEndpoints(String[] filePaths) { List endpointClasses = new ArrayList<>(); final Set httpMethodClasses = Set.of(GetMapping.class.getSimpleName(), PostMapping.class.getSimpleName(), PutMapping.class.getSimpleName(), - DeleteMapping.class.getSimpleName(), PatchMapping.class.getSimpleName(), RequestMapping.class.getSimpleName()); + DeleteMapping.class.getSimpleName(), PatchMapping.class.getSimpleName(), RequestMapping.class.getSimpleName()); List filesFailedToParse = new ArrayList<>(); for (String filePath : filePaths) { @@ -111,11 +111,11 @@ private static void parseServerEndpoints(String[] filePaths) { */ private static List extractAnnotationPathValues(ClassOrInterfaceDeclaration javaClass, Set httpMethodClasses, String classRequestMappingString) { return javaClass.getMethods().stream() - .flatMap(method -> method.getAnnotations().stream().filter(annotation -> httpMethodClasses.contains(annotation.getNameAsString())) - .flatMap(annotation -> extractPathsFromAnnotation(annotation).stream() - .map(path -> new EndpointInformation(classRequestMappingString, method.getNameAsString(), annotation.getNameAsString(), path, - javaClass.getNameAsString(), method.getBegin().get().line, method.getAnnotations().stream().map(AnnotationExpr::toString).toList())))) - .toList(); + .flatMap(method -> method.getAnnotations().stream().filter(annotation -> httpMethodClasses.contains(annotation.getNameAsString())) + .flatMap(annotation -> extractPathsFromAnnotation(annotation).stream() + .map(path -> new EndpointInformation(classRequestMappingString, method.getNameAsString(), annotation.getNameAsString(), path, + javaClass.getNameAsString(), method.getBegin().get().line, method.getAnnotations().stream().map(AnnotationExpr::toString).toList())))) + .toList(); } /** @@ -158,23 +158,23 @@ else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { */ private static String extractClassRequestMapping(ClassOrInterfaceDeclaration javaClass, Set httpMethodClasses) { boolean hasEndpoint = javaClass.getMethods().stream().flatMap(method -> method.getAnnotations().stream()) - .anyMatch(annotation -> httpMethodClasses.contains(annotation.getNameAsString())); + .anyMatch(annotation -> httpMethodClasses.contains(annotation.getNameAsString())); if (!hasEndpoint) { return ""; } String classRequestMapping = javaClass.getAnnotations().stream().filter(annotation -> annotation.getNameAsString().equals(RequestMapping.class.getSimpleName())).findFirst() - .map(annotation -> { - if (annotation instanceof SingleMemberAnnotationExpr singleMemberAnnotationExpr) { - return singleMemberAnnotationExpr.getMemberValue().toString(); - } - else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { - return normalAnnotationExpr.getPairs().stream().filter(pair -> "path".equals(pair.getNameAsString())).map(pair -> pair.getValue().toString()).findFirst() - .orElse(""); - } - return ""; - }).orElse(""); + .map(annotation -> { + if (annotation instanceof SingleMemberAnnotationExpr singleMemberAnnotationExpr) { + return singleMemberAnnotationExpr.getMemberValue().toString(); + } + else if (annotation instanceof NormalAnnotationExpr normalAnnotationExpr) { + return normalAnnotationExpr.getPairs().stream().filter(pair -> "path".equals(pair.getNameAsString())).map(pair -> pair.getValue().toString()).findFirst() + .orElse(""); + } + return ""; + }).orElse(""); return classRequestMapping; } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java index cb0d781d521a..19f8cbc6c56f 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallAnalyzer.java @@ -37,8 +37,8 @@ private static void analyzeRestCalls() { try { List endpointClasses = mapper.readValue(new File(EndpointParser.ENDPOINT_PARSING_RESULT_PATH), - new TypeReference>() { - }); + new TypeReference>() { + }); List restCalls = mapper.readValue(new File(EndpointParser.REST_CALL_PARSING_RESULT_PATH), new TypeReference>() { }); @@ -63,7 +63,7 @@ private static void analyzeRestCalls() { checkForWildcardMatches(restCall, endpointsWithMatchingUri, restCallURI, endpointMap); List endpointsWithMatchingHttpMethod = endpointsWithMatchingUri.stream() - .filter(endpoint -> endpoint.getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())).toList(); + .filter(endpoint -> endpoint.getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())).toList(); if (endpointsWithMatchingHttpMethod.isEmpty()) { restCallsWithoutMatchingEndpoint.add(restCall); @@ -98,11 +98,11 @@ private static void analyzeRestCalls() { * @param endpointMap The map of endpoint URIs to their corresponding information. */ private static void checkForWildcardMatches(RestCallInformation restCall, List matchingEndpoints, String restCallURI, - Map> endpointMap) { + Map> endpointMap) { if (matchingEndpoints.isEmpty() && restCallURI.endsWith("*")) { for (String uri : endpointMap.keySet()) { if (uri.startsWith(restCallURI.substring(0, restCallURI.length() - 1)) - && endpointMap.get(uri).get(0).getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())) { + && endpointMap.get(uri).get(0).getHttpMethod().toLowerCase().equals(restCall.method().toLowerCase())) { matchingEndpoints.addAll(endpointMap.get(uri)); } } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java index b7ba39e66f0d..3af710694639 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java @@ -5,6 +5,7 @@ public record RestCallInformation(String method, String url, String filePath, in public String buildCompleteRestCallURI() { return this.url.replace("`", ""); } + public String buildComparableRestCallUri() { // Replace arguments with placeholder String result = this.buildCompleteRestCallURI().replaceAll("\\$\\{.*?\\}", ":param:"); From 4b88ec5d5ac30936b1d59ff0aef5349e65984342 Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:17:17 +0200 Subject: [PATCH 4/7] fix mistake in EndpointAnalyzer.java --- .../java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java index 9007ccba9126..b096ba1201c6 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointAnalyzer.java @@ -61,11 +61,12 @@ private static void analyzeEndpoints() { String endpointURI = endpoint.buildComparableEndpointUri(); List restCallsWithMatchingURI = restCallMap.getOrDefault(endpointURI, new ArrayList<>()); - List matchingRestCalls = restCallsWithMatchingURI.stream() - .filter(restCall -> restCall.method().toLowerCase().equals(endpoint.getHttpMethod().toLowerCase())).toList(); // Check for wildcard endpoints if no exact match is found - checkForWildcardEndpoints(endpoint, matchingRestCalls, endpointURI, restCallMap); + checkForWildcardEndpoints(endpoint, restCallsWithMatchingURI, endpointURI, restCallMap); + + List matchingRestCalls = restCallsWithMatchingURI.stream() + .filter(restCall -> restCall.method().toLowerCase().equals(endpoint.getHttpMethod().toLowerCase())).toList(); if (matchingRestCalls.isEmpty()) { unusedEndpoints.add(endpoint); From 5c63f20f0b83ecdc469d111f2b662ada258bd1c6 Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:03:54 +0200 Subject: [PATCH 5/7] replace let with const and make methods package private where possible --- .../de/tum/cit/endpointanalysis/EndpointInformation.java | 4 ++-- .../de/tum/cit/endpointanalysis/RestCallInformation.java | 4 ++-- .../src/main/typeScript/Postprocessor.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java index 2bdc8f17927a..52f33cef81e2 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointInformation.java @@ -6,7 +6,7 @@ public record EndpointInformation(String requestMapping, String endpoint, String httpMethodAnnotation, String uri, String className, int line, List otherAnnotations) { - public String buildCompleteEndpointURI() { + String buildCompleteEndpointURI() { StringBuilder result = new StringBuilder(); if (this.requestMapping != null && !this.requestMapping.isEmpty()) { // Remove quotes from the requestMapping as they are used to define the String in the source code but are not part of the URI @@ -23,7 +23,7 @@ String buildComparableEndpointUri() { } @JsonIgnore - public String getHttpMethod() { + String getHttpMethod() { return switch (this.httpMethodAnnotation) { case "GetMapping" -> "get"; case "PostMapping" -> "post"; diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java index 3af710694639..e086ae880961 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java @@ -2,11 +2,11 @@ public record RestCallInformation(String method, String url, String filePath, int line) { - public String buildCompleteRestCallURI() { + String buildCompleteRestCallURI() { return this.url.replace("`", ""); } - public String buildComparableRestCallUri() { + String buildComparableRestCallUri() { // Replace arguments with placeholder String result = this.buildCompleteRestCallURI().replaceAll("\\$\\{.*?\\}", ":param:"); diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts index 67bc380e89d7..3d4026324e51 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts @@ -110,7 +110,7 @@ export class Postprocessor { const filePath = this.filePath; if (urlEvaluationResult.resultType === ParsingResultType.EVALUATE_URL_SUCCESS) { - for (let url of urlEvaluationResult.result) { + for (const url of urlEvaluationResult.result) { this.restCalls.push({ method, url, line, filePath: filePath }); } } @@ -541,7 +541,7 @@ export class Postprocessor { simpleTraverse(methodDefinition, { enter: (node) => { if (node.type === 'VariableDeclaration') { - for (let decl of node.declarations) { + for (const decl of node.declarations) { if (decl.id.type === 'Identifier' && decl.id.name === name && decl.init) { const tempResult = this.evaluateUrl(decl.init, methodDefinition, restCall, classBody); if (tempResult.resultType === ParsingResultType.EVALUATE_URL_SUCCESS) { @@ -587,7 +587,7 @@ export class Postprocessor { * @returns An array of AST nodes representing the parameters of the constructor. */ getConstructorArgumentsFromClassBody(classBody: TSESTree.ClassBody): TSESTree.Parameter[] { - for (let node of classBody.body) { + for (const node of classBody.body) { if (node.type === 'MethodDefinition' && node.key.type === 'Identifier' && node.key.name === 'constructor') { return node.value.params; } @@ -615,7 +615,7 @@ export class Postprocessor { const superClass = Preprocessor.PREPROCESSING_RESULTS.get(this.getClassNameFromClassBody(classBody)); if (superClass) { const constructorArguments = this.getConstructorArgumentsFromClassBody(classBody.body); - for (let superConstructorCallArguments of superClass.superConstructorCalls) { + for (const superConstructorCallArguments of superClass.superConstructorCalls) { for (let i = 0; i < superConstructorCallArguments.arguments.length; i++) { let constructorArgument = constructorArguments[i]; if (superConstructorCallArguments.arguments[i] !== '' && constructorArgument.type === 'TSParameterProperty' From d78ed06b908c067bb1a44fdb2e9d74e26f72e6d8 Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:11:51 +0200 Subject: [PATCH 6/7] Codereview Patrik --- .../de/tum/cit/endpointanalysis/RestCallInformation.java | 2 ++ .../src/main/typeScript/Postprocessor.ts | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java index e086ae880961..b6567d707a47 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/RestCallInformation.java @@ -12,6 +12,8 @@ String buildComparableRestCallUri() { // Remove query parameters result = result.split("\\?")[0]; + + // Some URIs in the artemis client start with a redundant `/`. To be able to compare them to the endpoint URIs, we remove it. if (result.startsWith("/")) { result = result.substring(1); } diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts index 3d4026324e51..e55c0b15611a 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts @@ -44,7 +44,7 @@ export class Postprocessor { private readonly ast: TSESTree.Program; /** - * @param filePath - The name of the file being processed. + * @param filePath - The path of the file being processed. * @param ast - The abstract syntax tree (AST) of the processed file. */ constructor(filePath: string, ast: TSESTree.Program) { @@ -61,7 +61,7 @@ export class Postprocessor { } }); if (this.restCalls.length > 0) { - Postprocessor.filesWithRestCalls.push( {filePath: this.filePath, restCalls: this.restCalls} ); + Postprocessor.filesWithRestCalls.push({ filePath: this.filePath, restCalls: this.restCalls }); } } @@ -111,7 +111,7 @@ export class Postprocessor { const filePath = this.filePath; if (urlEvaluationResult.resultType === ParsingResultType.EVALUATE_URL_SUCCESS) { for (const url of urlEvaluationResult.result) { - this.restCalls.push({ method, url, line, filePath: filePath }); + this.restCalls.push({ method, url, line, filePath }); } } } From 7a691221f39309c8e74e3c9fffbbd6c75cb7fc4f Mon Sep 17 00:00:00 2001 From: Jan-Thurner <107639007+Jan-Thurner@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:28:13 +0200 Subject: [PATCH 7/7] Codereview coderabbit --- .../src/main/typeScript/Postprocessor.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts index e55c0b15611a..46b95a821a18 100644 --- a/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts +++ b/supporting_scripts/analysis-of-endpoint-connections/src/main/typeScript/Postprocessor.ts @@ -175,6 +175,7 @@ export class Postprocessor { } /** + * Evaluates a template literal AST node to determine its URL value. * Evaluates a template literal AST node to determine its URL value. * * This method evaluates the provided template literal node by calling `evaluateTemplateLiteralExpression`. @@ -617,7 +618,7 @@ export class Postprocessor { const constructorArguments = this.getConstructorArgumentsFromClassBody(classBody.body); for (const superConstructorCallArguments of superClass.superConstructorCalls) { for (let i = 0; i < superConstructorCallArguments.arguments.length; i++) { - let constructorArgument = constructorArguments[i]; + const constructorArgument = constructorArguments[i]; if (superConstructorCallArguments.arguments[i] !== '' && constructorArgument.type === 'TSParameterProperty' && constructorArgument.parameter.type === 'Identifier' && constructorArgument.parameter.name === memberExprKey) { memberExpressionResult.push(superConstructorCallArguments.arguments[i]);