diff --git a/pom.xml b/pom.xml index 38c2decde..b3b60323f 100644 --- a/pom.xml +++ b/pom.xml @@ -137,7 +137,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.14.0 1.8 1.8 @@ -146,6 +146,8 @@ -Xlint:-processing + + -Xlint:-options diff --git a/scheme/pom.xml b/scheme/pom.xml index 08fa74580..1dac3046b 100644 --- a/scheme/pom.xml +++ b/scheme/pom.xml @@ -35,5 +35,10 @@ log4j-slf4j-impl test + + tech.ydb.test + ydb-junit4-support + test + diff --git a/scheme/src/main/java/tech/ydb/scheme/description/DescribePathResult.java b/scheme/src/main/java/tech/ydb/scheme/description/DescribePathResult.java index 38439d5cb..e9da37c5b 100644 --- a/scheme/src/main/java/tech/ydb/scheme/description/DescribePathResult.java +++ b/scheme/src/main/java/tech/ydb/scheme/description/DescribePathResult.java @@ -8,16 +8,23 @@ */ public class DescribePathResult { private final SchemeOperationProtos.Entry self; + private final Entry entry; protected DescribePathResult(SchemeOperationProtos.Entry self) { this.self = self; + this.entry = new Entry(self); } public DescribePathResult(SchemeOperationProtos.DescribePathResult result) { this(result.getSelf()); } + @Deprecated public SchemeOperationProtos.Entry getSelf() { return self; } + + public Entry getEntry() { + return entry; + } } diff --git a/scheme/src/main/java/tech/ydb/scheme/description/Entry.java b/scheme/src/main/java/tech/ydb/scheme/description/Entry.java new file mode 100644 index 000000000..4b3129d7c --- /dev/null +++ b/scheme/src/main/java/tech/ydb/scheme/description/Entry.java @@ -0,0 +1,85 @@ +package tech.ydb.scheme.description; + +import java.util.Objects; + +import tech.ydb.proto.scheme.SchemeOperationProtos; + +/** + * + * @author Aleksandr Gorshenin + */ +public class Entry { + private final String name; + private final String owner; + private final EntryType type; + private final long sizeBytes; + + public Entry(SchemeOperationProtos.Entry pb) { + this.name = pb.getName(); + this.owner = pb.getOwner(); + this.type = EntryType.fromCode(pb.getTypeValue()); + this.sizeBytes = pb.getSizeBytes(); + } + + /** + * @return Name of scheme entry (dir2 of /dir1/dir2) + */ + public String getName() { + return this.name; + } + + /** + * @return SID (Security ID) of user or group + */ + public String getOwner() { + return this.owner; + } + + /** + * Approximate size of entry in bytes. Currently filled for: + * + * Empty (zero) in other cases. + * + * @return Size of entry in bytes + */ + public long getSizeBytes() { + return this.sizeBytes; + } + + public EntryType getType() { + return this.type; + } + + @Override + public int hashCode() { + return Objects.hash(name, owner, type, sizeBytes); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || obj.getClass() != Entry.class) { + return false; + } + Entry o = (Entry) obj; + return Objects.equals(name, o.name) + && Objects.equals(owner, o.owner) + && type == o.type + && sizeBytes == o.sizeBytes; + } + + @Override + public String toString() { + return "Entry{name='" + name + "'" + + (owner == null || owner.isEmpty() ? "" : ", owner='" + owner + "'") + + ", type=" + type + + (sizeBytes == 0 ? "" : ", size=" + Long.toUnsignedString(sizeBytes)) + + "}"; + } +} + diff --git a/scheme/src/main/java/tech/ydb/scheme/description/EntryType.java b/scheme/src/main/java/tech/ydb/scheme/description/EntryType.java new file mode 100644 index 000000000..86279b485 --- /dev/null +++ b/scheme/src/main/java/tech/ydb/scheme/description/EntryType.java @@ -0,0 +1,44 @@ +package tech.ydb.scheme.description; + +/** + * + * @author Aleksandr Gorshenin + */ +public enum EntryType { + UNSPECIFIED(0), + + DIRECTORY(1), + TABLE(2), + PERS_QUEUE_GROUP(3), + DATABASE(4), + RTMR_VOLUME(5), + BLOCK_STORE_VOLUME(6), + COORDINATION_NODE(7), + COLUMN_STORE(12), + COLUMN_TABLE(13), + SEQUENCE(15), + REPLICATION(16), + TOPIC(17), + EXTERNAL_TABLE(18), + EXTERNAL_DATA_SOURCE(19), + VIEW(20); + + private final int code; + + EntryType(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + public static EntryType fromCode(int code) { + for (EntryType type: EntryType.values()) { + if (code == type.code) { + return type; + } + } + return UNSPECIFIED; + } +} diff --git a/scheme/src/main/java/tech/ydb/scheme/description/ListDirectoryResult.java b/scheme/src/main/java/tech/ydb/scheme/description/ListDirectoryResult.java index d0891491f..37d7eaccb 100644 --- a/scheme/src/main/java/tech/ydb/scheme/description/ListDirectoryResult.java +++ b/scheme/src/main/java/tech/ydb/scheme/description/ListDirectoryResult.java @@ -1,6 +1,7 @@ package tech.ydb.scheme.description; import java.util.List; +import java.util.stream.Collectors; import tech.ydb.proto.scheme.SchemeOperationProtos; @@ -10,13 +11,20 @@ public class ListDirectoryResult extends DescribePathResult { private final List children; + private final List entryChildren; public ListDirectoryResult(SchemeOperationProtos.ListDirectoryResult result) { super(result.getSelf()); this.children = result.getChildrenList(); + this.entryChildren = result.getChildrenList().stream().map(Entry::new).collect(Collectors.toList()); } + @Deprecated public List getChildren() { return children; } + + public List getEntryChildren() { + return entryChildren; + } } diff --git a/scheme/src/test/java/tech/ydb/scheme/BaseIntegrationTest.java b/scheme/src/test/java/tech/ydb/scheme/BaseIntegrationTest.java new file mode 100644 index 000000000..428aa0bb8 --- /dev/null +++ b/scheme/src/test/java/tech/ydb/scheme/BaseIntegrationTest.java @@ -0,0 +1,117 @@ +package tech.ydb.scheme; + +import java.util.Optional; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; + +import tech.ydb.core.Result; +import tech.ydb.core.Status; +import tech.ydb.core.StatusCode; +import tech.ydb.scheme.description.DescribePathResult; +import tech.ydb.scheme.description.Entry; +import tech.ydb.scheme.description.EntryType; +import tech.ydb.scheme.description.ListDirectoryResult; +import tech.ydb.test.junit4.GrpcTransportRule; + +/** + * + * @author Aleksandr Gorshenin + */ +public class BaseIntegrationTest { + @ClassRule + public final static GrpcTransportRule transport = new GrpcTransportRule(); + + private final static SchemeClient client = SchemeClient.newClient(transport).build(); + + @AfterClass + public static void close() { + client.close(); + } + + @Test + public void rootPathTest() { + Result describeRoot = client.describePath("/").join(); + Assert.assertTrue(describeRoot.isSuccess()); + DescribePathResult root = describeRoot.getValue(); + Assert.assertEquals("/", root.getEntry().getName()); + Assert.assertEquals("", root.getEntry().getOwner()); + + Result listRoot = client.listDirectory("/").join(); + Assert.assertTrue(listRoot.isSuccess()); + ListDirectoryResult listResult = listRoot.getValue(); + Assert.assertEquals("/", listResult.getEntry().getName()); + Assert.assertEquals("", listResult.getEntry().getOwner()); + Assert.assertEquals("/", listResult.getEntry().getName()); + Assert.assertEquals(0l, listResult.getEntry().getSizeBytes()); + Assert.assertEquals(EntryType.DIRECTORY, listResult.getEntry().getType()); + Assert.assertEquals("Entry{name='/', type=DIRECTORY}", listResult.getEntry().toString()); + + + Assert.assertEquals(1, listResult.getEntryChildren().size()); + listResult.getEntryChildren().get(0); + Entry firstChild = listResult.getEntryChildren().get(0); + Assert.assertTrue(transport.getDatabase().startsWith("/" + firstChild.getName())); + Assert.assertEquals(EntryType.DIRECTORY, firstChild.getType()); + } + + @Test + public void invalidPathTest() { + Result describeInvalid = client.describePath("/invalid-path").join(); + Assert.assertFalse(describeInvalid.isSuccess()); + Assert.assertEquals(StatusCode.SCHEME_ERROR, describeInvalid.getStatus().getCode()); + Assert.assertEquals( + "Status{code = SCHEME_ERROR(code=400070), issues = [Root not found (S_ERROR)]}", + describeInvalid.getStatus().toString() + ); + + Result listInvalid = client.listDirectory("/invalid-path").join(); + Assert.assertFalse(listInvalid.isSuccess()); + Assert.assertEquals(StatusCode.SCHEME_ERROR, listInvalid.getStatus().getCode()); + Assert.assertEquals( + "Status{code = SCHEME_ERROR(code=400070), issues = [Root not found (S_ERROR)]}", + listInvalid.getStatus().toString() + ); + + Status makeDirectory= client.makeDirectory("/invalid-path").join(); + Assert.assertFalse(makeDirectory.isSuccess()); + Assert.assertEquals(StatusCode.BAD_REQUEST, makeDirectory.getCode()); + + Status removeDirectory= client.removeDirectory("/invalid-path").join(); + Assert.assertFalse(removeDirectory.isSuccess()); + Assert.assertEquals(StatusCode.SCHEME_ERROR, removeDirectory.getCode()); + Assert.assertEquals( + "Status{code = SCHEME_ERROR(code=400070), issues = [#200200 Path does not exist (S_ERROR)]}", + removeDirectory.toString() + ); + } + + @Test + public void createAndDeleteTest() { + String basePath = transport.getDatabase(); + String dirName = "test_dir"; + + Status dirCreate1 = client.makeDirectory(basePath + "/" + dirName).join(); + Assert.assertTrue(dirCreate1.isSuccess()); + + // Directory creating is idempotent + Status dirCreate2 = client.makeDirectory(basePath + "/" + dirName).join(); + Assert.assertTrue(dirCreate2.isSuccess()); + + Result dirEntry = client.describePath(basePath + "/" + dirName).join(); + Assert.assertTrue(dirEntry.isSuccess()); + Assert.assertEquals(dirName, dirEntry.getValue().getEntry().getName()); + + Result rootList = client.listDirectory(basePath).join(); + Assert.assertTrue(rootList.isSuccess()); + Optional child = rootList.getValue().getEntryChildren().stream() + .filter(e -> dirName.equals(e.getName())).findFirst(); + Assert.assertTrue(child.isPresent()); + Assert.assertEquals(child.get(), dirEntry.getValue().getEntry()); + + Status dirDelete = client.removeDirectory(basePath + "/" + dirName).join(); + Assert.assertTrue(dirDelete.isSuccess()); + } +} diff --git a/scheme/src/test/resources/log4j2.xml b/scheme/src/test/resources/log4j2.xml new file mode 100644 index 000000000..0515e3a0a --- /dev/null +++ b/scheme/src/test/resources/log4j2.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file