From ad65ebe5e9d2e3942b0e6a229836d16edc5add7c Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Thu, 18 Jan 2024 00:15:06 +0900 Subject: [PATCH] [DROOLS-7600] Impact Analysis : export as JSON (#5643) --- bom/drools-bom/pom.xml | 11 ++ .../drools-impact-analysis-graph-json/pom.xml | 66 ++++++++ .../graph/json/GraphJsonGenerator.java | 90 +++++++++++ .../analysis/graph/json/JsonOutputTest.java | 147 ++++++++++++++++++ .../src/test/resources/logback-test.xml | 39 +++++ .../drools-impact-analysis-graph/pom.xml | 1 + 6 files changed, 354 insertions(+) create mode 100644 drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/pom.xml create mode 100644 drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/main/java/org/drools/impact/analysis/graph/json/GraphJsonGenerator.java create mode 100644 drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/java/org/drools/impact/analysis/graph/json/JsonOutputTest.java create mode 100644 drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/resources/logback-test.xml diff --git a/bom/drools-bom/pom.xml b/bom/drools-bom/pom.xml index f718a910cf78..51378329dddd 100644 --- a/bom/drools-bom/pom.xml +++ b/bom/drools-bom/pom.xml @@ -884,6 +884,17 @@ ${project.version} sources + + org.drools + drools-impact-analysis-graph-json + ${project.version} + + + org.drools + drools-impact-analysis-graph-json + ${project.version} + sources + org.drools diff --git a/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/pom.xml b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/pom.xml new file mode 100644 index 000000000000..32de869fa022 --- /dev/null +++ b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/pom.xml @@ -0,0 +1,66 @@ + + + + + drools-impact-analysis-graph + org.drools + 999-SNAPSHOT + + 4.0.0 + + drools-impact-analysis-graph-json + + Drools :: Impact Analysis Graph :: JSON + + + org.drools.impact.analysis.graph.json + + + + + org.drools + drools-impact-analysis-graph-common + + + org.drools + drools-impact-analysis-parser + + + com.fasterxml.jackson.core + jackson-databind + + + + junit + junit + test + + + org.assertj + assertj-core + test + + + + diff --git a/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/main/java/org/drools/impact/analysis/graph/json/GraphJsonGenerator.java b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/main/java/org/drools/impact/analysis/graph/json/GraphJsonGenerator.java new file mode 100644 index 000000000000..e578b434e1e6 --- /dev/null +++ b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/main/java/org/drools/impact/analysis/graph/json/GraphJsonGenerator.java @@ -0,0 +1,90 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.drools.impact.analysis.graph.json; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.drools.impact.analysis.graph.Graph; +import org.drools.impact.analysis.graph.Link; +import org.drools.impact.analysis.graph.Node; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GraphJsonGenerator { + + private static final Logger logger = LoggerFactory.getLogger(GraphJsonGenerator.class); + + private GraphJsonGenerator() { + // Creating instances of this class is not allowed. + } + + public static String generateJson(Graph graph) { + Map>> map = convertToMap(graph); + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.writeValueAsString(map); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e); + } + } + + private static Map>> convertToMap(Graph graph) { + Map>> map = new HashMap<>(); + List> nodes = new ArrayList<>(); + List> edges = new ArrayList<>(); + map.put("NODES", nodes); + map.put("EDGES", edges); + for (Node node : graph.getNodeMap().values()) { + Map nodeMap = new HashMap<>(); + nodeMap.put("id", node.getId()); + nodeMap.put("type", "node"); + nodeMap.put("label", node.getRuleName()); + nodes.add(nodeMap); + + for (Link outgoingLink : node.getOutgoingLinks()) { + Map edgeMap = new HashMap<>(); + edgeMap.put("id", "edge-" + node.getId() + "-" + outgoingLink.getTarget().getId()); + edgeMap.put("type", "edge"); + edgeMap.put("source", node.getId()); + edgeMap.put("target", outgoingLink.getTarget().getId()); + switch (outgoingLink.getReactivityType()) { + case POSITIVE: + edgeMap.put("edgeStyle", "solid"); + break; + case NEGATIVE: + edgeMap.put("edgeStyle", "dashed"); + break; + case UNKNOWN: + edgeMap.put("edgeStyle", "dotted"); + break; + default: + logger.warn("Unexpected reactivity type: {}", outgoingLink.getReactivityType()); + edgeMap.put("edgeStyle", "solid"); + } + edges.add(edgeMap); + } + } + return map; + } +} diff --git a/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/java/org/drools/impact/analysis/graph/json/JsonOutputTest.java b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/java/org/drools/impact/analysis/graph/json/JsonOutputTest.java new file mode 100644 index 000000000000..1cd927715cd0 --- /dev/null +++ b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/java/org/drools/impact/analysis/graph/json/JsonOutputTest.java @@ -0,0 +1,147 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.drools.impact.analysis.graph.json; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.drools.impact.analysis.graph.Graph; +import org.drools.impact.analysis.graph.Node; +import org.drools.impact.analysis.graph.ReactivityType; +import org.drools.impact.analysis.model.Rule; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This class is to test from drools-impact-analysis-graph-common Graph to json output. + *

+ * If you want to test from DRL (involving parser), use drools-impact-analysis-itests + */ +public class JsonOutputTest { + + private static final String EXPECTED_JSON = + """ + { + "NODES":[ + { + "id":"org.example.rule1", + "label":"rule1", + "type":"node" + }, + { + "id":"org.example.rule5", + "label":"rule5", + "type":"node" + }, + { + "id":"org.example.rule4", + "label":"rule4", + "type":"node" + }, + { + "id":"org.example.rule3", + "label":"rule3", + "type":"node" + }, + { + "id":"org.example.rule2", + "label":"rule2", + "type":"node" + } + ], + "EDGES":[ + { + "id":"edge-org.example.rule1-org.example.rule2", + "source":"org.example.rule1", + "type":"edge", + "edgeStyle":"solid", + "target":"org.example.rule2" + }, + { + "id":"edge-org.example.rule1-org.example.rule3", + "source":"org.example.rule1", + "type":"edge", + "edgeStyle":"dashed", + "target":"org.example.rule3" + }, + { + "id":"edge-org.example.rule3-org.example.rule5", + "source":"org.example.rule3", + "type":"edge", + "edgeStyle":"solid", + "target":"org.example.rule5" + }, + { + "id":"edge-org.example.rule2-org.example.rule4", + "source":"org.example.rule2", + "type":"edge", + "edgeStyle":"dotted", + "target":"org.example.rule4" + } + ] + } + """; + + @Test + public void generate_simpleGraph() throws JsonProcessingException { + Graph graph = createSimpleGraph(); + String actualJson = GraphJsonGenerator.generateJson(graph); + assertJson(actualJson, EXPECTED_JSON); + } + + private static void assertJson(String actualJson, String expectedJson) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + Map>> actualMap = objectMapper.readValue(actualJson, Map.class); + Map>> expectedMap = objectMapper.readValue(expectedJson, Map.class); + + List> actualNodes = actualMap.get("NODES"); + List> expectedNodes = expectedMap.get("NODES"); + assertThat(actualNodes).containsExactlyInAnyOrderElementsOf(expectedNodes); + + List> actualEdges = actualMap.get("EDGES"); + List> expectedEdges = expectedMap.get("EDGES"); + assertThat(actualEdges).containsExactlyInAnyOrderElementsOf(expectedEdges); + } + + private Graph createSimpleGraph() { + Node node1 = new Node(new Rule("org.example", "rule1", "example")); + Node node2 = new Node(new Rule("org.example", "rule2", "example")); + Node node3 = new Node(new Rule("org.example", "rule3", "example")); + Node node4 = new Node(new Rule("org.example", "rule4", "example")); + Node node5 = new Node(new Rule("org.example", "rule5", "example")); + + Node.linkNodes(node1, node2, ReactivityType.POSITIVE); + Node.linkNodes(node1, node3, ReactivityType.NEGATIVE); + Node.linkNodes(node2, node4, ReactivityType.UNKNOWN); + Node.linkNodes(node3, node5, ReactivityType.POSITIVE); + + Map nodeMap = new HashMap<>(); + nodeMap.put(node1.getFqdn(), node1); + nodeMap.put(node2.getFqdn(), node2); + nodeMap.put(node3.getFqdn(), node3); + nodeMap.put(node4.getFqdn(), node4); + nodeMap.put(node5.getFqdn(), node5); + + return new Graph(nodeMap); + } +} diff --git a/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/resources/logback-test.xml b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/resources/logback-test.xml new file mode 100644 index 000000000000..32ecf9c8d4c0 --- /dev/null +++ b/drools-impact-analysis/drools-impact-analysis-graph/drools-impact-analysis-graph-json/src/test/resources/logback-test.xml @@ -0,0 +1,39 @@ + + + + + + + %date{HH:mm:ss.SSS} [%thread] %-5level %class{36} %msg%n + + + + + + + + + + + + + diff --git a/drools-impact-analysis/drools-impact-analysis-graph/pom.xml b/drools-impact-analysis/drools-impact-analysis-graph/pom.xml index 589f0965d16e..5e220a4c428d 100644 --- a/drools-impact-analysis/drools-impact-analysis-graph/pom.xml +++ b/drools-impact-analysis/drools-impact-analysis-graph/pom.xml @@ -37,6 +37,7 @@ drools-impact-analysis-graph-common drools-impact-analysis-graph-graphviz + drools-impact-analysis-graph-json