simple read/write files

This commit is contained in:
2024-05-12 17:43:13 +02:00
parent bebe0fb5d6
commit f76000f6c5
9 changed files with 168 additions and 65 deletions

View File

@@ -43,6 +43,12 @@
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@@ -1,10 +1,13 @@
package com.usatiuk.dhfs.storage.api;
import com.google.protobuf.ByteString;
import com.usatiuk.dhfs.storage.repository.ObjectRepository;
import io.quarkus.grpc.GrpcService;
import io.smallrye.mutiny.Uni;
import jakarta.inject.Inject;
import java.nio.ByteBuffer;
@GrpcService
public class DhfsObjectGrpcService implements DhfsObjectGrpc {
@Inject
@@ -20,13 +23,31 @@ public class DhfsObjectGrpcService implements DhfsObjectGrpc {
@Override
public Uni<ReadObjectReply> readObject(ReadObjectRequest request) {
return Uni.createFrom().item("Hello ")
.map(msg -> ReadObjectReply.newBuilder().build());
return objectRepository.readObject(request.getNamespace(), request.getName())
.map(n -> ReadObjectReply.newBuilder().setData(ByteString.copyFrom(n.getData())).build());
}
@Override
public Uni<WriteObjectReply> writeObject(WriteObjectRequest request) {
return Uni.createFrom().item("Hello ")
.map(msg -> WriteObjectReply.newBuilder().build());
return objectRepository.writeObject(request.getNamespace(), request.getName(), ByteBuffer.wrap(request.getData().toByteArray()))
.map(n -> WriteObjectReply.newBuilder().build());
}
@Override
public Uni<DeleteObjectReply> deleteObject(DeleteObjectRequest request) {
return objectRepository.deleteObject(request.getNamespace(), request.getName())
.map(n -> DeleteObjectReply.newBuilder().build());
}
@Override
public Uni<CreateNamespaceReply> createNamespace(CreateNamespaceRequest request) {
return objectRepository.createNamespace(request.getNamespace())
.map(n -> CreateNamespaceReply.newBuilder().build());
}
@Override
public Uni<DeleteNamespaceReply> deleteNamespace(DeleteNamespaceRequest request) {
return objectRepository.deleteNamespace(request.getNamespace())
.map(n -> DeleteNamespaceReply.newBuilder().build());
}
}

View File

@@ -1,5 +1,12 @@
package com.usatiuk.dhfs.storage.data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Accessors(chain = true)
@Getter
@Setter
public class Namespace {
String name;
}

View File

@@ -1,7 +1,14 @@
package com.usatiuk.dhfs.storage.data;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.nio.ByteBuffer;
@Accessors(chain = true)
@Getter
@Setter
public class Object {
Namespace namespace;

View File

@@ -14,5 +14,12 @@ public interface ObjectRepository {
@Nonnull
public Uni<Object> readObject(String namespace, String name);
@Nonnull
public Uni<Void> writeObject(String namespace, String name, Object data);
public Uni<Void> writeObject(String namespace, String name, ByteBuffer data);
@Nonnull
public Uni<Void> deleteObject(String namespace, String name);
@Nonnull
public Uni<Void> createNamespace(String namespace);
@Nonnull
public Uni<Void> deleteNamespace(String namespace);
}

View File

@@ -1,21 +1,24 @@
package com.usatiuk.dhfs.storage.repository;
import com.usatiuk.dhfs.storage.data.Namespace;
import com.usatiuk.dhfs.storage.data.Object;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.quarkus.logging.Log;
import io.quarkus.runtime.Shutdown;
import io.quarkus.runtime.Startup;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.core.buffer.Buffer;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@ApplicationScoped
public class SimpleFileObjectRepository implements ObjectRepository {
@@ -27,11 +30,7 @@ public class SimpleFileObjectRepository implements ObjectRepository {
@Startup
void init() {
if (!Paths.get(root).toFile().exists()) {
Paths.get(root).toFile().mkdirs();
Log.info("Creted root " + root);
}
Paths.get(root).toFile().mkdirs();
Log.info("Initializing with root " + root);
}
@@ -40,55 +39,71 @@ public class SimpleFileObjectRepository implements ObjectRepository {
Log.info("Shutdown");
}
private Multi<String> TraverseDir(String dir) {
return vertx.fileSystem().readDir(dir).onItem().transformToMulti(
t -> {
List<Multi<String>> results = new ArrayList<>();
t.forEach(entry -> {
if (Paths.get(entry).toFile().isDirectory()) {
results.add(TraverseDir(Paths.get(entry).toString()));
} else {
results.add(Multi.createFrom().item(entry));
}
});
return Multi.createBy().merging().streams(results);
}
);
}
@Nonnull
@Override
public Multi<String> findObjects(String namespace, String prefix) {
Path path = Paths.get(root, namespace, prefix);
Path rootDir = path.toFile().isDirectory() ? path : path.getParent();
String prefixInRoot = path.toFile().isDirectory() ? "" : path.getFileName().toString();
return vertx.fileSystem().readDir(rootDir.toString()).onItem().transformToMulti(
t -> {
List<Multi<String>> results = new ArrayList<>();
t.forEach(entry -> {
if (entry.startsWith(prefixInRoot)) {
if (Paths.get(entry).toFile().isDirectory()) {
results.add(TraverseDir(Paths.get(entry).toString()));
} else {
results.add(Multi.createFrom().item(entry));
}
}
});
return Multi.createBy().merging().streams(results);
}
).map(f -> rootDir.relativize(Paths.get(f)).toString());
Path nsRoot = Paths.get(root, namespace);
if (!nsRoot.toFile().isDirectory())
throw new StatusRuntimeException(Status.NOT_FOUND);
return vertx.fileSystem().readDir(nsRoot.toString()).onItem()
.transformToMulti(v -> Multi.createFrom().iterable(v))
.select().where(n -> n.startsWith(prefix))
.map(f -> nsRoot.relativize(Paths.get(f)).toString());
}
@Nonnull
@Override
public Uni<Object> readObject(String namespace, String name) {
return null;
var ret = new Object();
ret.setName(name)
.setNamespace(new Namespace().setName(namespace));
var file = Path.of(root, namespace, name);
if (!file.toFile().exists())
throw new StatusRuntimeException(Status.NOT_FOUND);
return vertx.fileSystem().readFile(file.toString()).map(r -> ret.setData(ByteBuffer.wrap(r.getBytes())));
}
@Nonnull
@Override
public Uni<Void> writeObject(String namespace, String name, Object data) {
return null;
public Uni<Void> writeObject(String namespace, String name, ByteBuffer data) {
var file = Path.of(root, namespace, name);
return vertx.fileSystem().writeFile(file.toString(), Buffer.buffer(data.array()));
}
@Nonnull
@Override
public Uni<Void> deleteObject(String namespace, String name) {
var file = Path.of(root, namespace, name);
if (!file.toFile().exists())
throw new StatusRuntimeException(Status.NOT_FOUND);
return vertx.fileSystem().delete(file.toString());
}
@Nonnull
@Override
public Uni<Void> createNamespace(String namespace) {
if (Paths.get(root, namespace).toFile().exists())
return Uni.createFrom().voidItem();
if (!Paths.get(root, namespace).toFile().mkdirs())
throw new StatusRuntimeException(Status.INTERNAL);
return Uni.createFrom().voidItem();
}
@Nonnull
@Override
public Uni<Void> deleteNamespace(String namespace) {
if (!Paths.get(root, namespace).toFile().exists())
throw new StatusRuntimeException(Status.NOT_FOUND);
if (!Paths.get(root, namespace).toFile().delete())
throw new StatusRuntimeException(Status.INTERNAL);
return Uni.createFrom().voidItem();
}
}

View File

@@ -11,6 +11,9 @@ service DhfsObjectGrpc {
rpc ReadObject (ReadObjectRequest) returns (ReadObjectReply) {}
rpc WriteObject (WriteObjectRequest) returns (WriteObjectReply) {}
rpc DeleteObject (DeleteObjectRequest) returns (DeleteObjectReply) {}
rpc CreateNamespace (CreateNamespaceRequest) returns (CreateNamespaceReply) {}
rpc DeleteNamespace (DeleteNamespaceRequest) returns (DeleteNamespaceReply) {}
}
message FindObjectsRequest {
@@ -31,7 +34,6 @@ message ReadObjectRequest {
}
message ReadObjectReply {
string message = 1;
bytes data = 10;
}
@@ -42,7 +44,6 @@ message WriteObjectRequest {
}
message WriteObjectReply {
bool ok = 1;
}
message DeleteObjectRequest {
@@ -51,5 +52,19 @@ message DeleteObjectRequest {
}
message DeleteObjectReply {
bool ok = 1;
}
message DeleteNamespaceRequest {
string namespace = 1;
}
message DeleteNamespaceReply {
}
message CreateNamespaceRequest {
string namespace = 1;
}
message CreateNamespaceReply {
}

View File

@@ -1,17 +1,18 @@
package com.usatiuk.dhfs.storage;
import com.usatiuk.dhfs.storage.api.DhfsObjectGrpc;
import com.usatiuk.dhfs.storage.api.FindObjectsReply;
import com.usatiuk.dhfs.storage.api.FindObjectsRequest;
import com.google.protobuf.ByteString;
import com.usatiuk.dhfs.storage.api.*;
import io.quarkus.grpc.GrpcClient;
import io.quarkus.test.junit.QuarkusTest;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.util.List;
@QuarkusTest
class DhfsObjectGrpcService {
class DhfsObjectGrpcService extends SimpleFileRepoTest {
@GrpcClient
DhfsObjectGrpc dhfsObjectGrpc;
@@ -19,9 +20,21 @@ class DhfsObjectGrpcService {
String tempDirectory;
@Test
void testFind() {
FindObjectsReply reply = dhfsObjectGrpc
.findObjects(FindObjectsRequest.newBuilder().setNamespace("TestNs").build()).await().atMost(Duration.ofSeconds(5));
void writeReadTest() {
dhfsObjectGrpc.createNamespace(
CreateNamespaceRequest.newBuilder().setNamespace("testns").build())
.await().atMost(Duration.ofSeconds(5));
dhfsObjectGrpc.writeObject(
WriteObjectRequest.newBuilder().setNamespace("testns").setName("cool_file")
.setData(ByteString.copyFrom("Hello world".getBytes())).build())
.await().atMost(Duration.ofSeconds(5));
var read = dhfsObjectGrpc.readObject(
ReadObjectRequest.newBuilder().setNamespace("testns").setName("cool_file").build())
.await().atMost(Duration.ofSeconds(5));
Assertions.assertArrayEquals(read.getData().toByteArray(), "Hello world".getBytes());
var found = dhfsObjectGrpc.findObjects(FindObjectsRequest.newBuilder().setNamespace("testns").build())
.await().atMost(Duration.ofSeconds(5));
Assertions.assertIterableEquals(found.getFoundList().stream().map(l -> l.getName()).toList(), List.of("cool_file"));
}
}

View File

@@ -2,21 +2,33 @@ package com.usatiuk.dhfs.storage;
import io.quarkus.test.junit.QuarkusTest;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
@QuarkusTest
public abstract class SimpleFileRepoTest {
@ConfigProperty(name = "dhfs.filerepo.root")
String tempDirectory;
void purgeDirectory(File dir) {
for (File file : Objects.requireNonNull(dir.listFiles())) {
if (file.isDirectory())
purgeDirectory(file);
file.delete();
}
}
@BeforeEach
void setup() {
purgeDirectory(Path.of(tempDirectory).toFile());
}
@AfterAll
static void teardown() {
@AfterEach
void teardown() {
purgeDirectory(Path.of(tempDirectory).toFile());
}
}