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 <jeff.van.dam@est.tech>
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 @@
public class Attribute {
private String name;
private String dataType;
+ private String yangDataType;
@Builder.Default
private Collection<Object> 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.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 @@
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 @@
List<Relationship> 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
String label = "<TABLE border='1' cellborder='0' cellspacing='0' cellpadding='4'>";
for (Attribute attribute : attributes) {
label = label.concat("<TR> <TD bgcolor='#EEEEEE' align='left'>" + attribute
- .getName() + "</TD> <TD align='right' bgcolor='#EEEEEE'>" + attribute.getDataType() + "</TD> </TR>");
+ .getName() + "</TD> <TD align='right' bgcolor='#EEEEEE'>" + attribute
+ .getYangDataType() + "</TD> </TR>");
}
label = label.concat("</TABLE>");
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<Entity> entities) throws IOException {
+ if (generateEntityGraph) {
+ List<String> moduleNames = entities.stream().map(Entity::getModuleReferenceName).distinct().toList();
+ for (String moduleName : moduleNames) {
+ List<Entity> 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<Entity> 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<Entity> entities) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("@startuml\n");
+ sb.append("skinparam class {\n");
+ sb.append(" BackgroundColor<<Entity>> LightGray\n");
+ sb.append(" BackgroundColor<<Module>> LightBlue\n");
+ sb.append("}\n");
+ sb.append(String.format("class %s <<Module>> {\n}\n", moduleName));
+ for (Entity entity : entities) {
+ sb.append(String.format("class %s <<Entity>> {\n", entity.getEntityName()));
+ List<Attribute> 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 @@
}
private MutableGraph prepareGraph(List<Relationship> moduleRelationships, List<Entity> 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 @@
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<Relationship> relationships, List<Entity> entities) throws IOException {
+ if (generateRelationshipGraph) {
+ List<String> moduleNames = relationships.stream().map(Relationship::getModuleReferenceName).distinct().toList();
+ for (String moduleName : moduleNames) {
+ List<Relationship> moduleRelationships = relationships.stream().filter(relationship -> moduleName.equals(
+ relationship.getModuleReferenceName())).toList();
+ List<Entity> 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<Relationship> relationships, List<Entity> 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<Relationship> relationships, List<Entity> 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);
+ }
+}