From 155edacb2b072225a5cbd73024b23e3302ca85cd Mon Sep 17 00:00:00 2001 From: yichen88 Date: Tue, 25 May 2021 17:13:48 +0200 Subject: [PATCH 1/5] Add orphan node check Signed-off-by: yichen88 --- .../afs/cassandra/CassandraAppStorage.java | 36 +++++++++++++++++++ .../cassandra/CassandraAppStorageTest.java | 17 ++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java index c9ad9cbc..a8621cde 100644 --- a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java +++ b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java @@ -48,6 +48,8 @@ public class CassandraAppStorage extends AbstractAppStorage { public static final String REF_NOT_FOUND = "REFERENCE_NOT_FOUND"; + public static final String ORPHAN_NODE = "ORPHAN_NODE"; + private final String fileSystemName; private final Supplier contextSupplier; @@ -1504,6 +1506,9 @@ public List checkFileSystem(FileSystemCheckOptions options case REF_NOT_FOUND: checkReferenceNotFound(results, options); break; + case ORPHAN_NODE: + checkOrphanNode(results, options); + break; default: LOGGER.warn("Check {} not supported in {}", type, getClass()); } @@ -1512,6 +1517,37 @@ public List checkFileSystem(FileSystemCheckOptions options return results; } + private void checkOrphanNode(List results, FileSystemCheckOptions options) { + List statements = new ArrayList<>(); + // get all child id which parent name is null + ResultSet resultSet = getSession().execute(select(ID, CHILD_ID, NAME, CHILD_NAME).from(CHILDREN_BY_NAME_AND_CLASS)); + for (Row row : resultSet) { + if (row.getString(NAME) == null) { + UUID nodeId = row.getUUID(CHILD_ID); + String nodeName = row.getString(CHILD_NAME); + UUID fakeParentId = row.getUUID(ID); + FileSystemCheckIssue issue = new FileSystemCheckIssue().setNodeId(nodeId.toString()) + .setNodeName(nodeName) + .setType(ORPHAN_NODE) + .setDescription(nodeName + "(" + nodeId + ") is an orphan node. Its fake parent id:" + fakeParentId); + if (options.isRepair()) { + statements.add(delete().from(CHILDREN_BY_NAME_AND_CLASS) + .where(eq(ID, nodeId))); + statements.add(delete().from(CHILDREN_BY_NAME_AND_CLASS) + .where(eq(ID, fakeParentId))); + issue.setRepaired(true); + issue.setResolutionDescription("Delete row and its parent row"); + } + results.add(issue); + } + } + if (options.isRepair()) { + for (Statement statement : statements) { + getSession().execute(statement); + } + } + } + private void checkReferenceNotFound(List results, FileSystemCheckOptions options) { List statements = new ArrayList<>(); Set notFoundIds = new HashSet<>(); diff --git a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java index b1428a93..ae27c792 100644 --- a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java +++ b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java @@ -26,7 +26,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto; import static com.powsybl.afs.cassandra.CassandraConstants.*; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.*; +import static org.junit.Assert.fail; import static org.junit.Assert.*; /** @@ -48,6 +49,20 @@ protected void nextDependentTests() { testSupportedChecks(); testInconsistendNodeRepair(); testAbsentChildRepair(); + testOrphanNodeRepair(); + } + + private void testOrphanNodeRepair() { + NodeInfo orphanNode = storage.createNode(UUIDs.timeBased().toString(), "orphanNodes", FOLDER_PSEUDO_CLASS, "", 0, new NodeGenericMetadata()); + assertThatThrownBy(() -> storage.getParentNode(orphanNode.getId())).isInstanceOf(NullPointerException.class); + FileSystemCheckOptions repairOption = new FileSystemCheckOptionsBuilder() + .addCheckTypes(CassandraAppStorage.ORPHAN_NODE) + .repair().build(); + List issues = storage.checkFileSystem(repairOption); + assertThat(issues).hasOnlyOneElementSatisfying(i -> assertEquals(orphanNode.getId(), i.getNodeId())); + assertThatThrownBy(() -> storage.getParentNode(orphanNode.getId())) + .isInstanceOf(CassandraAfsException.class) + .hasMessageContaining("not found"); } void testInconsistendNodeRepair() { From b666a70974ffe886c935144a6524bb491471f5e2 Mon Sep 17 00:00:00 2001 From: yichen88 Date: Thu, 27 May 2021 11:02:16 +0200 Subject: [PATCH 2/5] Fix Signed-off-by: yichen88 --- .../afs/cassandra/CassandraAppStorage.java | 21 +++++++++--------- .../cassandra/CassandraAppStorageTest.java | 22 +++++++++++++++++-- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java index a8621cde..5478fbb3 100644 --- a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java +++ b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java @@ -1490,7 +1490,7 @@ public void close() { @Override public List getSupportedFileSystemChecks() { - return ImmutableList.of(FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, REF_NOT_FOUND); + return ImmutableList.of(FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, REF_NOT_FOUND, ORPHAN_NODE); } @Override @@ -1518,9 +1518,10 @@ public List checkFileSystem(FileSystemCheckOptions options } private void checkOrphanNode(List results, FileSystemCheckOptions options) { - List statements = new ArrayList<>(); // get all child id which parent name is null ResultSet resultSet = getSession().execute(select(ID, CHILD_ID, NAME, CHILD_NAME).from(CHILDREN_BY_NAME_AND_CLASS)); + List orphanIds = new ArrayList<>(); + Set fakeParentIds = new HashSet<>(); for (Row row : resultSet) { if (row.getString(NAME) == null) { UUID nodeId = row.getUUID(CHILD_ID); @@ -1529,21 +1530,21 @@ private void checkOrphanNode(List results, FileSystemCheck FileSystemCheckIssue issue = new FileSystemCheckIssue().setNodeId(nodeId.toString()) .setNodeName(nodeName) .setType(ORPHAN_NODE) - .setDescription(nodeName + "(" + nodeId + ") is an orphan node. Its fake parent id:" + fakeParentId); + .setDescription(nodeName + "(" + nodeId + ") is an orphan node. Its fake parent id=" + fakeParentId); if (options.isRepair()) { - statements.add(delete().from(CHILDREN_BY_NAME_AND_CLASS) - .where(eq(ID, nodeId))); - statements.add(delete().from(CHILDREN_BY_NAME_AND_CLASS) - .where(eq(ID, fakeParentId))); + orphanIds.add(nodeId); + fakeParentIds.add(fakeParentId); issue.setRepaired(true); - issue.setResolutionDescription("Delete row and its parent row"); + issue.setResolutionDescription("Deleted node [name=" + nodeName + ", id=" + nodeId + "] and reference to null name node [id=" + fakeParentId + "]"); } results.add(issue); } } if (options.isRepair()) { - for (Statement statement : statements) { - getSession().execute(statement); + orphanIds.forEach(this::deleteNode); + for (UUID fakeParentId : fakeParentIds) { + getSession().execute(delete().from(CHILDREN_BY_NAME_AND_CLASS) + .where(eq(ID, fakeParentId))); } } } diff --git a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java index ae27c792..882ecc39 100644 --- a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java +++ b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java @@ -16,6 +16,9 @@ import org.cassandraunit.dataset.cql.ClassPathCQLDataSet; import org.junit.Rule; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -54,15 +57,30 @@ protected void nextDependentTests() { private void testOrphanNodeRepair() { NodeInfo orphanNode = storage.createNode(UUIDs.timeBased().toString(), "orphanNodes", FOLDER_PSEUDO_CLASS, "", 0, new NodeGenericMetadata()); - assertThatThrownBy(() -> storage.getParentNode(orphanNode.getId())).isInstanceOf(NullPointerException.class); + storage.setConsistent(orphanNode.getId()); + try (OutputStream os = storage.writeBinaryData(orphanNode.getId(), "blob")) { + os.write("word2".getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + fail(); + } + storage.flush(); + NodeInfo orphanChild = storage.createNode(orphanNode.getId(), "orphanChild", FOLDER_PSEUDO_CLASS, "", 0, new NodeGenericMetadata()); + storage.setConsistent(orphanChild.getId()); FileSystemCheckOptions repairOption = new FileSystemCheckOptionsBuilder() .addCheckTypes(CassandraAppStorage.ORPHAN_NODE) .repair().build(); List issues = storage.checkFileSystem(repairOption); assertThat(issues).hasOnlyOneElementSatisfying(i -> assertEquals(orphanNode.getId(), i.getNodeId())); + assertThatThrownBy(() -> storage.getNodeInfo(orphanNode.getId())) + .isInstanceOf(CassandraAfsException.class) + .hasMessageContaining("not found"); assertThatThrownBy(() -> storage.getParentNode(orphanNode.getId())) .isInstanceOf(CassandraAfsException.class) .hasMessageContaining("not found"); + assertThatThrownBy(() -> storage.getNodeInfo(orphanChild.getId())) + .isInstanceOf(CassandraAfsException.class) + .hasMessageContaining("not found"); + assertThat(storage.getDataNames(orphanNode.getId())).isEmpty(); } void testInconsistendNodeRepair() { @@ -162,6 +180,6 @@ void testAbsentChildRepair() { void testSupportedChecks() { assertThat(storage.getSupportedFileSystemChecks()) - .containsExactlyInAnyOrder(CassandraAppStorage.REF_NOT_FOUND, FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES); + .containsExactlyInAnyOrder(CassandraAppStorage.REF_NOT_FOUND, FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, CassandraAppStorage.ORPHAN_NODE); } } From 45e0fca275470564d0c243af7dc771e5fb914c22 Mon Sep 17 00:00:00 2001 From: yichen88 Date: Thu, 27 May 2021 14:16:57 +0200 Subject: [PATCH 3/5] Add orphan data check Signed-off-by: yichen88 --- .../afs/cassandra/CassandraAppStorage.java | 63 +++++++++++++++---- .../cassandra/CassandraAppStorageTest.java | 53 +++++++++++++--- 2 files changed, 94 insertions(+), 22 deletions(-) diff --git a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java index 5478fbb3..15d4f54e 100644 --- a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java +++ b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java @@ -50,6 +50,8 @@ public class CassandraAppStorage extends AbstractAppStorage { public static final String ORPHAN_NODE = "ORPHAN_NODE"; + public static final String ORPHAN_DATA = "ORPHAN_DATA"; + private final String fileSystemName; private final Supplier contextSupplier; @@ -832,9 +834,7 @@ private UUID deleteNode(UUID nodeUuid) { statements.add(delete().from(DEPENDENCIES).where(eq(FROM_ID, nodeUuid))); statements.add(delete().from(BACKWARD_DEPENDENCIES).where(in(TO_ID, new ArrayList<>(dependencies.values())))); - for (Statement statement : statements) { - getSession().execute(statement); - } + executeStatements(statements); backwardDependencies.entrySet().stream().flatMap(dep -> dep.getValue().stream().map(depUuid -> Pair.of(dep.getKey(), depUuid))).forEach(dep -> { pushEvent(new DependencyRemoved(dep.getValue().toString(), dep.getKey()), APPSTORAGE_DEPENDENCY_TOPIC); @@ -1118,9 +1118,7 @@ public boolean removeData(String nodeId, String name) { List statements = new ArrayList<>(); removeData(nodeUuid, name, statements); - for (Statement statement : statements) { - getSession().execute(statement); - } + executeStatements(statements); return true; } @@ -1350,9 +1348,7 @@ public void clearTimeSeries(String nodeId) { List statements = new ArrayList<>(); clearTimeSeries(nodeUuid, statements); - for (Statement statement : statements) { - getSession().execute(statement); - } + executeStatements(statements); pushEvent(new TimeSeriesCleared(nodeUuid.toString()), APPSTORAGE_TIMESERIES_TOPIC); } @@ -1490,7 +1486,8 @@ public void close() { @Override public List getSupportedFileSystemChecks() { - return ImmutableList.of(FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, REF_NOT_FOUND, ORPHAN_NODE); + return ImmutableList.of(FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, + REF_NOT_FOUND, ORPHAN_NODE, ORPHAN_DATA); } @Override @@ -1509,6 +1506,9 @@ public List checkFileSystem(FileSystemCheckOptions options case ORPHAN_NODE: checkOrphanNode(results, options); break; + case ORPHAN_DATA: + checkOrphanData(results, options); + break; default: LOGGER.warn("Check {} not supported in {}", type, getClass()); } @@ -1517,6 +1517,39 @@ public List checkFileSystem(FileSystemCheckOptions options return results; } + private void checkOrphanData(List results, FileSystemCheckOptions options) { + Set existingNodeIds = new HashSet<>(); + Set orphanDataIds = new HashSet<>(); + ResultSet existingNodes = getSession().execute(select(ID).distinct().from(CHILDREN_BY_NAME_AND_CLASS)); + for (Row row : existingNodes) { + existingNodeIds.add(row.getUUID(ID)); + } + ResultSet nodeDatas = getSession().execute(select(ID, NAME).distinct().from(NODE_DATA)); + for (Row row : nodeDatas) { + UUID uuid = row.getUUID(ID); + if (!existingNodeIds.contains(uuid)) { + orphanDataIds.add(uuid); + FileSystemCheckIssue issue = new FileSystemCheckIssue().setNodeName("N/A") + .setNodeId(uuid.toString()) + .setType(ORPHAN_DATA) + .setDescription("Orphan data(" + row.getString(NAME) + ") is binding to non-existing node(" + uuid + ")") + .setRepaired(options.isRepair()); + if (options.isRepair()) { + issue.setRepaired(true) + .setResolutionDescription("Delete orphan data(" + row.getString(NAME) + ")."); + } + results.add(issue); + } + } + if (options.isRepair()) { + List statements = new ArrayList<>(); + for (UUID id : orphanDataIds) { + removeAllData(id, statements); + } + executeStatements(statements); + } + } + private void checkOrphanNode(List results, FileSystemCheckOptions options) { // get all child id which parent name is null ResultSet resultSet = getSession().execute(select(ID, CHILD_ID, NAME, CHILD_NAME).from(CHILDREN_BY_NAME_AND_CLASS)); @@ -1576,9 +1609,13 @@ private void checkReferenceNotFound(List results, FileSyst } } if (options.isRepair()) { - for (Statement statement : statements) { - getSession().execute(statement); - } + executeStatements(statements); + } + } + + private void executeStatements(List statements) { + for (Statement statement : statements) { + getSession().execute(statement); } } diff --git a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java index 882ecc39..ace9d9be 100644 --- a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java +++ b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java @@ -53,6 +53,37 @@ protected void nextDependentTests() { testInconsistendNodeRepair(); testAbsentChildRepair(); testOrphanNodeRepair(); + testOrphanDataRepair(); + } + + private void testOrphanDataRepair() { + NodeInfo rootFolderInfo = storage.createRootNodeIfNotExists(storage.getFileSystemName(), FOLDER_PSEUDO_CLASS); + try (OutputStream os = storage.writeBinaryData(rootFolderInfo.getId(), "should_exist")) { + os.write("word2".getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + fail(); + } + String orphanDataId = UUIDs.timeBased().toString(); + try (OutputStream os = storage.writeBinaryData(orphanDataId, "blob")) { + os.write("word2".getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + fail(); + } + assertThat(storage.getDataNames(orphanDataId)).containsOnly("blob"); + assertAfsNodeNotFound(orphanDataId); + + FileSystemCheckOptions repairOption = new FileSystemCheckOptionsBuilder() + .addCheckTypes(CassandraAppStorage.ORPHAN_DATA) + .repair().build(); + List issues = storage.checkFileSystem(repairOption); + assertThat(issues).hasOnlyOneElementSatisfying(i -> { + assertEquals(orphanDataId, i.getNodeId()); + assertEquals(CassandraAppStorage.ORPHAN_DATA, i.getType()); + assertEquals("N/A", i.getNodeName()); + }); + + assertTrue(storage.dataExists(rootFolderInfo.getId(), "should_exist")); + assertFalse(storage.dataExists(orphanDataId, "blob")); } private void testOrphanNodeRepair() { @@ -71,16 +102,16 @@ private void testOrphanNodeRepair() { .repair().build(); List issues = storage.checkFileSystem(repairOption); assertThat(issues).hasOnlyOneElementSatisfying(i -> assertEquals(orphanNode.getId(), i.getNodeId())); - assertThatThrownBy(() -> storage.getNodeInfo(orphanNode.getId())) - .isInstanceOf(CassandraAfsException.class) - .hasMessageContaining("not found"); - assertThatThrownBy(() -> storage.getParentNode(orphanNode.getId())) - .isInstanceOf(CassandraAfsException.class) - .hasMessageContaining("not found"); - assertThatThrownBy(() -> storage.getNodeInfo(orphanChild.getId())) + assertAfsNodeNotFound(orphanNode.getId()); + assertAfsNodeNotFound(orphanNode.getId()); + assertAfsNodeNotFound(orphanChild.getId()); + assertThat(storage.getDataNames(orphanNode.getId())).isEmpty(); + } + + private void assertAfsNodeNotFound(String id) { + assertThatThrownBy(() -> storage.getNodeInfo(id)) .isInstanceOf(CassandraAfsException.class) .hasMessageContaining("not found"); - assertThat(storage.getDataNames(orphanNode.getId())).isEmpty(); } void testInconsistendNodeRepair() { @@ -180,6 +211,10 @@ void testAbsentChildRepair() { void testSupportedChecks() { assertThat(storage.getSupportedFileSystemChecks()) - .containsExactlyInAnyOrder(CassandraAppStorage.REF_NOT_FOUND, FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, CassandraAppStorage.ORPHAN_NODE); + .containsExactlyInAnyOrder(CassandraAppStorage.REF_NOT_FOUND, + FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, + CassandraAppStorage.ORPHAN_NODE, + CassandraAppStorage.ORPHAN_DATA + ); } } From d584abba5fa912805be2d48e5519eaebe1d5de21 Mon Sep 17 00:00:00 2001 From: yichen88 Date: Fri, 28 May 2021 15:41:40 +0200 Subject: [PATCH 4/5] Add show afs content in tree Signed-off-by: yichen88 --- .../afs/cassandra/CassandraAppStorage.java | 12 +- .../cassandra/DisplayTreeIssueBuilder.java | 121 ++++++++++++++++++ .../cassandra/CassandraAppStorageTest.java | 3 +- 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java diff --git a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java index 15d4f54e..89144e3d 100644 --- a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java +++ b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/CassandraAppStorage.java @@ -52,6 +52,8 @@ public class CassandraAppStorage extends AbstractAppStorage { public static final String ORPHAN_DATA = "ORPHAN_DATA"; + public static final String DISPLAY_TREE = "DISPLAY_TREE"; + private final String fileSystemName; private final Supplier contextSupplier; @@ -1487,7 +1489,7 @@ public void close() { @Override public List getSupportedFileSystemChecks() { return ImmutableList.of(FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, - REF_NOT_FOUND, ORPHAN_NODE, ORPHAN_DATA); + REF_NOT_FOUND, ORPHAN_NODE, ORPHAN_DATA, DISPLAY_TREE); } @Override @@ -1509,6 +1511,9 @@ public List checkFileSystem(FileSystemCheckOptions options case ORPHAN_DATA: checkOrphanData(results, options); break; + case DISPLAY_TREE: + displayTree(results); + break; default: LOGGER.warn("Check {} not supported in {}", type, getClass()); } @@ -1517,6 +1522,11 @@ public List checkFileSystem(FileSystemCheckOptions options return results; } + private void displayTree(List results) { + ResultSet allTables = getSession().execute(select(ID, NAME, PSEUDO_CLASS, CHILD_ID, PARENT_ID).from(CHILDREN_BY_NAME_AND_CLASS)); + results.add(new DisplayTreeIssueBuilder(allTables).build()); + } + private void checkOrphanData(List results, FileSystemCheckOptions options) { Set existingNodeIds = new HashSet<>(); Set orphanDataIds = new HashSet<>(); diff --git a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java new file mode 100644 index 00000000..32ebcae8 --- /dev/null +++ b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2021, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.afs.cassandra; + +import com.datastax.driver.core.ResultSet; +import com.datastax.driver.core.Row; +import com.powsybl.afs.storage.check.FileSystemCheckIssue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Yichen TANG + */ +class DisplayTreeIssueBuilder { + + private final TreeNode root = new TreeNode("display tree"); + private final Map nodes = new HashMap<>(); + private final Map infos = new HashMap<>(); + + DisplayTreeIssueBuilder(ResultSet resultSet) { + for (Row row : resultSet) { + RowBean rowBean = new RowBean(row.getUUID(0).toString(), + row.getString(1), + row.getString(2), + row.getUUID(3) == null ? null : row.getUUID(3).toString()); + cacheNodeInfo(rowBean); + nodes.computeIfAbsent(rowBean.id, TreeNode::new); + if (rowBean.cId != null) { + nodes.computeIfAbsent(rowBean.cId, TreeNode::new); + nodes.get(rowBean.id).children.add(rowBean.cId); + } + if (row.getUUID(4) == null) { + root.children.add(rowBean.id); + } + } + } + + private void cacheNodeInfo(RowBean bean) { + if (!infos.containsKey(bean.id)) { + infos.put(bean.id, new NodeInfo(bean.name, bean.pseudoClass)); + } + } + + static class NodeInfo { + + private final String name; + private final String pseudoClass; + + NodeInfo(String name, String pseudoClass) { + this.name = name; + this.pseudoClass = pseudoClass; + } + + @Override + public String toString() { + return name + "(" + pseudoClass + ")"; + } + } + + static class RowBean { + + private final String id; + private final String name; + private final String pseudoClass; + private final String cId; + + RowBean(String id, String name, String pseudoClass, String cId) { + this.id = id; + this.name = name; + this.pseudoClass = pseudoClass; + this.cId = cId; + } + } + + static class TreeNode { + String id; + String name; + List children = new ArrayList<>(); + + TreeNode(String id) { + this.id = id; + } + + TreeNode(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String toString() { + return id + "_" + name; + } + } + + FileSystemCheckIssue build() { + StringBuilder sb = new StringBuilder(); + printNode(sb, root, 0); + return new FileSystemCheckIssue().setDescription(sb.toString()); + } + + void printNode(StringBuilder sb, TreeNode node, int level) { + for (int i = 0; i < level - 1; i++) { + sb.append(" "); + } + if (level != 0) { + sb.append(infos.get(node.id)) + .append(" ") + .append(node.id) + .append("\n"); + } + int nextLevel = level + 1; + node.children.forEach(e -> printNode(sb, nodes.get(e), nextLevel)); + } +} diff --git a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java index ace9d9be..c7004737 100644 --- a/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java +++ b/afs-cassandra/src/test/java/com/powsybl/afs/cassandra/CassandraAppStorageTest.java @@ -214,7 +214,8 @@ void testSupportedChecks() { .containsExactlyInAnyOrder(CassandraAppStorage.REF_NOT_FOUND, FileSystemCheckOptions.EXPIRED_INCONSISTENT_NODES, CassandraAppStorage.ORPHAN_NODE, - CassandraAppStorage.ORPHAN_DATA + CassandraAppStorage.ORPHAN_DATA, + CassandraAppStorage.DISPLAY_TREE ); } } From fd5a3d2e80a8fc6969c5c5afac93e6389caccf70 Mon Sep 17 00:00:00 2001 From: yichen88 Date: Tue, 1 Jun 2021 10:20:34 +0200 Subject: [PATCH 5/5] Fix Signed-off-by: yichen88 --- .../com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java index 32ebcae8..e7e091d3 100644 --- a/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java +++ b/afs-cassandra/src/main/java/com/powsybl/afs/cassandra/DisplayTreeIssueBuilder.java @@ -10,10 +10,10 @@ import com.datastax.driver.core.Row; import com.powsybl.afs.storage.check.FileSystemCheckIssue; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * @author Yichen TANG @@ -82,7 +82,7 @@ static class RowBean { static class TreeNode { String id; String name; - List children = new ArrayList<>(); + Set children = new HashSet<>(); TreeNode(String id) { this.id = id;