Skip to content

Commit

Permalink
feat: Refactor spoon/qodana mining to event based strategy (#829)
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinWitt committed Jul 13, 2023
1 parent e0a3031 commit b776ccc
Show file tree
Hide file tree
Showing 15 changed files with 295 additions and 336 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies {
// Use JUnit Jupiter for testing.
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3'
implementation 'com.google.guava:guava:32.1.1-jre'
implementation 'fr.inria.gforge.spoon:spoon-core:+'
implementation 'fr.inria.gforge.spoon:spoon-core:10.4.0'
implementation 'com.google.flogger:flogger:0.7.4'
implementation 'com.google.flogger:flogger-system-backend:0.7.4'
implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.4.8'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,8 @@ public ProjectGraphQLDto addProject(String projectUrl, String projectName) {
if (!projectRepository.existsByProjectUrl(projectUrl)) {
logger.atInfo().log("Project does not exist yet, creating it");
Project project = new Project(projectUrl, projectName);
periodicMiner.addToQueue(project);
return mapToDto(projectRepository.create(project));
} else {
periodicMiner.addToQueue(
projectRepository.findByProjectUrl(projectUrl).get(0));

logger.atInfo().log("Project %s already exists", projectName);
throw new RuntimeException("Project already exists");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package io.github.martinwitt.laughing_train.mining;

import com.google.common.flogger.FluentLogger;
import io.github.martinwitt.laughing_train.data.Project;
import io.github.martinwitt.laughing_train.data.result.CodeAnalyzerResult;
import io.github.martinwitt.laughing_train.domain.entity.AnalyzerStatus;
import io.github.martinwitt.laughing_train.domain.entity.GitHubCommit;
import io.github.martinwitt.laughing_train.mining.requests.MineNextProject;
import io.github.martinwitt.laughing_train.mining.requests.StoreResults;
import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository;
import io.quarkus.vertx.ConsumeEvent;
import io.vertx.core.eventbus.EventBus;
import java.util.ArrayList;
import java.util.List;

public class AnalyzerResultsPersistence {

public static final String SERVICE_NAME = "analyzerResultsPersistence";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
ProjectRepository projectRepository;
EventBus eventBus;

public AnalyzerResultsPersistence(ProjectRepository projectRepository, EventBus eventBus) {
this.projectRepository = projectRepository;
this.eventBus = eventBus;
}

@ConsumeEvent(value = SERVICE_NAME, blocking = true)
void persistResults(StoreResults storeResults) {
Project project = storeResults.project();
CodeAnalyzerResult result = storeResults.result();
addOrUpdateCommitHash(project, result, SERVICE_NAME);
if (result instanceof CodeAnalyzerResult.Failure failure) {
logger.atInfo().log("Analyzer %s failed for project %s", SERVICE_NAME, project.name());

} else if (result instanceof CodeAnalyzerResult.Success success) {
logger.atInfo().log("Analyzer %s succeeded for project %s", SERVICE_NAME, project.name());
}
eventBus.publish("miner", new MineNextProject(storeResults.analyzerName()));
}

private AnalyzerStatus getAnalyzerStatus(CodeAnalyzerResult spoonResult, String name) {
AnalyzerStatus analyzerStatus = null;
if (spoonResult instanceof CodeAnalyzerResult.Success success) {
analyzerStatus = AnalyzerStatus.success(name, success.results().size());
} else if (spoonResult instanceof CodeAnalyzerResult.Failure failure) {
analyzerStatus = AnalyzerStatus.failure(name, 0);
}
return analyzerStatus;
}

private void addOrUpdateCommitHash(Project project, CodeAnalyzerResult spoonResult, String analyzerName) {
String name = project.name();
String commitHash = project.commitHash();
List<io.github.martinwitt.laughing_train.domain.entity.Project> list =
projectRepository.findByProjectUrl(project.url());
AnalyzerStatus analyzerStatus = getAnalyzerStatus(spoonResult, analyzerName);
if (list.isEmpty()) {
io.github.martinwitt.laughing_train.domain.entity.Project newProject =
new io.github.martinwitt.laughing_train.domain.entity.Project(name, project.url());
newProject.addCommitHash(commitHash);
var commits = newProject.getCommits();
commits.stream()
.filter(v -> v.getCommitHash().equals(commitHash))
.findFirst()
.ifPresent(v -> {
v.addAnalyzerStatus(analyzerStatus);
});
projectRepository.create(newProject);
} else {
logger.atInfo().log("Updating commit hash for %s", name);
var oldProject = list.get(0);
oldProject.addCommitHash(commitHash);
var commits = oldProject.getCommits();
GitHubCommit gitHubCommit = new GitHubCommit(commitHash, new ArrayList<>());
commits.add(gitHubCommit);
gitHubCommit.addAnalyzerStatus(analyzerStatus);
oldProject.addCommitHash(gitHubCommit);
projectRepository.save(oldProject);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.github.martinwitt.laughing_train.mining;

public interface CodeMiner {

String getAnalyzerName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.github.martinwitt.laughing_train.mining;

import io.github.martinwitt.laughing_train.mining.requests.MineNextProject;
import io.quarkus.runtime.StartupEvent;
import io.vertx.mutiny.core.Vertx;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;

@ApplicationScoped
public class MiningStartup {

@Inject
Vertx vertx;

void startup(@Observes StartupEvent event) {
vertx.eventBus().send("miner", new MineNextProject(QodanaPeriodicMiner.ANALYZER_NAME));
vertx.eventBus().send("miner", new MineNextProject(SpoonPeriodicMiner.ANALYZER_NAME));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.github.martinwitt.laughing_train.mining;

import io.github.martinwitt.laughing_train.data.ProjectRequest;
import io.github.martinwitt.laughing_train.data.ProjectResult;
import io.github.martinwitt.laughing_train.domain.entity.Project;
import io.github.martinwitt.laughing_train.mining.requests.GetProject;
import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository;
import io.github.martinwitt.laughing_train.services.ProjectService;
import io.quarkus.vertx.ConsumeEvent;
import java.io.IOException;
import java.util.Random;

public class ProjectSupplier {

public static final String SERVICE_NAME = "projectSupplier";

final SearchProjectService searchProjectService;
final ProjectRepository projectRepository;
final ProjectService projectService;
private static final Random random = new Random();

ProjectSupplier(
SearchProjectService searchProjectService,
ProjectRepository projectRepository,
ProjectService projectService) {
this.searchProjectService = searchProjectService;
this.projectRepository = projectRepository;
this.projectService = projectService;
}

@ConsumeEvent(value = SERVICE_NAME, blocking = true)
ProjectResult supplyProject(GetProject getProject) {
try {
Project project = getRandomProject();
return checkoutProject(project);
} catch (IOException e) {
return new ProjectResult.Error(e.getMessage());
}
}

private ProjectResult checkoutProject(Project project) throws IOException {
return projectService.handleProjectRequest(new ProjectRequest.WithUrl(project.getProjectUrl()));
}

private Project getRandomProject() throws IOException {
if (random.nextBoolean()) {
return searchProjectService.searchProjectOnGithub();
} else {
return getKnownProject();
}
}

private Project getKnownProject() {
var list = projectRepository.getAll();
return list.get(random.nextInt(list.size()));
}
}
Loading

0 comments on commit b776ccc

Please sign in to comment.