From abe98a3945fd93b0b1380361f0d16f422499c967 Mon Sep 17 00:00:00 2001 From: JvD_Ericsson Date: Thu, 15 Aug 2024 12:08:44 +0100 Subject: [PATCH] teiv, pgsql-schema-generator: Update graph generation update entity graph to contain the yang data types update the relationship graph to contain: - directional arrows - cardinality Added generators for puml graph Issue-ID: SMO-156 Change-Id: Ic16d06b8059c780a6e7a06ff2120399d0070f1dd Signed-off-by: JvD_Ericsson --- .../oran/smo/teiv/pgsqlgenerator/Attribute.java | 1 + .../oran/smo/teiv/pgsqlgenerator/Processor.java | 6 ++ .../teiv/pgsqlgenerator/YangModelProcessor.java | 26 +++--- .../grapghgenerator/EntityGraphGenerator.java | 3 +- .../grapghgenerator/EntityGraphGeneratorUml.java | 86 +++++++++++++++++++ .../RelationshipGraphGenerator.java | 28 +++++- .../RelationshipGraphGeneratorUml.java | 99 ++++++++++++++++++++++ 7 files changed, 235 insertions(+), 14 deletions(-) create mode 100644 pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGeneratorUml.java create mode 100644 pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGeneratorUml.java diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Attribute.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Attribute.java index 72ce40b..a079f31 100644 --- a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Attribute.java +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/Attribute.java @@ -32,6 +32,7 @@ import lombok.Singular; public class Attribute { private String name; private String dataType; + private String yangDataType; @Builder.Default private Collection constraints = List.of(); private String defaultValue; 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 4d24eee..4112fa7 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 @@ -28,6 +28,8 @@ import java.util.Comparator; import java.util.List; import org.oran.smo.teiv.pgsqlgenerator.grapghgenerator.EntityGraphGenerator; +import org.oran.smo.teiv.pgsqlgenerator.grapghgenerator.EntityGraphGeneratorUml; +import org.oran.smo.teiv.pgsqlgenerator.grapghgenerator.RelationshipGraphGeneratorUml; import org.oran.smo.teiv.pgsqlgenerator.schema.consumerdata.ConsumerDataSchemaGenerator; import org.oran.smo.teiv.pgsqlgenerator.schema.data.DataSchemaGenerator; import org.oran.smo.teiv.pgsqlgenerator.schema.model.ModelSchemaGenerator; @@ -48,7 +50,9 @@ public class Processor { private final ModelSchemaGenerator modelSchemaGenerator; private final ConsumerDataSchemaGenerator consumerDataSchemaGenerator; private final RelationshipGraphGenerator relationshipGraphGenerator; + private final RelationshipGraphGeneratorUml relationshipGraphGeneratorUml; private final EntityGraphGenerator entityGraphGenerator; + private final EntityGraphGeneratorUml entityGraphGeneratorUml; @Value("${yang-model.source}") private String yangModelDirectory; @@ -69,7 +73,9 @@ public class Processor { List relationshipsFromModelService = yangModelProcessor.getRelationshipsFromYang(pathToImplementing); relationshipGraphGenerator.generate(relationshipsFromModelService, entitiesFromModelService); + relationshipGraphGeneratorUml.generate(relationshipsFromModelService, entitiesFromModelService); entityGraphGenerator.generate(entitiesFromModelService); + entityGraphGeneratorUml.generate(entitiesFromModelService); dataSchemaGenerator.generate(moduleReferences, entitiesFromModelService, relationshipsFromModelService); modelSchemaGenerator.generate(moduleReferences, entitiesFromModelService, relationshipsFromModelService); consumerDataSchemaGenerator.generate(moduleReferences, entitiesFromModelService, relationshipsFromModelService); diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/YangModelProcessor.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/YangModelProcessor.java index ff4a9cf..6d4e790 100644 --- a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/YangModelProcessor.java +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/YangModelProcessor.java @@ -135,7 +135,8 @@ public class YangModelProcessor { List constraint = List.of(PrimaryKeyConstraint.builder().constraintName("PK_" + yList.getListName() + "_id") .tableName(yList.getListName()).columnToAddConstraintTo("id").build()); - attributes.add(Attribute.builder().name("id").dataType(TEXT).constraints(constraint).build()); + attributes.add(Attribute.builder().name("id").yangDataType("string").dataType(TEXT).constraints(constraint) + .build()); yList.getContainers().forEach(yContainer -> { System.out.printf("\t\tContainer Name: %s \n", yContainer.getContainerName()); if (yContainer.getContainerName().equals("attributes")) { @@ -149,12 +150,13 @@ public class YangModelProcessor { if (yLeaf.getDefault() != null) { - attributes.add(Attribute.builder().name(yLeaf.getLeafName()).dataType(dataTypeMapping.get( - yLeaf.getType().getDataType())).defaultValue(yLeaf.getDefault().getValue()) - .constraints(new ArrayList()).build()); + attributes.add(Attribute.builder().name(yLeaf.getLeafName()).yangDataType(yLeaf.getType() + .getDataType()).dataType(dataTypeMapping.get(yLeaf.getType().getDataType())) + .defaultValue(yLeaf.getDefault().getValue()).constraints(new ArrayList()).build()); } else { - attributes.add(Attribute.builder().name(yLeaf.getLeafName()).dataType(dataTypeMapping.get( - yLeaf.getType().getDataType())).constraints(new ArrayList()).build()); + attributes.add(Attribute.builder().name(yLeaf.getLeafName()).yangDataType(yLeaf.getType() + .getDataType()).dataType(dataTypeMapping.get(yLeaf.getType().getDataType())) + .constraints(new ArrayList()).build()); } }); yContainer.getLeafLists().forEach(yLeafList -> { @@ -164,8 +166,9 @@ public class YangModelProcessor { System.out.printf("\t\t\t\tData Type: %s \n", dataTypeMapping.get(yLeafList.getType() .getDataType())); - attributes.add(Attribute.builder().name(yLeafList.getLeafListName()).dataType(JSONB).indexType( - IndexType.GIN_TRGM_OPS_ON_LIST_AS_JSONB).constraints(new ArrayList()).build()); + attributes.add(Attribute.builder().name(yLeafList.getLeafListName()).yangDataType(yLeafList + .getType().getDataType()).dataType(JSONB).indexType( + IndexType.GIN_TRGM_OPS_ON_LIST_AS_JSONB).constraints(new ArrayList()).build()); }); yContainer.getContainers().forEach(container -> { @@ -176,7 +179,8 @@ public class YangModelProcessor { String dataType = dataTypeMapping.get(container.getUses().toString()); Attribute.AttributeBuilder attributeBuilder = Attribute.builder().name(container - .getContainerName()).dataType(dataType).constraints(new ArrayList()); + .getContainerName()).yangDataType(dataType).dataType(dataType).constraints( + new ArrayList()); if (container.getContainerName().equals("geo-location")) { dataType = dataTypeMapping.get("geo:geo-location"); } @@ -191,8 +195,8 @@ public class YangModelProcessor { attributes.add(Attribute.builder().name(uses.getDomElement().getValue().substring(uses .getDomElement().getValue().indexOf(':') + 1, uses.getDomElement().getValue().length())) - .dataType(dataTypeMapping.get(uses.getDomElement().getValue())).constraints( - new ArrayList()).build()); + .yangDataType(uses.getDomElement().getValue()).dataType(dataTypeMapping.get(uses + .getDomElement().getValue())).constraints(new ArrayList()).build()); }); } }); 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 index 78ea033..6c564d6 100644 --- 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 @@ -90,7 +90,8 @@ public class EntityGraphGenerator { String label = ""; for (Attribute attribute : attributes) { label = label.concat(""); + .getName() + ""); } label = label.concat("
" + attribute - .getName() + " " + attribute.getDataType() + "
" + attribute + .getYangDataType() + "
"); MutableNode attributeNode = Factory.mutNode(moduleEntity.getEntityName() + "-attributes").attrs().add(Label.html( diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGeneratorUml.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGeneratorUml.java new file mode 100644 index 0000000..b15c99f --- /dev/null +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/EntityGraphGeneratorUml.java @@ -0,0 +1,86 @@ +/* + * ============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 java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +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 lombok.extern.slf4j.Slf4j; + +@Component +@Slf4j +public class EntityGraphGeneratorUml { + + @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 { + String plantUmlSource = prepareGraphSource(name, entities); + File outputFile = new File(graphOutput, name + ".puml"); + try (PrintWriter writer = new PrintWriter(outputFile)) { + writer.write(plantUmlSource); + } + log.info("PUML generated at: {}", outputFile.getAbsolutePath()); + } + + private String prepareGraphSource(String moduleName, List entities) { + StringBuilder sb = new StringBuilder(); + sb.append("@startuml\n"); + sb.append("skinparam class {\n"); + sb.append(" BackgroundColor<> LightGray\n"); + sb.append(" BackgroundColor<> LightBlue\n"); + sb.append("}\n"); + sb.append(String.format("class %s <> {\n}\n", moduleName)); + for (Entity entity : entities) { + sb.append(String.format("class %s <> {\n", entity.getEntityName())); + List attributes = entity.getAttributes(); + for (Attribute attribute : attributes) { + sb.append(String.format(" %s : %s\n", attribute.getName(), attribute.getYangDataType())); + } + sb.append("}\n"); + sb.append(String.format("\"%s\" --> %s\n", moduleName, entity.getEntityName())); + } + sb.append("@enduml\n"); + return sb.toString(); + } +} 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 index bec8e3b..98acbdf 100644 --- 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 @@ -20,6 +20,10 @@ */ package org.oran.smo.teiv.pgsqlgenerator.grapghgenerator; +import guru.nidi.graphviz.attribute.Arrow; +import guru.nidi.graphviz.attribute.Color; +import guru.nidi.graphviz.attribute.EndLabel; +import guru.nidi.graphviz.attribute.Shape; import guru.nidi.graphviz.model.Factory; import guru.nidi.graphviz.model.MutableGraph; import guru.nidi.graphviz.model.MutableNode; @@ -70,7 +74,8 @@ public class RelationshipGraphGenerator { } private MutableGraph prepareGraph(List moduleRelationships, List moduleEntities) { - MutableGraph g = Factory.mutGraph("moduleName").setDirected(false); + MutableGraph g = Factory.mutGraph("moduleName").setDirected(true).linkAttrs().add(Color.DARKSLATEGRAY4).nodeAttrs() + .add(Shape.BOX); for (Entity moduleEntity : moduleEntities) { MutableNode node = Factory.mutNode(moduleEntity.getEntityName()); g.add(node); @@ -80,9 +85,28 @@ public class RelationshipGraphGenerator { 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)))); + Label aSideCardinality = Label.of(getCardinality(moduleRelationship.getASideMinCardinality(), moduleRelationship + .getASideMaxCardinality())); + Label bSideCardinality = Label.of(getCardinality(moduleRelationship.getBSideMinCardinality(), moduleRelationship + .getBSideMaxCardinality())); + + g.add(nodeA.addLink(Factory.to(nodeB).with(Label.of(label), EndLabel.head(aSideCardinality, null, null), + EndLabel.tail(bSideCardinality, null, null), Arrow.VEE))); } return g; } + + private String getCardinality(long minCardinality, long maxCardinality) { + return formatCardinality(minCardinality) + ".." + formatCardinality(maxCardinality); + } + + private String formatCardinality(long cardinality) { + if (cardinality == Long.MAX_VALUE) { + return "*"; + } else { + return String.valueOf(cardinality); + } + } } diff --git a/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGeneratorUml.java b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGeneratorUml.java new file mode 100644 index 0000000..f5a4211 --- /dev/null +++ b/pgsql-schema-generator/src/main/java/org/oran/smo/teiv/pgsqlgenerator/grapghgenerator/RelationshipGraphGeneratorUml.java @@ -0,0 +1,99 @@ +/* + * ============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 java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; + +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 lombok.extern.slf4j.Slf4j; + +@Component +@Slf4j +public class RelationshipGraphGeneratorUml { + + @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 { + String plantUmlSource = prepareGraph(relationships, entities); + File outputFile = new File(graphOutput, name + "-rel.puml"); + try (PrintWriter writer = new PrintWriter(outputFile)) { + writer.write(plantUmlSource); + } + log.info("PUML generated at: {}", outputFile.getAbsolutePath()); + } + + private String prepareGraph(List relationships, List entities) { + StringBuilder sb = new StringBuilder(); + sb.append("@startuml\n"); + sb.append("skinparam componentStyle rectangle\n"); + for (Entity entity : entities) { + sb.append(String.format("class %s {\n", entity.getEntityName())); + sb.append("}\n"); + } + for (Relationship relationship : relationships) { + String label = relationship.getName().split("_")[1]; + String aSideCardinality = getCardinality(relationship.getASideMinCardinality(), relationship + .getASideMaxCardinality()); + String bSideCardinality = getCardinality(relationship.getBSideMinCardinality(), relationship + .getBSideMaxCardinality()); + + sb.append(String.format("%s \"%s\" --> \"%s\" %s : %s\n", relationship.getASideMOType(), aSideCardinality, + bSideCardinality, relationship.getBSideMOType(), label)); + } + sb.append("@enduml\n"); + return sb.toString(); + } + + private String getCardinality(long minCardinality, long maxCardinality) { + return formatCardinality(minCardinality) + ".." + formatCardinality(maxCardinality); + } + + private String formatCardinality(long cardinality) { + return cardinality == Long.MAX_VALUE ? "*" : String.valueOf(cardinality); + } +} -- 2.16.6