From f30b4c72fcb4887e90ffe0cd5d30f0f48cb3f6b5 Mon Sep 17 00:00:00 2001 From: rbaul <4RFVbgt%> Date: Tue, 6 Feb 2024 15:36:05 +0200 Subject: [PATCH] Added cycle issue finder --- .../src/app/api/project-api.model.ts | 1 + .../project-topology.component.html | 36 +++++++++----- .../project-topology.component.ts | 11 +++++ microservice-visualization/build.gradle | 7 ++- .../domain/model/Project.java | 18 +++---- .../service/AlgorithmUtils.java | 47 +++++++++++++++++++ .../service/loaders/ProjectLoaderService.java | 27 +++++++++++ .../web/dto/ProjectDto.java | 2 + 8 files changed, 127 insertions(+), 22 deletions(-) create mode 100644 microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/AlgorithmUtils.java diff --git a/microservice-visualization-webapp/src/app/api/project-api.model.ts b/microservice-visualization-webapp/src/app/api/project-api.model.ts index 82d7a7c..ceb35ef 100644 --- a/microservice-visualization-webapp/src/app/api/project-api.model.ts +++ b/microservice-visualization-webapp/src/app/api/project-api.model.ts @@ -23,6 +23,7 @@ export interface ProjectDto { // connections?: Map, connections?: any, dependencies?: any, + librariesCycle?: [string[]], projectVersion: ProjectVersionLiteDto } diff --git a/microservice-visualization-webapp/src/app/project-topology/project-topology.component.html b/microservice-visualization-webapp/src/app/project-topology/project-topology.component.html index a0aab18..51f3f3a 100644 --- a/microservice-visualization-webapp/src/app/project-topology/project-topology.component.html +++ b/microservice-visualization-webapp/src/app/project-topology/project-topology.component.html @@ -14,14 +14,22 @@
- - - - - - - - + + + + + + + + - + + + + +
@@ -51,4 +63,4 @@

Custom Content

--> - + \ No newline at end of file diff --git a/microservice-visualization-webapp/src/app/project-topology/project-topology.component.ts b/microservice-visualization-webapp/src/app/project-topology/project-topology.component.ts index cc3ebfd..1fbe8d9 100644 --- a/microservice-visualization-webapp/src/app/project-topology/project-topology.component.ts +++ b/microservice-visualization-webapp/src/app/project-topology/project-topology.component.ts @@ -11,6 +11,7 @@ import { DataSet, IdType, Network } from 'vis-network/standalone'; import { ApplicationLiteDto, ApplicationType } from '../api/application-api.model'; import { COLLAPSED_NAME, GROUP_MARGIN, MOVE_TO_SCALE, NODE_HEIGHT, NODE_WIDTH, SCALE_FACTORY, topologyOptions } from './project-topology.const'; + interface GroupCluster { id?: string, name?: string, @@ -47,6 +48,7 @@ export enum TopologyType { DropdownModule, FormsModule, ButtonModule, + DropdownModule // ContextMenuModule ], templateUrl: './project-topology.component.html', @@ -86,6 +88,8 @@ export class ProjectTopologyComponent implements OnInit, AfterViewInit, OnChange selectedApp?: ApplicationLiteDto; selectedNodes: string[] = []; + cycleIssues?: [string[]]; + selectedCycleIssue?: string[]; // items: MenuItem[] = []; @@ -334,6 +338,8 @@ export class ProjectTopologyComponent implements OnInit, AfterViewInit, OnChange this.moveToAppOptions = this.data.applications; + this.cycleIssues = this.data.librariesCycle; + const edges: any[] = []; if (this.type === TopologyType.ALL || this.type === TopologyType.MICROSERVICES) { @@ -504,6 +510,11 @@ export class ProjectTopologyComponent implements OnInit, AfterViewInit, OnChange this.onFilterApps(applictionsToShow); } + onCycleIssueSelect(cycleIssue: string[]): void { + const applictionsToShow = this.apps.filter((app) => cycleIssue.includes(app.name)); + this.onFilterApps(applictionsToShow); + } + /** * Move to specific node change */ diff --git a/microservice-visualization/build.gradle b/microservice-visualization/build.gradle index 110a7f9..a001434 100644 --- a/microservice-visualization/build.gradle +++ b/microservice-visualization/build.gradle @@ -29,7 +29,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.flywaydb:flyway-core' - // OpenApi implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.+' @@ -39,6 +38,12 @@ dependencies { // GitHub implementation 'org.kohsuke:github-api:1.318' + // JGraphT + implementation 'org.jgrapht:jgrapht-core:1.5.2' + + // Hibernate types + implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.1' + // WebApp implementation project(':microservice-visualization-webapp') diff --git a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/domain/model/Project.java b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/domain/model/Project.java index 70f0928..fdc56cd 100644 --- a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/domain/model/Project.java +++ b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/domain/model/Project.java @@ -1,14 +1,7 @@ package com.github.rbaul.microservice_visualization.domain.model; -import jakarta.persistence.CascadeType; -import jakarta.persistence.ElementCollection; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; +import io.hypersistence.utils.hibernate.type.json.JsonType; +import jakarta.persistence.*; import jakarta.validation.constraints.NotEmpty; import lombok.AllArgsConstructor; import lombok.Builder; @@ -16,6 +9,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; +import org.hibernate.annotations.Type; import org.springframework.util.CollectionUtils; import java.util.HashSet; @@ -52,6 +46,12 @@ public class Project { @ToString.Exclude @ElementCollection private Map> dependencies; + + @ToString.Exclude + @Type(JsonType.class) + @Column(columnDefinition = "json") + @Basic(fetch = FetchType.LAZY) + private Set> librariesCycle; @ToString.Exclude @ElementCollection diff --git a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/AlgorithmUtils.java b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/AlgorithmUtils.java new file mode 100644 index 0000000..5151ba3 --- /dev/null +++ b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/AlgorithmUtils.java @@ -0,0 +1,47 @@ +package com.github.rbaul.microservice_visualization.service; + +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; +import org.jgrapht.Graph; +import org.jgrapht.GraphPath; +import org.jgrapht.alg.cycle.PatonCycleBase; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.SimpleGraph; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@Slf4j +@UtilityClass +public class AlgorithmUtils { + + // Function to find all simple cycles in an undirected graph using Paton's algorithm + public Set> findSimpleCycles(Map> connections) { + Graph graph = new SimpleGraph<>(CustomEdge.class); + + // Vertex + connections.keySet().forEach(graph::addVertex); + + // Edges + connections.forEach((vertex, cons) -> { + for (String con : cons) { + graph.addEdge(vertex, con, new CustomEdge()); + } + }); + + return findSimpleCycles(graph); + } + + // Function to find all simple cycles in an undirected graph using Paton's algorithm + private Set> findSimpleCycles(Graph graph) { + PatonCycleBase cycleBase = new PatonCycleBase<>(graph); + return cycleBase.getCycleBasis().getCyclesAsGraphPaths().stream().map(GraphPath::getVertexList).collect(Collectors.toSet()); + } + + // Custom edge class (required by JGraphT for undirected graphs) + private static class CustomEdge extends DefaultEdge { + // You can add custom properties if needed + } +} diff --git a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/loaders/ProjectLoaderService.java b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/loaders/ProjectLoaderService.java index e35af83..3f14364 100644 --- a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/loaders/ProjectLoaderService.java +++ b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/service/loaders/ProjectLoaderService.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.github.rbaul.microservice_visualization.config.MicroserviceVisualizationProperties; import com.github.rbaul.microservice_visualization.domain.model.*; +import com.github.rbaul.microservice_visualization.service.AlgorithmUtils; import com.github.rbaul.microservice_visualization.service.model.ApplicationDependency; import com.github.rbaul.microservice_visualization.service.model.ProjectConfig; import com.github.rbaul.microservice_visualization.utils.ConverterUtils; @@ -125,6 +126,28 @@ protected Map> createDependenciesMap(Project project) { return appConnections; } + protected Map> createLibDependenciesMap(Project project) { + Map> appConnections = new HashMap<>(); + Set appNames = project.getApplications().stream() + .filter(application -> application.getType() == ApplicationType.LIBRARY).map(Application::getName).collect(Collectors.toSet()); + + project.getApplications().forEach(application -> { + if (application.getType() == ApplicationType.LIBRARY) { + appConnections.put(application.getName(), new ArrayList<>()); + if (application.getDependencies() != null) { + application.getDependencies().forEach(dep -> { + Dependency dependency = ConverterUtils.convertDependency(dep); + if (!application.getName().equals(dependency.name()) && appNames.contains(dependency.name())) { + appConnections.get(application.getName()).add(dependency.name()); + } + }); + } + } + }); + + return appConnections; + } + protected Set createProjectRelevantTags(Project project) { Set tags = new HashSet<>(); @@ -214,6 +237,10 @@ protected void setApplicationToProject(Project project, ProjectConfig projectCon project.setConnections(createTopology(project, projectConfig.getApplicationPostfix(), projectConfig.getApplicationApiPostfixes())); // Dependencies project.setDependencies(createDependenciesMap(project)); + + // Cycle dependencies + project.setLibrariesCycle(AlgorithmUtils.findSimpleCycles(createLibDependenciesMap(project))); + // Relevant Tags project.setTags(createProjectRelevantTags(project)); if (!CollectionUtils.isEmpty(projectConfig.getGroups())) { diff --git a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/web/dto/ProjectDto.java b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/web/dto/ProjectDto.java index 3018d69..b78afdb 100644 --- a/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/web/dto/ProjectDto.java +++ b/microservice-visualization/src/main/java/com/github/rbaul/microservice_visualization/web/dto/ProjectDto.java @@ -25,6 +25,8 @@ public class ProjectDto { private Map> connections; private Map> dependencies; + + private Set> librariesCycle; private List groups;