mirror of
https://github.com/usatiuk/dhfs.git
synced 2025-10-28 20:47:49 +01:00
simple read/write files
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user