nextBackoff() {
+ retryNumber += 1;
+ if (retryNumber > maxNumRetries) {
+ return Optional.empty();
+ }
+
+ int upperBound = (int) Math.pow(2, retryNumber);
+ return Optional.of(ONE_SECOND.multipliedBy(random.nextInt(upperBound)));
+ }
+ }
+}
diff --git a/common/src/main/java/com/skyflow/generated/auth/rest/core/Stream.java b/common/src/main/java/com/skyflow/generated/auth/rest/core/Stream.java
new file mode 100644
index 00000000..4a984faa
--- /dev/null
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/core/Stream.java
@@ -0,0 +1,97 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.auth.rest.core;
+
+import java.io.Reader;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Scanner;
+
+/**
+ * The {@code Stream} class implements {@link Iterable} to provide a simple mechanism for reading and parsing
+ * objects of a given type from data streamed via a {@link Reader} using a specified delimiter.
+ *
+ * {@code Stream} assumes that data is being pushed to the provided {@link Reader} asynchronously and utilizes a
+ * {@code Scanner} to block during iteration if the next object is not available.
+ *
+ * @param The type of objects in the stream.
+ */
+public final class Stream implements Iterable {
+ /**
+ * The {@link Class} of the objects in the stream.
+ */
+ private final Class valueType;
+ /**
+ * The {@link Scanner} used for reading from the input stream and blocking when needed during iteration.
+ */
+ private final Scanner scanner;
+
+ /**
+ * Constructs a new {@code Stream} with the specified value type, reader, and delimiter.
+ *
+ * @param valueType The class of the objects in the stream.
+ * @param reader The reader that provides the streamed data.
+ * @param delimiter The delimiter used to separate elements in the stream.
+ */
+ public Stream(Class valueType, Reader reader, String delimiter) {
+ this.scanner = new Scanner(reader).useDelimiter(delimiter);
+ this.valueType = valueType;
+ }
+
+ /**
+ * Returns an iterator over the elements in this stream that blocks during iteration when the next object is
+ * not yet available.
+ *
+ * @return An iterator that can be used to traverse the elements in the stream.
+ */
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+ /**
+ * Returns {@code true} if there are more elements in the stream.
+ *
+ * Will block and wait for input if the stream has not ended and the next object is not yet available.
+ *
+ * @return {@code true} if there are more elements, {@code false} otherwise.
+ */
+ @Override
+ public boolean hasNext() {
+ return scanner.hasNext();
+ }
+
+ /**
+ * Returns the next element in the stream.
+ *
+ * Will block and wait for input if the stream has not ended and the next object is not yet available.
+ *
+ * @return The next element in the stream.
+ * @throws NoSuchElementException If there are no more elements in the stream.
+ */
+ @Override
+ public T next() {
+ if (!scanner.hasNext()) {
+ throw new NoSuchElementException();
+ } else {
+ try {
+ T parsedResponse = ObjectMappers.JSON_MAPPER.readValue(
+ scanner.next().trim(), valueType);
+ return parsedResponse;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Removing elements from {@code Stream} is not supported.
+ *
+ * @throws UnsupportedOperationException Always, as removal is not supported.
+ */
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+}
diff --git a/common/src/main/java/com/skyflow/generated/auth/rest/core/Suppliers.java b/common/src/main/java/com/skyflow/generated/auth/rest/core/Suppliers.java
new file mode 100644
index 00000000..d3ab5e53
--- /dev/null
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/core/Suppliers.java
@@ -0,0 +1,23 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.auth.rest.core;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
+public final class Suppliers {
+ private Suppliers() {}
+
+ public static Supplier memoize(Supplier delegate) {
+ AtomicReference value = new AtomicReference<>();
+ return () -> {
+ T val = value.get();
+ if (val == null) {
+ val = value.updateAndGet(cur -> cur == null ? Objects.requireNonNull(delegate.get()) : cur);
+ }
+ return val;
+ };
+ }
+}
diff --git a/common/src/main/java/com/skyflow/generated/auth/rest/errors/BadRequestError.java b/common/src/main/java/com/skyflow/generated/auth/rest/errors/BadRequestError.java
new file mode 100644
index 00000000..4517200d
--- /dev/null
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/errors/BadRequestError.java
@@ -0,0 +1,34 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.auth.rest.errors;
+
+import com.skyflow.generated.auth.rest.core.ApiClientApiException;
+import okhttp3.Response;
+
+import java.util.Map;
+
+public final class BadRequestError extends ApiClientApiException {
+ /**
+ * The body of the response that triggered the exception.
+ */
+ private final Map body;
+
+ public BadRequestError(Map body) {
+ super("BadRequestError", 400, body);
+ this.body = body;
+ }
+
+ public BadRequestError(Map body, Response rawResponse) {
+ super("BadRequestError", 400, body, rawResponse);
+ this.body = body;
+ }
+
+ /**
+ * @return the body
+ */
+ @Override
+ public Map body() {
+ return this.body;
+ }
+}
diff --git a/common/src/main/java/com/skyflow/generated/auth/rest/errors/HttpStatus.java b/common/src/main/java/com/skyflow/generated/auth/rest/errors/HttpStatus.java
new file mode 100644
index 00000000..2e1c45d2
--- /dev/null
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/errors/HttpStatus.java
@@ -0,0 +1,15 @@
+package com.skyflow.generated.auth.rest.errors;
+
+public enum HttpStatus {
+ BAD_REQUEST("Bad Request");
+
+ private final String httpStatus;
+
+ HttpStatus(String httpStatus) {
+ this.httpStatus = httpStatus;
+ }
+
+ public String getHttpStatus() {
+ return httpStatus;
+ }
+}
diff --git a/common/src/main/java/com/skyflow/generated/auth/rest/errors/NotFoundError.java b/common/src/main/java/com/skyflow/generated/auth/rest/errors/NotFoundError.java
new file mode 100644
index 00000000..60a1771c
--- /dev/null
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/errors/NotFoundError.java
@@ -0,0 +1,34 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.auth.rest.errors;
+
+import com.skyflow.generated.auth.rest.core.ApiClientApiException;
+import okhttp3.Response;
+
+import java.util.Map;
+
+public final class NotFoundError extends ApiClientApiException {
+ /**
+ * The body of the response that triggered the exception.
+ */
+ private final Map body;
+
+ public NotFoundError(Map body) {
+ super("NotFoundError", 404, body);
+ this.body = body;
+ }
+
+ public NotFoundError(Map body, Response rawResponse) {
+ super("NotFoundError", 404, body, rawResponse);
+ this.body = body;
+ }
+
+ /**
+ * @return the body
+ */
+ @Override
+ public Map body() {
+ return this.body;
+ }
+}
diff --git a/common/src/main/java/com/skyflow/generated/auth/rest/errors/UnauthorizedError.java b/common/src/main/java/com/skyflow/generated/auth/rest/errors/UnauthorizedError.java
new file mode 100644
index 00000000..42f1c624
--- /dev/null
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/errors/UnauthorizedError.java
@@ -0,0 +1,34 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.auth.rest.errors;
+
+import com.skyflow.generated.auth.rest.core.ApiClientApiException;
+import okhttp3.Response;
+
+import java.util.Map;
+
+public final class UnauthorizedError extends ApiClientApiException {
+ /**
+ * The body of the response that triggered the exception.
+ */
+ private final Map body;
+
+ public UnauthorizedError(Map body) {
+ super("UnauthorizedError", 401, body);
+ this.body = body;
+ }
+
+ public UnauthorizedError(Map body, Response rawResponse) {
+ super("UnauthorizedError", 401, body, rawResponse);
+ this.body = body;
+ }
+
+ /**
+ * @return the body
+ */
+ @Override
+ public Map body() {
+ return this.body;
+ }
+}
diff --git a/src/main/java/com/skyflow/generated/rest/resources/authentication/AsyncAuthenticationClient.java b/common/src/main/java/com/skyflow/generated/auth/rest/resources/authentication/AsyncAuthenticationClient.java
similarity index 84%
rename from src/main/java/com/skyflow/generated/rest/resources/authentication/AsyncAuthenticationClient.java
rename to common/src/main/java/com/skyflow/generated/auth/rest/resources/authentication/AsyncAuthenticationClient.java
index 43ffab73..c742b239 100644
--- a/src/main/java/com/skyflow/generated/rest/resources/authentication/AsyncAuthenticationClient.java
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/resources/authentication/AsyncAuthenticationClient.java
@@ -1,12 +1,13 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/
-package com.skyflow.generated.rest.resources.authentication;
+package com.skyflow.generated.auth.rest.resources.authentication;
+
+import com.skyflow.generated.auth.rest.core.ClientOptions;
+import com.skyflow.generated.auth.rest.core.RequestOptions;
+import com.skyflow.generated.auth.rest.resources.authentication.requests.V1GetAuthTokenRequest;
+import com.skyflow.generated.auth.rest.types.V1GetAuthTokenResponse;
-import com.skyflow.generated.rest.core.ClientOptions;
-import com.skyflow.generated.rest.core.RequestOptions;
-import com.skyflow.generated.rest.resources.authentication.requests.V1GetAuthTokenRequest;
-import com.skyflow.generated.rest.types.V1GetAuthTokenResponse;
import java.util.concurrent.CompletableFuture;
public class AsyncAuthenticationClient {
diff --git a/src/main/java/com/skyflow/generated/rest/resources/authentication/AsyncRawAuthenticationClient.java b/common/src/main/java/com/skyflow/generated/auth/rest/resources/authentication/AsyncRawAuthenticationClient.java
similarity index 82%
rename from src/main/java/com/skyflow/generated/rest/resources/authentication/AsyncRawAuthenticationClient.java
rename to common/src/main/java/com/skyflow/generated/auth/rest/resources/authentication/AsyncRawAuthenticationClient.java
index eca4ab90..56a47dc0 100644
--- a/src/main/java/com/skyflow/generated/rest/resources/authentication/AsyncRawAuthenticationClient.java
+++ b/common/src/main/java/com/skyflow/generated/auth/rest/resources/authentication/AsyncRawAuthenticationClient.java
@@ -1,33 +1,22 @@
/**
* This file was auto-generated by Fern from our API Definition.
*/
-package com.skyflow.generated.rest.resources.authentication;
+package com.skyflow.generated.auth.rest.resources.authentication;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.skyflow.generated.rest.core.ApiClientApiException;
-import com.skyflow.generated.rest.core.ApiClientException;
-import com.skyflow.generated.rest.core.ApiClientHttpResponse;
-import com.skyflow.generated.rest.core.ClientOptions;
-import com.skyflow.generated.rest.core.MediaTypes;
-import com.skyflow.generated.rest.core.ObjectMappers;
-import com.skyflow.generated.rest.core.RequestOptions;
-import com.skyflow.generated.rest.errors.BadRequestError;
-import com.skyflow.generated.rest.errors.NotFoundError;
-import com.skyflow.generated.rest.errors.UnauthorizedError;
-import com.skyflow.generated.rest.resources.authentication.requests.V1GetAuthTokenRequest;
-import com.skyflow.generated.rest.types.V1GetAuthTokenResponse;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.skyflow.generated.auth.rest.core.*;
+import com.skyflow.generated.auth.rest.errors.BadRequestError;
+import com.skyflow.generated.auth.rest.errors.NotFoundError;
+import com.skyflow.generated.auth.rest.errors.UnauthorizedError;
+import com.skyflow.generated.auth.rest.resources.authentication.requests.V1GetAuthTokenRequest;
+import com.skyflow.generated.auth.rest.types.V1GetAuthTokenResponse;
+import okhttp3.*;
+import org.jetbrains.annotations.NotNull;
+
import java.io.IOException;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
-import okhttp3.Call;
-import okhttp3.Callback;
-import okhttp3.Headers;
-import okhttp3.HttpUrl;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-import org.jetbrains.annotations.NotNull;
public class AsyncRawAuthenticationClient {
protected final ClientOptions clientOptions;
@@ -88,17 +77,20 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO
switch (response.code()) {
case 400:
future.completeExceptionally(new BadRequestError(
- ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ ObjectMappers.JSON_MAPPER.readValue(
+ responseBodyString, new TypeReference
*/
public ArrayList> getData() {
return data;
diff --git a/common/src/main/java/com/skyflow/vault/data/BaseInsertRequest.java b/common/src/main/java/com/skyflow/vault/data/BaseInsertRequest.java
new file mode 100644
index 00000000..d2f6f812
--- /dev/null
+++ b/common/src/main/java/com/skyflow/vault/data/BaseInsertRequest.java
@@ -0,0 +1,25 @@
+package com.skyflow.vault.data;
+
+class BaseInsertRequest {
+ private final BaseInsertRequestBuilder builder;
+
+ protected BaseInsertRequest(BaseInsertRequestBuilder builder) {
+ this.builder = builder;
+ }
+
+ public String getTable() {
+ return this.builder.table;
+ }
+
+ static class BaseInsertRequestBuilder {
+ protected String table;
+ protected BaseInsertRequestBuilder() {
+ }
+
+ public BaseInsertRequestBuilder table(String table) {
+ this.table = table;
+ return this;
+ }
+
+ }
+}
diff --git a/src/main/java/com/skyflow/vault/data/InsertResponse.java b/common/src/main/java/com/skyflow/vault/data/BaseInsertResponse.java
similarity index 80%
rename from src/main/java/com/skyflow/vault/data/InsertResponse.java
rename to common/src/main/java/com/skyflow/vault/data/BaseInsertResponse.java
index 3d311525..84196bf7 100644
--- a/src/main/java/com/skyflow/vault/data/InsertResponse.java
+++ b/common/src/main/java/com/skyflow/vault/data/BaseInsertResponse.java
@@ -5,11 +5,11 @@
import java.util.ArrayList;
import java.util.HashMap;
-public class InsertResponse {
+public class BaseInsertResponse {
private final ArrayList> insertedFields;
private final ArrayList> errors;
- public InsertResponse(ArrayList> insertedFields, ArrayList> errors) {
+ public BaseInsertResponse(ArrayList> insertedFields, ArrayList> errors) {
this.insertedFields = insertedFields;
this.errors = errors;
}
@@ -28,3 +28,4 @@ public String toString() {
return gson.toJson(this);
}
}
+
diff --git a/common/src/main/java/com/skyflow/vault/data/BaseQueryRequest.java b/common/src/main/java/com/skyflow/vault/data/BaseQueryRequest.java
new file mode 100644
index 00000000..01ca3d46
--- /dev/null
+++ b/common/src/main/java/com/skyflow/vault/data/BaseQueryRequest.java
@@ -0,0 +1,25 @@
+package com.skyflow.vault.data;
+
+public class BaseQueryRequest {
+ private final BaseQueryRequestBuilder builder;
+
+ protected BaseQueryRequest(BaseQueryRequestBuilder builder) {
+ this.builder = builder;
+ }
+
+ public String getQuery() {
+ return this.builder.query;
+ }
+
+ static class BaseQueryRequestBuilder {
+ protected String query;
+
+ protected BaseQueryRequestBuilder() {
+ }
+
+ public BaseQueryRequestBuilder query(String query) {
+ this.query = query;
+ return this;
+ }
+ }
+}
diff --git a/common/src/main/java/com/skyflow/vault/data/BaseQueryResponse.java b/common/src/main/java/com/skyflow/vault/data/BaseQueryResponse.java
new file mode 100644
index 00000000..1930d66d
--- /dev/null
+++ b/common/src/main/java/com/skyflow/vault/data/BaseQueryResponse.java
@@ -0,0 +1,37 @@
+package com.skyflow.vault.data;
+
+import com.google.gson.Gson;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class BaseQueryResponse {
+ private final ArrayList> fields;
+ private final ArrayList> errors;
+
+ public BaseQueryResponse(ArrayList> fields) {
+ this.fields = fields;
+ this.errors = null;
+ }
+
+ /**
+ * Returns the list of record maps from the Query response. Each map contains all
+ * field name/value pairs for the record.
+ */
+ public ArrayList> getFields() {
+ return fields;
+ }
+
+ /**
+ * Always returns null. The Query API does not support partial-error responses.
+ */
+ public ArrayList> getErrors() {
+ return errors;
+ }
+
+ @Override
+ public String toString() {
+ Gson gson = new Gson().newBuilder().serializeNulls().create();
+ return gson.toJson(this);
+ }
+}
diff --git a/common/src/main/java/com/skyflow/vault/data/RequestContext.java b/common/src/main/java/com/skyflow/vault/data/RequestContext.java
new file mode 100644
index 00000000..0cf6055b
--- /dev/null
+++ b/common/src/main/java/com/skyflow/vault/data/RequestContext.java
@@ -0,0 +1,26 @@
+package com.skyflow.vault.data;
+
+import com.skyflow.enums.CustomHeaderKey;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class RequestContext {
+ private final String operation;
+ private final Map headers = new HashMap<>();
+
+ public RequestContext(String operation) {
+ this.operation = operation;
+ }
+
+ public String getOperation() { return operation; }
+
+ public void addHeader(CustomHeaderKey key, String value) {
+ headers.put(key, value);
+ }
+
+ public Map getHeaders() {
+ return Collections.unmodifiableMap(headers);
+ }
+}
diff --git a/common/src/main/java/com/skyflow/vault/data/RequestInterceptor.java b/common/src/main/java/com/skyflow/vault/data/RequestInterceptor.java
new file mode 100644
index 00000000..37f5261d
--- /dev/null
+++ b/common/src/main/java/com/skyflow/vault/data/RequestInterceptor.java
@@ -0,0 +1,6 @@
+package com.skyflow.vault.data;
+
+@FunctionalInterface
+public interface RequestInterceptor {
+ void intercept(RequestContext context);
+}
diff --git a/common/src/test/java/com/skyflow/BaseSkyflowTests.java b/common/src/test/java/com/skyflow/BaseSkyflowTests.java
new file mode 100644
index 00000000..cfd65ad5
--- /dev/null
+++ b/common/src/test/java/com/skyflow/BaseSkyflowTests.java
@@ -0,0 +1,440 @@
+package com.skyflow;
+
+import com.skyflow.config.BaseVaultConfig;
+import com.skyflow.config.Credentials;
+import com.skyflow.enums.Env;
+import com.skyflow.enums.LogLevel;
+import com.skyflow.errors.ErrorCode;
+import com.skyflow.errors.ErrorMessage;
+import com.skyflow.errors.SkyflowException;
+import com.skyflow.logs.ErrorLogs;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class BaseSkyflowTests {
+ private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception";
+ private static final String EXCEPTION_NOT_THROWN = "Should have thrown an exception";
+ private static String vaultID = null;
+ private static String clusterID = null;
+ private static String newClusterID = null;
+ private static String token = null;
+
+ @BeforeClass
+ public static void setup() {
+ vaultID = "test_vault_id";
+ clusterID = "test_cluster_id";
+ newClusterID = "new_test_cluster_id";
+ token = "test_token";
+ }
+
+ private static BaseVaultConfig newConfig(String vaultId, String clusterId, Env env) {
+ BaseVaultConfig config = new BaseVaultConfig();
+ config.setVaultId(vaultId);
+ config.setClusterId(clusterId);
+ config.setEnv(env);
+ return config;
+ }
+
+ @Test
+ public void testAddingExistingVaultConfigThrows() {
+ try {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().build();
+ client.addVaultConfig(config).addVaultConfig(config);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdAlreadyInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdatingNonExistentVaultConfigInBuilderThrows() {
+ try {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow.builder().updateVaultConfig(config).build();
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdatingNonExistentVaultConfigInClientThrows() {
+ try {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().build();
+ client.updateVaultConfig(config);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testRemovingNonExistentVaultConfigInBuilderThrows() {
+ try {
+ TestSkyflow.builder().removeVaultConfig(vaultID).build();
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testRemovingExistingVaultConfigSucceeds() {
+ try {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(config).build();
+ client.removeVaultConfig(vaultID);
+ } catch (SkyflowException e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testRemovingVaultConfigWithoutAddingThrows() {
+ try {
+ TestSkyflow client = TestSkyflow.builder().build();
+ client.removeVaultConfig(vaultID);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGettingNonExistentVaultConfigReturnsNull() {
+ TestSkyflow client = TestSkyflow.builder().build();
+ Assert.assertNull(client.getVaultConfig(vaultID));
+ }
+
+ @Test
+ public void testVaultThrowsWhenNoConfigAdded() {
+ try {
+ TestSkyflow client = TestSkyflow.builder().build();
+ client.vault();
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testVaultByIdThrowsAfterRemoval() {
+ try {
+ BaseVaultConfig primary = newConfig(vaultID, clusterID, Env.SANDBOX);
+ BaseVaultConfig secondary = newConfig(vaultID + "123", clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(primary).addVaultConfig(secondary).build();
+ client.removeVaultConfig(vaultID);
+ client.vault(vaultID);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.VaultIdNotInConfigList.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testDefaultLogLevel() {
+ TestSkyflow client = TestSkyflow.builder().setLogLevel(null).build();
+ Assert.assertEquals(LogLevel.ERROR, client.getLogLevel());
+ }
+
+ @Test
+ public void testSetLogLevel() {
+ TestSkyflow client = TestSkyflow.builder().setLogLevel(LogLevel.INFO).build();
+ Assert.assertEquals(LogLevel.INFO, client.getLogLevel());
+ client.setLogLevel(LogLevel.WARN);
+ Assert.assertEquals(LogLevel.WARN, client.getLogLevel());
+ }
+
+ @Test
+ public void testAddingInvalidSkyflowCredentialsThrows() {
+ try {
+ Credentials credentials = new Credentials();
+ TestSkyflow.builder().addSkyflowCredentials(credentials).build();
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.NoTokenGenerationMeansPassed.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdatingValidSkyflowCredentialsSucceeds() {
+ try {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ Credentials credentials = new Credentials();
+ credentials.setToken(token);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(config).build();
+ client.updateSkyflowCredentials(credentials);
+ } catch (SkyflowException e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testUpdateVaultConfigNullCredentialsFallsBackToPrevious() {
+ try {
+ Credentials creds = new Credentials();
+ creds.setToken(token);
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ config.setCredentials(creds);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(config).build();
+
+ BaseVaultConfig partialUpdate = newConfig(vaultID, clusterID, Env.SANDBOX);
+ client.updateVaultConfig(partialUpdate);
+ Assert.assertNotNull(client.getVaultConfig(vaultID).getCredentials());
+ } catch (SkyflowException e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testUpdateVaultConfigWithNewClusterIdAndCredentialsUpdatesAllFields() {
+ try {
+ Credentials creds = new Credentials();
+ creds.setToken(token);
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.DEV);
+ config.setCredentials(creds);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(config).build();
+
+ Credentials newCreds = new Credentials();
+ newCreds.setToken("updated-token-value");
+ BaseVaultConfig update = newConfig(vaultID, newClusterID, Env.PROD);
+ update.setCredentials(newCreds);
+ client.updateVaultConfig(update);
+
+ Assert.assertEquals(newClusterID, client.getVaultConfig(vaultID).getClusterId());
+ Assert.assertEquals(Env.PROD, client.getVaultConfig(vaultID).getEnv());
+ Assert.assertEquals("updated-token-value", client.getVaultConfig(vaultID).getCredentials().getToken());
+ } catch (SkyflowException e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testUpdateVaultConfigWithNullEnvFallsBackToPreviousEnv() {
+ // BaseVaultConfig's setEnv/constructor never store null (default to PROD), so getEnv()
+ // never returns null via the normal API. Override it to exercise the fallback branch
+ // in mergeVaultConfig.
+ try {
+ Credentials creds = new Credentials();
+ creds.setToken(token);
+ BaseVaultConfig initial = newConfig(vaultID, clusterID, Env.SANDBOX);
+ initial.setCredentials(creds);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(initial).build();
+
+ BaseVaultConfig updateWithNullEnv = new BaseVaultConfig() {
+ @Override
+ public Env getEnv() {
+ return null;
+ }
+ };
+ updateWithNullEnv.setVaultId(vaultID);
+ updateWithNullEnv.setClusterId(clusterID);
+ updateWithNullEnv.setCredentials(creds);
+
+ client.updateVaultConfig(updateWithNullEnv);
+ Assert.assertEquals(Env.SANDBOX, client.getVaultConfig(vaultID).getEnv());
+ } catch (SkyflowException e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testMergeVaultConfigWithNullClusterIdFallsBackToPreviousClusterId() throws SkyflowException {
+ BaseVaultConfig existing = newConfig(vaultID, clusterID, Env.DEV);
+ BaseVaultConfig incoming = new BaseVaultConfig();
+ incoming.setVaultId(vaultID);
+ // clusterId intentionally left null
+
+ BaseVaultConfig result = TestSkyflow.builder().mergeVaultConfig(incoming, existing);
+ Assert.assertEquals(clusterID, result.getClusterId());
+ }
+
+ @Test
+ public void testVaultReturnsFirstEntryWhenNoVaultIdSpecified() throws SkyflowException {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(config).build();
+
+ Object vault = client.vault();
+
+ Assert.assertNotNull(vault);
+ Assert.assertSame(vault, client.vault(vaultID));
+ }
+
+ @Test
+ public void testVaultByIdReturnsConfigSpecificEntry() throws SkyflowException {
+ String secondaryId = vaultID + "123";
+ BaseVaultConfig primary = newConfig(vaultID, clusterID, Env.SANDBOX);
+ BaseVaultConfig secondary = newConfig(secondaryId, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(primary).addVaultConfig(secondary).build();
+
+ Object primaryVault = client.vault(vaultID);
+ Object secondaryVault = client.vault(secondaryId);
+
+ Assert.assertNotNull(primaryVault);
+ Assert.assertNotNull(secondaryVault);
+ Assert.assertNotSame(primaryVault, secondaryVault);
+ Assert.assertSame(primaryVault, client.vault(vaultID));
+ }
+
+ @Test
+ public void testGettingExistingVaultConfigReturnsStoredConfig() throws SkyflowException {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder().addVaultConfig(config).build();
+
+ BaseVaultConfig stored = client.getVaultConfig(vaultID);
+
+ Assert.assertNotNull(stored);
+ Assert.assertEquals(vaultID, stored.getVaultId());
+ Assert.assertEquals(clusterID, stored.getClusterId());
+ Assert.assertEquals(Env.SANDBOX, stored.getEnv());
+ }
+
+ @Test
+ public void testInstanceSetLogLevelNullResetsToDefault() throws SkyflowException {
+ TestSkyflow client = TestSkyflow.builder().setLogLevel(LogLevel.INFO).build();
+ Assert.assertEquals(LogLevel.INFO, client.getLogLevel());
+
+ client.setLogLevel(null);
+
+ Assert.assertEquals(LogLevel.ERROR, client.getLogLevel());
+ }
+
+ @Test
+ public void testUpdateVaultConfigLeavesOldConfigWhenHookThrows() throws SkyflowException {
+ BaseVaultConfig config = newConfig(vaultID, clusterID, Env.SANDBOX);
+ TestSkyflow client = TestSkyflow.builder()
+ .addVaultConfig(config)
+ .failVaultConfigUpdateFor(vaultID)
+ .build();
+
+ BaseVaultConfig update = newConfig(vaultID, newClusterID, Env.PROD);
+ try {
+ client.updateVaultConfig(update);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ // expected: onVaultConfigUpdated hook forced a failure
+ }
+
+ BaseVaultConfig stillOld = client.getVaultConfig(vaultID);
+ Assert.assertEquals(clusterID, stillOld.getClusterId());
+ Assert.assertEquals(Env.SANDBOX, stillOld.getEnv());
+ }
+
+ private static class TestSkyflow extends BaseSkyflow {
+ private final TestSkyflowClientBuilder builder;
+
+ private TestSkyflow(TestSkyflowClientBuilder builder) {
+ super(builder);
+ this.builder = builder;
+ }
+
+ static TestSkyflowClientBuilder builder() {
+ return new TestSkyflowClientBuilder();
+ }
+
+ @Override
+ protected TestSkyflow self() {
+ return this;
+ }
+
+ Object vault() throws SkyflowException {
+ return resolveOrThrow(this.builder.vaultClientsMap, null,
+ ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList);
+ }
+
+ Object vault(String vaultId) throws SkyflowException {
+ return resolveOrThrow(this.builder.vaultClientsMap, vaultId,
+ ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList);
+ }
+
+ private static class TestSkyflowClientBuilder extends BaseSkyflowClientBuilder {
+ private final java.util.LinkedHashMap vaultClientsMap = new java.util.LinkedHashMap<>();
+ private String vaultIdToFailUpdate;
+
+ @Override
+ protected void validateVaultConfig(BaseVaultConfig vaultConfig) throws SkyflowException {
+ if (vaultConfig.getVaultId() == null || vaultConfig.getVaultId().trim().isEmpty()) {
+ throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyVaultId.getMessage());
+ }
+ }
+
+ @Override
+ protected void onVaultConfigAdded(BaseVaultConfig vaultConfig) {
+ this.vaultClientsMap.put(vaultConfig.getVaultId(), new Object());
+ }
+
+ @Override
+ protected void onVaultConfigUpdated(BaseVaultConfig updatedConfig) throws SkyflowException {
+ if (updatedConfig.getVaultId().equals(this.vaultIdToFailUpdate)) {
+ throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), "forced update hook failure");
+ }
+ this.vaultClientsMap.put(updatedConfig.getVaultId(), new Object());
+ }
+
+ TestSkyflowClientBuilder failVaultConfigUpdateFor(String vaultId) {
+ this.vaultIdToFailUpdate = vaultId;
+ return this;
+ }
+
+ @Override
+ protected void onVaultConfigRemoved(String vaultId) {
+ this.vaultClientsMap.remove(vaultId);
+ }
+
+ @Override
+ protected boolean hasVaultClient(String vaultId) {
+ return this.vaultClientsMap.containsKey(vaultId);
+ }
+
+ @Override
+ protected void onCredentialsUpdated(Credentials credentials) {
+ // no-op: this test double only exercises template orchestration, not propagation
+ }
+
+ @Override
+ public TestSkyflowClientBuilder addVaultConfig(BaseVaultConfig vaultConfig) throws SkyflowException {
+ super.addVaultConfig(vaultConfig);
+ return this;
+ }
+
+ @Override
+ public TestSkyflowClientBuilder updateVaultConfig(BaseVaultConfig vaultConfig) throws SkyflowException {
+ super.updateVaultConfig(vaultConfig);
+ return this;
+ }
+
+ @Override
+ public TestSkyflowClientBuilder removeVaultConfig(String vaultId) throws SkyflowException {
+ super.removeVaultConfig(vaultId);
+ return this;
+ }
+
+ @Override
+ public TestSkyflowClientBuilder addSkyflowCredentials(Credentials credentials) throws SkyflowException {
+ super.addSkyflowCredentials(credentials);
+ return this;
+ }
+
+ @Override
+ public TestSkyflowClientBuilder setLogLevel(LogLevel logLevel) {
+ super.setLogLevel(logLevel);
+ return this;
+ }
+
+ TestSkyflow build() {
+ return new TestSkyflow(this);
+ }
+ }
+ }
+}
diff --git a/common/src/test/java/com/skyflow/BaseVaultClientTests.java b/common/src/test/java/com/skyflow/BaseVaultClientTests.java
new file mode 100644
index 00000000..ec78c397
--- /dev/null
+++ b/common/src/test/java/com/skyflow/BaseVaultClientTests.java
@@ -0,0 +1,304 @@
+package com.skyflow;
+
+import com.skyflow.config.BaseCredentials;
+import com.skyflow.config.BaseVaultConfig;
+import com.skyflow.errors.ErrorMessage;
+import com.skyflow.errors.SkyflowException;
+import com.skyflow.logs.ErrorLogs;
+import com.skyflow.utils.BaseConstants;
+import okhttp3.Call;
+import okhttp3.Connection;
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Protocol;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+public class BaseVaultClientTests {
+
+ private static final String ENV_FILE = ".env";
+ private byte[] originalEnvContent;
+
+ @Before
+ public void saveEnvFileState() throws IOException {
+ File f = new File(ENV_FILE);
+ originalEnvContent = f.exists() ? Files.readAllBytes(Paths.get(ENV_FILE)) : null;
+ }
+
+ @After
+ public void restoreEnvFile() throws IOException {
+ if (originalEnvContent != null) {
+ Files.write(Paths.get(ENV_FILE), originalEnvContent);
+ } else {
+ Files.deleteIfExists(Paths.get(ENV_FILE));
+ }
+ }
+
+ private BaseVaultClient newClient(BaseCredentials commonCredentials) {
+ return new BaseVaultClient<>(new BaseVaultConfig(), commonCredentials);
+ }
+
+ @Test
+ public void testPrioritiseCredentials_prefersVaultSpecificCredentials() throws SkyflowException {
+ BaseCredentials vaultSpecific = new BaseCredentials();
+ vaultSpecific.setApiKey("test_api_key");
+ BaseVaultClient client = newClient(null);
+
+ client.prioritiseCredentials(vaultSpecific);
+
+ Assert.assertEquals(vaultSpecific, client.finalCredentials);
+ }
+
+ @Test
+ public void testPrioritiseCredentials_fallsBackToCommonCredentials() throws SkyflowException {
+ BaseCredentials common = new BaseCredentials();
+ common.setApiKey("common_api_key");
+ BaseVaultClient client = newClient(common);
+
+ client.prioritiseCredentials(null);
+
+ Assert.assertEquals(common, client.finalCredentials);
+ }
+
+ @Test
+ public void testPrioritiseCredentials_credentialChange_resetsTokenAndApiKey() throws SkyflowException {
+ BaseCredentials credentialsA = new BaseCredentials();
+ credentialsA.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y");
+ BaseVaultClient client = newClient(null);
+
+ client.prioritiseCredentials(credentialsA);
+ client.token = "cached-token";
+ client.apiKey = "cached-api-key";
+
+ BaseCredentials credentialsB = new BaseCredentials();
+ credentialsB.setToken("other-token");
+ client.prioritiseCredentials(credentialsB);
+
+ Assert.assertNull(client.token);
+ Assert.assertNull(client.apiKey);
+ }
+
+ @Test
+ public void testSetBearerToken_withApiKey() throws SkyflowException {
+ BaseCredentials creds = new BaseCredentials();
+ creds.setApiKey("sky-ab123-abcd1234cdef1234abcd4321cdef4321");
+ BaseVaultClient client = newClient(null);
+
+ client.setBearerToken(creds);
+
+ Assert.assertEquals("sky-ab123-abcd1234cdef1234abcd4321cdef4321", client.token);
+ }
+
+ @Test
+ public void testSetBearerToken_generatesTokenWhenNull() throws SkyflowException {
+ BaseCredentials creds = new BaseCredentials();
+ creds.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y");
+ BaseVaultClient client = newClient(null);
+
+ client.setBearerToken(creds);
+
+ Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
+ }
+
+ @Test
+ public void testSetBearerToken_reusesValidNonExpiredToken() throws SkyflowException {
+ BaseCredentials creds = new BaseCredentials();
+ creds.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y");
+ BaseVaultClient client = newClient(null);
+
+ // First call: token=null → generates from creds.getToken()
+ client.setBearerToken(creds);
+ Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
+
+ // Second call: token valid, not expired → reuse branch
+ client.setBearerToken(creds);
+ Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
+ }
+
+ @Test
+ public void testSetBearerToken_noCredentials_throwsEmptyCredentials() {
+ BaseVaultClient client = newClient(null);
+ try {
+ client.setBearerToken(null);
+ Assert.fail("Should have thrown SkyflowException");
+ } catch (SkyflowException e) {
+ // message varies by environment (EmptyCredentials when no .env, or credential error when .env provides creds)
+ }
+ }
+
+ /**
+ * Covers the dotenv success path: Dotenv.load() succeeds and returns a
+ * non-null credentials string, so finalCredentials is set via credentialsString.
+ */
+ @Test
+ public void testPrioritiseCredentials_dotenvReturnsCredentials_setsCredentials() throws Exception {
+ try (FileWriter fw = new FileWriter(ENV_FILE)) {
+ fw.write(BaseConstants.ENV_CREDENTIALS_KEY_NAME + "={\"token\":\"env-token-value\"}\n");
+ }
+
+ BaseVaultClient client = newClient(null);
+ client.prioritiseCredentials(null);
+
+ Assert.assertNotNull(client.finalCredentials);
+ Assert.assertEquals("{\"token\":\"env-token-value\"}", client.finalCredentials.getCredentialsString());
+ }
+
+ /**
+ * Covers the path where dotenv loads but the key is absent (returns null),
+ * causing SkyflowException(EmptyCredentials) to be thrown directly.
+ */
+ @Test
+ public void testPrioritiseCredentials_dotenvKeyMissing_throwsSkyflowException() throws Exception {
+ try (FileWriter fw = new FileWriter(ENV_FILE)) {
+ fw.write("SOME_OTHER_KEY=some_value\n");
+ }
+
+ BaseVaultClient client = newClient(null);
+ try {
+ client.prioritiseCredentials(null);
+ Assert.fail("Should have thrown SkyflowException");
+ } catch (SkyflowException e) {
+ Assert.assertTrue(e.getMessage().contains(ErrorMessage.EmptyCredentials.getMessage()));
+ }
+ }
+
+ /**
+ * Covers buildSharedHttpClient: the interceptor it installs must inject
+ * "Authorization: Bearer " using the supplied tokenSupplier. No real network call is
+ * made; a hand-rolled Interceptor.Chain captures the request that would be sent.
+ */
+ @Test
+ public void testBuildSharedHttpClient_injectsAuthorizationHeaderFromTokenSupplier() throws IOException {
+ BaseVaultClient client = newClient(null);
+ Supplier tokenSupplier = () -> "test-shared-token";
+
+ OkHttpClient httpClient = client.buildSharedHttpClient(tokenSupplier);
+
+ Assert.assertEquals(1, httpClient.interceptors().size());
+ Interceptor interceptor = httpClient.interceptors().get(0);
+
+ Request originalRequest = new Request.Builder().url("https://example.com/").build();
+ final Request[] capturedRequest = new Request[1];
+ Interceptor.Chain fakeChain = new Interceptor.Chain() {
+ @Override
+ public Request request() {
+ return originalRequest;
+ }
+
+ @Override
+ public Response proceed(Request request) {
+ capturedRequest[0] = request;
+ return new Response.Builder()
+ .request(request)
+ .protocol(Protocol.HTTP_1_1)
+ .code(200)
+ .message("OK")
+ .build();
+ }
+
+ @Override
+ public Connection connection() {
+ return null;
+ }
+
+ @Override
+ public Call call() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int connectTimeoutMillis() {
+ return 0;
+ }
+
+ @Override
+ public Interceptor.Chain withConnectTimeout(int timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int readTimeoutMillis() {
+ return 0;
+ }
+
+ @Override
+ public Interceptor.Chain withReadTimeout(int timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int writeTimeoutMillis() {
+ return 0;
+ }
+
+ @Override
+ public Interceptor.Chain withWriteTimeout(int timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ interceptor.intercept(fakeChain);
+
+ Assert.assertNotNull(capturedRequest[0]);
+ Assert.assertEquals("Bearer test-shared-token", capturedRequest[0].header("Authorization"));
+ }
+
+ /**
+ * Covers wrapApiException: the returned SkyflowException should carry the given status code,
+ * and its message should reflect the JSON-serialized responseBody (round-tripped through Gson
+ * exactly as wrapApiException does internally).
+ */
+ @Test
+ public void testWrapApiException_carriesStatusCodeAndJsonResponseBody() {
+ Map errorBody = new HashMap<>();
+ errorBody.put("message", "something went wrong");
+ Map responseBody = new HashMap<>();
+ responseBody.put("error", errorBody);
+ Map> headers = new HashMap<>();
+
+ SkyflowException exception = BaseVaultClient.wrapApiException(
+ 400, new RuntimeException("network error"), headers, responseBody, ErrorLogs.INSERT_RECORDS_REJECTED);
+
+ Assert.assertEquals(400, exception.getHttpCode());
+ Assert.assertEquals("something went wrong", exception.getMessage());
+ }
+
+ /**
+ * Covers setBearerToken's "token present but expired -> regenerate" branch. A hand-crafted
+ * always-expired JWT ("x.eyJleHAiOjF9.y", exp=1 => 1970) is seeded directly into the token
+ * field, bypassing setBearerToken. The very same credentials instance used to seed
+ * finalCredentials is then reused when calling setBearerToken, so prioritiseCredentials does
+ * NOT treat this as a credential change (that branch is covered by
+ * testPrioritiseCredentials_credentialChange_resetsTokenAndApiKey) - isolating the isExpired
+ * branch specifically.
+ */
+ @Test
+ public void testSetBearerToken_expiredToken_regeneratesToken() throws SkyflowException {
+ BaseCredentials creds = new BaseCredentials();
+ creds.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y"); // far-future exp -> not expired once (re)generated
+ BaseVaultClient client = newClient(null);
+
+ client.prioritiseCredentials(creds);
+ client.token = "x.eyJleHAiOjF9.y"; // exp=1 (1970) -> always expired, seeded directly
+
+ client.setBearerToken(creds);
+
+ Assert.assertEquals("x.eyJleHAiOjk5OTk5OTk5OTl9.y", client.token);
+ Assert.assertNotEquals("x.eyJleHAiOjF9.y", client.token);
+ }
+}
diff --git a/common/src/test/java/com/skyflow/config/BaseVaultConfigTests.java b/common/src/test/java/com/skyflow/config/BaseVaultConfigTests.java
new file mode 100644
index 00000000..bea158b5
--- /dev/null
+++ b/common/src/test/java/com/skyflow/config/BaseVaultConfigTests.java
@@ -0,0 +1,75 @@
+package com.skyflow.config;
+
+import com.skyflow.enums.Env;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BaseVaultConfigTests {
+
+ @Test
+ public void testDefaultConstructorDefaults() {
+ BaseVaultConfig config = new BaseVaultConfig();
+ Assert.assertNull(config.getVaultId());
+ Assert.assertNull(config.getClusterId());
+ Assert.assertEquals(Env.PROD, config.getEnv());
+ Assert.assertNull(config.getCredentials());
+ }
+
+ @Test
+ public void testGettersAndSetters() {
+ BaseVaultConfig config = new BaseVaultConfig();
+ Credentials credentials = new Credentials();
+ credentials.setToken("test_token");
+
+ config.setVaultId("vault_id");
+ config.setClusterId("cluster_id");
+ config.setEnv(Env.SANDBOX);
+ config.setCredentials(credentials);
+
+ Assert.assertEquals("vault_id", config.getVaultId());
+ Assert.assertEquals("cluster_id", config.getClusterId());
+ Assert.assertEquals(Env.SANDBOX, config.getEnv());
+ Assert.assertEquals(credentials, config.getCredentials());
+ }
+
+ @Test
+ public void testSetEnvNullFallsBackToProd() {
+ BaseVaultConfig config = new BaseVaultConfig();
+ config.setEnv(Env.DEV);
+ config.setEnv(null);
+ Assert.assertEquals(Env.PROD, config.getEnv());
+ }
+
+ @Test
+ public void testCloneProducesIndependentCopy() throws CloneNotSupportedException {
+ BaseVaultConfig original = new BaseVaultConfig();
+ original.setVaultId("vault_id");
+ original.setClusterId("cluster_id");
+ original.setEnv(Env.STAGE);
+ Credentials credentials = new Credentials();
+ credentials.setToken("original_token");
+ original.setCredentials(credentials);
+
+ BaseVaultConfig cloned = (BaseVaultConfig) original.clone();
+
+ Assert.assertNotSame(original, cloned);
+ Assert.assertEquals(original.getVaultId(), cloned.getVaultId());
+ Assert.assertEquals(original.getClusterId(), cloned.getClusterId());
+ Assert.assertEquals(original.getEnv(), cloned.getEnv());
+ Assert.assertNotSame(original.getCredentials(), cloned.getCredentials());
+ Assert.assertEquals("original_token", cloned.getCredentials().getToken());
+
+ // Mutating the clone's credentials must not affect the original
+ cloned.getCredentials().setToken("mutated_token");
+ Assert.assertEquals("original_token", original.getCredentials().getToken());
+ }
+
+ @Test
+ public void testCloneWithNullCredentials() throws CloneNotSupportedException {
+ BaseVaultConfig original = new BaseVaultConfig();
+ original.setVaultId("vault_id");
+
+ BaseVaultConfig cloned = (BaseVaultConfig) original.clone();
+ Assert.assertNull(cloned.getCredentials());
+ }
+}
diff --git a/src/test/java/com/skyflow/config/CredentialsTests.java b/common/src/test/java/com/skyflow/config/CredentialsTests.java
similarity index 89%
rename from src/test/java/com/skyflow/config/CredentialsTests.java
rename to common/src/test/java/com/skyflow/config/CredentialsTests.java
index a9a9153f..cdf7fc3f 100644
--- a/src/test/java/com/skyflow/config/CredentialsTests.java
+++ b/common/src/test/java/com/skyflow/config/CredentialsTests.java
@@ -3,7 +3,7 @@
import com.skyflow.errors.ErrorCode;
import com.skyflow.errors.ErrorMessage;
import com.skyflow.errors.SkyflowException;
-import com.skyflow.utils.validations.Validations;
+import com.skyflow.utils.validations.BaseValidations;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -13,8 +13,6 @@
import java.util.HashMap;
import java.util.Map;
-import com.skyflow.utils.Utils;
-
public class CredentialsTests {
private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception";
private static final String EXCEPTION_NOT_THROWN = "Should have thrown an exception";
@@ -49,7 +47,7 @@ public void testValidCredentialsWithPath() {
try {
Credentials credentials = new Credentials();
credentials.setPath(path);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.assertNull(credentials.getCredentialsString());
Assert.assertNull(credentials.getToken());
Assert.assertNull(credentials.getApiKey());
@@ -63,7 +61,7 @@ public void testValidCredentialsWithCredentialsString() {
try {
Credentials credentials = new Credentials();
credentials.setCredentialsString(credentialsString);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.assertNull(credentials.getPath());
Assert.assertNull(credentials.getToken());
Assert.assertNull(credentials.getApiKey());
@@ -77,7 +75,7 @@ public void testValidCredentialsWithToken() {
try {
Credentials credentials = new Credentials();
credentials.setToken(token);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.assertNull(credentials.getPath());
Assert.assertNull(credentials.getCredentialsString());
Assert.assertNull(credentials.getApiKey());
@@ -91,7 +89,7 @@ public void testValidCredentialsWithApikey() {
try {
Credentials credentials = new Credentials();
credentials.setApiKey(validApiKey);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.assertNull(credentials.getPath());
Assert.assertNull(credentials.getCredentialsString());
Assert.assertNull(credentials.getToken());
@@ -108,7 +106,7 @@ public void testValidCredentialsWithRolesAndContext() {
credentials.setApiKey(validApiKey);
credentials.setRoles(roles);
credentials.setContext(context);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.assertNull(credentials.getPath());
Assert.assertNull(credentials.getCredentialsString());
Assert.assertNull(credentials.getToken());
@@ -124,7 +122,7 @@ public void testEmptyPathInCredentials() {
try {
Credentials credentials = new Credentials();
credentials.setPath("");
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -137,7 +135,7 @@ public void testEmptyCredentialsStringInCredentials() {
try {
Credentials credentials = new Credentials();
credentials.setCredentialsString("");
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -150,7 +148,7 @@ public void testEmptyTokenInCredentials() {
try {
Credentials credentials = new Credentials();
credentials.setToken("");
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -163,7 +161,7 @@ public void testEmptyApikeyInCredentials() {
try {
Credentials credentials = new Credentials();
credentials.setApiKey("");
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -176,7 +174,7 @@ public void testInvalidApikeyInCredentials() {
try {
Credentials credentials = new Credentials();
credentials.setApiKey(invalidApiKey);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -190,7 +188,7 @@ public void testBothTokenAndPathInCredentials() {
Credentials credentials = new Credentials();
credentials.setPath(path);
credentials.setToken(token);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -202,7 +200,7 @@ public void testBothTokenAndPathInCredentials() {
public void testNothingPassedInCredentials() {
try {
Credentials credentials = new Credentials();
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -216,7 +214,7 @@ public void testEmptyRolesInCredentials() {
Credentials credentials = new Credentials();
credentials.setPath(path);
credentials.setRoles(roles);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -232,7 +230,7 @@ public void testNullRoleInRolesInCredentials() {
roles.add(role);
roles.add(null);
credentials.setRoles(roles);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -248,7 +246,7 @@ public void testEmptyRoleInRolesInCredentials() {
roles.add(role);
roles.add("");
credentials.setRoles(roles);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -262,7 +260,7 @@ public void testEmptyContextInCredentials() {
Credentials credentials = new Credentials();
credentials.setPath(path);
credentials.setContext("");
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -280,7 +278,7 @@ public void testValidMapContextInCredentials() {
ctxMap.put("department", "finance");
ctxMap.put("user_id", "user_12345");
credentials.setContext(ctxMap);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
} catch (SkyflowException e) {
Assert.fail(INVALID_EXCEPTION_THROWN);
}
@@ -293,7 +291,7 @@ public void testEmptyMapContextInCredentials() {
credentials.setPath(path);
Map ctxMap = new HashMap<>();
credentials.setContext(ctxMap);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -310,7 +308,7 @@ public void testInvalidMapKeyInContextCredentials() {
ctxMap.put("valid_key", "value");
ctxMap.put("invalid-key", "value");
credentials.setContext(ctxMap);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
@@ -329,7 +327,7 @@ public void testMapContextWithNestedObjects() {
ctxMap.put("role", "admin");
ctxMap.put("metadata", nested);
credentials.setContext(ctxMap);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
} catch (SkyflowException e) {
Assert.fail(INVALID_EXCEPTION_THROWN);
}
@@ -346,7 +344,7 @@ public void testMapContextWithMixedValueTypes() {
ctxMap.put("active", true);
ctxMap.put("timestamp", "2025-12-25T10:30:00Z");
credentials.setContext(ctxMap);
- Validations.validateCredentials(credentials);
+ BaseValidations.validateCredentials(credentials);
} catch (SkyflowException e) {
Assert.fail(INVALID_EXCEPTION_THROWN);
}
diff --git a/src/test/java/com/skyflow/errors/SkyflowExceptionTest.java b/common/src/test/java/com/skyflow/errors/SkyflowExceptionTest.java
similarity index 100%
rename from src/test/java/com/skyflow/errors/SkyflowExceptionTest.java
rename to common/src/test/java/com/skyflow/errors/SkyflowExceptionTest.java
diff --git a/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java b/common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java
similarity index 76%
rename from src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java
rename to common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java
index 3aeca811..fb5a2c32 100644
--- a/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java
+++ b/common/src/test/java/com/skyflow/serviceaccount/util/BearerTokenTests.java
@@ -1,16 +1,23 @@
package com.skyflow.serviceaccount.util;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
import com.skyflow.errors.ErrorCode;
import com.skyflow.errors.ErrorMessage;
import com.skyflow.errors.SkyflowException;
-import com.skyflow.utils.Constants;
-import com.skyflow.utils.Utils;
+import com.skyflow.generated.auth.rest.core.ApiClientException;
+import com.skyflow.utils.BaseConstants;
+import com.skyflow.utils.BaseUtils;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@@ -92,7 +99,7 @@ public void testEmptyCredentialsFilePath() {
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), ""), e.getMessage()
+ BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), ""), e.getMessage()
);
}
}
@@ -107,7 +114,7 @@ public void testInvalidFilePath() {
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), invalidFilePath),
+ BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), invalidFilePath),
e.getMessage()
);
}
@@ -123,7 +130,7 @@ public void testInvalidCredentialsFile() {
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath),
+ BaseUtils.parameterizedString(ErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath),
e.getMessage()
);
}
@@ -264,8 +271,58 @@ public void testBearerTokenWithNewFormCredentialKeys() {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
// InvalidKeySpec confirms all credential fields were resolved — failure is at RSA parsing, not field lookup
Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.InvalidKeySpec.getMessage(), Constants.SDK_PREFIX),
+ BaseUtils.parameterizedString(ErrorMessage.InvalidKeySpec.getMessage(), BaseConstants.SDK_PREFIX),
e.getMessage());
}
}
+
+ /**
+ * Generates a real, valid PKCS#8-encoded RSA private key PEM string, using the same
+ * header/footer BaseUtils.getPrivateKeyFromPem expects, so tests can exercise JWT
+ * signing (getSignedToken()) instead of always failing at key parsing.
+ */
+ private static String generateValidPkcs8PrivateKeyPem() throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ String base64EncodedKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
+ return BaseConstants.PKCS8_PRIVATE_HEADER + "\n" + base64EncodedKey + "\n" + BaseConstants.PKCS8_PRIVATE_FOOTER;
+ }
+
+ @Test
+ public void testGetSignedTokenAndScopeUsingRolesExecuteWithValidKeyRolesAndContext() {
+ try {
+ String privateKeyPem = generateValidPkcs8PrivateKeyPem();
+ ArrayList testRoles = new ArrayList<>();
+ testRoles.add("test_role_one");
+ testRoles.add("test_role_two");
+ Map ctxMap = new HashMap<>();
+ ctxMap.put("role", "admin");
+ ctxMap.put("department", "finance");
+
+ JsonObject credentials = new JsonObject();
+ credentials.addProperty("privateKey", privateKeyPem);
+ credentials.addProperty("clientId", "client_id_value");
+ credentials.addProperty("keyId", "key_id_value");
+ // Syntactically valid but unreachable, so failure surfaces at the network call,
+ // proving getSignedToken() (with the ctx claim) and getScopeUsingRoles() (roles != null)
+ // both executed successfully first.
+ credentials.addProperty("tokenUri", "https://localhost:1");
+ String credentialsString = new Gson().toJson(credentials);
+
+ BearerToken bearerToken = BearerToken.builder()
+ .setCredentials(credentialsString)
+ .setCtx(ctxMap)
+ .setRoles(testRoles)
+ .build();
+ bearerToken.getBearerToken();
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (ApiClientException e) {
+ // Reaching this network-layer exception (rather than a SkyflowException from key
+ // parsing) confirms getSignedToken() and getScopeUsingRoles() both ran successfully.
+ Assert.assertTrue(e.getCause() instanceof IOException);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN + ": " + e);
+ }
+ }
}
diff --git a/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java b/common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java
similarity index 58%
rename from src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java
rename to common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java
index 93d69b0e..79542476 100644
--- a/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java
+++ b/common/src/test/java/com/skyflow/serviceaccount/util/SignedDataTokensTests.java
@@ -1,16 +1,25 @@
package com.skyflow.serviceaccount.util;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
import com.skyflow.errors.ErrorCode;
import com.skyflow.errors.ErrorMessage;
import com.skyflow.errors.SkyflowException;
-import com.skyflow.utils.Utils;
+import com.skyflow.utils.BaseConstants;
+import com.skyflow.utils.BaseUtils;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
public class SignedDataTokensTests {
@@ -85,7 +94,7 @@ public void testEmptyCredentialsFilePath() {
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
- Assert.assertEquals(Utils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), ""), e.getMessage());
+ Assert.assertEquals(BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), ""), e.getMessage());
}
}
@@ -99,7 +108,7 @@ public void testInvalidFilePath() {
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), invalidFilePath),
+ BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), invalidFilePath),
e.getMessage());
}
}
@@ -114,7 +123,7 @@ public void testInvalidCredentialsFile() {
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath),
+ BaseUtils.parameterizedString(ErrorMessage.FileInvalidJson.getMessage(), invalidJsonFilePath),
e.getMessage()
);
}
@@ -128,12 +137,10 @@ public void testEmptyCredentialsString() {
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
- Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.InvalidCredentials.getMessage(), invalidJsonFilePath),
- e.getMessage()
- );
+ // InvalidCredentials has no %s1 placeholder, so no extra arg is passed here.
+ Assert.assertEquals(ErrorMessage.InvalidCredentials.getMessage(), e.getMessage());
} catch (Exception e) {
- System.out.println(e);
+ Assert.fail(INVALID_EXCEPTION_THROWN + ": " + e);
}
}
@@ -145,10 +152,8 @@ public void testInvalidCredentialsString() {
Assert.fail(EXCEPTION_NOT_THROWN);
} catch (SkyflowException e) {
Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
- Assert.assertEquals(
- Utils.parameterizedString(ErrorMessage.CredentialsStringInvalidJson.getMessage(), invalidJsonFilePath),
- e.getMessage()
- );
+ // CredentialsStringInvalidJson has no %s1 placeholder, so no extra arg is passed here.
+ Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage());
}
}
@@ -258,4 +263,118 @@ public void testSignedDataTokenResponse() {
Assert.fail(INVALID_EXCEPTION_THROWN);
}
}
+
+ /**
+ * Generates a real, valid PKCS#8-encoded RSA private key PEM string, using the same
+ * header/footer BaseUtils.getPrivateKeyFromPem expects, so tests can exercise the actual
+ * JWT signing path (getSignedToken()) instead of always failing at key parsing.
+ */
+ private static String generateValidPkcs8PrivateKeyPem() throws Exception {
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ KeyPair keyPair = keyPairGenerator.generateKeyPair();
+ String base64EncodedKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
+ return BaseConstants.PKCS8_PRIVATE_HEADER + "\n" + base64EncodedKey + "\n" + BaseConstants.PKCS8_PRIVATE_FOOTER;
+ }
+
+ /**
+ * Decodes (without verifying) the payload segment of a compact JWT so tests can assert
+ * on its claims. These tokens are self-signed with a key generated in-test, so decoding
+ * the payload directly is sufficient to verify claim content.
+ */
+ private static JsonObject decodeJwtPayload(String jwt) {
+ String[] parts = jwt.split("\\.");
+ byte[] payloadBytes = Base64.getUrlDecoder().decode(parts[1]);
+ return JsonParser.parseString(new String(payloadBytes, StandardCharsets.UTF_8)).getAsJsonObject();
+ }
+
+ @Test
+ public void testGetSignedDataTokensWithValidKeyMultipleTokensTimeToLiveAndContext() {
+ try {
+ String privateKeyPem = generateValidPkcs8PrivateKeyPem();
+ ArrayList tokens = new ArrayList<>();
+ tokens.add("data_token_one");
+ tokens.add("data_token_two");
+ Map ctxMap = new HashMap<>();
+ ctxMap.put("role", "admin");
+ ctxMap.put("department", "finance");
+
+ JsonObject credentials = new JsonObject();
+ credentials.addProperty("privateKey", privateKeyPem);
+ credentials.addProperty("clientId", "client_id_value");
+ credentials.addProperty("keyId", "key_id_value");
+ String credentialsJsonString = new Gson().toJson(credentials);
+
+ int timeToLiveSeconds = 120;
+ long beforeCreationEpochSeconds = System.currentTimeMillis() / 1000;
+ List responses = SignedDataTokens.builder()
+ .setCredentials(credentialsJsonString)
+ .setDataTokens(tokens)
+ .setTimeToLive(timeToLiveSeconds)
+ .setCtx(ctxMap)
+ .build()
+ .getSignedDataTokens();
+
+ Assert.assertEquals(tokens.size(), responses.size());
+ for (int i = 0; i < tokens.size(); i++) {
+ SignedDataTokenResponse response = responses.get(i);
+ Assert.assertEquals(tokens.get(i), response.getToken());
+ Assert.assertTrue(response.getSignedToken().startsWith(BaseConstants.SIGNED_DATA_TOKEN_PREFIX));
+
+ String signedJwt = response.getSignedToken().substring(BaseConstants.SIGNED_DATA_TOKEN_PREFIX.length());
+ JsonObject payload = decodeJwtPayload(signedJwt);
+ Assert.assertEquals(tokens.get(i), payload.get("tok").getAsString());
+ Assert.assertEquals("key_id_value", payload.get("key").getAsString());
+ Assert.assertEquals("client_id_value", payload.get("sub").getAsString());
+ Assert.assertEquals("sdk", payload.get("iss").getAsString());
+
+ Assert.assertTrue(payload.has("ctx"));
+ Assert.assertEquals("admin", payload.getAsJsonObject("ctx").get("role").getAsString());
+ Assert.assertEquals("finance", payload.getAsJsonObject("ctx").get("department").getAsString());
+
+ long expirationEpochSeconds = payload.get("exp").getAsLong();
+ long expectedExpirationEpochSeconds = beforeCreationEpochSeconds + timeToLiveSeconds;
+ // Small allowance for time elapsed during test execution
+ Assert.assertTrue(Math.abs(expirationEpochSeconds - expectedExpirationEpochSeconds) <= 5);
+ }
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN + ": " + e);
+ }
+ }
+
+ @Test
+ public void testGetSignedDataTokensWithValidKeyAndDefaultTimeToLive() {
+ try {
+ String privateKeyPem = generateValidPkcs8PrivateKeyPem();
+ ArrayList tokens = new ArrayList<>();
+ tokens.add("default_ttl_token");
+
+ JsonObject credentials = new JsonObject();
+ credentials.addProperty("privateKey", privateKeyPem);
+ credentials.addProperty("clientId", "client_id_value");
+ credentials.addProperty("keyId", "key_id_value");
+ String credentialsJsonString = new Gson().toJson(credentials);
+
+ long beforeCreationEpochSeconds = System.currentTimeMillis() / 1000;
+ List responses = SignedDataTokens.builder()
+ .setCredentials(credentialsJsonString)
+ .setDataTokens(tokens)
+ // timeToLive intentionally not set, exercising the default-60-second branch
+ .build()
+ .getSignedDataTokens();
+
+ Assert.assertEquals(1, responses.size());
+ String signedJwt = responses.get(0).getSignedToken().substring(BaseConstants.SIGNED_DATA_TOKEN_PREFIX.length());
+ JsonObject payload = decodeJwtPayload(signedJwt);
+ Assert.assertEquals(tokens.get(0), payload.get("tok").getAsString());
+ // No context was set, so the "ctx" claim branch should be skipped entirely
+ Assert.assertFalse(payload.has("ctx"));
+
+ long expirationEpochSeconds = payload.get("exp").getAsLong();
+ long expectedExpirationEpochSeconds = beforeCreationEpochSeconds + 60;
+ Assert.assertTrue(Math.abs(expirationEpochSeconds - expectedExpirationEpochSeconds) <= 5);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN + ": " + e);
+ }
+ }
}
diff --git a/src/test/java/com/skyflow/serviceaccount/util/TokenTests.java b/common/src/test/java/com/skyflow/serviceaccount/util/TokenTests.java
similarity index 94%
rename from src/test/java/com/skyflow/serviceaccount/util/TokenTests.java
rename to common/src/test/java/com/skyflow/serviceaccount/util/TokenTests.java
index 88887681..cb631071 100644
--- a/src/test/java/com/skyflow/serviceaccount/util/TokenTests.java
+++ b/common/src/test/java/com/skyflow/serviceaccount/util/TokenTests.java
@@ -1,7 +1,7 @@
package com.skyflow.serviceaccount.util;
-import com.skyflow.Skyflow;
import com.skyflow.enums.LogLevel;
+import com.skyflow.utils.logger.LogUtil;
import io.github.cdimascio.dotenv.Dotenv;
import org.junit.Assert;
import org.junit.BeforeClass;
@@ -12,7 +12,7 @@ public class TokenTests {
@BeforeClass
public static void setup() {
- Skyflow skyflowClient = Skyflow.builder().setLogLevel(LogLevel.DEBUG).build();
+ LogUtil.setupLogger(LogLevel.DEBUG);
}
@Test
diff --git a/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java b/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java
new file mode 100644
index 00000000..bc1bd216
--- /dev/null
+++ b/common/src/test/java/com/skyflow/utils/BaseUtilsTests.java
@@ -0,0 +1,244 @@
+package com.skyflow.utils;
+
+import com.google.gson.JsonObject;
+import com.skyflow.config.Credentials;
+import com.skyflow.enums.Env;
+import com.skyflow.errors.ErrorCode;
+import com.skyflow.errors.ErrorMessage;
+import com.skyflow.errors.SkyflowException;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BaseUtilsTests {
+ private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception";
+ private static final String EXCEPTION_NOT_THROWN = "Should have thrown an exception";
+ private static String clusterId = null;
+ private static String url = null;
+ private static String filePath = null;
+ private static String credentialsString = null;
+ private static String token = null;
+ private static String context = null;
+ private static ArrayList roles = null;
+
+ @BeforeClass
+ public static void setup() {
+ clusterId = "test_cluster_id";
+ url = "https://test-url.com/java/unit/tests";
+ filePath = "invalid/file/path/credentials.json";
+ credentialsString = "invalid credentials string";
+ token = "invalid-token";
+ context = "test_context";
+ roles = new ArrayList<>();
+ roles.add("test_role");
+ }
+
+ @Test
+ public void testGetVaultURLForDev() {
+ try {
+ String vaultURL = BaseUtils.getVaultURL(clusterId, Env.DEV, BaseConstants.V2_VAULT_DOMAIN);
+ String devUrl = "https://test_cluster_id.vault.skyflowapis.dev";
+ Assert.assertEquals(devUrl, vaultURL);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testGetVaultURLForStage() {
+ try {
+ String vaultURL = BaseUtils.getVaultURL(clusterId, Env.STAGE, BaseConstants.V2_VAULT_DOMAIN);
+ String stageUrl = "https://test_cluster_id.vault.skyflowapis.tech";
+ Assert.assertEquals(stageUrl, vaultURL);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testGetVaultURLForSandbox() {
+ try {
+ String vaultURL = BaseUtils.getVaultURL(clusterId, Env.SANDBOX, BaseConstants.V2_VAULT_DOMAIN);
+ String sandboxUrl = "https://test_cluster_id.vault.skyflowapis-preview.com";
+ Assert.assertEquals(sandboxUrl, vaultURL);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testGetVaultURLForProd() {
+ try {
+ String vaultURL = BaseUtils.getVaultURL(clusterId, Env.PROD, BaseConstants.V2_VAULT_DOMAIN);
+ String prodUrl = "https://test_cluster_id.vault.skyflowapis.com";
+ Assert.assertEquals(prodUrl, vaultURL);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testGetBaseURL() {
+ try {
+ String baseURL = BaseUtils.getBaseURL(url);
+ String expected = "https://test-url.com";
+ Assert.assertEquals(expected, baseURL);
+ } catch (Exception e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testGenerateBearerTokenWithCredentialsFile() {
+ try {
+ Credentials credentials = new Credentials();
+ credentials.setPath(filePath);
+ credentials.setContext(context);
+ credentials.setRoles(roles);
+ BaseUtils.generateBearerToken(credentials);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(
+ BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), filePath),
+ e.getMessage()
+ );
+ }
+ }
+
+ @Test
+ public void testGenerateBearerTokenWithCredentialsString() {
+ try {
+ Credentials credentials = new Credentials();
+ credentials.setCredentialsString(credentialsString);
+ credentials.setContext(context);
+ credentials.setRoles(roles);
+ BaseUtils.generateBearerToken(credentials);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGenerateBearerTokenWithToken() {
+ try {
+ Credentials credentials = new Credentials();
+ credentials.setToken(token);
+ credentials.setContext(context);
+ credentials.setRoles(roles);
+ String bearerToken = BaseUtils.generateBearerToken(credentials);
+ Assert.assertEquals(token, bearerToken);
+ } catch (SkyflowException e) {
+ Assert.fail(INVALID_EXCEPTION_THROWN);
+ }
+ }
+
+ @Test
+ public void testGetBaseURLWithMalformedURL() {
+ try {
+ BaseUtils.getBaseURL("not a url");
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (MalformedURLException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testGenerateBearerTokenWithCredentialsFileAndMapContext() {
+ try {
+ Map mapContext = new HashMap<>();
+ mapContext.put("test_key", "test_value");
+ Credentials credentials = new Credentials();
+ credentials.setPath(filePath);
+ credentials.setContext(mapContext);
+ credentials.setRoles(roles);
+ BaseUtils.generateBearerToken(credentials);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (ClassCastException e) {
+ Assert.fail("Map context should not cause a ClassCastException");
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(
+ BaseUtils.parameterizedString(ErrorMessage.FileNotFound.getMessage(), filePath),
+ e.getMessage()
+ );
+ }
+ }
+
+ @Test
+ public void testGenerateBearerTokenWithCredentialsStringAndMapContext() {
+ try {
+ Map mapContext = new HashMap<>();
+ mapContext.put("test_key", "test_value");
+ Credentials credentials = new Credentials();
+ credentials.setCredentialsString(credentialsString);
+ credentials.setContext(mapContext);
+ credentials.setRoles(roles);
+ BaseUtils.generateBearerToken(credentials);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (ClassCastException e) {
+ Assert.fail("Map context should not cause a ClassCastException");
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGenerateBearerTokenWithContextNeitherStringNorMap() {
+ try {
+ // context is left unset (null), which is neither a String nor a Map -- this should
+ // simply be skipped rather than throwing any type-related exception.
+ Credentials credentials = new Credentials();
+ credentials.setCredentialsString(credentialsString);
+ credentials.setRoles(roles);
+ BaseUtils.generateBearerToken(credentials);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (ClassCastException e) {
+ Assert.fail("Non String/Map context should not cause a ClassCastException");
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.CredentialsStringInvalidJson.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetPrivateKeyFromPemWithMissingHeader() {
+ try {
+ BaseUtils.getPrivateKeyFromPem("this-is-not-a-pem-key-at-all");
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.JwtInvalidFormat.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetPrivateKeyFromPemWithInvalidBase64Body() {
+ String malformedPem = "-----BEGIN PRIVATE KEY-----\nnot-valid-base64!!!\n-----END PRIVATE KEY-----";
+ try {
+ BaseUtils.getPrivateKeyFromPem(malformedPem);
+ Assert.fail(EXCEPTION_NOT_THROWN);
+ } catch (IllegalArgumentException e) {
+ Assert.fail("Invalid base64 content should be wrapped into a SkyflowException, not thrown raw");
+ } catch (SkyflowException e) {
+ Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode());
+ Assert.assertEquals(ErrorMessage.InvalidKeySpec.getMessage(), e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetCommonMetrics() {
+ JsonObject metrics = BaseUtils.getCommonMetrics();
+ Assert.assertNotNull(metrics.get(BaseConstants.SDK_METRIC_CLIENT_DEVICE_MODEL));
+ Assert.assertNotNull(metrics.get(BaseConstants.SDK_METRIC_RUNTIME_DETAILS));
+ Assert.assertNotNull(metrics.get(BaseConstants.SDK_METRIC_CLIENT_OS_DETAILS));
+ }
+}
diff --git a/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java b/common/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java
similarity index 58%
rename from src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java
rename to common/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java
index 9754687a..11108e4b 100644
--- a/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java
+++ b/common/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java
@@ -96,4 +96,68 @@ public void testInfoLogSuppressedWhenLogLevelIsWarn() {
.anyMatch(r -> r.getLevel().equals(Level.INFO));
Assert.assertFalse("INFO log should NOT appear when LogLevel is WARN", infoCaptured);
}
+
+ @Test
+ public void testDebugLogAppearsWhenLogLevelIsDebug() {
+ LogUtil.setupLogger(LogLevel.DEBUG);
+ CapturingHandler handler = attachCapture();
+
+ LogUtil.printDebugLog("debug message");
+
+ boolean debugCaptured = handler.records.stream()
+ .anyMatch(r -> r.getLevel().equals(Level.CONFIG)
+ && r.getMessage().contains("debug message"));
+ Assert.assertTrue("DEBUG log should appear when LogLevel is DEBUG", debugCaptured);
+ }
+
+ @Test
+ public void testDebugLogSuppressedWhenLogLevelIsInfo() {
+ LogUtil.setupLogger(LogLevel.INFO);
+ CapturingHandler handler = attachCapture();
+
+ LogUtil.printDebugLog("suppressed debug message");
+
+ boolean debugCaptured = handler.records.stream()
+ .anyMatch(r -> r.getLevel().equals(Level.CONFIG));
+ Assert.assertFalse("DEBUG log should NOT appear when LogLevel is INFO", debugCaptured);
+ }
+
+ @Test
+ public void testErrorLogAppearsWhenLogLevelIsError() {
+ LogUtil.setupLogger(LogLevel.ERROR);
+ CapturingHandler handler = attachCapture();
+
+ LogUtil.printErrorLog("error message");
+
+ boolean errorCaptured = handler.records.stream()
+ .anyMatch(r -> r.getLevel().equals(Level.SEVERE)
+ && r.getMessage().contains("error message"));
+ Assert.assertTrue("ERROR log should appear when LogLevel is ERROR", errorCaptured);
+ }
+
+ @Test
+ public void testErrorLogAppearsWhenLogLevelIsDebug() {
+ LogUtil.setupLogger(LogLevel.DEBUG);
+ CapturingHandler handler = attachCapture();
+
+ LogUtil.printErrorLog("debug level error message");
+
+ boolean errorCaptured = handler.records.stream()
+ .anyMatch(r -> r.getLevel().equals(Level.SEVERE)
+ && r.getMessage().contains("debug level error message"));
+ Assert.assertTrue("ERROR log should appear when LogLevel is DEBUG", errorCaptured);
+ }
+
+ @Test
+ public void testNoLogsAppearWhenLogLevelIsOff() {
+ LogUtil.setupLogger(LogLevel.OFF);
+ CapturingHandler handler = attachCapture();
+
+ LogUtil.printErrorLog("off error message");
+ LogUtil.printWarningLog("off warning message");
+ LogUtil.printInfoLog("off info message");
+ LogUtil.printDebugLog("off debug message");
+
+ Assert.assertTrue("No logs should appear when LogLevel is OFF", handler.records.isEmpty());
+ }
}
diff --git a/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeDataTests.java b/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeDataTests.java
new file mode 100644
index 00000000..08b4f9da
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeDataTests.java
@@ -0,0 +1,28 @@
+package com.skyflow.vault.data;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BaseDetokenizeDataTests {
+
+ @Test
+ public void testGetTokenReturnsConstructorValue() {
+ BaseDetokenizeData data = new BaseDetokenizeData("token-value");
+
+ Assert.assertEquals("token-value", data.getToken());
+ }
+
+ @Test
+ public void testNullToken() {
+ BaseDetokenizeData data = new BaseDetokenizeData(null);
+
+ Assert.assertNull(data.getToken());
+ }
+
+ @Test
+ public void testEmptyToken() {
+ BaseDetokenizeData data = new BaseDetokenizeData("");
+
+ Assert.assertEquals("", data.getToken());
+ }
+}
diff --git a/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeRecordResponseTests.java b/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeRecordResponseTests.java
new file mode 100644
index 00000000..52992e46
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeRecordResponseTests.java
@@ -0,0 +1,39 @@
+package com.skyflow.vault.data;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BaseDetokenizeRecordResponseTests {
+
+ @Test
+ public void testGettersReturnConstructorValuesOnSuccess() {
+ BaseDetokenizeRecordResponse response = new BaseDetokenizeRecordResponse("token-value", null);
+
+ Assert.assertEquals("token-value", response.getToken());
+ Assert.assertNull(response.getError());
+ }
+
+ @Test
+ public void testGettersReturnConstructorValuesOnError() {
+ BaseDetokenizeRecordResponse response = new BaseDetokenizeRecordResponse(null, "some error");
+
+ Assert.assertNull(response.getToken());
+ Assert.assertEquals("some error", response.getError());
+ }
+
+ @Test
+ public void testBothTokenAndErrorNull() {
+ BaseDetokenizeRecordResponse response = new BaseDetokenizeRecordResponse(null, null);
+
+ Assert.assertNull(response.getToken());
+ Assert.assertNull(response.getError());
+ }
+
+ @Test
+ public void testBothTokenAndErrorPopulated() {
+ BaseDetokenizeRecordResponse response = new BaseDetokenizeRecordResponse("token-value", "some error");
+
+ Assert.assertEquals("token-value", response.getToken());
+ Assert.assertEquals("some error", response.getError());
+ }
+}
diff --git a/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeRequestTests.java b/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeRequestTests.java
new file mode 100644
index 00000000..0b414353
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/BaseDetokenizeRequestTests.java
@@ -0,0 +1,24 @@
+package com.skyflow.vault.data;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BaseDetokenizeRequestTests {
+
+ @Test
+ public void testInstantiationDoesNotThrow() {
+ BaseDetokenizeRequest request = new BaseDetokenizeRequest();
+
+ Assert.assertNotNull(request);
+ }
+
+ @Test
+ public void testUsableAsExtensionPointForSubclasses() {
+ // BaseDetokenizeRequest carries no state of its own; it exists purely so module-specific
+ // DetokenizeRequest classes (e.g. flowvault's) can extend it. Verify the subtype relationship holds.
+ BaseDetokenizeRequest request = new BaseDetokenizeRequest() {
+ };
+
+ Assert.assertTrue(request instanceof BaseDetokenizeRequest);
+ }
+}
diff --git a/common/src/test/java/com/skyflow/vault/data/BaseInsertRequestTests.java b/common/src/test/java/com/skyflow/vault/data/BaseInsertRequestTests.java
new file mode 100644
index 00000000..e8ce8e06
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/BaseInsertRequestTests.java
@@ -0,0 +1,41 @@
+package com.skyflow.vault.data;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BaseInsertRequestTests {
+
+ @Test
+ public void testBuilderSetsTable() {
+ BaseInsertRequest.BaseInsertRequestBuilder builder = new BaseInsertRequest.BaseInsertRequestBuilder();
+ builder.table("test_table");
+ BaseInsertRequest request = new BaseInsertRequest(builder);
+
+ Assert.assertEquals("test_table", request.getTable());
+ }
+
+ @Test
+ public void testBuilderTableMethodReturnsSameBuilderInstance() {
+ BaseInsertRequest.BaseInsertRequestBuilder builder = new BaseInsertRequest.BaseInsertRequestBuilder();
+ BaseInsertRequest.BaseInsertRequestBuilder returned = builder.table("test_table");
+
+ Assert.assertSame(builder, returned);
+ }
+
+ @Test
+ public void testNullTableWhenNeverSet() {
+ BaseInsertRequest.BaseInsertRequestBuilder builder = new BaseInsertRequest.BaseInsertRequestBuilder();
+ BaseInsertRequest request = new BaseInsertRequest(builder);
+
+ Assert.assertNull(request.getTable());
+ }
+
+ @Test
+ public void testEmptyTable() {
+ BaseInsertRequest.BaseInsertRequestBuilder builder = new BaseInsertRequest.BaseInsertRequestBuilder();
+ builder.table("");
+ BaseInsertRequest request = new BaseInsertRequest(builder);
+
+ Assert.assertEquals("", request.getTable());
+ }
+}
diff --git a/common/src/test/java/com/skyflow/vault/data/BaseInsertResponseTests.java b/common/src/test/java/com/skyflow/vault/data/BaseInsertResponseTests.java
new file mode 100644
index 00000000..93b47f32
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/BaseInsertResponseTests.java
@@ -0,0 +1,69 @@
+package com.skyflow.vault.data;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+public class BaseInsertResponseTests {
+
+ @Test
+ public void testGettersReturnConstructorValues() {
+ ArrayList> insertedFields = new ArrayList<>();
+ HashMap field = new HashMap<>();
+ field.put("skyflow_id", "id-1");
+ insertedFields.add(field);
+
+ ArrayList> errors = new ArrayList<>();
+ HashMap error = new HashMap<>();
+ error.put("error", "some error");
+ errors.add(error);
+
+ BaseInsertResponse response = new BaseInsertResponse(insertedFields, errors);
+
+ Assert.assertEquals(insertedFields, response.getInsertedFields());
+ Assert.assertEquals(errors, response.getErrors());
+ }
+
+ @Test
+ public void testNullInsertedFieldsAndErrors() {
+ BaseInsertResponse response = new BaseInsertResponse(null, null);
+
+ Assert.assertNull(response.getInsertedFields());
+ Assert.assertNull(response.getErrors());
+ }
+
+ @Test
+ public void testEmptyInsertedFieldsAndErrors() {
+ BaseInsertResponse response = new BaseInsertResponse(new ArrayList<>(), new ArrayList<>());
+
+ Assert.assertTrue(response.getInsertedFields().isEmpty());
+ Assert.assertTrue(response.getErrors().isEmpty());
+ }
+
+ @Test
+ public void testToStringWithPopulatedFieldsContainsValues() {
+ ArrayList> insertedFields = new ArrayList<>();
+ HashMap field = new HashMap<>();
+ field.put("skyflow_id", "id-1");
+ insertedFields.add(field);
+
+ BaseInsertResponse response = new BaseInsertResponse(insertedFields, new ArrayList<>());
+ String result = response.toString();
+
+ Assert.assertNotNull(result);
+ Assert.assertTrue(result.contains("insertedFields"));
+ Assert.assertTrue(result.contains("skyflow_id"));
+ Assert.assertTrue(result.contains("id-1"));
+ }
+
+ @Test
+ public void testToStringWithNullFieldsDoesNotThrowAndSerializesNulls() {
+ BaseInsertResponse response = new BaseInsertResponse(null, null);
+ String result = response.toString();
+
+ Assert.assertNotNull(result);
+ Assert.assertTrue(result.contains("null"));
+ }
+}
diff --git a/common/src/test/java/com/skyflow/vault/data/RequestContextTests.java b/common/src/test/java/com/skyflow/vault/data/RequestContextTests.java
new file mode 100644
index 00000000..d8747425
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/RequestContextTests.java
@@ -0,0 +1,68 @@
+package com.skyflow.vault.data;
+
+import com.skyflow.enums.CustomHeaderKey;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Map;
+
+public class RequestContextTests {
+
+ @Test
+ public void testGetOperationReturnsConstructorValue() {
+ RequestContext context = new RequestContext("INSERT");
+
+ Assert.assertEquals("INSERT", context.getOperation());
+ }
+
+ @Test
+ public void testNullOperation() {
+ RequestContext context = new RequestContext(null);
+
+ Assert.assertNull(context.getOperation());
+ }
+
+ @Test
+ public void testGetHeadersReturnsEmptyMapByDefault() {
+ RequestContext context = new RequestContext("INSERT");
+
+ Assert.assertTrue(context.getHeaders().isEmpty());
+ }
+
+ @Test
+ public void testAddHeaderIsReflectedInGetHeaders() {
+ RequestContext context = new RequestContext("INSERT");
+ context.addHeader(CustomHeaderKey.SkyflowAccountID, "account-id-value");
+
+ Map headers = context.getHeaders();
+
+ Assert.assertEquals(1, headers.size());
+ Assert.assertEquals("account-id-value", headers.get(CustomHeaderKey.SkyflowAccountID));
+ }
+
+ @Test
+ public void testAddHeaderOverwritesExistingValueForSameKey() {
+ RequestContext context = new RequestContext("INSERT");
+ context.addHeader(CustomHeaderKey.SkyflowAccountID, "first-value");
+ context.addHeader(CustomHeaderKey.SkyflowAccountID, "second-value");
+
+ Assert.assertEquals(1, context.getHeaders().size());
+ Assert.assertEquals("second-value", context.getHeaders().get(CustomHeaderKey.SkyflowAccountID));
+ }
+
+ @Test
+ public void testAddMultipleDistinctHeaders() {
+ RequestContext context = new RequestContext("DETOKENIZE");
+ context.addHeader(CustomHeaderKey.SkyflowAccountID, "account-id-value");
+ context.addHeader(CustomHeaderKey.SkyflowAccountName, "account-name-value");
+
+ Assert.assertEquals(2, context.getHeaders().size());
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testGetHeadersReturnsUnmodifiableMap() {
+ RequestContext context = new RequestContext("INSERT");
+
+ context.getHeaders().put(CustomHeaderKey.RequestIDHeader, "request-id-value");
+ }
+}
diff --git a/common/src/test/java/com/skyflow/vault/data/RequestInterceptorTests.java b/common/src/test/java/com/skyflow/vault/data/RequestInterceptorTests.java
new file mode 100644
index 00000000..a03e773d
--- /dev/null
+++ b/common/src/test/java/com/skyflow/vault/data/RequestInterceptorTests.java
@@ -0,0 +1,28 @@
+package com.skyflow.vault.data;
+
+import com.skyflow.enums.CustomHeaderKey;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RequestInterceptorTests {
+
+ @Test
+ public void testInterceptMutatesRequestContext() {
+ RequestInterceptor interceptor = context -> context.addHeader(CustomHeaderKey.SkyflowAccountID, "account-id-value");
+ RequestContext context = new RequestContext("INSERT");
+
+ interceptor.intercept(context);
+
+ Assert.assertEquals("account-id-value", context.getHeaders().get(CustomHeaderKey.SkyflowAccountID));
+ }
+
+ @Test
+ public void testInterceptorIsFunctionalInterfaceUsableAsLambda() {
+ final boolean[] invoked = {false};
+ RequestInterceptor interceptor = context -> invoked[0] = true;
+
+ interceptor.intercept(new RequestContext("DETOKENIZE"));
+
+ Assert.assertTrue(invoked[0]);
+ }
+}
diff --git a/src/test/resources/invalidPrivateKeyCredentials.json b/common/src/test/resources/invalidPrivateKeyCredentials.json
similarity index 100%
rename from src/test/resources/invalidPrivateKeyCredentials.json
rename to common/src/test/resources/invalidPrivateKeyCredentials.json
diff --git a/src/test/resources/invalidTokenURICredentials.json b/common/src/test/resources/invalidTokenURICredentials.json
similarity index 100%
rename from src/test/resources/invalidTokenURICredentials.json
rename to common/src/test/resources/invalidTokenURICredentials.json
diff --git a/src/test/resources/noClientIDCredentials.json b/common/src/test/resources/noClientIDCredentials.json
similarity index 100%
rename from src/test/resources/noClientIDCredentials.json
rename to common/src/test/resources/noClientIDCredentials.json
diff --git a/src/test/resources/noKeyIDCredentials.json b/common/src/test/resources/noKeyIDCredentials.json
similarity index 100%
rename from src/test/resources/noKeyIDCredentials.json
rename to common/src/test/resources/noKeyIDCredentials.json
diff --git a/src/test/resources/noPrivateKeyCredentials.json b/common/src/test/resources/noPrivateKeyCredentials.json
similarity index 100%
rename from src/test/resources/noPrivateKeyCredentials.json
rename to common/src/test/resources/noPrivateKeyCredentials.json
diff --git a/src/test/resources/noTokenURICredentials.json b/common/src/test/resources/noTokenURICredentials.json
similarity index 100%
rename from src/test/resources/noTokenURICredentials.json
rename to common/src/test/resources/noTokenURICredentials.json
diff --git a/src/test/resources/notJson.txt b/common/src/test/resources/notJson.txt
similarity index 100%
rename from src/test/resources/notJson.txt
rename to common/src/test/resources/notJson.txt
diff --git a/flowvault/dependency-reduced-pom.xml b/flowvault/dependency-reduced-pom.xml
new file mode 100644
index 00000000..92fc11f2
--- /dev/null
+++ b/flowvault/dependency-reduced-pom.xml
@@ -0,0 +1,195 @@
+
+
+
+ skyflow
+ com.skyflow
+ 1.0.0
+
+ 4.0.0
+ skyflow-flowvault-java
+ ${project.groupId}:${project.artifactId}
+ 1.0.0
+ Skyflow V3 SDK for the Java programming language
+ https://github.com/skyflowapi/skyflow-java/tree/main
+
+
+ skyflow
+ skyflow
+
+
+
+
+ The MIT License (MIT)
+ https://github.com/skyflowapi/skyflow-java/blob/main/LICENSE
+
+
+
+
+
+ true
+ src/main/resources
+
+
+
+
+ maven-shade-plugin
+ 3.6.0
+
+
+ package
+
+ shade
+
+
+
+
+ com.skyflow:common
+
+
+
+
+
+
+
+
+
+
+ jfrog
+
+
+ central
+ prekarilabs.jfrog.io-releases
+ https://prekarilabs.jfrog.io/artifactory/skyflow-java
+
+
+ snapshots
+ prekarilabs.jfrog.io-snapshots
+ https://prekarilabs.jfrog.io/artifactory/skyflow-java
+
+
+
+
+ maven-central
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.4.0
+ true
+
+ central
+ true
+ true
+
+
+
+
+
+
+ central
+ https://central.sonatype.com/api/v1/publisher/upload
+
+
+ central-snapshots
+ https://central.sonatype.com/api/v1/publisher/upload
+
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.17.2
+ compile
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+ 2.18.6
+ compile
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.18.6
+ compile
+
+
+ io.github.cdimascio
+ dotenv-java
+ 2.2.0
+ compile
+
+
+ com.google.code.gson
+ gson
+ 2.10.1
+ compile
+
+
+ com.squareup.okhttp3
+ okhttp
+ 4.12.0
+ compile
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.12.6
+ compile
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ hamcrest-core
+ org.hamcrest
+
+
+
+
+ org.powermock
+ powermock-module-junit4
+ 2.0.9
+ test
+
+
+ powermock-module-junit4-common
+ org.powermock
+
+
+ hamcrest-core
+ org.hamcrest
+
+
+
+
+ org.powermock
+ powermock-api-mockito2
+ 2.0.9
+ test
+
+
+ powermock-api-support
+ org.powermock
+
+
+ mockito-core
+ org.mockito
+
+
+
+
+
+ UTF-8
+ 8
+ false
+ 8
+ ${project.version}
+
+
diff --git a/flowvault/pom.xml b/flowvault/pom.xml
new file mode 100644
index 00000000..1efc6173
--- /dev/null
+++ b/flowvault/pom.xml
@@ -0,0 +1,129 @@
+
+
+ 4.0.0
+
+ com.skyflow
+ skyflow
+ 1.0.0
+ ../pom.xml
+
+
+ skyflow-flowvault-java
+ 1.0.0
+ jar
+ ${project.groupId}:${project.artifactId}
+ Skyflow V3 SDK for the Java programming language
+ https://github.com/skyflowapi/skyflow-java/tree/main
+
+
+
+ The MIT License (MIT)
+ https://github.com/skyflowapi/skyflow-java/blob/main/LICENSE
+
+
+
+
+ skyflow
+ skyflow
+
+
+
+
+ 8
+ 8
+ UTF-8
+ false
+ ${project.version}
+
+
+
+
+
+
+ com.skyflow
+ common
+ 1.0.0
+ compile
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+ package
+
+ shade
+
+
+
+
+ com.skyflow:common
+
+
+
+
+
+
+
+
+
+
+
+ jfrog
+
+
+ central
+ prekarilabs.jfrog.io-releases
+ https://prekarilabs.jfrog.io/artifactory/skyflow-java
+
+
+ snapshots
+ prekarilabs.jfrog.io-snapshots
+ https://prekarilabs.jfrog.io/artifactory/skyflow-java
+
+
+
+
+ maven-central
+
+
+ central
+ https://central.sonatype.com/api/v1/publisher/upload
+
+
+ central-snapshots
+ https://central.sonatype.com/api/v1/publisher/upload
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.4.0
+ true
+
+ central
+ true
+ true
+
+
+
+
+
+
+
+
diff --git a/flowvault/src/main/java/com/skyflow/Skyflow.java b/flowvault/src/main/java/com/skyflow/Skyflow.java
new file mode 100644
index 00000000..96b5f45a
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/Skyflow.java
@@ -0,0 +1,118 @@
+package com.skyflow;
+
+import com.skyflow.config.Credentials;
+import com.skyflow.config.VaultConfig;
+import com.skyflow.enums.LogLevel;
+import com.skyflow.errors.ErrorMessage;
+import com.skyflow.errors.SkyflowException;
+import com.skyflow.logs.ErrorLogs;
+import com.skyflow.logs.InfoLogs;
+import com.skyflow.utils.Constants;
+import com.skyflow.utils.SdkVersion;
+import com.skyflow.utils.Utils;
+import com.skyflow.utils.logger.LogUtil;
+import com.skyflow.utils.validations.Validations;
+import com.skyflow.vault.controller.VaultController;
+
+import java.util.LinkedHashMap;
+
+public final class Skyflow extends BaseSkyflow {
+
+ private final SkyflowClientBuilder builder;
+
+ private Skyflow(SkyflowClientBuilder builder) {
+ super(builder);
+ this.builder = builder;
+ }
+
+ @Override
+ protected Skyflow self() {
+ return this;
+ }
+
+ public static SkyflowClientBuilder builder() {
+ SdkVersion.setSdkPrefix(Constants.SDK_PREFIX);
+ return new SkyflowClientBuilder();
+ }
+
+ public VaultConfig getVaultConfig() {
+ Object[] array = this.builder.vaultConfigMap.values().toArray();
+ return (VaultConfig) array[0];
+ }
+
+ public VaultController vault() throws SkyflowException {
+ return resolveOrThrow(this.builder.vaultClientsMap, null, ErrorLogs.VAULT_CONFIG_DOES_NOT_EXIST, ErrorMessage.VaultIdNotInConfigList);
+ }
+
+ public static final class SkyflowClientBuilder extends BaseSkyflowClientBuilder {
+ private final LinkedHashMap vaultClientsMap = new LinkedHashMap<>();
+
+ @Override
+ protected void validateVaultConfig(VaultConfig vaultConfig) throws SkyflowException {
+ Validations.validateVaultConfiguration(vaultConfig);
+ }
+
+ @Override
+ protected void onVaultConfigAdded(VaultConfig vaultConfig) throws SkyflowException {
+ this.vaultClientsMap.put(vaultConfig.getVaultId(), new VaultController(vaultConfig, this.skyflowCredentials));
+ LogUtil.printInfoLog(Utils.parameterizedString(InfoLogs.VAULT_CONTROLLER_INITIALIZED.getLog(), vaultConfig.getVaultId()));
+ }
+
+ @Override
+ protected void onVaultConfigUpdated(VaultConfig updatedConfig) throws SkyflowException {
+ this.vaultClientsMap.put(updatedConfig.getVaultId(), new VaultController(updatedConfig, this.skyflowCredentials));
+ }
+
+ @Override
+ protected void onVaultConfigRemoved(String vaultId) throws SkyflowException {
+ this.vaultClientsMap.remove(vaultId);
+ }
+
+ @Override
+ protected boolean hasVaultClient(String vaultId) {
+ return this.vaultClientsMap.containsKey(vaultId);
+ }
+
+ @Override
+ protected void onCredentialsUpdated(Credentials credentials) throws SkyflowException {
+ for (VaultController vault : this.vaultClientsMap.values()) {
+ vault.setCommonCredentials(credentials);
+ }
+ }
+
+ @Override
+ public SkyflowClientBuilder addVaultConfig(VaultConfig vaultConfig) throws SkyflowException {
+ super.addVaultConfig(vaultConfig);
+ return this;
+ }
+
+ @Override
+ public SkyflowClientBuilder updateVaultConfig(VaultConfig vaultConfig) throws SkyflowException {
+ super.updateVaultConfig(vaultConfig);
+ return this;
+ }
+
+ @Override
+ public SkyflowClientBuilder removeVaultConfig(String vaultId) throws SkyflowException {
+ super.removeVaultConfig(vaultId);
+ return this;
+ }
+
+ @Override
+ public SkyflowClientBuilder addSkyflowCredentials(Credentials credentials) throws SkyflowException {
+ super.addSkyflowCredentials(credentials);
+ return this;
+ }
+
+ @Override
+ public SkyflowClientBuilder setLogLevel(LogLevel logLevel) {
+ super.setLogLevel(logLevel);
+ return this;
+ }
+
+ public Skyflow build() {
+ return new Skyflow(this);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/flowvault/src/main/java/com/skyflow/VaultClient.java b/flowvault/src/main/java/com/skyflow/VaultClient.java
new file mode 100644
index 00000000..9033e642
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/VaultClient.java
@@ -0,0 +1,71 @@
+package com.skyflow;
+
+import com.skyflow.config.Credentials;
+import com.skyflow.config.VaultConfig;
+import com.skyflow.errors.SkyflowException;
+import com.skyflow.generated.rest.ApiClient;
+import com.skyflow.generated.rest.ApiClientBuilder;
+import com.skyflow.generated.rest.resources.flowservice.FlowserviceClient;
+import com.skyflow.generated.rest.resources.records.RecordsClient;
+import com.skyflow.utils.Utils;
+
+public class VaultClient extends BaseVaultClient {
+ private final ApiClientBuilder apiClientBuilder;
+ private ApiClient apiClient;
+
+ protected VaultClient(VaultConfig vaultConfig, Credentials credentials) throws SkyflowException {
+ super(vaultConfig, credentials);
+ this.apiClientBuilder = new ApiClientBuilder();
+ this.apiClient = null;
+ updateVaultURL();
+ }
+
+ protected FlowserviceClient getRecordsApi() {
+ return this.apiClient.flowservice();
+ }
+
+ protected RecordsClient getQueryApi() {
+ return this.apiClient.records();
+ }
+
+ protected void setCommonCredentials(Credentials commonCredentials) throws SkyflowException {
+ this.commonCredentials = commonCredentials;
+ super.prioritiseCredentials(this.vaultConfig.getCredentials());
+ }
+
+ protected synchronized void setBearerToken() throws SkyflowException {
+ super.setBearerToken(this.vaultConfig.getCredentials());
+ if (apiClient == null) {
+ updateExecutorInHTTP();
+ this.apiClient = this.apiClientBuilder.build();
+ }
+ }
+
+ private void updateVaultURL() throws SkyflowException {
+ // Fetch vaultURL from ENV
+ String vaultURL = Utils.getEnvVaultURL();
+
+ // If vaultURL from ENV is null or empty, fetch vaultURL from vault config
+ if (vaultURL == null || vaultURL.isEmpty()) {
+ vaultURL = this.vaultConfig.getVaultURL();
+ }
+
+ // If vaultURL from vault config is also null or empty, construct vaultURL from clusterId passed in vault config
+ if (vaultURL == null || vaultURL.isEmpty()) {
+ vaultURL = Utils.getVaultURL(this.vaultConfig.getClusterId(), this.vaultConfig.getEnv());
+ }
+ this.apiClientBuilder.url(vaultURL);
+ if (!vaultURL.equals(this.currentVaultURL)) {
+ this.currentVaultURL = vaultURL;
+ this.apiClient = null;
+ }
+ }
+
+ protected void updateExecutorInHTTP() {
+ if (sharedHttpClient == null) {
+ sharedHttpClient = buildSharedHttpClient(() -> this.token);
+ apiClientBuilder.httpClient(sharedHttpClient);
+ }
+ }
+
+}
diff --git a/flowvault/src/main/java/com/skyflow/config/VaultConfig.java b/flowvault/src/main/java/com/skyflow/config/VaultConfig.java
new file mode 100644
index 00000000..8966d29f
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/config/VaultConfig.java
@@ -0,0 +1,20 @@
+package com.skyflow.config;
+
+public class VaultConfig extends BaseVaultConfig {
+
+ private String vaultURL;
+
+ public VaultConfig() {
+ super();
+ this.vaultURL = null;
+ }
+
+ public String getVaultURL() {
+ return vaultURL;
+ }
+
+ public void setVaultURL(String vaultURL) {
+ this.vaultURL = vaultURL;
+ }
+
+}
diff --git a/flowvault/src/main/java/com/skyflow/enums/InterfaceName.java b/flowvault/src/main/java/com/skyflow/enums/InterfaceName.java
new file mode 100644
index 00000000..475f8ad2
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/enums/InterfaceName.java
@@ -0,0 +1,21 @@
+package com.skyflow.enums;
+
+public enum InterfaceName {
+ INSERT("insert"),
+ DETOKENIZE("detokenize"),
+ DELETE("delete tokens"),
+ TOKENIZE("tokenize"),
+ QUERY("query"),
+ GET("get");
+
+
+ private final String interfaceName;
+
+ InterfaceName(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ public String getName() {
+ return interfaceName;
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/enums/UpsertType.java b/flowvault/src/main/java/com/skyflow/enums/UpsertType.java
new file mode 100644
index 00000000..fa2350d2
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/enums/UpsertType.java
@@ -0,0 +1,18 @@
+package com.skyflow.enums;
+
+public enum UpsertType {
+ UPDATE("UPDATE"),
+
+ REPLACE("REPLACE");
+
+ private final String value;
+
+ UpsertType(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return this.value;
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/ApiClient.java b/flowvault/src/main/java/com/skyflow/generated/rest/ApiClient.java
new file mode 100644
index 00000000..02b972dd
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/ApiClient.java
@@ -0,0 +1,37 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest;
+
+import com.skyflow.generated.rest.core.ClientOptions;
+import com.skyflow.generated.rest.core.Suppliers;
+import com.skyflow.generated.rest.resources.flowservice.FlowserviceClient;
+import com.skyflow.generated.rest.resources.records.RecordsClient;
+
+import java.util.function.Supplier;
+
+public class ApiClient {
+ protected final ClientOptions clientOptions;
+
+ protected final Supplier recordsClient;
+
+ protected final Supplier flowserviceClient;
+
+ public ApiClient(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ this.recordsClient = Suppliers.memoize(() -> new RecordsClient(clientOptions));
+ this.flowserviceClient = Suppliers.memoize(() -> new FlowserviceClient(clientOptions));
+ }
+
+ public RecordsClient records() {
+ return this.recordsClient.get();
+ }
+
+ public FlowserviceClient flowservice() {
+ return this.flowserviceClient.get();
+ }
+
+ public static ApiClientBuilder builder() {
+ return new ApiClientBuilder();
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/ApiClientBuilder.java b/flowvault/src/main/java/com/skyflow/generated/rest/ApiClientBuilder.java
new file mode 100644
index 00000000..fa3d6e9d
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/ApiClientBuilder.java
@@ -0,0 +1,48 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest;
+
+import com.skyflow.generated.rest.core.ClientOptions;
+import com.skyflow.generated.rest.core.Environment;
+import okhttp3.OkHttpClient;
+
+public final class ApiClientBuilder {
+ private ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder();
+
+ private Environment environment;
+
+ public ApiClientBuilder url(String url) {
+ this.environment = Environment.custom(url);
+ return this;
+ }
+
+ /**
+ * Sets the timeout (in seconds) for the client. Defaults to 60 seconds.
+ */
+ public ApiClientBuilder timeout(int timeout) {
+ this.clientOptionsBuilder.timeout(timeout);
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of retries for the client. Defaults to 2 retries.
+ */
+ public ApiClientBuilder maxRetries(int maxRetries) {
+ this.clientOptionsBuilder.maxRetries(maxRetries);
+ return this;
+ }
+
+ /**
+ * Sets the underlying OkHttp client
+ */
+ public ApiClientBuilder httpClient(OkHttpClient httpClient) {
+ this.clientOptionsBuilder.httpClient(httpClient);
+ return this;
+ }
+
+ public ApiClient build() {
+ clientOptionsBuilder.environment(this.environment);
+ return new ApiClient(clientOptionsBuilder.build());
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/AsyncApiClient.java b/flowvault/src/main/java/com/skyflow/generated/rest/AsyncApiClient.java
new file mode 100644
index 00000000..840c0bad
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/AsyncApiClient.java
@@ -0,0 +1,37 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest;
+
+import com.skyflow.generated.rest.core.ClientOptions;
+import com.skyflow.generated.rest.core.Suppliers;
+import com.skyflow.generated.rest.resources.flowservice.AsyncFlowserviceClient;
+import com.skyflow.generated.rest.resources.records.AsyncRecordsClient;
+
+import java.util.function.Supplier;
+
+public class AsyncApiClient {
+ protected final ClientOptions clientOptions;
+
+ protected final Supplier recordsClient;
+
+ protected final Supplier flowserviceClient;
+
+ public AsyncApiClient(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ this.recordsClient = Suppliers.memoize(() -> new AsyncRecordsClient(clientOptions));
+ this.flowserviceClient = Suppliers.memoize(() -> new AsyncFlowserviceClient(clientOptions));
+ }
+
+ public AsyncRecordsClient records() {
+ return this.recordsClient.get();
+ }
+
+ public AsyncFlowserviceClient flowservice() {
+ return this.flowserviceClient.get();
+ }
+
+ public static AsyncApiClientBuilder builder() {
+ return new AsyncApiClientBuilder();
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/AsyncApiClientBuilder.java b/flowvault/src/main/java/com/skyflow/generated/rest/AsyncApiClientBuilder.java
new file mode 100644
index 00000000..10c08d51
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/AsyncApiClientBuilder.java
@@ -0,0 +1,48 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest;
+
+import com.skyflow.generated.rest.core.ClientOptions;
+import com.skyflow.generated.rest.core.Environment;
+import okhttp3.OkHttpClient;
+
+public final class AsyncApiClientBuilder {
+ private ClientOptions.Builder clientOptionsBuilder = ClientOptions.builder();
+
+ private Environment environment;
+
+ public AsyncApiClientBuilder url(String url) {
+ this.environment = Environment.custom(url);
+ return this;
+ }
+
+ /**
+ * Sets the timeout (in seconds) for the client. Defaults to 60 seconds.
+ */
+ public AsyncApiClientBuilder timeout(int timeout) {
+ this.clientOptionsBuilder.timeout(timeout);
+ return this;
+ }
+
+ /**
+ * Sets the maximum number of retries for the client. Defaults to 2 retries.
+ */
+ public AsyncApiClientBuilder maxRetries(int maxRetries) {
+ this.clientOptionsBuilder.maxRetries(maxRetries);
+ return this;
+ }
+
+ /**
+ * Sets the underlying OkHttp client
+ */
+ public AsyncApiClientBuilder httpClient(OkHttpClient httpClient) {
+ this.clientOptionsBuilder.httpClient(httpClient);
+ return this;
+ }
+
+ public AsyncApiClient build() {
+ clientOptionsBuilder.environment(this.environment);
+ return new AsyncApiClient(clientOptionsBuilder.build());
+ }
+}
diff --git a/src/main/java/com/skyflow/generated/rest/core/ApiClientApiException.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientApiException.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/ApiClientApiException.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientApiException.java
index 4fab1d41..be5247eb 100644
--- a/src/main/java/com/skyflow/generated/rest/core/ApiClientApiException.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientApiException.java
@@ -3,11 +3,12 @@
*/
package com.skyflow.generated.rest.core;
+import okhttp3.Response;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import okhttp3.Response;
/**
* This exception type will be thrown for any non-2XX API responses.
diff --git a/src/main/java/com/skyflow/generated/rest/core/ApiClientException.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientException.java
similarity index 100%
rename from src/main/java/com/skyflow/generated/rest/core/ApiClientException.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientException.java
diff --git a/src/main/java/com/skyflow/generated/rest/core/ApiClientHttpResponse.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientHttpResponse.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/ApiClientHttpResponse.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientHttpResponse.java
index 9c81f1f5..c743352c 100644
--- a/src/main/java/com/skyflow/generated/rest/core/ApiClientHttpResponse.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/ApiClientHttpResponse.java
@@ -3,11 +3,12 @@
*/
package com.skyflow.generated.rest.core;
+import okhttp3.Response;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import okhttp3.Response;
public final class ApiClientHttpResponse {
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/core/ClientOptions.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ClientOptions.java
new file mode 100644
index 00000000..2a5f0710
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/ClientOptions.java
@@ -0,0 +1,171 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.core;
+
+import okhttp3.OkHttpClient;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+public final class ClientOptions {
+ private final Environment environment;
+
+ private final Map headers;
+
+ private final Map> headerSuppliers;
+
+ private final OkHttpClient httpClient;
+
+ private final int timeout;
+
+ private ClientOptions(
+ Environment environment,
+ Map headers,
+ Map> headerSuppliers,
+ OkHttpClient httpClient,
+ int timeout) {
+ this.environment = environment;
+ this.headers = new HashMap<>();
+ this.headers.putAll(headers);
+ this.headers.putAll(new HashMap() {
+ {
+ put("X-Fern-Language", "JAVA");
+ put("X-Fern-SDK-Name", "com.skyflow.fern:api-sdk");
+ put("X-Fern-SDK-Version", "0.0.98");
+ }
+ });
+ this.headerSuppliers = headerSuppliers;
+ this.httpClient = httpClient;
+ this.timeout = timeout;
+ }
+
+ public Environment environment() {
+ return this.environment;
+ }
+
+ public Map headers(RequestOptions requestOptions) {
+ Map values = new HashMap<>(this.headers);
+ headerSuppliers.forEach((key, supplier) -> {
+ values.put(key, supplier.get());
+ });
+ if (requestOptions != null) {
+ values.putAll(requestOptions.getHeaders());
+ }
+ return values;
+ }
+
+ public int timeout(RequestOptions requestOptions) {
+ if (requestOptions == null) {
+ return this.timeout;
+ }
+ return requestOptions.getTimeout().orElse(this.timeout);
+ }
+
+ public OkHttpClient httpClient() {
+ return this.httpClient;
+ }
+
+ public OkHttpClient httpClientWithTimeout(RequestOptions requestOptions) {
+ if (requestOptions == null) {
+ return this.httpClient;
+ }
+ return this.httpClient
+ .newBuilder()
+ .callTimeout(requestOptions.getTimeout().get(), requestOptions.getTimeoutTimeUnit())
+ .connectTimeout(0, TimeUnit.SECONDS)
+ .writeTimeout(0, TimeUnit.SECONDS)
+ .readTimeout(0, TimeUnit.SECONDS)
+ .build();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private Environment environment;
+
+ private final Map headers = new HashMap<>();
+
+ private final Map> headerSuppliers = new HashMap<>();
+
+ private int maxRetries = 2;
+
+ private Optional timeout = Optional.empty();
+
+ private OkHttpClient httpClient = null;
+
+ public Builder environment(Environment environment) {
+ this.environment = environment;
+ return this;
+ }
+
+ public Builder addHeader(String key, String value) {
+ this.headers.put(key, value);
+ return this;
+ }
+
+ public Builder addHeader(String key, Supplier value) {
+ this.headerSuppliers.put(key, value);
+ return this;
+ }
+
+ /**
+ * Override the timeout in seconds. Defaults to 60 seconds.
+ */
+ public Builder timeout(int timeout) {
+ this.timeout = Optional.of(timeout);
+ return this;
+ }
+
+ /**
+ * Override the timeout in seconds. Defaults to 60 seconds.
+ */
+ public Builder timeout(Optional timeout) {
+ this.timeout = timeout;
+ return this;
+ }
+
+ /**
+ * Override the maximum number of retries. Defaults to 2 retries.
+ */
+ public Builder maxRetries(int maxRetries) {
+ this.maxRetries = maxRetries;
+ return this;
+ }
+
+ public Builder httpClient(OkHttpClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ public ClientOptions build() {
+ OkHttpClient.Builder httpClientBuilder =
+ this.httpClient != null ? this.httpClient.newBuilder() : new OkHttpClient.Builder();
+
+ if (this.httpClient != null) {
+ timeout.ifPresent(timeout -> httpClientBuilder
+ .callTimeout(timeout, TimeUnit.SECONDS)
+ .connectTimeout(0, TimeUnit.SECONDS)
+ .writeTimeout(0, TimeUnit.SECONDS)
+ .readTimeout(0, TimeUnit.SECONDS));
+ } else {
+ httpClientBuilder
+ .callTimeout(this.timeout.orElse(60), TimeUnit.SECONDS)
+ .connectTimeout(0, TimeUnit.SECONDS)
+ .writeTimeout(0, TimeUnit.SECONDS)
+ .readTimeout(0, TimeUnit.SECONDS)
+ .addInterceptor(new RetryInterceptor(this.maxRetries));
+ }
+
+ this.httpClient = httpClientBuilder.build();
+ this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000);
+
+ return new ClientOptions(environment, headers, headerSuppliers, httpClient, this.timeout.get());
+ }
+ }
+}
diff --git a/src/main/java/com/skyflow/generated/rest/core/DateTimeDeserializer.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/DateTimeDeserializer.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/DateTimeDeserializer.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/DateTimeDeserializer.java
index 6be10979..a0a6d7c4 100644
--- a/src/main/java/com/skyflow/generated/rest/core/DateTimeDeserializer.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/DateTimeDeserializer.java
@@ -8,6 +8,7 @@
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
+
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/core/Environment.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/Environment.java
new file mode 100644
index 00000000..2fc27c36
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/Environment.java
@@ -0,0 +1,20 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.core;
+
+public final class Environment {
+ private final String url;
+
+ private Environment(String url) {
+ this.url = url;
+ }
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ public static Environment custom(String url) {
+ return new Environment(url);
+ }
+}
diff --git a/src/main/java/com/skyflow/generated/rest/core/FileStream.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/FileStream.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/FileStream.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/FileStream.java
index 6b459431..2131b0a4 100644
--- a/src/main/java/com/skyflow/generated/rest/core/FileStream.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/FileStream.java
@@ -3,12 +3,13 @@
*/
package com.skyflow.generated.rest.core;
-import java.io.InputStream;
-import java.util.Objects;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import org.jetbrains.annotations.Nullable;
+import java.io.InputStream;
+import java.util.Objects;
+
/**
* Represents a file stream with associated metadata for file uploads.
*/
diff --git a/src/main/java/com/skyflow/generated/rest/core/InputStreamRequestBody.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/InputStreamRequestBody.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/InputStreamRequestBody.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/InputStreamRequestBody.java
index 545f6088..55c3c971 100644
--- a/src/main/java/com/skyflow/generated/rest/core/InputStreamRequestBody.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/InputStreamRequestBody.java
@@ -3,9 +3,6 @@
*/
package com.skyflow.generated.rest.core;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Objects;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.internal.Util;
@@ -14,6 +11,10 @@
import okio.Source;
import org.jetbrains.annotations.Nullable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+
/**
* A custom implementation of OkHttp's RequestBody that wraps an InputStream.
* This class allows streaming of data from an InputStream directly to an HTTP request body,
diff --git a/src/main/java/com/skyflow/generated/rest/core/MediaTypes.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/MediaTypes.java
similarity index 100%
rename from src/main/java/com/skyflow/generated/rest/core/MediaTypes.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/MediaTypes.java
diff --git a/src/main/java/com/skyflow/generated/rest/core/Nullable.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/Nullable.java
similarity index 100%
rename from src/main/java/com/skyflow/generated/rest/core/Nullable.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/Nullable.java
diff --git a/src/main/java/com/skyflow/generated/rest/core/NullableNonemptyFilter.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/NullableNonemptyFilter.java
similarity index 100%
rename from src/main/java/com/skyflow/generated/rest/core/NullableNonemptyFilter.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/NullableNonemptyFilter.java
diff --git a/src/main/java/com/skyflow/generated/rest/core/ObjectMappers.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ObjectMappers.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/ObjectMappers.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/ObjectMappers.java
index 3b7894e0..acec32b4 100644
--- a/src/main/java/com/skyflow/generated/rest/core/ObjectMappers.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/ObjectMappers.java
@@ -10,6 +10,7 @@
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+
import java.io.IOException;
public final class ObjectMappers {
diff --git a/src/main/java/com/skyflow/generated/rest/core/QueryStringMapper.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/QueryStringMapper.java
similarity index 97%
rename from src/main/java/com/skyflow/generated/rest/core/QueryStringMapper.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/QueryStringMapper.java
index e9e18fb9..c0687736 100644
--- a/src/main/java/com/skyflow/generated/rest/core/QueryStringMapper.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/QueryStringMapper.java
@@ -7,14 +7,11 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
import okhttp3.HttpUrl;
import okhttp3.MultipartBody;
+import java.util.*;
+
public class QueryStringMapper {
private static final ObjectMapper MAPPER = ObjectMappers.JSON_MAPPER;
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/core/RequestOptions.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/RequestOptions.java
new file mode 100644
index 00000000..b8a8d14b
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/RequestOptions.java
@@ -0,0 +1,87 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.core;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+
+public final class RequestOptions {
+ private final Optional timeout;
+
+ private final TimeUnit timeoutTimeUnit;
+
+ private final Map headers;
+
+ private final Map> headerSuppliers;
+
+ private RequestOptions(
+ Optional timeout,
+ TimeUnit timeoutTimeUnit,
+ Map headers,
+ Map> headerSuppliers) {
+ this.timeout = timeout;
+ this.timeoutTimeUnit = timeoutTimeUnit;
+ this.headers = headers;
+ this.headerSuppliers = headerSuppliers;
+ }
+
+ public Optional getTimeout() {
+ return timeout;
+ }
+
+ public TimeUnit getTimeoutTimeUnit() {
+ return timeoutTimeUnit;
+ }
+
+ public Map getHeaders() {
+ Map headers = new HashMap<>();
+ headers.putAll(this.headers);
+ this.headerSuppliers.forEach((key, supplier) -> {
+ headers.put(key, supplier.get());
+ });
+ return headers;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private Optional timeout = Optional.empty();
+
+ private TimeUnit timeoutTimeUnit = TimeUnit.SECONDS;
+
+ private final Map headers = new HashMap<>();
+
+ private final Map> headerSuppliers = new HashMap<>();
+
+ public Builder timeout(Integer timeout) {
+ this.timeout = Optional.of(timeout);
+ return this;
+ }
+
+ public Builder timeout(Integer timeout, TimeUnit timeoutTimeUnit) {
+ this.timeout = Optional.of(timeout);
+ this.timeoutTimeUnit = timeoutTimeUnit;
+ return this;
+ }
+
+ public Builder addHeader(String key, String value) {
+ this.headers.put(key, value);
+ return this;
+ }
+
+ public Builder addHeader(String key, Supplier value) {
+ this.headerSuppliers.put(key, value);
+ return this;
+ }
+
+ public RequestOptions build() {
+ return new RequestOptions(timeout, timeoutTimeUnit, headers, headerSuppliers);
+ }
+ }
+}
diff --git a/src/main/java/com/skyflow/generated/rest/core/ResponseBodyInputStream.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ResponseBodyInputStream.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/ResponseBodyInputStream.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/ResponseBodyInputStream.java
index d8df7715..1bb0b5dc 100644
--- a/src/main/java/com/skyflow/generated/rest/core/ResponseBodyInputStream.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/ResponseBodyInputStream.java
@@ -3,9 +3,10 @@
*/
package com.skyflow.generated.rest.core;
+import okhttp3.Response;
+
import java.io.FilterInputStream;
import java.io.IOException;
-import okhttp3.Response;
/**
* A custom InputStream that wraps the InputStream from the OkHttp Response and ensures that the
diff --git a/src/main/java/com/skyflow/generated/rest/core/ResponseBodyReader.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/ResponseBodyReader.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/ResponseBodyReader.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/ResponseBodyReader.java
index ed894407..e6c1a525 100644
--- a/src/main/java/com/skyflow/generated/rest/core/ResponseBodyReader.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/ResponseBodyReader.java
@@ -3,9 +3,10 @@
*/
package com.skyflow.generated.rest.core;
+import okhttp3.Response;
+
import java.io.FilterReader;
import java.io.IOException;
-import okhttp3.Response;
/**
* A custom Reader that wraps the Reader from the OkHttp Response and ensures that the
diff --git a/src/main/java/com/skyflow/generated/rest/core/RetryInterceptor.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/RetryInterceptor.java
similarity index 99%
rename from src/main/java/com/skyflow/generated/rest/core/RetryInterceptor.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/RetryInterceptor.java
index eda7d265..7a28c3c9 100644
--- a/src/main/java/com/skyflow/generated/rest/core/RetryInterceptor.java
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/core/RetryInterceptor.java
@@ -3,12 +3,13 @@
*/
package com.skyflow.generated.rest.core;
+import okhttp3.Interceptor;
+import okhttp3.Response;
+
import java.io.IOException;
import java.time.Duration;
import java.util.Optional;
import java.util.Random;
-import okhttp3.Interceptor;
-import okhttp3.Response;
public class RetryInterceptor implements Interceptor {
diff --git a/src/main/java/com/skyflow/generated/rest/core/Stream.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/Stream.java
similarity index 100%
rename from src/main/java/com/skyflow/generated/rest/core/Stream.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/Stream.java
diff --git a/src/main/java/com/skyflow/generated/rest/core/Suppliers.java b/flowvault/src/main/java/com/skyflow/generated/rest/core/Suppliers.java
similarity index 100%
rename from src/main/java/com/skyflow/generated/rest/core/Suppliers.java
rename to flowvault/src/main/java/com/skyflow/generated/rest/core/Suppliers.java
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/AsyncFlowserviceClient.java b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/AsyncFlowserviceClient.java
new file mode 100644
index 00000000..1fd59fe9
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/AsyncFlowserviceClient.java
@@ -0,0 +1,129 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.resources.flowservice;
+
+import com.skyflow.generated.rest.core.ClientOptions;
+import com.skyflow.generated.rest.core.RequestOptions;
+import com.skyflow.generated.rest.resources.flowservice.requests.*;
+import com.skyflow.generated.rest.types.*;
+
+import java.util.concurrent.CompletableFuture;
+
+public class AsyncFlowserviceClient {
+ protected final ClientOptions clientOptions;
+
+ private final AsyncRawFlowserviceClient rawClient;
+
+ public AsyncFlowserviceClient(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ this.rawClient = new AsyncRawFlowserviceClient(clientOptions);
+ }
+
+ /**
+ * Get responses with HTTP metadata like headers
+ */
+ public AsyncRawFlowserviceClient withRawResponse() {
+ return this.rawClient;
+ }
+
+ public CompletableFuture delete() {
+ return this.rawClient.delete().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture delete(V1DeleteRequest request) {
+ return this.rawClient.delete(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture delete(V1DeleteRequest request, RequestOptions requestOptions) {
+ return this.rawClient.delete(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture get() {
+ return this.rawClient.get().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture get(V1GetRequest request) {
+ return this.rawClient.get(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture get(V1GetRequest request, RequestOptions requestOptions) {
+ return this.rawClient.get(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture insert() {
+ return this.rawClient.insert().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture insert(V1InsertRequest request) {
+ return this.rawClient.insert(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture insert(V1InsertRequest request, RequestOptions requestOptions) {
+ return this.rawClient.insert(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture update() {
+ return this.rawClient.update().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture update(V1UpdateRequest request) {
+ return this.rawClient.update(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture update(V1UpdateRequest request, RequestOptions requestOptions) {
+ return this.rawClient.update(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture deletetoken() {
+ return this.rawClient.deletetoken().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture deletetoken(V1FlowDeleteTokenRequest request) {
+ return this.rawClient.deletetoken(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture deletetoken(
+ V1FlowDeleteTokenRequest request, RequestOptions requestOptions) {
+ return this.rawClient.deletetoken(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture detokenize() {
+ return this.rawClient.detokenize().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture detokenize(V1FlowDetokenizeRequest request) {
+ return this.rawClient.detokenize(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture detokenize(
+ V1FlowDetokenizeRequest request, RequestOptions requestOptions) {
+ return this.rawClient.detokenize(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture tokenize() {
+ return this.rawClient.tokenize().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture tokenize(V1FlowTokenizeRequest request) {
+ return this.rawClient.tokenize(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture tokenize(
+ V1FlowTokenizeRequest request, RequestOptions requestOptions) {
+ return this.rawClient.tokenize(request, requestOptions).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture flowvaultmetrics() {
+ return this.rawClient.flowvaultmetrics().thenApply(response -> response.body());
+ }
+
+ public CompletableFuture flowvaultmetrics(V1FlowVaultMetricsRequest request) {
+ return this.rawClient.flowvaultmetrics(request).thenApply(response -> response.body());
+ }
+
+ public CompletableFuture flowvaultmetrics(
+ V1FlowVaultMetricsRequest request, RequestOptions requestOptions) {
+ return this.rawClient.flowvaultmetrics(request, requestOptions).thenApply(response -> response.body());
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/AsyncRawFlowserviceClient.java b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/AsyncRawFlowserviceClient.java
new file mode 100644
index 00000000..17f85d0c
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/AsyncRawFlowserviceClient.java
@@ -0,0 +1,533 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.resources.flowservice;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.skyflow.generated.rest.core.*;
+import com.skyflow.generated.rest.resources.flowservice.requests.*;
+import com.skyflow.generated.rest.types.*;
+import okhttp3.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+
+public class AsyncRawFlowserviceClient {
+ protected final ClientOptions clientOptions;
+
+ public AsyncRawFlowserviceClient(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ }
+
+ public CompletableFuture> delete() {
+ return delete(V1DeleteRequest.builder().build());
+ }
+
+ public CompletableFuture> delete(V1DeleteRequest request) {
+ return delete(request, null);
+ }
+
+ public CompletableFuture> delete(
+ V1DeleteRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/records/delete")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(responseBody.string(), V1DeleteResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> get() {
+ return get(V1GetRequest.builder().build());
+ }
+
+ public CompletableFuture> get(V1GetRequest request) {
+ return get(request, null);
+ }
+
+ public CompletableFuture> get(
+ V1GetRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/records/get")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(responseBody.string(), V1GetResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> insert() {
+ return insert(V1InsertRequest.builder().build());
+ }
+
+ public CompletableFuture> insert(V1InsertRequest request) {
+ return insert(request, null);
+ }
+
+ public CompletableFuture> insert(
+ V1InsertRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/records/insert")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(responseBody.string(), V1InsertResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> update() {
+ return update(V1UpdateRequest.builder().build());
+ }
+
+ public CompletableFuture> update(V1UpdateRequest request) {
+ return update(request, null);
+ }
+
+ public CompletableFuture> update(
+ V1UpdateRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/records/update")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(responseBody.string(), V1UpdateResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> deletetoken() {
+ return deletetoken(V1FlowDeleteTokenRequest.builder().build());
+ }
+
+ public CompletableFuture> deletetoken(
+ V1FlowDeleteTokenRequest request) {
+ return deletetoken(request, null);
+ }
+
+ public CompletableFuture> deletetoken(
+ V1FlowDeleteTokenRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/tokens/delete")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(
+ responseBody.string(), V1FlowDeleteTokenResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> detokenize() {
+ return detokenize(V1FlowDetokenizeRequest.builder().build());
+ }
+
+ public CompletableFuture> detokenize(
+ V1FlowDetokenizeRequest request) {
+ return detokenize(request, null);
+ }
+
+ public CompletableFuture> detokenize(
+ V1FlowDetokenizeRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/tokens/detokenize")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(
+ responseBody.string(), V1FlowDetokenizeResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> tokenize() {
+ return tokenize(V1FlowTokenizeRequest.builder().build());
+ }
+
+ public CompletableFuture> tokenize(V1FlowTokenizeRequest request) {
+ return tokenize(request, null);
+ }
+
+ public CompletableFuture> tokenize(
+ V1FlowTokenizeRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/tokens/tokenize")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(
+ responseBody.string(), V1FlowTokenizeResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+
+ public CompletableFuture> flowvaultmetrics() {
+ return flowvaultmetrics(V1FlowVaultMetricsRequest.builder().build());
+ }
+
+ public CompletableFuture> flowvaultmetrics(
+ V1FlowVaultMetricsRequest request) {
+ return flowvaultmetrics(request, null);
+ }
+
+ public CompletableFuture> flowvaultmetrics(
+ V1FlowVaultMetricsRequest request, RequestOptions requestOptions) {
+ HttpUrl httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl())
+ .newBuilder()
+ .addPathSegments("v2/vaults/metrics")
+ .build();
+ RequestBody body;
+ try {
+ body = RequestBody.create(
+ ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new ApiClientException("Failed to serialize request", e);
+ }
+ Request okhttpRequest = new Request.Builder()
+ .url(httpUrl)
+ .method("POST", body)
+ .headers(Headers.of(clientOptions.headers(requestOptions)))
+ .addHeader("Content-Type", "application/json")
+ .addHeader("Accept", "application/json")
+ .build();
+ OkHttpClient client = clientOptions.httpClient();
+ if (requestOptions != null && requestOptions.getTimeout().isPresent()) {
+ client = clientOptions.httpClientWithTimeout(requestOptions);
+ }
+ CompletableFuture> future = new CompletableFuture<>();
+ client.newCall(okhttpRequest).enqueue(new Callback() {
+ @Override
+ public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+ try (ResponseBody responseBody = response.body()) {
+ if (response.isSuccessful()) {
+ future.complete(new ApiClientHttpResponse<>(
+ ObjectMappers.JSON_MAPPER.readValue(
+ responseBody.string(), V1FlowVaultMetricsResponse.class),
+ response));
+ return;
+ }
+ String responseBodyString = responseBody != null ? responseBody.string() : "{}";
+ future.completeExceptionally(new ApiClientApiException(
+ "Error with status code " + response.code(),
+ response.code(),
+ ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class),
+ response));
+ return;
+ } catch (IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ }
+
+ @Override
+ public void onFailure(@NotNull Call call, @NotNull IOException e) {
+ future.completeExceptionally(new ApiClientException("Network error executing HTTP request", e));
+ }
+ });
+ return future;
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/FlowserviceClient.java b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/FlowserviceClient.java
new file mode 100644
index 00000000..8da9c9a5
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/FlowserviceClient.java
@@ -0,0 +1,124 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.resources.flowservice;
+
+import com.skyflow.generated.rest.core.ClientOptions;
+import com.skyflow.generated.rest.core.RequestOptions;
+import com.skyflow.generated.rest.resources.flowservice.requests.*;
+import com.skyflow.generated.rest.types.*;
+
+public class FlowserviceClient {
+ protected final ClientOptions clientOptions;
+
+ private final RawFlowserviceClient rawClient;
+
+ public FlowserviceClient(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ this.rawClient = new RawFlowserviceClient(clientOptions);
+ }
+
+ /**
+ * Get responses with HTTP metadata like headers
+ */
+ public RawFlowserviceClient withRawResponse() {
+ return this.rawClient;
+ }
+
+ public V1DeleteResponse delete() {
+ return this.rawClient.delete().body();
+ }
+
+ public V1DeleteResponse delete(V1DeleteRequest request) {
+ return this.rawClient.delete(request).body();
+ }
+
+ public V1DeleteResponse delete(V1DeleteRequest request, RequestOptions requestOptions) {
+ return this.rawClient.delete(request, requestOptions).body();
+ }
+
+ public V1GetResponse get() {
+ return this.rawClient.get().body();
+ }
+
+ public V1GetResponse get(V1GetRequest request) {
+ return this.rawClient.get(request).body();
+ }
+
+ public V1GetResponse get(V1GetRequest request, RequestOptions requestOptions) {
+ return this.rawClient.get(request, requestOptions).body();
+ }
+
+ public V1InsertResponse insert() {
+ return this.rawClient.insert().body();
+ }
+
+ public V1InsertResponse insert(V1InsertRequest request) {
+ return this.rawClient.insert(request).body();
+ }
+
+ public V1InsertResponse insert(V1InsertRequest request, RequestOptions requestOptions) {
+ return this.rawClient.insert(request, requestOptions).body();
+ }
+
+ public V1UpdateResponse update() {
+ return this.rawClient.update().body();
+ }
+
+ public V1UpdateResponse update(V1UpdateRequest request) {
+ return this.rawClient.update(request).body();
+ }
+
+ public V1UpdateResponse update(V1UpdateRequest request, RequestOptions requestOptions) {
+ return this.rawClient.update(request, requestOptions).body();
+ }
+
+ public V1FlowDeleteTokenResponse deletetoken() {
+ return this.rawClient.deletetoken().body();
+ }
+
+ public V1FlowDeleteTokenResponse deletetoken(V1FlowDeleteTokenRequest request) {
+ return this.rawClient.deletetoken(request).body();
+ }
+
+ public V1FlowDeleteTokenResponse deletetoken(V1FlowDeleteTokenRequest request, RequestOptions requestOptions) {
+ return this.rawClient.deletetoken(request, requestOptions).body();
+ }
+
+ public V1FlowDetokenizeResponse detokenize() {
+ return this.rawClient.detokenize().body();
+ }
+
+ public V1FlowDetokenizeResponse detokenize(V1FlowDetokenizeRequest request) {
+ return this.rawClient.detokenize(request).body();
+ }
+
+ public V1FlowDetokenizeResponse detokenize(V1FlowDetokenizeRequest request, RequestOptions requestOptions) {
+ return this.rawClient.detokenize(request, requestOptions).body();
+ }
+
+ public V1FlowTokenizeResponse tokenize() {
+ return this.rawClient.tokenize().body();
+ }
+
+ public V1FlowTokenizeResponse tokenize(V1FlowTokenizeRequest request) {
+ return this.rawClient.tokenize(request).body();
+ }
+
+ public V1FlowTokenizeResponse tokenize(V1FlowTokenizeRequest request, RequestOptions requestOptions) {
+ return this.rawClient.tokenize(request, requestOptions).body();
+ }
+
+ public V1FlowVaultMetricsResponse flowvaultmetrics() {
+ return this.rawClient.flowvaultmetrics().body();
+ }
+
+ public V1FlowVaultMetricsResponse flowvaultmetrics(V1FlowVaultMetricsRequest request) {
+ return this.rawClient.flowvaultmetrics(request).body();
+ }
+
+ public V1FlowVaultMetricsResponse flowvaultmetrics(
+ V1FlowVaultMetricsRequest request, RequestOptions requestOptions) {
+ return this.rawClient.flowvaultmetrics(request, requestOptions).body();
+ }
+}
diff --git a/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/RawFlowserviceClient.java b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/RawFlowserviceClient.java
new file mode 100644
index 00000000..f1203085
--- /dev/null
+++ b/flowvault/src/main/java/com/skyflow/generated/rest/resources/flowservice/RawFlowserviceClient.java
@@ -0,0 +1,412 @@
+/**
+ * This file was auto-generated by Fern from our API Definition.
+ */
+package com.skyflow.generated.rest.resources.flowservice;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.skyflow.generated.rest.core.*;
+import com.skyflow.generated.rest.resources.flowservice.requests.*;
+import com.skyflow.generated.rest.types.*;
+import okhttp3.*;
+
+import java.io.IOException;
+
+public class RawFlowserviceClient {
+ protected final ClientOptions clientOptions;
+
+ public RawFlowserviceClient(ClientOptions clientOptions) {
+ this.clientOptions = clientOptions;
+ }
+
+ public ApiClientHttpResponse delete() {
+ return delete(V1DeleteRequest.builder().build());
+ }
+
+ public ApiClientHttpResponse delete(V1DeleteRequest request) {
+ return delete(request, null);
+ }
+
+ public ApiClientHttpResponse