Skip to content

Commit

Permalink
Add ability to run functions (#39)
Browse files Browse the repository at this point in the history
* fixes function runs

* add changeset

* Use env vars for e2e tests

* use default timeout

* delete print statement
  • Loading branch information
Jeremy Wagemans authored Apr 25, 2024
1 parent 80550a3 commit 4dca8da
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 23 deletions.
5 changes: 5 additions & 0 deletions .changeset/eight-brooms-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"evervault-java": minor
---

Add ability to run functions
1 change: 1 addition & 0 deletions lib/src/main/java/com/evervault/Evervault.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ private Evervault(String apiKey, String appUuid, EcdhCurve ecdhCurve, String[] d
this.setupDecryptProvider(httpHandler);
this.setupRepeatableTaskScheduler(taskScheduler);
this.setupClientSideTokenProvider(httpHandler);
this.setupFunctionRunProvider(httpHandler);

this.setupKeyProviders(httpHandler, encryptService, encryptService, timeService, ecdhCurve);
EvervaultEncryptionService encryptForObject = new EvervaultEncryptionService(encryptService, this.generatedEcdhKey, this.sharedKey, this.teamKey);
Expand Down
11 changes: 11 additions & 0 deletions lib/src/main/java/com/evervault/contracts/IProvideFunctionRun.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.evervault.contracts;

import com.evervault.exceptions.HttpFailureException;
import com.evervault.models.FunctionRun;

import java.io.IOException;

public interface IProvideFunctionRun {
<T> FunctionRun<T> runFunction(String url, String functionName, Object payload, Class<T> responseType, boolean async, int timeout)
throws HttpFailureException, IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.evervault.exceptions;

public class FunctionRunException extends Exception {

public FunctionRunException(String message, String stack) {
super(message + ": " + stack);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.evervault.models;

public class CreateFunctionRunRequest {
Object payload;
boolean async;

public CreateFunctionRunRequest(Object payload, boolean async) {
this.payload = payload;
this.async = async;
}

}
37 changes: 37 additions & 0 deletions lib/src/main/java/com/evervault/models/FunctionRun.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.evervault.models;

public class FunctionRun<T> {
private String id;
private String status;
private T result;
private FunctionRunError error;

public String getId() {
return id;
}

public String getStatus() {
return status;
}

public T getResult() {
return result;
}

public FunctionRunError getError() {
return error;
}

public class FunctionRunError {
private String message;
private String stack;

public String getMessage() {
return message;
}

public String getStack() {
return stack;
}
}
}
63 changes: 62 additions & 1 deletion lib/src/main/java/com/evervault/services/EvervaultService.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.time.Instant;

public abstract class EvervaultService {
private static final int DEFAULT_FUNCTION_RUN_REQUEST_TIMEOUT = 30000;
protected IProvideCagePublicKeyFromHttpApi cagePublicKeyFromEndpointProvider;
protected IProvideECPublicKey ecPublicKeyProvider;
protected IProvideSharedKey sharedKeyProvider;
Expand All @@ -34,7 +35,7 @@ public abstract class EvervaultService {
protected IProvideCircuitBreaker circuitBreakerProvider;
protected CredentialsProvider credentialsProvider;
protected HttpRoutePlanner httpRoutePlanner;

protected IProvideFunctionRun functionRunProvider;
protected final static int NEW_KEY_TIMESTAMP = 15;
protected final static String RELAY_PORT = "443";
protected final int getCageHash = "getCagePublicKeyFromEndpoint".hashCode();
Expand Down Expand Up @@ -125,6 +126,13 @@ protected void setupRepeatableTaskScheduler(IScheduleRepeatableTask repeatableTa
this.repeatableTaskScheduler = repeatableTaskScheduler;
}

protected void setupFunctionRunProvider(IProvideFunctionRun functionRunProvider) {
if (functionRunProvider == null) {
throw new NullPointerException(IProvideFunctionRun.class.getName());
}

this.functionRunProvider = functionRunProvider;
}

protected void setupKeyProviders(IProvideCagePublicKeyFromHttpApi cagePublicKeyFromEndpointProvider,
IProvideECPublicKey ecPublicKeyProvider,
Expand Down Expand Up @@ -242,6 +250,59 @@ protected void updateKeysIfTimeIsDue() throws EvervaultException {
}
}

public <T> T run(String functionName, Object payload, Class<T> responseType) throws EvervaultException {
return run(functionName, payload, responseType, DEFAULT_FUNCTION_RUN_REQUEST_TIMEOUT);
}


public <T> T run(String functionName, Object payload, Class<T> responseType, int timeout) throws EvervaultException {
if (functionName == null || functionName.isEmpty()) {
throw new EvervaultException(new MandatoryParameterException("functionName"));
}

if (payload == null) {
throw new EvervaultException(new MandatoryParameterException("payload"));
}

if (responseType == null) {
throw new EvervaultException(new MandatoryParameterException("responseType"));
}

if (timeout < 1) {
throw new EvervaultException(new IllegalArgumentException("The function run request timeout must be greater than 0 ms."));
}

try {
FunctionRun<T> functionRun = functionRunProvider.runFunction(getEvervaultApiUrl(), functionName, payload, responseType, false, timeout);
if (functionRun.getError() != null) {
throw new EvervaultException(new FunctionRunException(functionRun.getError().getMessage(), functionRun.getError().getStack()));
} else {
return functionRun.getResult();
}
} catch (Exception e) {
throw new EvervaultException(e);
}
}

public String runAsynchronously(String functionName, Object payload) throws EvervaultException {
if (functionName == null || functionName.isEmpty()) {
throw new EvervaultException(new MandatoryParameterException("functionName"));
}

if (payload == null) {
throw new EvervaultException(new MandatoryParameterException("payload"));
}

try {
FunctionRun<Object> functionRun =
functionRunProvider.runFunction(getEvervaultApiUrl(), functionName, payload, Object.class, true, DEFAULT_FUNCTION_RUN_REQUEST_TIMEOUT);
return functionRun.getId();
} catch (Exception e) {
throw new EvervaultException(e);
}
}

@Deprecated
public CageRunResult run(String cageName, Object data, boolean async, String version) throws EvervaultException {
if (cageName == null || cageName.isEmpty()) {
throw new EvervaultException(new MandatoryParameterException("cageName"));
Expand Down
63 changes: 41 additions & 22 deletions lib/src/main/java/com/evervault/services/HttpHandler.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
package com.evervault.services;

import com.evervault.contracts.*;
import com.evervault.models.*;
import com.evervault.utils.Base64Handler;
import com.evervault.contracts.IProvideCageExecution;
import com.evervault.contracts.IProvideCagePublicKeyFromHttpApi;
import com.evervault.contracts.IProvideClientSideToken;
import com.evervault.contracts.IProvideDecrypt;
import com.evervault.contracts.IProvideOutboundRelayConfigFromHttpApi;
import com.evervault.contracts.IProvideRunToken;
import com.evervault.exceptions.HttpFailureException;
import com.evervault.models.CagePublicKey;
import com.evervault.models.CageRunResult;
import com.evervault.models.CreateTokenPayload;
import com.evervault.models.OutboundRelayConfigResult;
import com.evervault.models.RunTokenResult;
import com.evervault.models.TokenResult;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import java.io.*;
import java.lang.reflect.Type;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

public class HttpHandler implements IProvideCagePublicKeyFromHttpApi, IProvideCageExecution, IProvideRunToken, IProvideOutboundRelayConfigFromHttpApi, IProvideDecrypt, IProvideClientSideToken {
public class HttpHandler implements IProvideFunctionRun, IProvideCagePublicKeyFromHttpApi, IProvideCageExecution, IProvideRunToken, IProvideOutboundRelayConfigFromHttpApi, IProvideDecrypt, IProvideClientSideToken {
private final static String VERSION_PREFIX = "evervault-java/";
private final static String JSON_CONTENT_TYPE = "application/json";

Expand Down Expand Up @@ -59,7 +51,7 @@ public CagePublicKey getCagePublicKeyFromEndpoint(String url, Map<String, String
if (headerMap != null) {
headerMap.forEach((key, value) -> additionalHeaders.put(key, value));
}
HttpURLConnection connection = createConnection(connectionUrl, "GET", additionalHeaders);
HttpURLConnection connection = createConnection(connectionUrl, "GET", additionalHeaders, httpTimeout);
sendRequest(connection);
return parseResponseBody(connection, CagePublicKey.class);
}
Expand All @@ -78,13 +70,30 @@ public CageRunResult runCage(String url, String cageName, Object data, boolean a
additionalHeaders.put(VERSION_ID_HEADER_NAME, version);
}

HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders);
HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders, httpTimeout);
setRequestBody(connection, serializedData);
sendRequest(connection);

return parseResponseBody(connection, CageRunResult.class);
}

public <T> FunctionRun<T> runFunction(String url, String functionName, Object payload, Class<T> responseType, boolean async, int timeout) throws HttpFailureException, IOException {
CreateFunctionRunRequest request = new CreateFunctionRunRequest(payload, async);
String serializedData = new Gson().toJson(request);

URL connectionUrl = new URL(url + "/functions/" + functionName + "/runs");

Map<String, String> additionalHeaders = new HashMap<>();
additionalHeaders.put("Authorization", basicAuthorizationHeaderValue);

HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders, timeout);
setRequestBody(connection, serializedData);
sendRequest(connection);

Type functionRunType = TypeToken.getParameterized(FunctionRun.class, responseType).getType();
return parseResponseBody(connection, functionRunType);
}

@Override
public <T> T decrypt(String url, Object data, Class<T> valueType) throws HttpFailureException, IOException {
String serializedData = new Gson().toJson(data);
Expand All @@ -94,7 +103,7 @@ public <T> T decrypt(String url, Object data, Class<T> valueType) throws HttpFai
Map<String, String> additionalHeaders = new HashMap<>();
additionalHeaders.put("Authorization", basicAuthorizationHeaderValue);

HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders);
HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders, httpTimeout);
setRequestBody(connection, serializedData);
sendRequest(connection);

Expand All @@ -118,7 +127,7 @@ public TokenResult createClientSideToken(String url, String action, Object data,
Map<String, String> additionalHeaders = new HashMap<>();
additionalHeaders.put("Authorization", basicAuthorizationHeaderValue);

HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders);
HttpURLConnection connection = createConnection(connectionUrl, "POST", additionalHeaders, httpTimeout);
setRequestBody(connection, serializedData);
sendRequest(connection);

Expand All @@ -141,7 +150,8 @@ public RunTokenResult createRunToken(String url, String cageName, Object data) t
HttpURLConnection connection = createConnection(
connectionUrl,
"POST",
additionalHeaders
additionalHeaders,
httpTimeout
);
setRequestBody(connection, serializedData);
sendRequest(connection);
Expand All @@ -163,7 +173,8 @@ public OutboundRelayConfigResult getOutboundRelayConfig(String url) throws HttpF
HttpURLConnection connection = createConnection(
connectionUrl,
"GET",
additionalHeaders
additionalHeaders,
httpTimeout
);

sendRequest(connection);
Expand All @@ -189,16 +200,16 @@ private String buildAuthorizationHeaderValue(String appUuid, String apiKey) {
return builder.toString();
}

private HttpURLConnection createConnection(URL url, String method, Map<String, String> headers) throws IOException {
private HttpURLConnection createConnection(URL url, String method, Map<String, String> headers, int timeout) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if(headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
}
connection.setRequestMethod(method);
connection.setConnectTimeout(httpTimeout);
connection.setReadTimeout(httpTimeout);
connection.setConnectTimeout(timeout);
connection.setReadTimeout(timeout);
connection.setRequestProperty("User-Agent", VERSION_PREFIX + 1.0);
connection.setRequestProperty("Accept", JSON_CONTENT_TYPE);
return connection;
Expand Down Expand Up @@ -238,4 +249,12 @@ private <T> T parseResponseBody(HttpURLConnection connection, Class<T> clazz) th
}
return parsed;
}

private <T> T parseResponseBody(HttpURLConnection connection, Type type) throws IOException {
T parsed;
try (InputStreamReader reader = new InputStreamReader(connection.getInputStream())) {
parsed = new Gson().fromJson(reader, type);
}
return parsed;
}
}
Loading

0 comments on commit 4dca8da

Please sign in to comment.