Skip to content
Snippets Groups Projects
Commit 072d40d6 authored by Adam Laskowski's avatar Adam Laskowski
Browse files

SSDM-13251: Added support for write, copy and move methods. Refactored tests

parent 40b0be0b
No related branches found
No related tags found
1 merge request!40SSDM-13578 : 2PT : Database and V3 Implementation - include the new AFS "free"...
Showing
with 362 additions and 151 deletions
package ch.ethz.sis.afsclient.client; package ch.ethz.sis.afsclient.client;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Authenticator; import java.net.Authenticator;
import java.net.PasswordAuthentication; import java.net.PasswordAuthentication;
import java.net.URI; import java.net.URI;
...@@ -21,7 +24,6 @@ import ch.ethz.sis.afsjson.JsonObjectMapper; ...@@ -21,7 +24,6 @@ import ch.ethz.sis.afsjson.JsonObjectMapper;
import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper; import ch.ethz.sis.afsjson.jackson.JacksonObjectMapper;
import lombok.NonNull; import lombok.NonNull;
public final class AfsClient implements PublicAPI public final class AfsClient implements PublicAPI
{ {
...@@ -81,8 +83,9 @@ 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) public @NonNull String login(@NonNull final String userId, @NonNull final String password)
throws Exception throws Exception
{ {
Map<String, String> credentials = Map.of("userId", userId, "password", password);
String result = request("POST", "login", Map.of(), String result = request("POST", "login", Map.of(),
(userId + ":" + password).getBytes()); jsonObjectMapper.writeValue(credentials));
setSessionToken(result); setSessionToken(result);
return result; return result;
} }
...@@ -98,7 +101,8 @@ public final class AfsClient implements PublicAPI ...@@ -98,7 +101,8 @@ public final class AfsClient implements PublicAPI
public @NonNull Boolean logout() throws Exception public @NonNull Boolean logout() throws Exception
{ {
validateSessionToken(); 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); setSessionToken(null);
return result; return result;
} }
...@@ -129,14 +133,25 @@ public final class AfsClient implements PublicAPI ...@@ -129,14 +133,25 @@ public final class AfsClient implements PublicAPI
@NonNull final Long offset, final byte @NonNull [] data, @NonNull final Long offset, final byte @NonNull [] data,
final byte @NonNull [] md5Hash) throws Exception 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 @Override
public @NonNull Boolean delete(@NonNull final String owner, @NonNull final String source) public @NonNull Boolean delete(@NonNull final String owner, @NonNull final String source)
throws Exception 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 @Override
...@@ -145,7 +160,13 @@ public final class AfsClient implements PublicAPI ...@@ -145,7 +160,13 @@ public final class AfsClient implements PublicAPI
@NonNull final String target) @NonNull final String target)
throws Exception 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 @Override
...@@ -154,7 +175,13 @@ public final class AfsClient implements PublicAPI ...@@ -154,7 +175,13 @@ public final class AfsClient implements PublicAPI
@NonNull final String target) @NonNull final String target)
throws Exception 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 @Override
......
...@@ -42,6 +42,7 @@ public class AfsClientTest ...@@ -42,6 +42,7 @@ public class AfsClientTest
assertNotNull(token); assertNotNull(token);
assertEquals(token, afsClient.getSessionToken()); assertEquals(token, afsClient.getSessionToken());
assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); assertEquals("POST", httpServer.getHttpExchange().getRequestMethod());
assertTrue(httpServer.getLastRequestBody().length > 0);
} }
@Test @Test
...@@ -67,6 +68,7 @@ public class AfsClientTest ...@@ -67,6 +68,7 @@ public class AfsClientTest
assertTrue(result); assertTrue(result);
assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); assertEquals("GET", httpServer.getHttpExchange().getRequestMethod());
assertArrayEquals(httpServer.getLastRequestBody(), new byte[0]);
} }
@Test @Test
...@@ -94,6 +96,7 @@ public class AfsClientTest ...@@ -94,6 +96,7 @@ public class AfsClientTest
assertTrue(result); assertTrue(result);
assertNull(afsClient.getSessionToken()); assertNull(afsClient.getSessionToken());
assertEquals("POST", httpServer.getHttpExchange().getRequestMethod()); assertEquals("POST", httpServer.getHttpExchange().getRequestMethod());
assertTrue(httpServer.getLastRequestBody().length > 0);
} }
@Test @Test
...@@ -105,6 +108,7 @@ public class AfsClientTest ...@@ -105,6 +108,7 @@ public class AfsClientTest
afsClient.list("", "", true); afsClient.list("", "", true);
assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); assertEquals("GET", httpServer.getHttpExchange().getRequestMethod());
assertArrayEquals(httpServer.getLastRequestBody(), new byte[0]);
} }
@Test @Test
...@@ -115,30 +119,63 @@ public class AfsClientTest ...@@ -115,30 +119,63 @@ public class AfsClientTest
byte[] data = "ABCD".getBytes(); byte[] data = "ABCD".getBytes();
httpServer.setNextResponse(data); httpServer.setNextResponse(data);
byte[] result = afsClient.read("admin", "/", 0L, 1000); byte[] result = afsClient.read("", "", 0L, 1000);
assertEquals("GET", httpServer.getHttpExchange().getRequestMethod()); assertEquals("GET", httpServer.getHttpExchange().getRequestMethod());
assertArrayEquals(data, result); assertArrayEquals(data, result);
assertArrayEquals(httpServer.getLastRequestBody(), new byte[0]);
} }
@Test @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 @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 @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 @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 @Test
...@@ -166,7 +203,8 @@ public class AfsClientTest ...@@ -166,7 +203,8 @@ public class AfsClientTest
{ {
} }
private void login() throws Exception { private void login() throws Exception
{
afsClient.login("test", "test"); afsClient.login("test", "test");
} }
......
...@@ -22,7 +22,6 @@ import java.net.HttpURLConnection; ...@@ -22,7 +22,6 @@ import java.net.HttpURLConnection;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
public final class DummyHttpServer public final class DummyHttpServer
...@@ -36,6 +35,9 @@ public final class DummyHttpServer ...@@ -36,6 +35,9 @@ public final class DummyHttpServer
private static final String DEFAULT_RESPONSE = "{\"result\": \"success\"}"; private static final String DEFAULT_RESPONSE = "{\"result\": \"success\"}";
private byte[] nextResponse = DEFAULT_RESPONSE.getBytes(); private byte[] nextResponse = DEFAULT_RESPONSE.getBytes();
private byte[] lastRequestBody = null;
private String nextResponseType = "application/json"; private String nextResponseType = "application/json";
private HttpExchange httpExchange; private HttpExchange httpExchange;
...@@ -45,18 +47,17 @@ public final class DummyHttpServer ...@@ -45,18 +47,17 @@ public final class DummyHttpServer
this.httpServerPort = httpServerPort; this.httpServerPort = httpServerPort;
this.httpServerPath = httpServerPath; this.httpServerPath = httpServerPath;
httpServer = HttpServer.create(new InetSocketAddress(httpServerPort), 0); 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);
byte[] response = nextResponse; exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length);
exchange.getResponseHeaders().set("content-type", nextResponseType);
exchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.length); exchange.getResponseBody().write(response);
lastRequestBody = exchange.getRequestBody().readAllBytes();
exchange.getResponseBody().write(response);
exchange.close(); exchange.close();
httpExchange = exchange; httpExchange = exchange;
}
}); });
} }
...@@ -82,6 +83,11 @@ public final class DummyHttpServer ...@@ -82,6 +83,11 @@ public final class DummyHttpServer
this.nextResponseType = "application/octet-stream"; this.nextResponseType = "application/octet-stream";
} }
public byte[] getLastRequestBody()
{
return lastRequestBody;
}
public HttpExchange getHttpExchange() public HttpExchange getHttpExchange()
{ {
return httpExchange; return httpExchange;
......
...@@ -15,19 +15,17 @@ ...@@ -15,19 +15,17 @@
*/ */
package ch.ethz.sis.afsserver.server.impl; 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.exception.HTTPExceptions;
import ch.ethz.sis.afsserver.http.*; import ch.ethz.sis.afsserver.http.*;
import ch.ethz.sis.afsserver.server.*; import ch.ethz.sis.afsserver.server.*;
import ch.ethz.sis.afsserver.server.performance.Event; import ch.ethz.sis.afsserver.server.performance.Event;
import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; 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.afsjson.JsonObjectMapper;
import ch.ethz.sis.shared.log.LogManager; import ch.ethz.sis.shared.log.LogManager;
import ch.ethz.sis.shared.log.Logger; import ch.ethz.sis.shared.log.Logger;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import java.io.ByteArrayInputStream;
import java.util.*; import java.util.*;
/* /*
...@@ -92,84 +90,107 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler ...@@ -92,84 +90,107 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler
String interactiveSessionKey = null; String interactiveSessionKey = null;
String transactionManagerKey = null; String transactionManagerKey = null;
Map<String, Object> methodParameters = new HashMap<>(); Map<String, Object> methodParameters = new HashMap<>();
for (Map.Entry<String, List<String>> entry : uriParameters.entrySet()) if (HttpMethod.GET.equals(httpMethod))
{ {
String value = null; for (Map.Entry<String, List<String>> entry : uriParameters.entrySet())
if (entry.getValue() != null)
{ {
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); switch (entry.getKey())
} else if (entry.getValue().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;
default:
methodParameters.put(entry.getKey(), value);
break;
}
} catch (Exception e)
{ {
logger.catching(e);
return getHTTPResponse(new ApiResponse("1", null, return getHTTPResponse(new ApiResponse("1", null,
HTTPExceptions.INVALID_PARAMETERS.getCause())); HTTPExceptions.INVALID_PARAMETERS.getCause(
e.getClass().getSimpleName(),
e.getMessage())));
} }
} }
} else if (HttpMethod.POST.equals(httpMethod) || HttpMethod.DELETE.equals(httpMethod))
try {
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": return getHTTPResponse(new ApiResponse("1", null,
method = value; HTTPExceptions.INVALID_HTTP_METHOD.getCause()));
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;
} }
} catch (Exception e) method = methodParam.get(0);
}
if (!isValidMethod(httpMethod, method))
{ {
logger.catching(e);
return getHTTPResponse(new ApiResponse("1", null, return getHTTPResponse(new ApiResponse("1", null,
HTTPExceptions.INVALID_PARAMETERS.getCause(e.getClass().getSimpleName(), HTTPExceptions.INVALID_HTTP_METHOD.getCause()));
e.getMessage())));
} }
} Map<String, Object> bodyParameterMap = readBody(requestBody, HashMap.class);
// Process body parameters for (Map.Entry<String, Object> entry : bodyParameterMap.entrySet())
switch (method) { {
case "write": if (entry.getKey().equals("sessionToken"))
methodParameters.put("data", requestBody); {
break; sessionToken = (String) entry.getValue();
case "login": } else
// userId : password {
String[] credentials = new String(requestBody, UTF_8).split(":"); methodParameters.put(entry.getKey(), entry.getValue());
methodParameters.put("userId", credentials[0]); }
methodParameters.put("password", credentials[1]); }
break; } else
case "logout": {
sessionToken = new String(requestBody, UTF_8); return getHTTPResponse(new ApiResponse("1", null,
break; HTTPExceptions.INVALID_HTTP_METHOD.getCause()));
} }
ApiRequest apiRequest = new ApiRequest("1", method, methodParameters, sessionToken, ApiRequest apiRequest = new ApiRequest("1", method, methodParameters, sessionToken,
...@@ -213,6 +234,11 @@ public class ApiServerAdapter<CONNECTION, API> implements HttpServerHandler ...@@ -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. 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 private HttpResponse getHTTPResponse(Response response) throws Exception
{ {
boolean error = response.getError() != null; boolean error = response.getError() != null;
......
...@@ -23,13 +23,11 @@ import static org.junit.Assert.*; ...@@ -23,13 +23,11 @@ import static org.junit.Assert.*;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import ch.ethz.sis.afsapi.dto.File; import ch.ethz.sis.afsapi.dto.File;
import ch.ethz.sis.shared.io.IOUtils; import ch.ethz.sis.shared.io.IOUtils;
import org.apache.commons.io.FileUtils;
import org.junit.*; import org.junit.*;
import ch.ethz.sis.afs.manager.TransactionConnection; import ch.ethz.sis.afs.manager.TransactionConnection;
...@@ -38,7 +36,6 @@ import ch.ethz.sis.afsserver.server.Server; ...@@ -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.server.observer.impl.DummyServerObserver;
import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter; import ch.ethz.sis.afsserver.startup.AtomicFileSystemServerParameter;
import ch.ethz.sis.shared.startup.Configuration; import ch.ethz.sis.shared.startup.Configuration;
import org.junit.rules.TemporaryFolder;
public final class ApiClientTest public final class ApiClientTest
{ {
...@@ -55,11 +52,13 @@ public final class ApiClientTest ...@@ -55,11 +52,13 @@ public final class ApiClientTest
public static final String FILE_A = "A.txt"; public static final String FILE_A = "A.txt";
public static final byte[] DATA = "ABCD".getBytes(); public static final byte[] DATA = "ABCD".getBytes();
public static final String FILE_B = "B.txt"; public static final String FILE_B = "B.txt";
public static String owner = UUID.randomUUID().toString(); public static String owner = UUID.randomUUID().toString();
private String testDataRoot;
@BeforeClass @BeforeClass
public static void classSetUp() throws Exception public static void classSetUp() throws Exception
{ {
...@@ -84,7 +83,7 @@ public final class ApiClientTest ...@@ -84,7 +83,7 @@ public final class ApiClientTest
@Before @Before
public void setUp() throws Exception public void setUp() throws Exception
{ {
String testDataRoot = IOUtils.getPath(storageRoot, owner.toString()); testDataRoot = IOUtils.getPath(storageRoot, owner.toString());
IOUtils.createDirectories(testDataRoot); IOUtils.createDirectories(testDataRoot);
String testDataFile = IOUtils.getPath(testDataRoot, FILE_A); String testDataFile = IOUtils.getPath(testDataRoot, FILE_A);
IOUtils.createFile(testDataFile); IOUtils.createFile(testDataFile);
...@@ -171,6 +170,66 @@ public final class ApiClientTest ...@@ -171,6 +170,66 @@ public final class ApiClientTest
assertArrayEquals(DATA, bytes); 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 private String login() throws Exception
......
...@@ -19,113 +19,137 @@ import ch.ethz.sis.afsapi.dto.File; ...@@ -19,113 +19,137 @@ import ch.ethz.sis.afsapi.dto.File;
import ch.ethz.sis.afsapi.api.PublicAPI; import ch.ethz.sis.afsapi.api.PublicAPI;
import lombok.NonNull; import lombok.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; 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 @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( Map<String, Object> args = Map.of(
"owner", owner, "owner", owner,
"source", source, "source", source,
"recursively", recursively); "recursively", recursively);
return process("list", args); return process("list", args, Map.of());
} }
@Override @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( Map<String, Object> args = Map.of(
"owner", owner, "owner", owner,
"source", source, "source", source,
"offset", offset, "offset", offset,
"limit", limit); "limit", limit);
return process("read", args); return process("read", args, Map.of());
} }
@Override @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( Map<String, Object> args = Map.of(
"owner", owner, "owner", owner,
"source", source, "source", source,
"offset", offset, "offset", offset,
"data", data, "data", data,
"md5Hash", md5Hash); "md5Hash", md5Hash);
return process("write", args); return process("write", Map.of(), args);
} }
@Override @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( Map<String, Object> args = Map.of(
"owner", owner, "owner", owner,
"source", source); "source", source);
return process("delete", args); return process("delete", Map.of(), args);
} }
@Override @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( Map<String, Object> args = Map.of(
"sourceOwner", sourceOwner, "sourceOwner", sourceOwner,
"source", source, "source", source,
"targetOwner", targetOwner, "targetOwner", targetOwner,
"target", target); "target", target);
return process("copy", args); return process("copy", Map.of(), args);
} }
@Override @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( Map<String, Object> args = Map.of(
"sourceOwner", sourceOwner, "sourceOwner", sourceOwner,
"source", source, "source", source,
"targetOwner", targetOwner, "targetOwner", targetOwner,
"target", target); "target", target);
return process("move", args); return process("move", Map.of(), args);
} }
@Override @Override
public void begin(UUID transactionId) throws Exception { public void begin(UUID transactionId) throws Exception
{
//TODO: Unused //TODO: Unused
} }
@Override @Override
public Boolean prepare() throws Exception { public Boolean prepare() throws Exception
{
//TODO: Unused //TODO: Unused
return true; return true;
} }
@Override @Override
public void commit() throws Exception { public void commit() throws Exception
{
//TODO: Unused //TODO: Unused
} }
@Override @Override
public void rollback() throws Exception { public void rollback() throws Exception
{
//TODO: Unused //TODO: Unused
} }
@Override @Override
public List<UUID> recover() throws Exception { public List<UUID> recover() throws Exception
{
//TODO: Unused //TODO: Unused
return null; return null;
} }
@Override @Override
public String login(String userId, String password) throws Exception { public String login(String userId, String password) throws Exception
{
//TODO: Unused //TODO: Unused
return null; return null;
} }
@Override @Override
public Boolean isSessionValid() throws Exception { public Boolean isSessionValid() throws Exception
{
//TODO: Unused //TODO: Unused
return null; return null;
} }
@Override @Override
public Boolean logout() throws Exception { public Boolean logout() throws Exception
{
//TODO: Unused //TODO: Unused
return null; return null;
} }
......
...@@ -85,29 +85,29 @@ public abstract class PublicApiTest extends AbstractTest ...@@ -85,29 +85,29 @@ public abstract class PublicApiTest extends AbstractTest
@Test @Test
public void read() throws Exception 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); assertArrayEquals(DATA, bytes);
} }
@Test(expected = RuntimeException.class) @Test(expected = RuntimeException.class)
public void read_big_failure() throws Exception 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); assertArrayEquals(DATA, bytes);
} }
@Test @Test
public void write() throws Exception public void write() throws Exception
{ {
getPublicAPI().write(owner, "/" + FILE_B, 0L, DATA, IOUtils.getMD5(DATA)); getPublicAPI().write(owner, FILE_B, 0L, DATA, IOUtils.getMD5(DATA));
byte[] bytes = getPublicAPI().read(owner, "/" + FILE_B, 0L, DATA.length); byte[] bytes = getPublicAPI().read(owner, FILE_B, 0L, DATA.length);
assertArrayEquals(DATA, bytes); assertArrayEquals(DATA, bytes);
} }
@Test @Test
public void delete() throws Exception public void delete() throws Exception
{ {
Boolean deleted = getPublicAPI().delete(owner, "/" + FILE_A); Boolean deleted = getPublicAPI().delete(owner, FILE_A);
assertTrue(deleted); assertTrue(deleted);
List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE); List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE);
assertEquals(0, list.size()); assertEquals(0, list.size());
...@@ -116,15 +116,15 @@ public abstract class PublicApiTest extends AbstractTest ...@@ -116,15 +116,15 @@ public abstract class PublicApiTest extends AbstractTest
@Test @Test
public void copy() throws Exception public void copy() throws Exception
{ {
getPublicAPI().copy(owner, "/" + FILE_A, owner, "/" + FILE_B); getPublicAPI().copy(owner, FILE_A, owner, FILE_B);
byte[] bytes = getPublicAPI().read(owner, "/" + FILE_B, 0L, DATA.length); byte[] bytes = getPublicAPI().read(owner, FILE_B, 0L, DATA.length);
assertArrayEquals(DATA, bytes); assertArrayEquals(DATA, bytes);
} }
@Test @Test
public void move() throws Exception 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); List<File> list = getPublicAPI().list(owner, ROOT, Boolean.TRUE);
assertEquals(1, list.size()); assertEquals(1, list.size());
assertEquals(FILE_B, list.get(0).getName()); assertEquals(FILE_B, list.get(0).getName());
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package ch.ethz.sis.afsserver.impl; package ch.ethz.sis.afsserver.impl;
import ch.ethz.sis.afsserver.core.AbstractPublicAPIWrapper; import ch.ethz.sis.afsserver.core.AbstractPublicAPIWrapper;
import ch.ethz.sis.afsserver.http.HttpResponse; import ch.ethz.sis.afsserver.http.HttpResponse;
import ch.ethz.sis.afsserver.server.impl.ApiRequest; import ch.ethz.sis.afsserver.server.impl.ApiRequest;
...@@ -35,54 +34,82 @@ import java.util.*; ...@@ -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_BINARY_DATA;
import static ch.ethz.sis.afsserver.http.HttpResponse.CONTENT_TYPE_JSON; 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 static final Logger logger = LogManager.getLogger(APIServerAdapterWrapper.class);
private ApiServerAdapter apiServerAdapter; private ApiServerAdapter apiServerAdapter;
private JsonObjectMapper jsonObjectMapper; private JsonObjectMapper jsonObjectMapper;
public APIServerAdapterWrapper(ApiServerAdapter apiServerAdapter, JsonObjectMapper jsonObjectMapper) { public APIServerAdapterWrapper(ApiServerAdapter apiServerAdapter,
JsonObjectMapper jsonObjectMapper)
{
this.apiServerAdapter = apiServerAdapter; this.apiServerAdapter = apiServerAdapter;
this.jsonObjectMapper = jsonObjectMapper; 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()); Map<String, List<String>> result = new HashMap<>(args.size());
for (Map.Entry<String, Object> entry:args.entrySet()) { for (Map.Entry<String, Object> entry : args.entrySet())
if (entry.getKey().equals("data") && entry.getValue() instanceof byte[]) { {
if (entry.getKey().equals("data") && entry.getValue() instanceof byte[])
{
continue; // Skip continue; // Skip
} else if(entry.getValue() instanceof byte[]) { } else if (entry.getValue() instanceof byte[])
result.put(entry.getKey(), List.of(IOUtils.encodeBase64((byte[]) entry.getValue()))); {
} else { result.put(entry.getKey(),
List.of(IOUtils.encodeBase64((byte[]) entry.getValue())));
} else
{
result.put(entry.getKey(), List.of(String.valueOf(entry.getValue()))); result.put(entry.getKey(), List.of(String.valueOf(entry.getValue())));
} }
} }
return result; return result;
} }
public <E> E process(String apiMethod, Map<String, Object> args) { public <E> E process(String apiMethod, Map<String, Object> queryParams,
try { Map<String, Object> bodyParams)
{
try
{
HttpMethod httpMethod = ApiServerAdapter.getHttpMethod(apiMethod); HttpMethod httpMethod = ApiServerAdapter.getHttpMethod(apiMethod);
Map<String, List<String>> uriParameters = new HashMap<>();
Map<String, List<String>> uriParameters = getURIParameters(args);
uriParameters.put("method", List.of(apiMethod));
uriParameters.put("sessionToken", List.of(UUID.randomUUID().toString()));
byte[] requestBody = null; 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); uriParameters.put("method", List.of(apiMethod));
switch (response.getContentType()) {
HttpResponse response =
apiServerAdapter.process(httpMethod, uriParameters, requestBody);
switch (response.getContentType())
{
case CONTENT_TYPE_BINARY_DATA: case CONTENT_TYPE_BINARY_DATA:
return (E) response.getBody(); return (E) response.getBody();
case CONTENT_TYPE_JSON: case CONTENT_TYPE_JSON:
ApiResponse apiResponse = jsonObjectMapper.readValue(new ByteArrayInputStream(response.getBody()), ApiResponse.class); ApiResponse apiResponse =
return (E) apiResponse.getResult(); 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(throwable);
} }
throw new RuntimeException("This line should be unreachable"); throw new RuntimeException("This line should be unreachable");
......
...@@ -25,6 +25,7 @@ import ch.ethz.sis.afsserver.server.performance.PerformanceAuditor; ...@@ -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.LogManager;
import ch.ethz.sis.shared.log.Logger; import ch.ethz.sis.shared.log.Logger;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
...@@ -40,9 +41,12 @@ public class APIServerWrapper extends AbstractPublicAPIWrapper { ...@@ -40,9 +41,12 @@ public class APIServerWrapper extends AbstractPublicAPIWrapper {
this.apiResponseBuilder = new ApiResponseBuilder(); 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(); PerformanceAuditor performanceAuditor = new PerformanceAuditor();
// Random Session token just works for tests with dummy authentication // 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); ApiRequest request = new ApiRequest("test", method, args, UUID.randomUUID().toString(), null, null);
try { try {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment