From 05fdf9cef03ef43b357c0d2bfec0c252680356db Mon Sep 17 00:00:00 2001 From: Martin Wittlinger Date: Mon, 25 Dec 2023 20:38:32 +0100 Subject: [PATCH] feat: Remove unused legacy methods and refactor project (#1404) --- .../domain/entity/RemoteProject.java | 38 ++---- frontend/src/ProjectData.tsx | 14 ++- frontend/src/component/Sidebar.tsx | 65 ++++++++--- frontend/src/gql/graphql-types.ts | 88 ++++++++++---- frontend/src/index.tsx | 7 ++ frontend/src/pages/LiveViewPage.tsx | 76 ++++++++++++ .../api/graphql/dto/ProjectGraphQLDto.java | 10 -- .../graphql/endpoints/BadSmellGraphQL.java | 16 --- .../api/graphql/endpoints/ProjectGraphQL.java | 11 +- .../mining/AnalyzerResultsPersistence.java | 15 ++- .../mining/SearchProjectService.java | 13 +-- .../mining/SpoonPeriodicMiner.java | 6 +- .../mining/api/MiningGraphQL.java | 4 +- .../converter/ProjectDaoConverter.java | 5 - .../persistence/dao/ProjectDao.java | 14 --- .../impl/SqlProjectRepository.java | 21 ---- .../repository/ProjectRepository.java | 4 - .../summary/GetFixableBadSmells.java | 108 ------------------ .../summary/PeriodicRefactoringSummary.java | 97 ---------------- .../summary/PeriodicSummary.java | 2 + .../api/BadSmellGraphQLTest.java | 2 - .../AnalyzerResultsPersistenceTest.java | 30 +++++ .../impl/ProjectRepositoryImplTest.java | 20 +--- .../impl/SqlProjectRepositoryTest.java | 12 -- 24 files changed, 278 insertions(+), 400 deletions(-) create mode 100644 frontend/src/pages/LiveViewPage.tsx delete mode 100644 github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/GetFixableBadSmells.java delete mode 100644 github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicRefactoringSummary.java create mode 100644 github-bot/src/test/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistenceTest.java diff --git a/commons/src/main/java/io/github/martinwitt/laughing_train/domain/entity/RemoteProject.java b/commons/src/main/java/io/github/martinwitt/laughing_train/domain/entity/RemoteProject.java index 19ba6b233..45c8787e7 100644 --- a/commons/src/main/java/io/github/martinwitt/laughing_train/domain/entity/RemoteProject.java +++ b/commons/src/main/java/io/github/martinwitt/laughing_train/domain/entity/RemoteProject.java @@ -7,15 +7,13 @@ public class RemoteProject implements Serializable { - private String projectName; - private String projectUrl; - private List commitHashes; - private List commits; + private final String projectName; + private final String projectUrl; + private final List commits; public RemoteProject(String projectName, String projectUrl) { this.projectName = Objects.requireNonNull(projectName); this.projectUrl = Objects.requireNonNull(projectUrl); - commitHashes = new ArrayList<>(); commits = new ArrayList<>(); } @@ -33,19 +31,6 @@ public String getProjectUrl() { return projectUrl; } - /** - * @param commitHash the commitHash to add - */ - public void addCommitHash(String commitHash) { - if (!commitHashes.contains(commitHash)) { - commitHashes.add(commitHash); - } - } - - public boolean removeCommitHash(String commitHash) { - return commitHashes.remove(commitHash); - } - /** * @return the commits */ @@ -53,16 +38,8 @@ public List getCommits() { return commits; } - public boolean addCommitHash(GitHubCommit commit) { - addCommitHash(commit.getCommitHash()); - return commits.add(commit); - } - - /** - * @return the commitHashes - */ - public List getCommitHashes() { - return commitHashes; + public void addCommitHash(GitHubCommit commit) { + commits.add(commit); } /** @@ -72,7 +49,7 @@ public List getCommitHashes() { */ @Override public int hashCode() { - return Objects.hash(projectName, projectUrl, commitHashes); + return Objects.hash(projectName, projectUrl); } /** @@ -87,8 +64,7 @@ public boolean equals(Object obj) { } if (obj instanceof RemoteProject project) { return Objects.equals(projectName, project.projectName) - && Objects.equals(projectUrl, project.projectUrl) - && Objects.equals(commitHashes, project.commitHashes); + && Objects.equals(projectUrl, project.projectUrl); } return false; } diff --git a/frontend/src/ProjectData.tsx b/frontend/src/ProjectData.tsx index 3ae50a5fc..9bc8d89e6 100644 --- a/frontend/src/ProjectData.tsx +++ b/frontend/src/ProjectData.tsx @@ -7,7 +7,6 @@ export const fetchProjectQuery = gql` getProjects { projectName projectUrl - commitHashes commits { analyzerStatuses { analyzerName @@ -26,11 +25,24 @@ export const recentAnalyzerRuns = gql` recentAnalyzerRuns(size: 30) { analyzerName commitHash + numberOfIssues + projectName + projectUrl + status timestamp + } + } +`; +export const recentRuns = gql` + query recentRuns { + recentRuns(size: 30) { + analyzerName + commitHash numberOfIssues projectName projectUrl status + timestamp } } `; diff --git a/frontend/src/component/Sidebar.tsx b/frontend/src/component/Sidebar.tsx index 8803e3e52..23cf78123 100644 --- a/frontend/src/component/Sidebar.tsx +++ b/frontend/src/component/Sidebar.tsx @@ -1,45 +1,76 @@ import * as React from 'react'; +import { useNavigate } from 'react-router-dom'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; import Divider from '@mui/material/Divider'; import List from '@mui/material/List'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; +import ListItemButton from '@mui/material/ListItemButton'; import MenuIcon from '@mui/icons-material/Menu'; +import Collapse from '@mui/material/Collapse'; import { LoginButton } from './LoginButton'; -import { ListItemButton } from '@mui/material'; -import { useNavigate } from 'react-router-dom'; export type LinkType = { title: string; url: string }; -type Props = { links: LinkType[] }; +interface Props { + links: LinkType[]; +} + interface ListItemsProps { links: LinkType[]; + subLinks: LinkType[]; } function ListItems(props: ListItemsProps) { - const { links } = props; + const { links, subLinks } = props; const navigate = useNavigate(); + const [open, setOpen] = React.useState(true); return ( <> - {links.map(({ title, url }) => ( - - - - - navigate(url)} - /> - - ))} + {links.map((link) => + link.title === 'Home' ? ( +
+ setOpen(!open)}> + + + + + + + + {subLinks.map((subLink) => ( + navigate(subLink.url)} + > + + + ))} + + +
+ ) : ( + navigate(link.url)}> + + + + + + ) + )} ); } function Sidebar(props: Props) { + const subLinks: LinkType[] = [{ title: 'LiveFeed', url: '/livefeed' }]; + return ( - + ; /** Deletes a project from the database */ deleteProject?: Maybe>>; + mineProject: Scalars['Boolean']['output']; /** Refactoring the given bad smells */ refactor?: Maybe; }; @@ -85,6 +86,12 @@ export type MutationDeleteProjectArgs = { }; +/** Mutation root */ +export type MutationMineProjectArgs = { + url?: InputMaybe; +}; + + /** Mutation root */ export type MutationRefactorArgs = { badSmellIdentifier?: InputMaybe>>; @@ -102,7 +109,6 @@ export type Position = { export type Project = { __typename?: 'Project'; - commitHashes?: Maybe>>; commits?: Maybe>>; projectName?: Maybe; projectUrl?: Maybe; @@ -150,12 +156,8 @@ export type Query = { byRuleIDAndAnalyzerAndCommitHash?: Maybe>>; /** Gets all fixable bad smells rules */ fixableBadSmells?: Maybe>>; - /** Gets all fixable bad smells from the database by projectUrl */ - fixableByProjectName?: Maybe>>; /** Returns all github commits for a project from the database */ getGitHubCommitsForProject?: Maybe>>; - /** Gets all commit hashes for a project from the database */ - getHashesForProject?: Maybe>>; /** Gets project with given name from the database */ getProjectWithName?: Maybe; /** Gets all projects from the database */ @@ -164,6 +166,7 @@ export type Query = { login?: Maybe; /** Returns a sorted by date list of recent analyzer runs */ recentAnalyzerRuns?: Maybe>>; + recentRuns?: Maybe>>; }; @@ -206,24 +209,12 @@ export type QueryByRuleIdAndAnalyzerAndCommitHashArgs = { }; -/** Query root */ -export type QueryFixableByProjectNameArgs = { - projectUrl?: InputMaybe; -}; - - /** Query root */ export type QueryGetGitHubCommitsForProjectArgs = { projectName?: InputMaybe; }; -/** Query root */ -export type QueryGetHashesForProjectArgs = { - projectName?: InputMaybe; -}; - - /** Query root */ export type QueryGetProjectWithNameArgs = { projectName?: InputMaybe; @@ -241,6 +232,12 @@ export type QueryRecentAnalyzerRunsArgs = { size: Scalars['Int']['input']; }; + +/** Query root */ +export type QueryRecentRunsArgs = { + size: Scalars['Int']['input']; +}; + export type RuleId = { __typename?: 'RuleId'; id?: Maybe; @@ -261,12 +258,17 @@ export enum Status { export type GetProjectsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetProjectsQuery = { __typename?: 'Query', getProjects?: Array<{ __typename?: 'Project', projectName?: string | null, projectUrl?: string | null, commitHashes?: Array | null, commits?: Array<{ __typename?: 'GitHubCommit', commitHash?: string | null, analyzerStatuses?: Array<{ __typename?: 'AnalyzerStatus', analyzerName?: string | null, commitHash?: string | null, localDateTime?: string | null, numberOfIssues: number, status?: Status | null } | null> | null } | null> | null } | null> | null }; +export type GetProjectsQuery = { __typename?: 'Query', getProjects?: Array<{ __typename?: 'Project', projectName?: string | null, projectUrl?: string | null, commits?: Array<{ __typename?: 'GitHubCommit', commitHash?: string | null, analyzerStatuses?: Array<{ __typename?: 'AnalyzerStatus', analyzerName?: string | null, commitHash?: string | null, localDateTime?: string | null, numberOfIssues: number, status?: Status | null } | null> | null } | null> | null } | null> | null }; export type RecentAnalyzerRunsQueryVariables = Exact<{ [key: string]: never; }>; -export type RecentAnalyzerRunsQuery = { __typename?: 'Query', recentAnalyzerRuns?: Array<{ __typename?: 'AnalyzerRun', analyzerName?: string | null, commitHash?: string | null, timestamp?: string | null, numberOfIssues: number, projectName?: string | null, projectUrl?: string | null, status?: string | null } | null> | null }; +export type RecentAnalyzerRunsQuery = { __typename?: 'Query', recentAnalyzerRuns?: Array<{ __typename?: 'AnalyzerRun', analyzerName?: string | null, commitHash?: string | null, numberOfIssues: number, projectName?: string | null, projectUrl?: string | null, status?: string | null, timestamp?: string | null } | null> | null }; + +export type RecentRunsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type RecentRunsQuery = { __typename?: 'Query', recentRuns?: Array<{ __typename?: 'AnalyzerRun', analyzerName?: string | null, commitHash?: string | null, numberOfIssues: number, projectName?: string | null, projectUrl?: string | null, status?: string | null, timestamp?: string | null } | null> | null }; export type GetAvailableRefactoringsQueryVariables = Exact<{ [key: string]: never; }>; @@ -315,7 +317,6 @@ export const GetProjectsDocument = gql` getProjects { projectName projectUrl - commitHashes commits { analyzerStatuses { analyzerName @@ -366,11 +367,11 @@ export const RecentAnalyzerRunsDocument = gql` recentAnalyzerRuns(size: 30) { analyzerName commitHash - timestamp numberOfIssues projectName projectUrl status + timestamp } } `; @@ -406,6 +407,51 @@ export type RecentAnalyzerRunsQueryHookResult = ReturnType; export type RecentAnalyzerRunsSuspenseQueryHookResult = ReturnType; export type RecentAnalyzerRunsQueryResult = Apollo.QueryResult; +export const RecentRunsDocument = gql` + query recentRuns { + recentRuns(size: 30) { + analyzerName + commitHash + numberOfIssues + projectName + projectUrl + status + timestamp + } +} + `; + +/** + * __useRecentRunsQuery__ + * + * To run a query within a React component, call `useRecentRunsQuery` and pass it any options that fit your needs. + * When your component renders, `useRecentRunsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useRecentRunsQuery({ + * variables: { + * }, + * }); + */ +export function useRecentRunsQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(RecentRunsDocument, options); + } +export function useRecentRunsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(RecentRunsDocument, options); + } +export function useRecentRunsSuspenseQuery(baseOptions?: Apollo.SuspenseQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(RecentRunsDocument, options); + } +export type RecentRunsQueryHookResult = ReturnType; +export type RecentRunsLazyQueryHookResult = ReturnType; +export type RecentRunsSuspenseQueryHookResult = ReturnType; +export type RecentRunsQueryResult = Apollo.QueryResult; export const GetAvailableRefactoringsDocument = gql` query getAvailableRefactorings { availableRefactorings { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index d518336d3..db205d26b 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -19,6 +19,7 @@ import { RefactorView } from './pages/RefactorView'; import ResultView from './pages/ResultView'; import reportWebVitals from './reportWebVitals'; import { StatisticPage } from './pages/StatisticsPage'; +import { LiveViewPage } from './pages/LiveViewPage'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); @@ -33,6 +34,12 @@ const router = createBrowserRouter([ element: } />, errorElement: } />, }, + { + path: '/livefeed', + element: , + errorElement: } />, + }, + { path: '/mutation/addproject', element: , diff --git a/frontend/src/pages/LiveViewPage.tsx b/frontend/src/pages/LiveViewPage.tsx new file mode 100644 index 000000000..da869a7c0 --- /dev/null +++ b/frontend/src/pages/LiveViewPage.tsx @@ -0,0 +1,76 @@ +import PageLayout from './PageLayout'; +import { + Box, + Breadcrumbs, + LinearProgress, + Link, + Table, + TableCell, + TableHead, + Typography, +} from '@mui/material'; +import { useRecentRunsQuery } from '../gql/graphql-types'; +import { Error } from '@mui/icons-material'; +import React from 'react'; + +export function LiveViewPage() { + const { data, loading, error } = useRecentRunsQuery({}); + if (loading || error) { + return ( + <> + + + Home + + LiveView + + + + ); + } + if (!data?.recentRuns || data.recentRuns.length === 0) { + return ( + + + + Home + + LiveView + + + + + No Data found + + + + ); + } + return ( + + + + Home + + LiveView + + + + Project + Commit + Analyzer + Number of Issues + Status + Timestamp + +
+
+ ); +} diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/dto/ProjectGraphQLDto.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/dto/ProjectGraphQLDto.java index 7b1cd7a3d..22e7ceb5b 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/dto/ProjectGraphQLDto.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/dto/ProjectGraphQLDto.java @@ -10,7 +10,6 @@ public class ProjectGraphQLDto { private String projectName; private String projectUrl; - private List commitHashes; private List commits; @SuppressWarnings("NullAway") @@ -19,7 +18,6 @@ public ProjectGraphQLDto() {} public ProjectGraphQLDto(RemoteProject project) { this.projectName = project.getProjectName(); this.projectUrl = project.getProjectUrl(); - this.commitHashes = project.getCommitHashes(); this.commits = project.getCommits(); } @@ -39,14 +37,6 @@ public void setProjectUrl(String projectUrl) { this.projectUrl = projectUrl; } - public List getCommitHashes() { - return this.commitHashes; - } - - public void setCommitHashes(List commitHashes) { - this.commitHashes = commitHashes; - } - /** * @return the commits */ diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/BadSmellGraphQL.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/BadSmellGraphQL.java index dd89c16a2..6861967c0 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/BadSmellGraphQL.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/BadSmellGraphQL.java @@ -5,7 +5,6 @@ import io.github.martinwitt.laughing_train.persistence.BadSmell; import io.github.martinwitt.laughing_train.persistence.repository.BadSmellRepository; import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository; -import io.github.martinwitt.laughing_train.summary.GetFixableBadSmells; import io.github.martinwitt.spoon_analyzer.badsmells.SpoonRules; import jakarta.inject.Inject; import java.util.Arrays; @@ -20,8 +19,6 @@ public class BadSmellGraphQL { @Inject BadSmellRepository badSmellRepository; - @Inject GetFixableBadSmells getFixableBadSmells; - @Inject ProjectRepository projectRepository; @Query("byRuleID") @@ -73,19 +70,6 @@ public List getAllBadSmellsByIdentifier( return badSmellRepository.findByIdentifier(identifier).stream().map(this::mapToDto).toList(); } - @Query("fixableByProjectName") - @Description("Gets all fixable bad smells from the database by projectUrl") - public List getAllFixableBadSmellsByProjectUrl( - @Name("projectUrl") String projectUrl) { - var projects = projectRepository.findByProjectUrl(projectUrl); - if (projects.isEmpty()) { - return List.of(); - } - return getFixableBadSmells.getFixableBadSmells(projects.get(0)).stream() - .map(this::mapToDto) - .toList(); - } - @Query("fixableBadSmells") @Description("Gets all fixable bad smells rules") public List getAllFixableBadSmells() { diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/ProjectGraphQL.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/ProjectGraphQL.java index 9592bd8f1..26a416ed2 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/ProjectGraphQL.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/api/graphql/endpoints/ProjectGraphQL.java @@ -46,15 +46,6 @@ public ProjectGraphQLDto getProject(String projectName) { .orElseThrow(); } - @Query("getHashesForProject") - @Description("Gets all commit hashes for a project from the database") - public List getHashesForProject(String projectName) { - return projectRepository.findByProjectName(projectName).stream() - .findFirst() - .map(RemoteProject::getCommitHashes) - .orElseThrow(); - } - @Query("getGitHubCommitsForProject") @Description("Returns all github commits for a project from the database") public List getGitHubCommitsForProject(String projectName) { @@ -72,7 +63,7 @@ public ProjectGraphQLDto addProject(String projectUrl, String projectName) { if (!projectRepository.existsByProjectUrl(projectUrl)) { logger.atInfo().log("Project does not exist yet, creating it"); RemoteProject project = new RemoteProject(projectUrl, projectName); - return mapToDto(projectRepository.create(project)); + return mapToDto(projectRepository.save(project)); } else { logger.atInfo().log("Project %s already exists", projectName); throw new RuntimeException("Project already exists"); diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistence.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistence.java index 8b9269c2f..fee3947c2 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistence.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistence.java @@ -20,14 +20,14 @@ public class AnalyzerResultsPersistence extends AbstractVerticle { public static final String SERVICE_NAME = "analyzerResultsPersistence"; private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - ProjectRepository projectRepository; + private final ProjectRepository projectRepository; public AnalyzerResultsPersistence(ProjectRepository projectRepository) { this.projectRepository = projectRepository; } @Override - public void start() throws Exception { + public void start() { vertx.eventBus().consumer(SERVICE_NAME, v -> persistResults(v.body())); } @@ -75,7 +75,6 @@ private void addAnalyzerRun( String name, List list, String commitHash, AnalyzerStatus analyzerStatus) { logger.atInfo().log("Updating commit hash for %s", name); RemoteProject oldProject = list.getFirst(); - oldProject.addCommitHash(commitHash); List commits = oldProject.getCommits(); GitHubCommit gitHubCommit = new GitHubCommit(commitHash, new ArrayList<>()); commits.add(gitHubCommit); @@ -87,7 +86,6 @@ private void addAnalyzerRun( private void createNewProject( String name, GitProject gitProject, String commitHash, AnalyzerStatus analyzerStatus) { RemoteProject newProject = new RemoteProject(name, gitProject.url()); - newProject.addCommitHash(commitHash); List commits = newProject.getCommits(); var selectedCommit = commits.stream().filter(v -> v.getCommitHash().equals(commitHash)).findFirst(); @@ -96,7 +94,14 @@ private void createNewProject( logger.atInfo().log( "Adding new commit hash for %s with status %s for analyzer %s", name, analyzerStatus.getStatus(), analyzerStatus.getAnalyzerName()); + } else { + GitHubCommit gitHubCommit = new GitHubCommit(commitHash, new ArrayList<>()); + gitHubCommit.addAnalyzerStatus(analyzerStatus); + newProject.addCommitHash(gitHubCommit); + logger.atInfo().log( + "Adding new commit hash for %s with status %s for analyzer %s", + name, analyzerStatus.getStatus(), analyzerStatus.getAnalyzerName()); } - projectRepository.create(newProject); + projectRepository.save(newProject); } } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SearchProjectService.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SearchProjectService.java index c683bcd94..b94a74f8c 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SearchProjectService.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SearchProjectService.java @@ -7,17 +7,16 @@ import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository; import io.quarkus.logging.Log; import jakarta.enterprise.context.ApplicationScoped; +import java.io.IOException; +import java.util.List; +import java.util.Random; +import java.util.random.RandomGenerator; import org.checkerframework.checker.nullness.qual.Nullable; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.kohsuke.github.GHRepository; import org.kohsuke.github.GHRepositorySearchBuilder.Sort; import org.kohsuke.github.GitHub; -import java.io.IOException; -import java.util.List; -import java.util.Random; -import java.util.random.RandomGenerator; - /** * This service handles searches for random java projects on github. Use this service to get a * random project from github. See {@link #searchProjectOnGithub()}. @@ -65,7 +64,7 @@ public RemoteProject searchProjectOnGithub() throws IOException { private RemoteProject getProject(GHRepository ghRepo) { var list = projectRepository.findByProjectUrl(ghRepo.getHtmlUrl().toString()); if (list.isEmpty()) { - return projectRepository.create(toProject(ghRepo)); + return projectRepository.save(toProject(ghRepo)); } else { return list.get(0); } @@ -80,7 +79,7 @@ private RemoteProject getProject(GHRepository ghRepo) { private RemoteProject persistProject(RemoteProject project) { var list = projectRepository.findByProjectUrl(project.getProjectUrl()); if (list.isEmpty()) { - return projectRepository.create(project); + return projectRepository.save(project); } else { return project; } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SpoonPeriodicMiner.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SpoonPeriodicMiner.java index 2a0f409d3..2a1886fa8 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SpoonPeriodicMiner.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/SpoonPeriodicMiner.java @@ -7,6 +7,7 @@ import io.github.martinwitt.laughing_train.data.result.CodeAnalyzerResult; 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.github.martinwitt.laughing_train.services.SpoonAnalyzerService; import io.quarkus.arc.Unremovable; import io.vertx.core.AbstractVerticle; @@ -25,7 +26,10 @@ public class SpoonPeriodicMiner extends AbstractVerticle { private final ProjectSupplier projectSupplier; public SpoonPeriodicMiner( - Vertx vertx, SpoonAnalyzerService spoonAnalyzerService, ProjectSupplier projectSupplier) { + Vertx vertx, + SpoonAnalyzerService spoonAnalyzerService, + ProjectSupplier projectSupplier, + ProjectRepository projectRepository) { this.vertx = vertx; this.spoonAnalyzerService = spoonAnalyzerService; this.projectSupplier = projectSupplier; diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/api/MiningGraphQL.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/api/MiningGraphQL.java index 522b79580..2f1c5a320 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/api/MiningGraphQL.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/mining/api/MiningGraphQL.java @@ -47,8 +47,8 @@ private List fetchAnalyzerRuns(int limit) { @Authenticated @Mutation - public void mineProject(String url) { + public boolean mineProject(String url) { // TODO: Not Implemented - + return false; } } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/converter/ProjectDaoConverter.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/converter/ProjectDaoConverter.java index 8ddac64d8..7d9cd8a32 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/converter/ProjectDaoConverter.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/converter/ProjectDaoConverter.java @@ -23,11 +23,6 @@ public ProjectDao convertToDao(RemoteProject entity) { .flatMap(List::stream) .toList(); dao.setCommits(list); - list.stream() - .reduce( - (first, second) -> first.localDateTime.isAfter(second.localDateTime) ? first : second) - .map(v -> v.localDateTime) - .ifPresent(dao::setLatestRun); return dao; } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/dao/ProjectDao.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/dao/ProjectDao.java index 2b19ed5c7..3d9fad233 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/dao/ProjectDao.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/dao/ProjectDao.java @@ -5,17 +5,14 @@ import jakarta.persistence.CascadeType; import jakarta.persistence.Entity; import jakarta.persistence.OneToMany; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Entity -@SuppressWarnings("NullAway") public class ProjectDao extends PanacheEntity { private String projectName; private String projectUrl; - private LocalDateTime latestRun; @OneToMany(cascade = CascadeType.ALL) private List commits = new ArrayList<>(); @@ -66,15 +63,4 @@ public List getCommits() { public void setCommits(List commits) { this.commits = commits; } - - public LocalDateTime getLatestRun() { - if (null == latestRun) { - latestRun = LocalDateTime.MIN; - } - return latestRun; - } - - public void setLatestRun(LocalDateTime localDateTime) { - latestRun = localDateTime; - } } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepository.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepository.java index fbdfd49c2..9b67b5361 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepository.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepository.java @@ -5,7 +5,6 @@ import io.github.martinwitt.laughing_train.persistence.dao.ProjectDao; import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository; import io.quarkus.hibernate.orm.panache.PanacheRepository; -import io.quarkus.panache.common.Sort; import jakarta.enterprise.context.ApplicationScoped; import jakarta.transaction.Transactional; import java.util.List; @@ -57,23 +56,11 @@ public long deleteByProjectUrl(String projectUrl) { return delete("projectUrl", projectUrl); } - @Override - public RemoteProject create(RemoteProject project) { - var list = findByProjectUrl(project.getProjectUrl()); - if (list.isEmpty()) { - persist(projectDaoConverter.convertToDao(project)); - return project; - } else { - return list.getFirst(); - } - } - @Override public RemoteProject save(RemoteProject project) { ProjectDao projectDao = projectDaoConverter.convertToDao(project); if (find("projectUrl", projectDao.getProjectUrl()).stream().findFirst().isEmpty()) { ProjectDao dao = projectDaoConverter.convertToDao(project); - // sqlAnalyzerRunRepository.persist(dao.getCommits()); persist(dao); } else { var dao = projectDaoConverter.convertToDao(project); @@ -81,16 +68,8 @@ public RemoteProject save(RemoteProject project) { databaseEntry.setProjectName(dao.getProjectName()); databaseEntry.setProjectUrl(dao.getProjectUrl()); databaseEntry.setCommits(dao.getCommits()); - // sqlAnalyzerRunRepository.persist(databaseEntry.getCommits()); persist(databaseEntry); } return project; } - - @Override - public List getRecent(int size) { - return findAll(Sort.by("latestRun").descending()).page(0, size).stream() - .map(projectDaoConverter::convertToEntity) - .toList(); - } } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/repository/ProjectRepository.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/repository/ProjectRepository.java index 61f00533e..80444db44 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/repository/ProjectRepository.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/persistence/repository/ProjectRepository.java @@ -7,8 +7,6 @@ public interface ProjectRepository { List getAll(); - List getRecent(int size); - List findByProjectName(String projectName); boolean existsByProjectName(String projectName); @@ -21,7 +19,5 @@ public interface ProjectRepository { long deleteByProjectUrl(String projectUrl); - RemoteProject create(RemoteProject project); - RemoteProject save(RemoteProject project); } diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/GetFixableBadSmells.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/GetFixableBadSmells.java deleted file mode 100644 index 26f53917c..000000000 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/GetFixableBadSmells.java +++ /dev/null @@ -1,108 +0,0 @@ -package io.github.martinwitt.laughing_train.summary; - -import io.github.martinwitt.laughing_train.domain.entity.RemoteProject; -import io.github.martinwitt.laughing_train.domain.value.RuleId; -import io.github.martinwitt.laughing_train.persistence.BadSmell; -import io.github.martinwitt.laughing_train.persistence.repository.BadSmellRepository; -import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository; -import jakarta.enterprise.context.ApplicationScoped; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import xyz.keksdose.spoon.code_solver.analyzer.qodana.QodanaRules; - -@ApplicationScoped -public class GetFixableBadSmells { - - ProjectRepository projectRepository; - - BadSmellRepository badSmellRepository; - - GetFixableBadSmells(ProjectRepository projectRepository, BadSmellRepository badSmellRepository) { - this.projectRepository = projectRepository; - this.badSmellRepository = badSmellRepository; - } - - /** - * Returns a map of all projects and their fixable bad smells. A bad smell is fixable if there is - * a refactoring available. - * - * @return Map of all projects and their fixable bad smells. - */ - public Map> getFixableBadSmells() { - List allProjects = getAllProjects(); - return allProjects.stream() - .filter(v -> !v.getCommitHashes().isEmpty()) - .collect( - Collectors.toMap( - project -> project, - project -> getFixableBadSmells(getNewestHash(project).orElse("")))); - } - - /** - * Returns a list of all fixable bad smells of a project. A bad smell is fixable if there is a - * refactoring available. - * - * @param project Project to get the fixable bad smells from. - * @return List of all fixable bad smells of a project. - */ - public List getFixableBadSmells(RemoteProject project) { - return getFixableBadSmells(project, getNewestHash(project).orElse("")); - } - - /** - * Returns a list of all fixable bad smells of a project. A bad smell is fixable if there is a - * refactoring available. - * - * @param project Project to get the fixable bad smells from. - * @return List of all fixable bad smells of a project. - */ - public List getFixableBadSmells(RemoteProject project, String commitHash) { - return getFixableBadSmells(commitHash); - } - - /** - * Returns a list of all projects. - * - * @return List of all projects. - */ - private List getAllProjects() { - return projectRepository.getAll(); - } - - /** - * Returns the newest commit hash of a project. - * - * @param project Project to get the newest commit hash from. - * @return Newest commit hash of the project. - */ - private Optional getNewestHash(RemoteProject project) { - if (project.getCommitHashes().isEmpty()) { - return Optional.empty(); - } - return Optional.of(project.getCommitHashes().get(project.getCommitHashes().size() - 1)); - } - - private List getFixableBadSmells(String commitHash) { - Set ruleIDs = getAllQodanaRules(); - - List allBadSmells = badSmellRepository.findByCommitHash(commitHash); - return allBadSmells.stream() - .filter(badSmell -> ruleIDs.contains(badSmell.ruleID().id())) - .collect(Collectors.toList()); - } - - /** - * Returns all Qodana rules. There are only fixable bad smells for Qodana rules included in this - * set. - * - * @return Set of all Qodana rules. - */ - private Set getAllQodanaRules() { - EnumSet rules = EnumSet.allOf(QodanaRules.class); - return rules.stream().map(QodanaRules::getRuleId).map(RuleId::id).collect(Collectors.toSet()); - } -} diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicRefactoringSummary.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicRefactoringSummary.java deleted file mode 100644 index 68508ec6f..000000000 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicRefactoringSummary.java +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.martinwitt.laughing_train.summary; - -import io.github.martinwitt.laughing_train.commons.GitHubConnector; -import io.github.martinwitt.laughing_train.commons.result.Result; -import io.github.martinwitt.laughing_train.domain.entity.RemoteProject; -import io.github.martinwitt.laughing_train.domain.value.RuleId; -import io.github.martinwitt.laughing_train.persistence.BadSmell; -import io.github.martinwitt.laughing_train.persistence.repository.BadSmellRepository; -import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository; -import io.quarkus.scheduler.Scheduled; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import org.kohsuke.github.GHIssue; -import org.kohsuke.github.GHIssueState; -import org.kohsuke.github.GitHub; - -public class PeriodicRefactoringSummary { - - private static final String LABEL_NAME = "laughing-train-refactoring-summary"; - - ProjectRepository projectRepository; - BadSmellRepository badSmellRepository; - GetFixableBadSmells getFixableBadSmells; - - PeriodicRefactoringSummary( - ProjectRepository projectRepository, - BadSmellRepository badSmellRepository, - GetFixableBadSmells getFixableBadSmells) { - this.projectRepository = projectRepository; - this.badSmellRepository = badSmellRepository; - this.getFixableBadSmells = getFixableBadSmells; - } - - @Scheduled(every = "2h", delay = 10) - void createSummary() throws IOException { - var issue = searchSummaryIssueOnGithub(); - issue.setBody(createSummaryBody()); - } - - private String createSummaryBody() { - - StringBuilder summary = new StringBuilder(); - summary.append("# Summary of all refactoring opportunities:\n"); - for (RemoteProject project : projectRepository.getAll()) { - List badSmells = getFixableBadSmells.getFixableBadSmells(project); - Map> badSmellByRuleId = - badSmells.stream().collect(Collectors.groupingBy(BadSmell::ruleID)); - if (badSmells.isEmpty()) { - continue; - } - summary.append("## Project: ").append(project.getProjectName()).append("\n"); - summary.append("| Rule | Occurrences |\n"); - summary.append("| --- | --- |\n"); - for (var entry : badSmellByRuleId.entrySet()) { - if (entry.getValue().isEmpty()) { - continue; - } - summary - .append("| ") - .append(entry.getKey().id()) - .append(" | ") - .append(entry.getValue().size()) - .append(" |\n"); - } - } - return summary.toString(); - } - - /** - * Search for the summary issue on github. The summary issue contains an overview over all - * refactoring opportunities. - * - * @return the summary issue on github never null - */ - private GHIssue searchSummaryIssueOnGithub() throws IOException { - Result githubConnectionResult = GitHubConnector.connectOAuth(); - if (githubConnectionResult.isError()) { - throw new IOException("Could not connect to github"); - } - var list = - githubConnectionResult - .get() - .getRepository("MartinWitt/laughing-train") - .queryIssues() - .pageSize(1) - .label(LABEL_NAME) - .state(GHIssueState.OPEN) - .list() - .toList(); - if (list.isEmpty()) { - throw new IllegalStateException("No summary issue found"); - } - return list.get(0); - } -} diff --git a/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicSummary.java b/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicSummary.java index 1fcdd0820..9957dba0f 100644 --- a/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicSummary.java +++ b/github-bot/src/main/java/io/github/martinwitt/laughing_train/summary/PeriodicSummary.java @@ -8,6 +8,7 @@ import io.github.martinwitt.laughing_train.github.GitHubState; import io.github.martinwitt.laughing_train.github.Issue; import io.github.martinwitt.laughing_train.github.PullRequest; +import io.quarkus.arc.Unremovable; import io.quarkus.scheduler.Scheduled; import java.io.IOException; import java.util.Collection; @@ -27,6 +28,7 @@ * updated. The summary issue contains a list of all open issues and pull requests with their * status. */ +@Unremovable public class PeriodicSummary { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); diff --git a/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java b/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java index 18bb192ea..205c9e3b9 100644 --- a/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java +++ b/github-bot/src/test/java/io/github/martinwitt/laughing_train/api/BadSmellGraphQLTest.java @@ -97,8 +97,6 @@ void queryInsertedProject() throws Exception { .url("http://localhost:8081/graphql") .build(); RemoteProject project = new RemoteProject("aaa", "bbb"); - projectRepository.create(project); - project.addCommitHash("aaaa"); projectRepository.save(project); assertTrue( diff --git a/github-bot/src/test/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistenceTest.java b/github-bot/src/test/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistenceTest.java new file mode 100644 index 000000000..461d450ef --- /dev/null +++ b/github-bot/src/test/java/io/github/martinwitt/laughing_train/mining/AnalyzerResultsPersistenceTest.java @@ -0,0 +1,30 @@ +package io.github.martinwitt.laughing_train.mining; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.github.martinwitt.laughing_train.commons.GitProject; +import io.github.martinwitt.laughing_train.data.result.CodeAnalyzerResult; +import io.github.martinwitt.laughing_train.mining.api.AnalyzerRunRepository; +import io.github.martinwitt.laughing_train.mining.requests.StoreResults; +import io.quarkus.test.junit.QuarkusTest; +import jakarta.inject.Inject; +import org.instancio.Instancio; +import org.junit.jupiter.api.Test; + +@QuarkusTest +class AnalyzerResultsPersistenceTest { + + @Inject AnalyzerResultsPersistence analyzerResultsPersistence; + @Inject AnalyzerRunRepository analyzerRunRepository; + + @Test + void persistResults() { + StoreResults storeResults = + new StoreResults( + Instancio.create(GitProject.class), + Instancio.create(CodeAnalyzerResult.Success.class), + "test"); + analyzerResultsPersistence.persistResults(storeResults); + assertThat(analyzerRunRepository.findRecent(1)).isNotEmpty(); + } +} diff --git a/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/ProjectRepositoryImplTest.java b/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/ProjectRepositoryImplTest.java index e58872917..c0b30aa60 100644 --- a/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/ProjectRepositoryImplTest.java +++ b/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/ProjectRepositoryImplTest.java @@ -1,6 +1,6 @@ package io.github.martinwitt.laughing_train.persistence.impl; -import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; import io.github.martinwitt.laughing_train.domain.entity.RemoteProject; import io.github.martinwitt.laughing_train.persistence.repository.ProjectRepository; @@ -20,14 +20,14 @@ public class ProjectRepositoryImplTest { @Test void testCreate() { RemoteProject project = createMockProject(); - assertThat(projectRepository.create(project)).isEqualTo(project); + assertThat(projectRepository.save(project)).isEqualTo(project); } @Test void testDeleteByProjectUrl() { assertThat(projectRepository.getAll()).isEmpty(); RemoteProject project = createMockProject(); - projectRepository.create(project); + projectRepository.save(project); assertThat(projectRepository.getAll()).isNotEmpty(); assertThat(projectRepository.deleteByProjectUrl(project.getProjectUrl())).isEqualTo(1); @@ -37,7 +37,7 @@ void testDeleteByProjectUrl() { @Test void testFindByProjectUrl() { RemoteProject project = createMockProject(); - projectRepository.create(project); + projectRepository.save(project); assertThat(projectRepository.findByProjectUrl(project.getProjectUrl())) .hasSize(1) @@ -48,18 +48,6 @@ private RemoteProject createMockProject() { return Instancio.create(RemoteProject.class); } - @Test - void addCommitHashTest() { - RemoteProject project = createMockProject(); - assertThat(projectRepository.create(project)).isEqualTo(project); - project.addCommitHash("aaaa231adasdas"); - projectRepository.save(project); - - assertThat(projectRepository.findByProjectUrl(project.getProjectUrl())) - .hasSize(1) - .allMatch(v -> v.getCommitHashes().contains(project.getCommitHashes().get(0))); - } - @BeforeEach @AfterEach void setUp() { diff --git a/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepositoryTest.java b/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepositoryTest.java index 6ccc5e611..89316ae3b 100644 --- a/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepositoryTest.java +++ b/github-bot/src/test/java/io/github/martinwitt/laughing_train/persistence/impl/SqlProjectRepositoryTest.java @@ -37,16 +37,4 @@ void insertProject() { .sum(); assertThat(sqlAnalyzerRunRepository.findRecent(expectedSize)).hasSize(expectedSize); } - - @Test - void getRecent() { - RemoteProject remoteProject = Instancio.of(RemoteProject.class).assign().create(); - remoteProject.getCommits().stream() - .flatMap(v -> v.getAnalyzerStatuses().stream()) - .forEach(v -> v.setLocalDateTime(LocalDateTime.now())); - sqlProjectRepository.save(remoteProject); - assertThat(sqlProjectRepository.getRecent(1)).isNotEmpty(); - List recent = sqlProjectRepository.getRecent(1); - assertThat(recent).isNotEmpty(); - } }