diff --git a/.github/workflows/analysis-of-endpoint-connections.yml b/.github/workflows/analysis-of-endpoint-connections.yml index 2116605bea0a..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: @@ -20,7 +26,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 +65,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..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 @@ -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(); @@ -60,10 +60,13 @@ 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<>()); // 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); @@ -75,10 +78,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); } } @@ -119,26 +122,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..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 @@ -4,16 +4,16 @@ 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() { + 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 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(); } @@ -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/EndpointParser.java b/supporting_scripts/analysis-of-endpoint-connections/src/main/java/de/tum/cit/endpointanalysis/EndpointParser.java index b0ab2cdb1f0b..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 @@ -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); @@ -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..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 @@ -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(); @@ -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); } } @@ -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..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 @@ -1,18 +1,23 @@ 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() { + String buildCompleteRestCallURI() { return this.url.replace("`", ""); } - public String buildComparableRestCallUri() { + String buildComparableRestCallUri() { // Replace arguments with placeholder String result = this.buildCompleteRestCallURI().replaceAll("\\$\\{.*?\\}", ":param:"); // 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); + } + 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..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 @@ -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 path 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 }); + for (const url of urlEvaluationResult.result) { + this.restCalls.push({ method, url, line, filePath }); } } } @@ -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`. @@ -541,7 +542,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 +588,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,11 +616,11 @@ 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]; + const 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; }