Skip to content

Commit 1459e18

Browse files
committed
Merge branch 'feat/git-analysis-as-web-service' into 'main'
Feat/git analysis as web service See merge request ExplorViz/code/code-agent!10
2 parents da8434a + f7e90e5 commit 1459e18

File tree

11 files changed

+874
-381
lines changed

11 files changed

+874
-381
lines changed

build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies {
1919
implementation 'io.quarkus:quarkus-container-image-jib'
2020
implementation 'io.quarkus:quarkus-vertx'
2121
implementation 'io.quarkus:quarkus-rest'
22+
implementation 'io.quarkus:quarkus-rest-jackson'
2223
implementation 'io.quarkus:quarkus-grpc'
2324

2425
implementation 'com.google.protobuf:protobuf-java-util'
@@ -72,7 +73,7 @@ tasks.named('checkstyleMain') {
7273
}
7374

7475
tasks.named('pmdMain') {
75-
dependsOn 'jandex'
76+
dependsOn 'jandex', 'compileQuarkusGeneratedSourcesJava'
7677
}
7778

7879
tasks.named('compileTestJava') {

compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ services:
44
networks:
55
- explorviz
66
environment:
7+
- ANALYSIS_RUN_MODE=batch
78
- ANALYSIS_REMOTE_URL=https://github.com/spring-projects/spring-petclinic
89
- ANALYSIS_SEND_TO_REMOTE=true
910
#- GRPC_HOST=host.docker.internal

src/main/java/net/explorviz/code/analysis/GitAnalysis.java

Lines changed: 44 additions & 351 deletions
Large diffs are not rendered by default.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package net.explorviz.code.analysis.api;
2+
3+
import java.util.Optional;
4+
import net.explorviz.code.analysis.service.AnalysisConfig;
5+
6+
/**
7+
* Request object for triggering a Git analysis.
8+
*/
9+
public class AnalysisRequest {
10+
11+
private String repoPath;
12+
private String repoRemoteUrl;
13+
private String remoteStoragePath;
14+
private String username;
15+
private String password;
16+
private String branch;
17+
private String sourceDirectory;
18+
private String restrictAnalysisToFolders;
19+
private boolean fetchRemoteData = true;
20+
private boolean sendToRemote = true;
21+
private boolean calculateMetrics = true;
22+
private String startCommit;
23+
private String endCommit;
24+
private String landscapeToken = "";
25+
private String applicationName = "";
26+
27+
public AnalysisRequest() {
28+
}
29+
30+
public String getRepoPath() {
31+
return repoPath;
32+
}
33+
34+
public void setRepoPath(final String repoPath) {
35+
this.repoPath = repoPath;
36+
}
37+
38+
public String getRepoRemoteUrl() {
39+
return repoRemoteUrl;
40+
}
41+
42+
public void setRepoRemoteUrl(final String repoRemoteUrl) {
43+
this.repoRemoteUrl = repoRemoteUrl;
44+
}
45+
46+
public String getSourceDirectory() {
47+
return sourceDirectory;
48+
}
49+
50+
public void setSourceDirectory(final String sourceDirectory) {
51+
this.sourceDirectory = sourceDirectory;
52+
}
53+
54+
public String getRestrictAnalysisToFolders() {
55+
return restrictAnalysisToFolders;
56+
}
57+
58+
public void setRestrictAnalysisToFolders(final String restrictAnalysisToFolders) {
59+
this.restrictAnalysisToFolders = restrictAnalysisToFolders;
60+
}
61+
62+
public boolean isFetchRemoteData() {
63+
return fetchRemoteData;
64+
}
65+
66+
public void setFetchRemoteData(final boolean fetchRemoteData) {
67+
this.fetchRemoteData = fetchRemoteData;
68+
}
69+
70+
public boolean isSendToRemote() {
71+
return sendToRemote;
72+
}
73+
74+
public void setSendToRemote(final boolean sendToRemote) {
75+
this.sendToRemote = sendToRemote;
76+
}
77+
78+
public boolean isCalculateMetrics() {
79+
return calculateMetrics;
80+
}
81+
82+
public void setCalculateMetrics(final boolean calculateMetrics) {
83+
this.calculateMetrics = calculateMetrics;
84+
}
85+
86+
public String getStartCommit() {
87+
return startCommit;
88+
}
89+
90+
public void setStartCommit(final String startCommit) {
91+
this.startCommit = startCommit;
92+
}
93+
94+
public String getEndCommit() {
95+
return endCommit;
96+
}
97+
98+
public void setEndCommit(final String endCommit) {
99+
this.endCommit = endCommit;
100+
}
101+
102+
public String getLandscapeToken() {
103+
return landscapeToken;
104+
}
105+
106+
public void setLandscapeToken(final String landscapeToken) {
107+
this.landscapeToken = landscapeToken;
108+
}
109+
110+
public String getApplicationName() {
111+
return applicationName;
112+
}
113+
114+
public void setApplicationName(final String applicationName) {
115+
this.applicationName = applicationName;
116+
}
117+
118+
/**
119+
* Converts this request to an AnalysisConfig.
120+
*
121+
* @return The analysis configuration
122+
*/
123+
public AnalysisConfig toConfig() {
124+
return new AnalysisConfig.Builder()
125+
.repoPath(Optional.ofNullable(repoPath))
126+
.repoRemoteUrl(Optional.ofNullable(repoRemoteUrl))
127+
.gitUsername(Optional.ofNullable(username))
128+
.gitPassword(Optional.ofNullable(password))
129+
.branch(Optional.ofNullable(branch))
130+
.sourceDirectory(Optional.ofNullable(sourceDirectory))
131+
.restrictAnalysisToFolders(Optional.ofNullable(restrictAnalysisToFolders))
132+
.calculateMetrics(calculateMetrics)
133+
.startCommit(Optional.ofNullable(startCommit))
134+
.endCommit(Optional.ofNullable(endCommit))
135+
.landscapeToken(landscapeToken != null ? landscapeToken : "")
136+
.applicationName(applicationName != null ? applicationName : "")
137+
.build();
138+
}
139+
}
140+
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package net.explorviz.code.analysis.api;
2+
3+
import jakarta.inject.Inject;
4+
import jakarta.ws.rs.Consumes;
5+
import jakarta.ws.rs.POST;
6+
import jakarta.ws.rs.Path;
7+
import jakarta.ws.rs.Produces;
8+
import jakarta.ws.rs.core.MediaType;
9+
import jakarta.ws.rs.core.Response;
10+
import java.io.IOException;
11+
import net.explorviz.code.analysis.exceptions.NotFoundException;
12+
import net.explorviz.code.analysis.exceptions.PropertyNotDefinedException;
13+
import net.explorviz.code.analysis.export.DataExporter;
14+
import net.explorviz.code.analysis.export.GrpcExporter;
15+
import net.explorviz.code.analysis.export.JsonExporter;
16+
import net.explorviz.code.analysis.service.AnalysisConfig;
17+
import net.explorviz.code.analysis.service.AnalysisService;
18+
import org.eclipse.jgit.api.errors.GitAPIException;
19+
import org.eclipse.microprofile.config.inject.ConfigProperty;
20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
22+
23+
/**
24+
* REST resource for triggering Git analysis operations.
25+
*/
26+
@Path("/api/analysis")
27+
public class AnalysisResource {
28+
29+
private static final Logger LOGGER = LoggerFactory.getLogger(AnalysisResource.class);
30+
31+
@ConfigProperty(name = "explorviz.gitanalysis.send-to-remote", defaultValue = "true")
32+
/* default */ boolean sendToRemoteProperty; // NOCS
33+
34+
@Inject
35+
/* default */ AnalysisService analysisService; // NOCS
36+
37+
@Inject
38+
/* default */ GrpcExporter grpcExporter; // NOCS
39+
40+
/**
41+
* Triggers a Git repository analysis with the provided configuration.
42+
*
43+
* @param request The analysis request containing configuration
44+
* @return Response indicating success or failure
45+
*/
46+
@POST
47+
@Path("/trigger")
48+
@Consumes(MediaType.APPLICATION_JSON)
49+
@Produces(MediaType.TEXT_PLAIN)
50+
public Response triggerAnalysis(final AnalysisRequest request) {
51+
if (request == null) {
52+
LOGGER.error("Request body is null or invalid");
53+
return Response.status(Response.Status.BAD_REQUEST)
54+
.entity("Request body is required")
55+
.build();
56+
}
57+
58+
try {
59+
final String repoInfo = request.getRepoPath() != null ? request.getRepoPath()
60+
: (request.getRepoRemoteUrl() != null ? request.getRepoRemoteUrl() : "unknown");
61+
LOGGER.info("Triggering analysis for repository: {}", repoInfo);
62+
63+
final AnalysisConfig config = request.toConfig();
64+
65+
final DataExporter exporter;
66+
if (sendToRemoteProperty) {
67+
exporter = grpcExporter;
68+
} else {
69+
exporter = new JsonExporter();
70+
}
71+
72+
analysisService.analyzeAndSendRepo(config, exporter);
73+
74+
LOGGER.info("Analysis completed successfully");
75+
return Response.ok("Analysis completed successfully").build();
76+
77+
} catch (IOException | GitAPIException | NotFoundException | PropertyNotDefinedException e) {
78+
LOGGER.error("Analysis failed: {}", e.getMessage(), e);
79+
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
80+
.entity("Analysis failed: " + e.getMessage())
81+
.build();
82+
}
83+
}
84+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package net.explorviz.code.analysis.api;
2+
3+
import jakarta.ws.rs.GET;
4+
import jakarta.ws.rs.Path;
5+
import jakarta.ws.rs.Produces;
6+
import jakarta.ws.rs.core.MediaType;
7+
8+
@Path("/api/health")
9+
public class HealthResource {
10+
@GET
11+
@Produces(MediaType.TEXT_PLAIN)
12+
public String health() {
13+
return "OK";
14+
}
15+
}
16+

src/main/java/net/explorviz/code/analysis/git/GitRepositoryHandler.java

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import net.explorviz.code.analysis.FileIO;
1919
import net.explorviz.code.analysis.exceptions.NotFoundException;
2020
import net.explorviz.code.analysis.exceptions.PropertyNotDefinedException;
21+
import net.explorviz.code.analysis.service.AnalysisConfig;
2122
import net.explorviz.code.analysis.types.FileDescriptor;
2223
import net.explorviz.code.analysis.types.RemoteRepositoryObject;
2324
import net.explorviz.code.analysis.types.Triple;
@@ -64,24 +65,9 @@ public class GitRepositoryHandler { // NOPMD
6465
private static final String JAVA_PATH_SUFFIX = ".java";
6566
private static String repositoryPath;
6667

67-
@ConfigProperty(name = "explorviz.gitanalysis.local.storage-path")
68-
/* default */ Optional<String> repoPathProperty; // NOCS
69-
70-
@ConfigProperty(name = "explorviz.gitanalysis.remote.url")
71-
/* default */ Optional<String> repoUrlProperty; // NOCS
72-
7368
@ConfigProperty(name = "explorviz.gitanalysis.remote.storage-path")
7469
/* default */ Optional<String> repoLocalStoragePathProperty; // NOCS
7570

76-
@ConfigProperty(name = "explorviz.gitanalysis.remote.username")
77-
/* default */ Optional<String> usernameProperty; // NOCS
78-
79-
@ConfigProperty(name = "explorviz.gitanalysis.remote.password")
80-
/* default */ Optional<String> passwordProperty; // NOCS
81-
82-
@ConfigProperty(name = "explorviz.gitanalysis.branch")
83-
/* default */ Optional<String> repositoryBranchProperty; // NOCS
84-
8571
private Git git;
8672

8773
public static String getCurrentRepositoryPath() {
@@ -313,29 +299,32 @@ public Repository getGitRepository(final String localRepositoryPath,
313299
* directory will be created. <br> The branch given in {@code explorviz.gitanalysis.branch} will
314300
* be used if present, otherwise the default (remote) or current (local) will be used.
315301
*
302+
* @param config the analysis config
316303
* @return an opened Git {@link Repository}
317304
* @throws PropertyNotDefinedException gets thrown if a needed property is not present
318305
* @throws GitAPIException gets thrown if the git api encounters an error
319306
* @throws IOException gets thrown if JGit cannot open the Git repository.
320307
*/
321-
public Repository getGitRepository()
308+
public Repository getGitRepository(AnalysisConfig config)
322309
throws PropertyNotDefinedException, GitAPIException, IOException {
323-
if (repoUrlProperty.isEmpty() && repoPathProperty.isEmpty()) {
324-
throw new PropertyNotDefinedException("explorviz.gitanalysis.local.folder.path");
310+
if (config.getRepoPath().isEmpty() && config.getRepoRemoteUrl().isEmpty()) {
311+
throw new PropertyNotDefinedException("explorviz.gitanalysis.remote.url");
325312
}
326313

327314
final CredentialsProvider credentialsProvider;
328-
if (usernameProperty.isEmpty() || passwordProperty.isEmpty()) {
315+
if (config.getGitUsername().isEmpty() || config.getGitPassword().isEmpty()) {
329316
credentialsProvider = CredentialsProvider.getDefault();
330317
} else {
331-
credentialsProvider = new UsernamePasswordCredentialsProvider(usernameProperty.get(),
332-
passwordProperty.get());
318+
credentialsProvider = new UsernamePasswordCredentialsProvider(
319+
config.getGitUsername().get(),
320+
config.getGitPassword().get()
321+
);
333322
}
334323

335-
return getGitRepository(this.repoPathProperty.orElse(""),
336-
new RemoteRepositoryObject(this.repoUrlProperty.orElse(""),
324+
return getGitRepository(config.getRepoPath().orElse(""),
325+
new RemoteRepositoryObject(config.getRepoRemoteUrl().orElse(""),
337326
repoLocalStoragePathProperty.orElse(""), credentialsProvider,
338-
repositoryBranchProperty.orElse("")));
327+
config.getBranch().orElse("")));
339328
}
340329

341330
/**

0 commit comments

Comments
 (0)