From 072d40d66767b17f55174dba37838960e7513f17 Mon Sep 17 00:00:00 2001 From: alaskowski <alaskowski@ethz.ch> Date: Thu, 9 Mar 2023 15:13:31 +0100 Subject: [PATCH] SSDM-13251: Added support for write, copy and move methods. Refactored tests --- .../ethz/sis/afsclient/client/AfsClient.java | 41 ++++- .../sis/afsclient/client/AfsClientTest.java | 50 +++++- .../sis/afsclient/client/DummyHttpServer.java | 30 ++-- .../server/impl/ApiServerAdapter.java | 160 ++++++++++-------- .../ch/ethz/sis/afsserver/ApiClientTest.java | 69 +++++++- .../core/AbstractPublicAPIWrapper.java | 68 +++++--- .../sis/afsserver/core/PublicApiTest.java | 16 +- .../impl/APIServerAdapterWrapper.java | 73 +++++--- .../sis/afsserver/impl/APIServerWrapper.java | 6 +- 9 files changed, 362 insertions(+), 151 deletions(-) diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java index 141c393222a..04643c68493 100644 --- a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java @@ -1,6 +1,9 @@ package ch.ethz.sis.afsclient.client; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.net.Authenticator; import java.net.PasswordAuthentication; import java.net.URI; @@ -21,7 +24,6 @@ import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper; import lombok.NonNull; - public final class AfsClient implements PublicAPI { @@ -81,8 +83,9 @@ public final class AfsClient implements PublicAPI public @NonNull String login(@NonNull final String userId, @NonNull final String password) throws Exception { + Map<String, String> credentials = Map.of("userId", userId, "password", password); String result = request("POST", "login", Map.of(), - (userId + ":" + password).getBytes()); + jsonObjectMapper.writeValue(credentials)); setSessionToken(result); return result; } @@ -98,7 +101,8 @@ public final class AfsClient implements PublicAPI public @NonNull Boolean logout() throws Exception { validateSessionToken(); - Boolean result = request("POST", "logout", Map.of(), getSessionToken().getBytes()); + Boolean result = request("POST", "logout", Map.of(), + jsonObjectMapper.writeValue(Map.of("sessionToken", getSessionToken()))); setSessionToken(null); return result; } @@ -129,14 +133,25 @@ public final class AfsClient implements PublicAPI @NonNull final Long offset, final byte @NonNull [] data, final byte @NonNull [] md5Hash) throws Exception { - return null; + validateSessionToken(); + Map<String, Object> parameters = + Map.of("owner", owner, "source", source, + "offset", offset, "data", data, "md5Hash", md5Hash, + "sessionToken", getSessionToken()); + + return request("POST", "write", Map.of(), jsonObjectMapper.writeValue(parameters)); } @Override public @NonNull Boolean delete(@NonNull final String owner, @NonNull final String source) throws Exception { - return null; + validateSessionToken(); + Map<String, Object> parameters = + Map.of("owner", owner, "source", source, + "sessionToken", getSessionToken()); + + return request("DELETE", "delete", Map.of(), jsonObjectMapper.writeValue(parameters)); } @Override @@ -145,7 +160,13 @@ public final class AfsClient implements PublicAPI @NonNull final String target) throws Exception { - return null; + validateSessionToken(); + Map<String, Object> parameters = + Map.of("sourceOwner", sourceOwner, "source", source, + "targetOwner", targetOwner, "target", target, + "sessionToken", getSessionToken()); + + return request("POST", "copy", Map.of(), jsonObjectMapper.writeValue(parameters)); } @Override @@ -154,7 +175,13 @@ public final class AfsClient implements PublicAPI @NonNull final String target) throws Exception { - return null; + validateSessionToken(); + Map<String, Object> parameters = + Map.of("sourceOwner", sourceOwner, "source", source, + "targetOwner", targetOwner, "target", target, + "sessionToken", getSessionToken()); + + return request("POST", "move", Map.of(), jsonObjectMapper.writeValue(parameters)); } @Override diff --git a/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java index 56156c84813..654a2a554b5 100644 --- a/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java +++ b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/AfsClientTest.java @@ -42,6 +42,7 @@ public class AfsClientTest assertNotNull(token); assertEquals(token, afsClient.getSessionToken()); assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + assertTrue(httpServer.getLastRequestBody().length > 0); } @Test @@ -67,6 +68,7 @@ public class AfsClientTest assertTrue(result); assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); + assertArrayEquals(httpServer.getLastRequestBody(), new byte[0]); } @Test @@ -94,6 +96,7 @@ public class AfsClientTest assertTrue(result); assertNull(afsClient.getSessionToken()); assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + assertTrue(httpServer.getLastRequestBody().length > 0); } @Test @@ -105,6 +108,7 @@ public class AfsClientTest afsClient.list("", "", true); assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); + assertArrayEquals(httpServer.getLastRequestBody(), new byte[0]); } @Test @@ -115,30 +119,63 @@ public class AfsClientTest byte[] data = "ABCD".getBytes(); httpServer.setNextResponse(data); - byte[] result = afsClient.read("admin", "/", 0L, 1000); + byte[] result = afsClient.read("", "", 0L, 1000); assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); assertArrayEquals(data, result); + assertArrayEquals(httpServer.getLastRequestBody(), new byte[0]); } @Test - public void testWrite() throws Exception + public void write_methodIsPost() throws Exception { + login(); + + httpServer.setNextResponse("{\"result\": true}"); + Boolean result = afsClient.write("", "", 0L, new byte[0], new byte[0]); + + assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + assertTrue(result); + assertTrue(httpServer.getLastRequestBody().length > 0); } @Test - public void testDelete() throws Exception + public void delete_methodIsDelete() throws Exception { + login(); + + httpServer.setNextResponse("{\"result\": true}"); + Boolean result = afsClient.delete("", ""); + + assertEquals("DELETE", httpServer.getHttpExchange().getRequestMethod()); + assertTrue(result); + assertTrue(httpServer.getLastRequestBody().length > 0); } @Test - public void testCopy() throws Exception + public void copy_methodIsPost() throws Exception { + login(); + + httpServer.setNextResponse("{\"result\": true}"); + Boolean result = afsClient.copy("", "", "", ""); + + assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + assertTrue(result); + assertTrue(httpServer.getLastRequestBody().length > 0); } @Test - public void testMove() + public void move_methodIsPost() throws Exception { + login(); + + httpServer.setNextResponse("{\"result\": true}"); + Boolean result = afsClient.move("", "", "", ""); + + assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); + assertTrue(result); + assertTrue(httpServer.getLastRequestBody().length > 0); } @Test @@ -166,7 +203,8 @@ public class AfsClientTest { } - private void login() throws Exception { + private void login() throws Exception + { afsClient.login("test", "test"); } diff --git a/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java index dbc97199894..a1a06caea12 100644 --- a/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java +++ b/api-data-store-server-java/src/test/java/ch/ethz/sis/afsclient/client/DummyHttpServer.java @@ -22,7 +22,6 @@ import java.net.HttpURLConnection; import java.net.InetSocketAddress; import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; public final class DummyHttpServer @@ -36,6 +35,9 @@ public final class DummyHttpServer private static final String DEFAULT_RESPONSE = "{\"result\": \"success\"}"; private byte[] nextResponse = DEFAULT_RESPONSE.getBytes(); + + private byte[] lastRequestBody = null; + private String nextResponseType = "application/json"; private HttpExchange httpExchange; @@ -45,18 +47,17 @@ public final class DummyHttpServer this.httpServerPort = httpServerPort; this.httpServerPath = httpServerPath; httpServer = HttpServer.create(new InetSocketAddress(httpServerPort), 0); - httpServer.createContext(httpServerPath, new HttpHandler() + httpServer.createContext(httpServerPath, exchange -> { - public void handle(HttpExchange exchange) throws IOException - { - byte[] response = nextResponse; - exchange.getResponseHeaders().set("content-type", nextResponseType); - exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length); - - exchange.getResponseBody().write(response); - exchange.close(); - httpExchange = exchange; - } + byte[] response = nextResponse; + exchange.getResponseHeaders().set("content-type", nextResponseType); + exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length); + + exchange.getResponseBody().write(response); + lastRequestBody = exchange.getRequestBody().readAllBytes(); + + exchange.close(); + httpExchange = exchange; }); } @@ -82,6 +83,11 @@ public final class DummyHttpServer this.nextResponseType = "application/octet-stream"; } + public byte[] getLastRequestBody() + { + return lastRequestBody; + } + public HttpExchange getHttpExchange() { return httpExchange; 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 ae64cc19675..d0a5c3ad106 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 @@ -15,19 +15,17 @@ */ package ch.ethz.sis.afsserver.server.impl; -import static java.nio.charset.StandardCharsets.UTF_8; - import ch.ethz.sis.afsserver.exception.HTTPExceptions; import ch.ethz.sis.afsserver.http.*; import ch.ethz.sis.afsserver.server.*; import ch.ethz.sis.afsserver.server.performance.Event; import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; -import ch.ethz.sis.shared.io.IOUtils; import ch.ethz.sis.afsjson.JsonObjectMapper; import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.Logger; import io.netty.handler.codec.http.HttpMethod; +import java.io.ByteArrayInputStream; import java.util.*; /* @@ -92,84 +90,107 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler String interactiveSessionKey = null; String transactionManagerKey = null; Map<String, Object> methodParameters = new HashMap<>(); - for (Map.Entry<String, List<String>> entry : uriParameters.entrySet()) + if (HttpMethod.GET.equals(httpMethod)) { - String value = null; - if (entry.getValue() != null) + for (Map.Entry<String, List<String>> entry : uriParameters.entrySet()) { - if (entry.getValue().size() == 1) + String value = null; + if (entry.getValue() != null) + { + if (entry.getValue().size() == 1) + { + value = entry.getValue().get(0); + } else if (entry.getValue().size() > 1) + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_PARAMETERS.getCause())); + } + } + + try { - value = entry.getValue().get(0); - } else if (entry.getValue().size() > 1) + switch (entry.getKey()) + { + case "method": + method = value; + if (!isValidMethod(httpMethod, method)) + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_HTTP_METHOD.getCause())); + } + break; + case "sessionToken": + sessionToken = value; + break; + case "interactiveSessionKey": + interactiveSessionKey = value; + break; + case "transactionManagerKey": + transactionManagerKey = value; + break; + case "transactionId": + methodParameters.put(entry.getKey(), UUID.fromString(value)); + break; + case "recursively": + methodParameters.put(entry.getKey(), Boolean.valueOf(value)); + break; + case "offset": + methodParameters.put(entry.getKey(), Long.valueOf(value)); + break; + case "limit": + methodParameters.put(entry.getKey(), Integer.valueOf(value)); + break; + default: + methodParameters.put(entry.getKey(), value); + break; + } + } catch (Exception e) { + logger.catching(e); return getHTTPResponse(new ApiResponse("1", null, - HTTPExceptions.INVALID_PARAMETERS.getCause())); + HTTPExceptions.INVALID_PARAMETERS.getCause( + e.getClass().getSimpleName(), + e.getMessage()))); } } - - try + } else if (HttpMethod.POST.equals(httpMethod) || HttpMethod.DELETE.equals(httpMethod)) + { + if (!uriParameters.containsKey("method")) + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_HTTP_METHOD.getCause())); + } else { - switch (entry.getKey()) + List<String> methodParam = uriParameters.get("method"); + if (methodParam == null || methodParam.size() != 1) { - case "method": - method = value; - if (!isValidMethod(httpMethod, method)) - { - return getHTTPResponse(new ApiResponse("1", null, - HTTPExceptions.INVALID_HTTP_METHOD.getCause())); - } - break; - case "sessionToken": - sessionToken = value; - break; - case "interactiveSessionKey": - interactiveSessionKey = value; - break; - case "transactionManagerKey": - transactionManagerKey = value; - break; - case "transactionId": - methodParameters.put(entry.getKey(), UUID.fromString(value)); - break; - case "recursively": - methodParameters.put(entry.getKey(), Boolean.valueOf(value)); - break; - case "offset": - methodParameters.put(entry.getKey(), Long.valueOf(value)); - break; - case "limit": - methodParameters.put(entry.getKey(), Integer.valueOf(value)); - break; - case "md5Hash": - methodParameters.put(entry.getKey(), IOUtils.decodeBase64(value)); - break; - default: - methodParameters.put(entry.getKey(), value); - break; + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_HTTP_METHOD.getCause())); } - } catch (Exception e) + method = methodParam.get(0); + } + + if (!isValidMethod(httpMethod, method)) { - logger.catching(e); return getHTTPResponse(new ApiResponse("1", null, - HTTPExceptions.INVALID_PARAMETERS.getCause(e.getClass().getSimpleName(), - e.getMessage()))); + HTTPExceptions.INVALID_HTTP_METHOD.getCause())); } - } + Map<String, Object> bodyParameterMap = readBody(requestBody, HashMap.class); - // Process body parameters - switch (method) { - case "write": - methodParameters.put("data", requestBody); - break; - case "login": - // userId : password - String[] credentials = new String(requestBody, UTF_8).split(":"); - methodParameters.put("userId", credentials[0]); - methodParameters.put("password", credentials[1]); - break; - case "logout": - sessionToken = new String(requestBody, UTF_8); - break; + for (Map.Entry<String, Object> entry : bodyParameterMap.entrySet()) + { + if (entry.getKey().equals("sessionToken")) + { + sessionToken = (String) entry.getValue(); + } else + { + methodParameters.put(entry.getKey(), entry.getValue()); + } + } + } else + { + return getHTTPResponse(new ApiResponse("1", null, + HTTPExceptions.INVALID_HTTP_METHOD.getCause())); } ApiRequest apiRequest = new ApiRequest("1", method, methodParameters, sessionToken, @@ -213,6 +234,11 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler return null; // This should never happen, it would mean an error writing the Unknown error happened. } + private <T> T readBody(byte[] requestBody, Class<T> valueType) throws Exception + { + return (T) jsonObjectMapper.readValue(new ByteArrayInputStream(requestBody), valueType); + } + private HttpResponse getHTTPResponse(Response response) throws Exception { boolean error = response.getError() != null; diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java index 1d3d61be291..3d54804e03b 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/ApiClientTest.java @@ -23,13 +23,11 @@ import static org.junit.Assert.*; import java.io.IOException; import java.net.URI; -import java.nio.file.Paths; import java.util.List; import java.util.UUID; import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.shared.io.IOUtils; -import org.apache.commons.io.FileUtils; import org.junit.*; import ch.ethz.sis.afs.manager.TransactionConnection; @@ -38,7 +36,6 @@ import ch.ethz.sis.afsserver.server.Server; import ch.ethz.sis.afsserver.server.observer.impl.DummyServerObserver; import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter; import ch.ethz.sis.shared.startup.Configuration; -import org.junit.rules.TemporaryFolder; public final class ApiClientTest { @@ -55,11 +52,13 @@ public final class ApiClientTest public static final String FILE_A = "A.txt"; public static final byte[] DATA = "ABCD".getBytes(); - public static final String FILE_B = "B.txt"; public static String owner = UUID.randomUUID().toString(); + private String testDataRoot; + + @BeforeClass public static void classSetUp() throws Exception { @@ -84,7 +83,7 @@ public final class ApiClientTest @Before public void setUp() throws Exception { - String testDataRoot = IOUtils.getPath(storageRoot, owner.toString()); + testDataRoot = IOUtils.getPath(storageRoot, owner.toString()); IOUtils.createDirectories(testDataRoot); String testDataFile = IOUtils.getPath(testDataRoot, FILE_A); IOUtils.createFile(testDataFile); @@ -171,6 +170,66 @@ public final class ApiClientTest assertArrayEquals(DATA, bytes); } + @Test + public void write_zeroOffset_createsFile() throws Exception { + login(); + + Boolean result = afsClient.write(owner, FILE_B, 0L, DATA, IOUtils.getMD5(DATA)); + assertTrue(result); + + byte[] testDataFile = IOUtils.readFully(IOUtils.getPath(testDataRoot, FILE_B)); + assertArrayEquals(DATA, testDataFile); + } + + @Test + public void write_nonZeroOffset_createsFile() throws Exception { + login(); + + Long offset = 65L; + Boolean result = afsClient.write(owner, FILE_B, offset, DATA, IOUtils.getMD5(DATA)); + assertTrue(result); + + byte[] testDataFile = IOUtils.readFully(IOUtils.getPath(testDataRoot, FILE_A)); + assertArrayEquals(DATA, testDataFile); + } + + @Test + public void delete_fileIsGone() throws Exception { + login(); + + Boolean deleted = afsClient.delete(owner, FILE_A); + assertTrue(deleted); + + List<ch.ethz.sis.afs.api.dto.File> list = IOUtils.list(testDataRoot, true); + assertEquals(0, list.size()); + } + + @Test + public void copy_newFileIsCreated() throws Exception { + login(); + + Boolean result = afsClient.copy(owner, FILE_A, owner, FILE_B); + assertTrue(result); + + byte[] testDataFile = IOUtils.readFully(IOUtils.getPath(testDataRoot, FILE_B)); + assertArrayEquals(DATA, testDataFile); + } + + @Test + public void move_fileIsRenamed() throws Exception { + login(); + + Boolean result = afsClient.move(owner, FILE_A, owner, FILE_B); + assertTrue(result); + + List<ch.ethz.sis.afs.api.dto.File> list = IOUtils.list(testDataRoot, true); + assertEquals(1, list.size()); + assertEquals(FILE_B, list.get(0).getName()); + + byte[] testDataFile = IOUtils.readFully(IOUtils.getPath(testDataRoot, FILE_B)); + assertArrayEquals(DATA, testDataFile); + } + private String login() throws Exception diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java index 68d8338a2d4..27853db9e03 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/core/AbstractPublicAPIWrapper.java @@ -19,113 +19,137 @@ import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsapi.api.PublicAPI; import lombok.NonNull; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.UUID; -public abstract class AbstractPublicAPIWrapper implements PublicAPI { +public abstract class AbstractPublicAPIWrapper implements PublicAPI +{ - public abstract <E> E process(String method, Map<String, Object> methodParameters); + public abstract <E> E process(String method, Map<String, Object> queryParams, + Map<String, Object> bodyParams); @Override - public List<File> list(@NonNull String owner, @NonNull String source, @NonNull Boolean recursively) throws Exception { + public List<File> list(@NonNull String owner, @NonNull String source, + @NonNull Boolean recursively) throws Exception + { Map<String, Object> args = Map.of( "owner", owner, "source", source, "recursively", recursively); - return process("list", args); + return process("list", args, Map.of()); } @Override - public byte[] read(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull Integer limit) throws Exception { + public byte[] read(@NonNull String owner, @NonNull String source, @NonNull Long offset, + @NonNull Integer limit) throws Exception + { Map<String, Object> args = Map.of( "owner", owner, "source", source, "offset", offset, "limit", limit); - return process("read", args); + return process("read", args, Map.of()); } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, + @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception + { Map<String, Object> args = Map.of( "owner", owner, "source", source, "offset", offset, "data", data, "md5Hash", md5Hash); - return process("write", args); + return process("write", Map.of(), args); } @Override - public Boolean delete(@NonNull String owner, @NonNull String source) throws Exception { + public Boolean delete(@NonNull String owner, @NonNull String source) throws Exception + { Map<String, Object> args = Map.of( "owner", owner, "source", source); - return process("delete", args); + return process("delete", Map.of(), args); } @Override - public Boolean copy(@NonNull String sourceOwner, @NonNull String source, @NonNull String targetOwner, @NonNull String target) throws Exception { + public Boolean copy(@NonNull String sourceOwner, @NonNull String source, + @NonNull String targetOwner, @NonNull String target) throws Exception + { Map<String, Object> args = Map.of( "sourceOwner", sourceOwner, "source", source, "targetOwner", targetOwner, "target", target); - return process("copy", args); + return process("copy", Map.of(), args); } @Override - public Boolean move(@NonNull String sourceOwner, @NonNull String source, @NonNull String targetOwner, @NonNull String target) throws Exception { + public Boolean move(@NonNull String sourceOwner, @NonNull String source, + @NonNull String targetOwner, @NonNull String target) throws Exception + { Map<String, Object> args = Map.of( "sourceOwner", sourceOwner, "source", source, "targetOwner", targetOwner, "target", target); - return process("move", args); + return process("move", Map.of(), args); } @Override - public void begin(UUID transactionId) throws Exception { + public void begin(UUID transactionId) throws Exception + { //TODO: Unused } @Override - public Boolean prepare() throws Exception { + public Boolean prepare() throws Exception + { //TODO: Unused return true; } @Override - public void commit() throws Exception { + public void commit() throws Exception + { //TODO: Unused } @Override - public void rollback() throws Exception { + public void rollback() throws Exception + { //TODO: Unused } @Override - public List<UUID> recover() throws Exception { + public List<UUID> recover() throws Exception + { //TODO: Unused return null; } @Override - public String login(String userId, String password) throws Exception { + public String login(String userId, String password) throws Exception + { //TODO: Unused return null; } @Override - public Boolean isSessionValid() throws Exception { + public Boolean isSessionValid() throws Exception + { //TODO: Unused return null; } @Override - public Boolean logout() throws Exception { + public Boolean logout() throws Exception + { //TODO: Unused return null; } 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 4af73090c05..186b8a1e289 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 @@ -85,29 +85,29 @@ public abstract class PublicApiTest extends AbstractTest @Test public void read() throws Exception { - byte[] bytes = getPublicAPI().read(owner, "/" + FILE_A, 0L, DATA.length); + byte[] bytes = getPublicAPI().read(owner, FILE_A, 0L, DATA.length); assertArrayEquals(DATA, bytes); } @Test(expected = RuntimeException.class) public void read_big_failure() throws Exception { - byte[] bytes = getPublicAPI().read(owner, "/" + FILE_A, 0L, Integer.MAX_VALUE); + byte[] bytes = getPublicAPI().read(owner, FILE_A, 0L, Integer.MAX_VALUE); assertArrayEquals(DATA, bytes); } @Test public void write() throws Exception { - getPublicAPI().write(owner, "/" + FILE_B, 0L, DATA, IOUtils.getMD5(DATA)); - byte[] bytes = getPublicAPI().read(owner, "/" + FILE_B, 0L, DATA.length); + getPublicAPI().write(owner, FILE_B, 0L, DATA, IOUtils.getMD5(DATA)); + byte[] bytes = getPublicAPI().read(owner, FILE_B, 0L, DATA.length); assertArrayEquals(DATA, bytes); } @Test public void delete() throws Exception { - Boolean deleted = getPublicAPI().delete(owner, "/" + FILE_A); + Boolean deleted = getPublicAPI().delete(owner, FILE_A); assertTrue(deleted); List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE); assertEquals(0, list.size()); @@ -116,15 +116,15 @@ public abstract class PublicApiTest extends AbstractTest @Test public void copy() throws Exception { - getPublicAPI().copy(owner, "/" + FILE_A, owner, "/" + FILE_B); - byte[] bytes = getPublicAPI().read(owner, "/" + FILE_B, 0L, DATA.length); + getPublicAPI().copy(owner, FILE_A, owner, FILE_B); + byte[] bytes = getPublicAPI().read(owner, FILE_B, 0L, DATA.length); assertArrayEquals(DATA, bytes); } @Test public void move() throws Exception { - getPublicAPI().move(owner, "/" + FILE_A, owner, "/" + FILE_B); + getPublicAPI().move(owner, FILE_A, owner, FILE_B); List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE); assertEquals(1, list.size()); assertEquals(FILE_B, list.get(0).getName()); diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java index 6c6ff9644b4..b4fbd223a43 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerAdapterWrapper.java @@ -15,7 +15,6 @@ */ package ch.ethz.sis.afsserver.impl; - import ch.ethz.sis.afsserver.core.AbstractPublicAPIWrapper; import ch.ethz.sis.afsserver.http.HttpResponse; import ch.ethz.sis.afsserver.server.impl.ApiRequest; @@ -35,54 +34,82 @@ import java.util.*; import static ch.ethz.sis.afsserver.http.HttpResponse.CONTENT_TYPE_BINARY_DATA; import static ch.ethz.sis.afsserver.http.HttpResponse.CONTENT_TYPE_JSON; -public class APIServerAdapterWrapper extends AbstractPublicAPIWrapper { +public class APIServerAdapterWrapper extends AbstractPublicAPIWrapper +{ private static final Logger logger = LogManager.getLogger(APIServerAdapterWrapper.class); private ApiServerAdapter apiServerAdapter; + private JsonObjectMapper jsonObjectMapper; - public APIServerAdapterWrapper(ApiServerAdapter apiServerAdapter, JsonObjectMapper jsonObjectMapper) { + public APIServerAdapterWrapper(ApiServerAdapter apiServerAdapter, + JsonObjectMapper jsonObjectMapper) + { this.apiServerAdapter = apiServerAdapter; this.jsonObjectMapper = jsonObjectMapper; } - public Map<String, List<String>> getURIParameters(Map<String, Object> args) { + public Map<String, List<String>> getURIParameters(Map<String, Object> args) + { Map<String, List<String>> result = new HashMap<>(args.size()); - for (Map.Entry<String, Object> entry:args.entrySet()) { - if (entry.getKey().equals("data") && entry.getValue() instanceof byte[]) { + for (Map.Entry<String, Object> entry : args.entrySet()) + { + if (entry.getKey().equals("data") && entry.getValue() instanceof byte[]) + { continue; // Skip - } else if(entry.getValue() instanceof byte[]) { - result.put(entry.getKey(), List.of(IOUtils.encodeBase64((byte[]) entry.getValue()))); - } else { + } else if (entry.getValue() instanceof byte[]) + { + result.put(entry.getKey(), + List.of(IOUtils.encodeBase64((byte[]) entry.getValue()))); + } else + { result.put(entry.getKey(), List.of(String.valueOf(entry.getValue()))); } } return result; } - public <E> E process(String apiMethod, Map<String, Object> args) { - try { + public <E> E process(String apiMethod, Map<String, Object> queryParams, + Map<String, Object> bodyParams) + { + try + { HttpMethod httpMethod = ApiServerAdapter.getHttpMethod(apiMethod); - - Map<String, List<String>> uriParameters = getURIParameters(args); - uriParameters.put("method", List.of(apiMethod)); - uriParameters.put("sessionToken", List.of(UUID.randomUUID().toString())); - + Map<String, List<String>> uriParameters = new HashMap<>(); byte[] requestBody = null; - if (apiMethod.equals("write")) { - requestBody = (byte[]) args.get("data"); + + if (HttpMethod.GET.equals(httpMethod)) + { + uriParameters = getURIParameters(queryParams); + uriParameters.put("sessionToken", List.of(UUID.randomUUID().toString())); + } else if (HttpMethod.POST.equals(httpMethod) || HttpMethod.DELETE.equals(httpMethod)) + { + Map<String, Object> fullBodyParams = new HashMap<>(); + fullBodyParams.putAll(bodyParams); + fullBodyParams.put("sessionToken", UUID.randomUUID().toString()); + requestBody = jsonObjectMapper.writeValue(fullBodyParams); + } else + { + throw new IllegalArgumentException("Not supported HTTP method type!"); } - HttpResponse response = apiServerAdapter.process(httpMethod, uriParameters, requestBody); - switch (response.getContentType()) { + uriParameters.put("method", List.of(apiMethod)); + + HttpResponse response = + apiServerAdapter.process(httpMethod, uriParameters, requestBody); + switch (response.getContentType()) + { case CONTENT_TYPE_BINARY_DATA: return (E) response.getBody(); case CONTENT_TYPE_JSON: - ApiResponse apiResponse = jsonObjectMapper.readValue(new ByteArrayInputStream(response.getBody()), ApiResponse.class); - return (E) apiResponse.getResult(); + ApiResponse apiResponse = + jsonObjectMapper.readValue(new ByteArrayInputStream(response.getBody()), + ApiResponse.class); + return (E) apiResponse.getResult(); } - } catch (Throwable throwable) { + } catch (Throwable throwable) + { throw new RuntimeException(throwable); } throw new RuntimeException("This line should be unreachable"); diff --git a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerWrapper.java b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerWrapper.java index 15d5f4eb397..4f0b7cbc308 100644 --- a/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerWrapper.java +++ b/server-data-store/src/test/java/ch/ethz/sis/afsserver/impl/APIServerWrapper.java @@ -25,6 +25,7 @@ import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.Logger; +import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -40,9 +41,12 @@ public class APIServerWrapper extends AbstractPublicAPIWrapper { this.apiResponseBuilder = new ApiResponseBuilder(); } - public <E> E process(String method, Map<String, Object> args) { + public <E> E process(String method, Map<String, Object> queryParams, Map<String, Object> bodyParams) { PerformanceAuditor performanceAuditor = new PerformanceAuditor(); // Random Session token just works for tests with dummy authentication + Map<String, Object> args = new HashMap<>(); + args.putAll(queryParams); + args.putAll(bodyParams); ApiRequest request = new ApiRequest("test", method, args, UUID.randomUUID().toString(), null, null); try { -- GitLab