diff --git a/pgsql-schema-generator/pom.xml b/pgsql-schema-generator/pom.xml index 48fa2cf..5d59065 100644 --- a/pgsql-schema-generator/pom.xml +++ b/pgsql-schema-generator/pom.xml @@ -90,6 +90,14 @@ ${version.lombok} provided + + + + guru.nidi + graphviz-java + 0.18.1 + + org.mockito diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Processor.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Processor.java index 38f9898..943a15e 100644 --- a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Processor.java +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Processor.java @@ -26,8 +26,10 @@ import java.util.Collections; import java.util.List; +import org.oran.smo.teiv.pgsqlgenerator.grapghgenerator.EntityGraphGenerator; import org.oran.smo.teiv.pgsqlgenerator.schema.data.DataSchemaGenerator; import org.oran.smo.teiv.pgsqlgenerator.schema.model.ModelSchemaGenerator; +import org.oran.smo.teiv.pgsqlgenerator.grapghgenerator.RelationshipGraphGenerator; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.ResourceUtils; @@ -42,6 +44,8 @@ public class Processor { private final YangModelProcessor yangModelProcessor; private final DataSchemaGenerator dataSchemaGenerator; private final ModelSchemaGenerator modelSchemaGenerator; + private final RelationshipGraphGenerator relationshipGraphGenerator; + private final EntityGraphGenerator entityGraphGenerator; @Value("${yang-model.source}") private String yangModelDirectory; @@ -61,6 +65,8 @@ void process() throws IOException { List relationshipsFromModelService = yangModelProcessor.getRelationshipsFromYang(pathToImplementing); + relationshipGraphGenerator.generate(relationshipsFromModelService, entitiesFromModelService); + entityGraphGenerator.generate(entitiesFromModelService); dataSchemaGenerator.generate(moduleReferences, entitiesFromModelService, relationshipsFromModelService); modelSchemaGenerator.generate(moduleReferences, entitiesFromModelService, relationshipsFromModelService); } diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGenerator.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGenerator.java new file mode 100644 index 0000000..78ea033 --- /dev/null +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGenerator.java @@ -0,0 +1,101 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Ericsson + * Modifications Copyright (C) 2024 OpenInfra Foundation Europe + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.oran.smo.teiv.pgsqlgenerator.grapghgenerator; + +import guru.nidi.graphviz.attribute.*; +import guru.nidi.graphviz.engine.Format; +import guru.nidi.graphviz.engine.Graphviz; +import guru.nidi.graphviz.model.Factory; +import guru.nidi.graphviz.model.MutableGraph; +import guru.nidi.graphviz.model.MutableNode; +import lombok.extern.slf4j.Slf4j; +import org.oran.smo.teiv.pgsqlgenerator.Attribute; +import org.oran.smo.teiv.pgsqlgenerator.Entity; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static guru.nidi.graphviz.attribute.Rank.RankDir; + +@Component +@Slf4j +public class EntityGraphGenerator { + + @Value("${graphs.generate}") + private boolean generateEntityGraph; + + @Value("${graphs.output}") + private String graphOutput; + + public void generate(List entities) throws IOException { + if (generateEntityGraph) { + List moduleNames = entities.stream().map(Entity::getModuleReferenceName).distinct().toList(); + for (String moduleName : moduleNames) { + List moduleEntities = entities.stream().filter(entity -> moduleName.equals(entity + .getModuleReferenceName())).toList(); + generateGraph(moduleName, moduleEntities); + } + } else { + log.info("graphs.generate set to false"); + } + } + + private void generateGraph(String name, List entities) throws IOException { + MutableGraph g = prepareGraph(entities, name); + File outputFile = new File(graphOutput, name); + Graphviz.fromGraph(g).render(Format.SVG).toFile(outputFile); + log.info("Graph rendered to: {}", outputFile.getAbsolutePath()); + } + + private MutableGraph prepareGraph(List moduleEntities, String moduleName) { + MutableGraph g = Factory.mutGraph(moduleName).setDirected(true).graphAttrs().add(Rank.dir(RankDir.LEFT_TO_RIGHT)) + .nodeAttrs().add(Shape.RECT, Style.BOLD, Color.BLACK, Style.FILLED, Color.LIGHTGRAY.fill()); + MutableNode moduleNameNode = Factory.mutNode(moduleName).attrs().add(Color.LIGHTBLUE.fill()); + g.add(moduleNameNode); + for (Entity moduleEntity : moduleEntities) { + MutableNode moduleNode = Factory.mutNode(moduleEntity.getEntityName()); + g.add(moduleNode); + g.add(moduleNameNode.addLink(Factory.to(moduleNode))); + List attributes = moduleEntity.getAttributes(); + if (!attributes.isEmpty()) { + addAttributeNodeToGraph(g, attributes, moduleEntity, moduleNode); + } + } + return g; + } + + private void addAttributeNodeToGraph(MutableGraph graph, List attributes, Entity moduleEntity, + MutableNode moduleNode) { + String label = ""; + for (Attribute attribute : attributes) { + label = label.concat(""); + } + label = label.concat("
" + attribute + .getName() + " " + attribute.getDataType() + "
"); + MutableNode attributeNode = Factory.mutNode(moduleEntity.getEntityName() + "-attributes").attrs().add(Label.html( + label)); + graph.add(attributeNode); + graph.add(moduleNode.addLink(attributeNode)); + } +} diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGenerator.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGenerator.java new file mode 100644 index 0000000..6b76cc1 --- /dev/null +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGenerator.java @@ -0,0 +1,88 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 Ericsson + * Modifications Copyright (C) 2024 OpenInfra Foundation Europe + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.oran.smo.teiv.pgsqlgenerator.grapghgenerator; + +import guru.nidi.graphviz.model.Factory; +import guru.nidi.graphviz.model.MutableGraph; +import guru.nidi.graphviz.model.MutableNode; +import lombok.extern.slf4j.Slf4j; +import guru.nidi.graphviz.attribute.Label; +import guru.nidi.graphviz.engine.Format; +import guru.nidi.graphviz.engine.Graphviz; +import org.oran.smo.teiv.pgsqlgenerator.Entity; +import org.oran.smo.teiv.pgsqlgenerator.Relationship; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +@Component +@Slf4j +public class RelationshipGraphGenerator { + + @Value("${graphs.generate}") + private boolean generateRelationshipGraph; + + @Value("${graphs.output}") + private String graphOutput; + + public void generate(List relationships, List entities) throws IOException { + if (generateRelationshipGraph) { + List moduleNames = relationships.stream().map(Relationship::getModuleReferenceName).distinct().toList(); + for (String moduleName : moduleNames) { + List moduleRelationships = relationships.stream().filter(relationship -> moduleName.equals( + relationship.getModuleReferenceName())).toList(); + List moduleEntities = entities.stream().filter(entity -> moduleName.equals(entity + .getModuleReferenceName())).toList(); + generateGraph(moduleName, moduleRelationships, moduleEntities); + } + generateGraph("overall", relationships, entities); + } else { + log.info("graphs.generate set to false"); + } + } + + private void generateGraph(String name, List relationships, List entities) throws IOException { + MutableGraph g = prepareGraph(relationships, entities); + File outputFile = new File(graphOutput, name); + Graphviz.fromGraph(g).render(Format.SVG).toFile(outputFile); + log.info("Graph rendered to: {}", outputFile.getAbsolutePath()); + } + + private MutableGraph prepareGraph(List moduleRelationships, List moduleEntities) { + MutableGraph g = Factory.mutGraph("moduleName").setDirected(false); + for (Entity moduleEntity : moduleEntities) { + MutableNode node = Factory.mutNode(moduleEntity.getEntityName()); + g.add(node); + } + for (Relationship moduleRelationship : moduleRelationships) { + MutableNode nodeA = Factory.mutNode(moduleRelationship.getASideMOType()); + g.add(nodeA); + MutableNode nodeB = Factory.mutNode(moduleRelationship.getBSideMOType()); + g.add(nodeB); + String label = moduleRelationship.getName().split("_")[1]; + g.add(nodeA.addLink(Factory.to(nodeB).with(Label.of(label)))); + } + return g; + } +} diff --git a/pgsql-schema-generator/src/main/resources/application.yaml b/pgsql-schema-generator/src/main/resources/application.yaml index e2fb82b..1e3f11f 100644 --- a/pgsql-schema-generator/src/main/resources/application.yaml +++ b/pgsql-schema-generator/src/main/resources/application.yaml @@ -34,3 +34,6 @@ schema: output: target/01_init-oran-smo-teiv-model.sql exclusions: model-names: metadata, decorators +graphs: + generate: true + output: target/graphs diff --git a/pgsql-schema-generator/src/test/java/org/oran/smo/teiv/pgsqlgenerator/EndToEndTest.java b/pgsql-schema-generator/src/test/java/org/oran/smo/teiv/pgsqlgenerator/EndToEndTest.java index 1ba8f74..fcd4dae 100644 --- a/pgsql-schema-generator/src/test/java/org/oran/smo/teiv/pgsqlgenerator/EndToEndTest.java +++ b/pgsql-schema-generator/src/test/java/org/oran/smo/teiv/pgsqlgenerator/EndToEndTest.java @@ -55,6 +55,9 @@ class EndToEndTest { @Value("${schema.model.output}") private String actualModelSql; + @Value("${graphs.output}") + private String graphOutput; + @Test @Order(1) void generateDataAndModelSchemaTest() throws IOException { @@ -63,10 +66,13 @@ void generateDataAndModelSchemaTest() throws IOException { processor.process(); File generatedDataSql = new File(actualDataSql); File generatedModelSql = new File(actualModelSql); + File generatedGraphs = new File(graphOutput); //then Assertions.assertTrue(generatedDataSql.exists()); Assertions.assertTrue(generatedModelSql.exists()); + Assertions.assertTrue(generatedGraphs.exists() && generatedGraphs.isDirectory() && generatedGraphs + .listFiles().length > 0); } @Test diff --git a/pgsql-schema-generator/src/test/resources/application.yaml b/pgsql-schema-generator/src/test/resources/application.yaml index fe3176f..82829f7 100644 --- a/pgsql-schema-generator/src/test/resources/application.yaml +++ b/pgsql-schema-generator/src/test/resources/application.yaml @@ -36,4 +36,7 @@ test-result: data: src/test/resources/expected-db-schema/result_00_init-oran-smo-teiv-data.sql model: src/test/resources/expected-db-schema/result_01_init-oran-smo-teiv-model.sql exclusions: - model-names: metadata, decorators \ No newline at end of file + model-names: metadata, decorators +graphs: + generate: true + output: target/test-classes/graphs \ No newline at end of file