From 97d161ccad26b8f4f06b16f7756b3d749634b270 Mon Sep 17 00:00:00 2001 From: vkovtun <viktor.kovtun@id.ethz.ch> Date: Fri, 28 Jul 2023 18:16:34 +0200 Subject: [PATCH] SSDM-13839: Made tests pass. --- .../afs/dto/operation/CreateOperation.java | 2 +- .../afs/manager/TransactionConnection.java | 4 +- .../operation/CreateOperationExecutor.java | 78 +++++++++++++++++++ .../server/impl/ApiServerAdapter.java | 2 + .../afsserver/worker/proxy/ExecutorProxy.java | 2 +- .../sis/afsserver/core/PublicApiTest.java | 29 +++++++ 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/CreateOperation.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/CreateOperation.java index 0985ee5c0c3..9ab78216afc 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/CreateOperation.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/CreateOperation.java @@ -45,7 +45,7 @@ public class CreateOperation implements Operation { this.owner = owner; this.locks = List.of(new Lock<>(owner, source, LockType.Exclusive)); - this.name = OperationName.Write; + this.name = OperationName.Create; this.source = source; this.directory = directory; } diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java index 8ea39cd14e3..0f6ddafc414 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java @@ -39,6 +39,7 @@ import ch.ethz.sis.afs.dto.operation.ReadOperation; import ch.ethz.sis.afs.dto.operation.WriteOperation; import ch.ethz.sis.afs.exception.AFSExceptions; import ch.ethz.sis.afs.manager.operation.CopyOperationExecutor; +import ch.ethz.sis.afs.manager.operation.CreateOperationExecutor; import ch.ethz.sis.afs.manager.operation.DeleteOperationExecutor; import ch.ethz.sis.afs.manager.operation.ListOperationExecutor; import ch.ethz.sis.afs.manager.operation.MoveOperationExecutor; @@ -63,7 +64,8 @@ public class TransactionConnection implements TransactionalFileSystem { operationExecutors = Map.of(OperationName.Copy, CopyOperationExecutor.getInstance(), OperationName.Delete, DeleteOperationExecutor.getInstance(), OperationName.Move, MoveOperationExecutor.getInstance(), - OperationName.Write, WriteOperationExecutor.getInstance()); + OperationName.Write, WriteOperationExecutor.getInstance(), + OperationName.Create, CreateOperationExecutor.getInstance()); } private LockManager<UUID, String> lockManager; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java new file mode 100644 index 00000000000..76c03ee1887 --- /dev/null +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java @@ -0,0 +1,78 @@ +/* + * Copyright ETH 2022 - 2023 Zürich, Scientific IT Services + * + * Licensed 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 ch.ethz.sis.afs.manager.operation; + +import static ch.ethz.sis.afs.exception.AFSExceptions.MD5NotMatch; +import static ch.ethz.sis.afs.exception.AFSExceptions.PathInStore; +import static ch.ethz.sis.afs.exception.AFSExceptions.PathIsDirectory; + +import java.util.Arrays; + +import ch.ethz.sis.afs.api.dto.File; +import ch.ethz.sis.afs.dto.Transaction; +import ch.ethz.sis.afs.dto.operation.CreateOperation; +import ch.ethz.sis.afs.dto.operation.OperationName; +import ch.ethz.sis.afs.dto.operation.WriteOperation; +import ch.ethz.sis.afs.exception.AFSExceptions; +import ch.ethz.sis.shared.io.IOUtils; +import lombok.NonNull; + +public class CreateOperationExecutor implements OperationExecutor<CreateOperation> { + + // + // Singleton + // + + private static final CreateOperationExecutor INSTANCE; + + static { + INSTANCE = new CreateOperationExecutor(); + } + + private CreateOperationExecutor() { + } + + public static CreateOperationExecutor getInstance() { + return INSTANCE; + } + + // + // Operation + // + + + @Override + public boolean prepare(final @NonNull Transaction transaction, final CreateOperation operation) throws Exception { + // Check that file/directory does not exist + if (IOUtils.exists(operation.getSource())) + { + AFSExceptions.throwInstance(PathInStore, OperationName.Create.name(), operation.getSource()); + } + return true; + } + + @Override + public boolean commit(final @NonNull Transaction transaction, final CreateOperation operation) throws Exception { + final String directoriesToCreate = operation.isDirectory() ? operation.getSource() : IOUtils.getParentPath(operation.getSource()); + IOUtils.createDirectories(directoriesToCreate); + if (!operation.isDirectory()) + { + IOUtils.createFile(operation.getSource()); + } + return true; + } + +} diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java index a56cd2d14c0..af2b47e5a23 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/server/impl/ApiServerAdapter.java @@ -65,6 +65,7 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler case "read": case "isSessionValid": return GET; // all parameters from GET methods come on the query string + case "create": case "write": case "move": case "copy": @@ -148,6 +149,7 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler case "transactionId": methodParameters.put(entry.getKey(), UUID.fromString(value)); break; + case "directory": case "recursively": methodParameters.put(entry.getKey(), Boolean.valueOf(value)); break; diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java index 562efe11241..fe43032d200 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ExecutorProxy.java @@ -109,6 +109,6 @@ public class ExecutorProxy extends AbstractProxy { @Override public @NonNull Boolean create(@NonNull final String owner, @NonNull final String source, @NonNull final Boolean directory) throws Exception { - return workerContext.getConnection().create(source, directory); + return workerContext.getConnection().create(getPath(owner, source), directory); } } diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java index db4169e3aad..85068e6a6d1 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/PublicApiTest.java @@ -31,6 +31,7 @@ import java.util.UUID; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public abstract class PublicApiTest extends AbstractTest @@ -132,6 +133,34 @@ public abstract class PublicApiTest extends AbstractTest assertEquals(FILE_B, list.get(0).getName()); } + @Test + public void create_directory() throws Exception + { + getPublicAPI().create(owner, FILE_B, Boolean.TRUE); + + final List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE); + assertEquals(2, list.size()); + + final List<File> matchedFiles = list.stream().filter(file -> file.getName().equals(FILE_B)).toList(); + assertEquals(1, matchedFiles.size()); + assertTrue(matchedFiles.get(0).getDirectory()); + } + + @Test + public void create_file() throws Exception + { + getPublicAPI().create(owner, FILE_B, Boolean.FALSE); + + final List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE); + + final List<File> matchedFiles = list.stream().filter(file -> file.getName().equals(FILE_B)).toList(); + assertEquals(1, matchedFiles.size()); + assertFalse(matchedFiles.get(0).getDirectory()); + + byte[] bytes = getPublicAPI().read(owner, FILE_B, 0L, 0); + assertEquals(0, bytes.length); + } + @Test public void operation_state_begin_succeed() throws Exception { -- GitLab