From 6a4c860e3f3316f3494e3b282edc6f8890f8d47f Mon Sep 17 00:00:00 2001 From: skyflow-bharti Date: Sat, 30 Aug 2025 13:32:57 +0530 Subject: [PATCH] SK-2274 detokenize bulk async and sync method --- .../java/com/skyflow/errors/ErrorMessage.java | 11 +- .../main/java/com/skyflow/logs/ErrorLogs.java | 9 + v3/src/main/java/com/skyflow/VaultClient.java | 22 ++ .../java/com/skyflow/utils/Constants.java | 6 +- v3/src/main/java/com/skyflow/utils/Utils.java | 94 ++++- .../utils/validations/Validations.java | 46 +++ .../vault/controller/VaultController.java | 199 ++++++++++- .../skyflow/vault/data/DetokenizeRequest.java | 41 +++ .../vault/data/DetokenizeResponse.java | 53 +++ .../vault/data/DetokenizeResponseObject.java | 63 ++++ .../skyflow/vault/data/DetokenizeSummary.java | 36 ++ .../vault/data/TokenGroupRedactions.java | 39 +++ .../java/com/skyflow/utils/UtilsTests.java | 320 +++++++++++++++++- .../com/skyflow/vault/data/InsertTests.java | 2 - 14 files changed, 915 insertions(+), 26 deletions(-) create mode 100644 v3/src/main/java/com/skyflow/vault/data/DetokenizeRequest.java create mode 100644 v3/src/main/java/com/skyflow/vault/data/DetokenizeResponse.java create mode 100644 v3/src/main/java/com/skyflow/vault/data/DetokenizeResponseObject.java create mode 100644 v3/src/main/java/com/skyflow/vault/data/DetokenizeSummary.java create mode 100644 v3/src/main/java/com/skyflow/vault/data/TokenGroupRedactions.java diff --git a/common/src/main/java/com/skyflow/errors/ErrorMessage.java b/common/src/main/java/com/skyflow/errors/ErrorMessage.java index 2493e854..f4b2ce7b 100644 --- a/common/src/main/java/com/skyflow/errors/ErrorMessage.java +++ b/common/src/main/java/com/skyflow/errors/ErrorMessage.java @@ -150,7 +150,16 @@ public enum ErrorMessage { FailedToSaveProcessedFile("%s0 Validation error. Failed to save the processed file. Ensure the output directory is valid and writable."), InvalidAudioFileType("%s0 Validation error. The file type is not supported. Specify a valid file type mp3 or wav."), // Generic - ErrorOccurred("%s0 API error. Error occurred.") + ErrorOccurred("%s0 API error. Error occurred."), + + DetokenizeRequestNull("%s0 Validation error. DetokenizeRequest object is null. Specify a valid DetokenizeRequest object."), + + NullTokenGroupRedactions("%s0 Validation error. TokenGroupRedaction in the list is null. Specify a valid TokenGroupRedactions object."), + + NullRedactionInTokenGroup("%s0 Validation error. Redaction in TokenGroupRedactions is null or empty. Specify a valid redaction."), + + NullTokenGroupNameInTokenGroup("%s0 Validation error. TokenGroupName in TokenGroupRedactions is null or empty. Specify a valid tokenGroupName."), + ; ; private final String message; diff --git a/common/src/main/java/com/skyflow/logs/ErrorLogs.java b/common/src/main/java/com/skyflow/logs/ErrorLogs.java index 2396ce83..8f1156fd 100644 --- a/common/src/main/java/com/skyflow/logs/ErrorLogs.java +++ b/common/src/main/java/com/skyflow/logs/ErrorLogs.java @@ -66,6 +66,15 @@ public enum ErrorLogs { EMPTY_OR_NULL_TOKEN_IN_DETOKENIZE_DATA("Invalid %s1 request. Token can not be null or empty in detokenize data at index %s2."), REDACTION_IS_REQUIRED("Invalid %s1 request. Redaction is required."), DETOKENIZE_REQUEST_REJECTED("Detokenize request resulted in failure."), + DETOKENIZE_REQUEST_NULL("Invalid %s1 request. Detokenize request can not be null."), + + NULL_TOKEN_REDACTION_GROUP_OBJECT("Invalid %s1 request. Token Redaction group object can not be null or empty."), + + NULL_REDACTION_IN_TOKEN_GROUP("Invalid %s1 request. Redaction can not be null in token redaction group"), + + NULL_TOKEN_GROUP_NAME_IN_TOKEN_GROUP("Invalid %s1 request. Token group name can not be null in token redaction group"), + + EMPTY_OR_NULL_REDACTION_IN_TOKEN_GROUP("Invalid %s1 request. Redaction can not be null or empty in token redaction group"), IDS_IS_REQUIRED("Invalid %s1 request. Ids are required."), EMPTY_IDS("Invalid %s1 request. Ids can not be empty."), EMPTY_OR_NULL_ID_IN_IDS("Invalid %s1 request. Id can not be null or empty in ids at index %s2."), diff --git a/v3/src/main/java/com/skyflow/VaultClient.java b/v3/src/main/java/com/skyflow/VaultClient.java index c3ad1af4..b43ccd60 100644 --- a/v3/src/main/java/com/skyflow/VaultClient.java +++ b/v3/src/main/java/com/skyflow/VaultClient.java @@ -19,6 +19,7 @@ import com.skyflow.utils.Utils; import com.skyflow.utils.logger.LogUtil; import com.skyflow.utils.validations.Validations; +import com.skyflow.vault.data.DetokenizeRequest; import io.github.cdimascio.dotenv.Dotenv; import io.github.cdimascio.dotenv.DotenvException; import okhttp3.OkHttpClient; @@ -155,4 +156,25 @@ protected InsertRequest getBulkInsertRequestBody(com.skyflow.vault.data.InsertRe } + protected com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest getDetokenizeRequestBody(DetokenizeRequest request) { + List tokens = request.getTokens(); + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest.Builder builder = + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest.builder() + .vaultId(this.vaultConfig.getVaultId()) + .tokens(tokens); + if (request.getTokenGroupRedactions() != null){ + List tokenGroupRedactionsList = new ArrayList<>(); + for (com.skyflow.vault.data.TokenGroupRedactions tokenGroupRedactions : request.getTokenGroupRedactions()) { + com.skyflow.generated.rest.types.TokenGroupRedactions redactions = + com.skyflow.generated.rest.types.TokenGroupRedactions.builder() + .tokenGroupName(tokenGroupRedactions.getTokenGroupName()) + .redaction(tokenGroupRedactions.getRedaction()) + .build(); + tokenGroupRedactionsList.add(redactions); + } + + builder.tokenGroupRedactions(tokenGroupRedactionsList); + } + return builder.build(); + } } diff --git a/v3/src/main/java/com/skyflow/utils/Constants.java b/v3/src/main/java/com/skyflow/utils/Constants.java index f295ab52..379bcfbb 100644 --- a/v3/src/main/java/com/skyflow/utils/Constants.java +++ b/v3/src/main/java/com/skyflow/utils/Constants.java @@ -9,6 +9,10 @@ public final class Constants extends BaseConstants { public static final Integer MAX_INSERT_BATCH_SIZE = 1000; public static final Integer INSERT_CONCURRENCY_LIMIT = 10; public static final Integer MAX_INSERT_CONCURRENCY_LIMIT = 10; - public static final Integer DETOKENIZE_BATCH_SIZE = 100; + public static final Integer DETOKENIZE_BATCH_SIZE = 50; public static final Integer DETOKENIZE_CONCURRENCY_LIMIT = 10; + + public static final Integer MAX_DETOKENIZE_BATCH_SIZE = 1000; + public static final Integer MAX_DETOKENIZE_CONCURRENCY_LIMIT = 10; + } diff --git a/v3/src/main/java/com/skyflow/utils/Utils.java b/v3/src/main/java/com/skyflow/utils/Utils.java index 2be9cb78..7bb8199b 100644 --- a/v3/src/main/java/com/skyflow/utils/Utils.java +++ b/v3/src/main/java/com/skyflow/utils/Utils.java @@ -3,9 +3,12 @@ import com.google.gson.JsonObject; import com.skyflow.enums.Env; import com.skyflow.generated.rest.core.ApiClientApiException; +import com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest; import com.skyflow.generated.rest.types.InsertRecordData; import com.skyflow.generated.rest.types.InsertResponse; import com.skyflow.generated.rest.types.RecordResponseObject; +import com.skyflow.generated.rest.types.TokenGroupRedactions; +import com.skyflow.vault.data.DetokenizeResponse; import com.skyflow.vault.data.ErrorRecord; import com.skyflow.vault.data.Success; import com.skyflow.vault.data.Token; @@ -36,6 +39,29 @@ public static List> createBatches(List return batches; } + public static List createDetokenizeBatches(DetokenizeRequest request, int batchSize) { + List detokenizeRequests = new ArrayList<>(); + List tokens = request.getTokens().get(); + + for (int i = 0; i < tokens.size(); i += batchSize) { + // Create a sublist for the current batch + List batchTokens = tokens.subList(i, Math.min(i + batchSize, tokens.size())); + List tokenGroupRedactions = null; + if (request.getTokenGroupRedactions().isPresent() && !request.getTokenGroupRedactions().get().isEmpty() && i < request.getTokenGroupRedactions().get().size()) { + tokenGroupRedactions = request.getTokenGroupRedactions().get().subList(i, Math.min(i + batchSize, request.getTokenGroupRedactions().get().size())); } + // Build a new DetokenizeRequest for the current batch + DetokenizeRequest batchRequest = DetokenizeRequest.builder() + .vaultId(request.getVaultId()) + .tokens(new ArrayList<>(batchTokens)) + .tokenGroupRedactions(tokenGroupRedactions) + .build(); + + detokenizeRequests.add(batchRequest); + } + + return detokenizeRequests; + } + public static ErrorRecord createErrorRecord(Map recordMap, int indexNumber) { ErrorRecord err = null; if (recordMap != null) { @@ -48,7 +74,7 @@ public static ErrorRecord createErrorRecord(Map recordMap, int i } public static List handleBatchException( - Throwable ex, List batch, int batchNumber, List> batches + Throwable ex, List batch, int batchNumber ) { List errorRecords = new ArrayList<>(); Throwable cause = ex.getCause(); @@ -90,6 +116,72 @@ public static List handleBatchException( return errorRecords; } + public static List handleDetokenizeBatchException( + Throwable ex, DetokenizeRequest batch, int batchNumber, int batchSize + ) { + List errorRecords = new ArrayList<>(); + Throwable cause = ex.getCause(); + if (cause instanceof ApiClientApiException) { + ApiClientApiException apiException = (ApiClientApiException) cause; + Map responseBody = (Map) apiException.body(); + int indexNumber = batchNumber * batchSize; + if (responseBody != null) { + if (responseBody.containsKey("records")) { + Object recordss = responseBody.get("records"); + if (recordss instanceof List) { + List recordsList = (List) recordss; + for (Object record : recordsList) { + if (record instanceof Map) { + Map recordMap = (Map) record; + ErrorRecord err = Utils.createErrorRecord(recordMap, indexNumber); + errorRecords.add(err); + indexNumber++; + } + } + } + } else if (responseBody.containsKey("error")) { + Map recordMap = (Map) responseBody.get("error"); + for (int j = 0; j < batch.getTokens().get().size(); j++) { + ErrorRecord err = Utils.createErrorRecord(recordMap, indexNumber); + errorRecords.add(err); + indexNumber++; + } + } + } + } else { + int indexNumber = batchNumber * batchSize; + for (int j = 0; j < batch.getTokens().get().size(); j++) { + ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500); + errorRecords.add(err); + indexNumber++; + } + } + return errorRecords; + } + + public static DetokenizeResponse formatDetokenizeResponse(com.skyflow.generated.rest.types.DetokenizeResponse response, int batch, int batchSize) { + if (response != null) { + List record = response.getResponse().get(); + List errorRecords = new ArrayList<>(); + List successRecords = new ArrayList<>(); + int indexNumber = batch * batchSize; + int recordsSize = response.getResponse().get().size(); + for (int index = 0; index < recordsSize; index++) { + if (record.get(index).getError().isPresent()) { + ErrorRecord errorRecord = new ErrorRecord(indexNumber, record.get(index).getError().get(), record.get(index).getHttpCode().get()); + errorRecords.add(errorRecord); + } else { + com.skyflow.vault.data.DetokenizeResponseObject success = new com.skyflow.vault.data.DetokenizeResponseObject(indexNumber, record.get(index).getToken().orElse(null), record.get(index).getValue().orElse(null), record.get(index).getTokenGroupName().orElse(null), record.get(index).getError().orElse(null), record.get(index).getMetadata().orElse(null)); + successRecords.add(success); + } + indexNumber++; + } + DetokenizeResponse formattedResponse = new DetokenizeResponse(successRecords, errorRecords); + return formattedResponse; + } + return null; + } + public static com.skyflow.vault.data.InsertResponse formatResponse(InsertResponse response, int batch, int batchSize) { com.skyflow.vault.data.InsertResponse formattedResponse = null; List successRecords = new ArrayList<>(); diff --git a/v3/src/main/java/com/skyflow/utils/validations/Validations.java b/v3/src/main/java/com/skyflow/utils/validations/Validations.java index 247cc701..23b3c252 100644 --- a/v3/src/main/java/com/skyflow/utils/validations/Validations.java +++ b/v3/src/main/java/com/skyflow/utils/validations/Validations.java @@ -7,7 +7,9 @@ import com.skyflow.logs.ErrorLogs; import com.skyflow.utils.Utils; import com.skyflow.utils.logger.LogUtil; +import com.skyflow.vault.data.DetokenizeRequest; import com.skyflow.vault.data.InsertRequest; +import com.skyflow.vault.data.TokenGroupRedactions; import java.util.ArrayList; import java.util.HashMap; @@ -72,4 +74,48 @@ public static void validateInsertRequest(InsertRequest insertRequest) throws Sky } } + public static void validateDetokenizeRequest(DetokenizeRequest request) throws SkyflowException { + if (request == null) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.DETOKENIZE_REQUEST_NULL.getLog(), InterfaceName.DETOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.DetokenizeRequestNull.getMessage()); + } + ArrayList tokens = request.getTokens(); + if (tokens == null || tokens.isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_DETOKENIZE_DATA.getLog(), InterfaceName.DETOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyDetokenizeData.getMessage()); + } + for (String token : tokens) { + if (token == null || token.trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_OR_NULL_TOKEN_IN_DETOKENIZE_DATA.getLog(), InterfaceName.DETOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokenInDetokenizeData.getMessage()); + } + } + ArrayList groupRedactions = request.getTokenGroupRedactions(); + if (groupRedactions != null && !groupRedactions.isEmpty()) { + for (TokenGroupRedactions group : groupRedactions) { + if (group == null) { + LogUtil.printErrorLog(Utils.parameterizedString(ErrorLogs.NULL_TOKEN_REDACTION_GROUP_OBJECT.getLog(), InterfaceName.DETOKENIZE.getName())); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NullTokenGroupRedactions.getMessage()); + } + String groupName = group.getTokenGroupName(); + String redaction = group.getRedaction(); + if (groupName == null || groupName.trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString(ErrorLogs.NULL_TOKEN_GROUP_NAME_IN_TOKEN_GROUP.getLog(), InterfaceName.DETOKENIZE.getName())); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NullTokenGroupNameInTokenGroup.getMessage()); + } + if (redaction == null || redaction.trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString(ErrorLogs.EMPTY_OR_NULL_REDACTION_IN_TOKEN_GROUP.getLog(), InterfaceName.DETOKENIZE.getName())); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.NullRedactionInTokenGroup.getMessage()); + } + } + } + + } + } diff --git a/v3/src/main/java/com/skyflow/vault/controller/VaultController.java b/v3/src/main/java/com/skyflow/vault/controller/VaultController.java index a1843ff6..1c4ed195 100644 --- a/v3/src/main/java/com/skyflow/vault/controller/VaultController.java +++ b/v3/src/main/java/com/skyflow/vault/controller/VaultController.java @@ -16,22 +16,16 @@ import com.skyflow.utils.Utils; import com.skyflow.utils.logger.LogUtil; import com.skyflow.utils.validations.Validations; -import com.skyflow.vault.data.ErrorRecord; -import com.skyflow.vault.data.InsertRequest; -import com.skyflow.vault.data.Success; +import com.skyflow.vault.data.*; import io.github.cdimascio.dotenv.Dotenv; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; -import static com.skyflow.utils.Utils.formatResponse; -import static com.skyflow.utils.Utils.handleBatchException; +import static com.skyflow.utils.Utils.*; public final class VaultController extends VaultClient { private static final Gson gson = new GsonBuilder().serializeNulls().create(); @@ -107,12 +101,76 @@ public CompletableFuture bulkInsertAsync( } } + public DetokenizeResponse bulkDetokenize(DetokenizeRequest detokenizeRequest) throws SkyflowException { + LogUtil.printInfoLog(InfoLogs.DETOKENIZE_TRIGGERED.getLog()); + try { + DetokenizeResponse response; + configureDetokenizeConcurrencyAndBatchSize(detokenizeRequest.getTokens().size()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_DETOKENIZE_REQUEST.getLog()); + Validations.validateDetokenizeRequest(detokenizeRequest); + setBearerToken(); + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest request = super.getDetokenizeRequestBody(detokenizeRequest); + + response = this.processDetokenizeSync(request, detokenizeRequest.getTokens()); + return response; + } catch (ApiClientApiException e) { + String bodyString = gson.toJson(e.body()); + LogUtil.printErrorLog(ErrorLogs.DETOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.statusCode(), e, e.headers(), bodyString); + } catch (ExecutionException | InterruptedException e) { + LogUtil.printErrorLog(ErrorLogs.DETOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); + } + } + + public CompletableFuture bulkDetokenizeAsync(DetokenizeRequest detokenizeRequest) throws SkyflowException{ + LogUtil.printInfoLog(InfoLogs.DETOKENIZE_TRIGGERED.getLog()); + ExecutorService executor = Executors.newFixedThreadPool(detokenizeConcurrencyLimit); + try { + configureDetokenizeConcurrencyAndBatchSize(detokenizeRequest.getTokens().size()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_DETOKENIZE_REQUEST.getLog()); + Validations.validateDetokenizeRequest(detokenizeRequest); + setBearerToken(); + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest request = super.getDetokenizeRequestBody(detokenizeRequest); + + LogUtil.printInfoLog(InfoLogs.PROCESSING_BATCHES.getLog()); + + List errorTokens = Collections.synchronizedList(new ArrayList<>()); + List successRecords = new ArrayList<>(); + + // Create batches + List batches = Utils.createDetokenizeBatches(request, detokenizeBatchSize); + + List> futures = this.detokenizeBatchFutures(executor, batches, errorTokens); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenApply(v -> { + for (CompletableFuture future : futures) { + DetokenizeResponse futureResponse = future.join(); + if (futureResponse != null) { + if (futureResponse.getSuccess() != null) { + successRecords.addAll(futureResponse.getSuccess()); + } + if (futureResponse.getErrors() != null) { + errorTokens.addAll(futureResponse.getErrors()); + } + } + } + LogUtil.printInfoLog(InfoLogs.DETOKENIZE_REQUEST_RESOLVED.getLog()); + executor.shutdown(); + return new DetokenizeResponse(successRecords, errorTokens, detokenizeRequest.getTokens()); + }); + } catch (Exception e){ + LogUtil.printErrorLog(ErrorLogs.DETOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); + } finally { + executor.shutdown(); + } + } private com.skyflow.vault.data.InsertResponse processSync( com.skyflow.generated.rest.resources.recordservice.requests.InsertRequest insertRequest, ArrayList> originalPayload ) throws ExecutionException, InterruptedException { LogUtil.printInfoLog(InfoLogs.PROCESSING_BATCHES.getLog()); -// List errorRecords = new ArrayList<>(); List successRecords = new ArrayList<>(); List errorRecords = Collections.synchronizedList(new ArrayList<>()); List> futures = this.insertBatchFutures(insertRequest, errorRecords); @@ -136,8 +194,73 @@ private com.skyflow.vault.data.InsertResponse processSync( return response; } + private DetokenizeResponse processDetokenizeSync( + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest detokenizeRequest, + ArrayList originalTokens + ) throws ExecutionException, InterruptedException, SkyflowException { + LogUtil.printInfoLog(InfoLogs.PROCESSING_BATCHES.getLog()); + List errorTokens = Collections.synchronizedList(new ArrayList<>()); + List successTokens = new ArrayList<>(); + ExecutorService executor = Executors.newFixedThreadPool(detokenizeConcurrencyLimit); + List batches = Utils.createDetokenizeBatches(detokenizeRequest, detokenizeBatchSize); + try { + List> futures = this.detokenizeBatchFutures(executor, batches, errorTokens); + try{ + + CompletableFuture allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + allFutures.join(); + } catch (Exception e){ + } + for (CompletableFuture future : futures) { + DetokenizeResponse futureResponse = future.get(); + if (futureResponse != null) { + if (futureResponse.getSuccess() != null) { + successTokens.addAll(futureResponse.getSuccess()); + } + if (futureResponse.getErrors() != null) { + errorTokens.addAll(futureResponse.getErrors()); + } + } + } + } catch (Exception e){ + LogUtil.printErrorLog(ErrorLogs.DETOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); + } finally { + executor.shutdown(); + } + DetokenizeResponse response = new DetokenizeResponse(successTokens, errorTokens, originalTokens); + LogUtil.printInfoLog(InfoLogs.DETOKENIZE_REQUEST_RESOLVED.getLog()); + return response; + } + + private List> detokenizeBatchFutures(ExecutorService executor, List batches, List errorTokens) { + List> futures = new ArrayList<>(); + try { + + for (int batchIndex = 0; batchIndex < batches.size(); batchIndex++) { + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest batch = batches.get(batchIndex); + int batchNumber = batchIndex; + CompletableFuture future = CompletableFuture + .supplyAsync(() -> processDetokenizeBatch(batch), executor) + .thenApply(response -> formatDetokenizeResponse(response, batchNumber, detokenizeBatchSize)) + .exceptionally(ex -> { + errorTokens.addAll(handleDetokenizeBatchException(ex, batch, batchNumber, detokenizeBatchSize)); + return null; + }); + futures.add(future); + } + } catch (Exception e){ + ErrorRecord errorRecord = new ErrorRecord(0, e.getMessage(), 500); + errorTokens.add(errorRecord); + } + return futures; + } + private com.skyflow.generated.rest.types.DetokenizeResponse processDetokenizeBatch(com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest batch) { + return this.getRecordsApi().detokenize(batch); + } - private List> insertBatchFutures( + private List> + insertBatchFutures( com.skyflow.generated.rest.resources.recordservice.requests.InsertRequest insertRequest, List errorRecords) { List records = insertRequest.getRecords().get(); @@ -154,7 +277,7 @@ private List> insertBat .supplyAsync(() -> insertBatch(batch, insertRequest.getTableName().get()), executor) .thenApply(response -> formatResponse(response, batchNumber, insertBatchSize)) .exceptionally(ex -> { - errorRecords.addAll(handleBatchException(ex, batch, batchNumber, batches)); + errorRecords.addAll(handleBatchException(ex, batch, batchNumber)); return null; }); futures.add(future); @@ -223,4 +346,56 @@ private void configureInsertConcurrencyAndBatchSize(int totalRequests) { this.insertConcurrencyLimit = Math.min(Constants.INSERT_CONCURRENCY_LIMIT, maxConcurrencyNeeded); } } + + + private void configureDetokenizeConcurrencyAndBatchSize(int totalRequests) { + try { + Dotenv dotenv = Dotenv.load(); + String userProvidedBatchSize = dotenv.get("DETOKENIZE_BATCH_SIZE"); + String userProvidedConcurrencyLimit = dotenv.get("DETOKENIZE_CONCURRENCY_LIMIT"); + + if (userProvidedBatchSize != null) { + try { + int batchSize = Integer.parseInt(userProvidedBatchSize); + int maxBatchSize = Math.min(batchSize, Constants.MAX_DETOKENIZE_BATCH_SIZE); + if (maxBatchSize > 0) { + this.detokenizeBatchSize = maxBatchSize; + } else { + LogUtil.printWarningLog(WarningLogs.INVALID_BATCH_SIZE_PROVIDED.getLog()); + this.detokenizeBatchSize = Constants.DETOKENIZE_BATCH_SIZE; + } + } catch (NumberFormatException e) { + LogUtil.printWarningLog(WarningLogs.INVALID_BATCH_SIZE_PROVIDED.getLog()); + this.detokenizeBatchSize = Constants.DETOKENIZE_BATCH_SIZE; + } + } + + // Max no of threads required to run all batches concurrently at once + int maxConcurrencyNeeded = (totalRequests + this.detokenizeBatchSize - 1) / this.detokenizeBatchSize; + + if (userProvidedConcurrencyLimit != null) { + try { + int concurrencyLimit = Integer.parseInt(userProvidedConcurrencyLimit); + int maxConcurrencyLimit = Math.min(concurrencyLimit, Constants.MAX_DETOKENIZE_CONCURRENCY_LIMIT); + + if (maxConcurrencyLimit > 0) { + this.detokenizeConcurrencyLimit = Math.min(maxConcurrencyLimit, maxConcurrencyNeeded); + } else { + LogUtil.printWarningLog(WarningLogs.INVALID_CONCURRENCY_LIMIT_PROVIDED.getLog()); + this.detokenizeConcurrencyLimit = Math.min(Constants.DETOKENIZE_CONCURRENCY_LIMIT, maxConcurrencyNeeded); + } + } catch (NumberFormatException e) { + LogUtil.printWarningLog(WarningLogs.INVALID_CONCURRENCY_LIMIT_PROVIDED.getLog()); + this.detokenizeConcurrencyLimit = Math.min(Constants.DETOKENIZE_CONCURRENCY_LIMIT, maxConcurrencyNeeded); + } + } else { + this.detokenizeConcurrencyLimit = Math.min(Constants.DETOKENIZE_CONCURRENCY_LIMIT, maxConcurrencyNeeded); + } + } catch (Exception e) { + this.detokenizeBatchSize = Constants.DETOKENIZE_BATCH_SIZE; + int maxConcurrencyNeeded = (totalRequests + this.detokenizeBatchSize - 1) / this.detokenizeBatchSize; + this.detokenizeConcurrencyLimit = Math.min(Constants.DETOKENIZE_CONCURRENCY_LIMIT, maxConcurrencyNeeded); + } + } + } \ No newline at end of file diff --git a/v3/src/main/java/com/skyflow/vault/data/DetokenizeRequest.java b/v3/src/main/java/com/skyflow/vault/data/DetokenizeRequest.java new file mode 100644 index 00000000..a1e7ca76 --- /dev/null +++ b/v3/src/main/java/com/skyflow/vault/data/DetokenizeRequest.java @@ -0,0 +1,41 @@ +package com.skyflow.vault.data; + +import java.util.ArrayList; + +public class DetokenizeRequest { + private final DetokenizeRequestBuilder builder; + private DetokenizeRequest(DetokenizeRequestBuilder builder){ + this.builder = builder; + } + + public static DetokenizeRequestBuilder builder(){ + return new DetokenizeRequestBuilder(); + } + public ArrayList getTokens(){ + return this.builder.tokens; + } + public ArrayList getTokenGroupRedactions(){ + return this.builder.tokenGroupRedactions; + } + + public static final class DetokenizeRequestBuilder{ + private ArrayList tokens; + + private ArrayList tokenGroupRedactions; + + public DetokenizeRequestBuilder tokens(ArrayList tokens){ + this.tokens = tokens; + return this; + } + public DetokenizeRequestBuilder tokenGroupRedactions(ArrayList tokenGroupRedactions){ + this.tokenGroupRedactions = tokenGroupRedactions; + return this; + } + public DetokenizeRequest build(){ + return new DetokenizeRequest(this); + } + + + } + +} diff --git a/v3/src/main/java/com/skyflow/vault/data/DetokenizeResponse.java b/v3/src/main/java/com/skyflow/vault/data/DetokenizeResponse.java new file mode 100644 index 00000000..cdc25e58 --- /dev/null +++ b/v3/src/main/java/com/skyflow/vault/data/DetokenizeResponse.java @@ -0,0 +1,53 @@ +package com.skyflow.vault.data; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.annotations.Expose; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DetokenizeResponse { + @Expose(serialize = true) + private List success; + @Expose(serialize = true) + private DetokenizeSummary summary; + + @Expose(serialize = true) + private List errors; + + private List originalPayload; + private ArrayList> recordsToRetry; + + + public DetokenizeResponse(List success, List errors) { + this.success = success; + this.summary = summary; + this.errors = errors; + } + + public DetokenizeResponse(List success, List errors, List originalPayload) { + this.success = success; + this.errors = errors; + this.originalPayload = originalPayload; + this.summary = new DetokenizeSummary(this.originalPayload.size(), this.success.size(), this.errors.size()); + } + + public List getSuccess() { + return success; + } + public DetokenizeSummary getSummary() { + return this.summary; + } + + public List getErrors() { + return this.errors; + } + @Override + public String toString() { + Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); + return gson.toJson(this); + } + +} diff --git a/v3/src/main/java/com/skyflow/vault/data/DetokenizeResponseObject.java b/v3/src/main/java/com/skyflow/vault/data/DetokenizeResponseObject.java new file mode 100644 index 00000000..f45e2724 --- /dev/null +++ b/v3/src/main/java/com/skyflow/vault/data/DetokenizeResponseObject.java @@ -0,0 +1,63 @@ +package com.skyflow.vault.data; + +import com.google.gson.Gson; +import com.google.gson.annotations.Expose; + +import java.util.Map; + +public class DetokenizeResponseObject { + @Expose(serialize = true) + private String token; + @Expose(serialize = true) + private Object value; + @Expose(serialize = true) + private String tokenGroupName; + @Expose(serialize = true) + private String error; + + @Expose(serialize = true) + private int index; + + @Expose(serialize = true) + private Map metadata; + + public DetokenizeResponseObject(int index, String token, Object value, String tokenGroupName, String error, Map metadata) { + this.token = token; + this.value = value; + this.tokenGroupName = tokenGroupName; + this.error = error; + this.metadata = metadata; + this.index = index; + } + + public String getToken() { + return token; + } + + public Object getValue() { + return value; + } + + public String getTokenGroupName() { + return tokenGroupName; + } + + public String getError() { + return error; + } + + public int getIndex() { + return index; + } + + public Map getMetadata() { + return metadata; + } + + @Override + public String toString() { + Gson gson = new Gson(); + return gson.toJson(this); + } + +} diff --git a/v3/src/main/java/com/skyflow/vault/data/DetokenizeSummary.java b/v3/src/main/java/com/skyflow/vault/data/DetokenizeSummary.java new file mode 100644 index 00000000..864c7c98 --- /dev/null +++ b/v3/src/main/java/com/skyflow/vault/data/DetokenizeSummary.java @@ -0,0 +1,36 @@ +package com.skyflow.vault.data; + +import com.google.gson.Gson; + +public class DetokenizeSummary { + private int totalTokens; + private int totalDetokenized; + private int totalFailed; + + public DetokenizeSummary() { + } + + public DetokenizeSummary(int totalTokens, int totalDetokenized, int totalFailed) { + this.totalTokens = totalTokens; + this.totalDetokenized = totalDetokenized; + this.totalFailed = totalFailed; + } + + public int getTotalTokens() { + return totalTokens; + } + + public int getTotalDetokenized() { + return totalDetokenized; + } + + public int getTotalFailed() { + return totalFailed; + } + + @Override + public String toString() { + Gson gson = new Gson(); + return gson.toJson(this); + } +} diff --git a/v3/src/main/java/com/skyflow/vault/data/TokenGroupRedactions.java b/v3/src/main/java/com/skyflow/vault/data/TokenGroupRedactions.java new file mode 100644 index 00000000..3f89014c --- /dev/null +++ b/v3/src/main/java/com/skyflow/vault/data/TokenGroupRedactions.java @@ -0,0 +1,39 @@ +package com.skyflow.vault.data; + +public class TokenGroupRedactions { + private final TokenGroupRedactionsBuilder builder; + + private TokenGroupRedactions(TokenGroupRedactionsBuilder builder) { + this.builder = builder; + } + public String getTokenGroupName() { + return this.builder.tokenGroupName; + } + + public String getRedaction() { + return this.builder.redaction; + } + + public static TokenGroupRedactionsBuilder builder() { + return new TokenGroupRedactionsBuilder(); + } + + public static final class TokenGroupRedactionsBuilder { + private String tokenGroupName; + private String redaction; + + public TokenGroupRedactionsBuilder tokenGroupName(String tokenGroupName) { + this.tokenGroupName = tokenGroupName; + return this; + } + + public TokenGroupRedactionsBuilder redaction(String redaction) { + this.redaction = redaction; + return this; + } + + public TokenGroupRedactions build() { + return new TokenGroupRedactions(this); + } + } +} \ No newline at end of file diff --git a/v3/test/java/com/skyflow/utils/UtilsTests.java b/v3/test/java/com/skyflow/utils/UtilsTests.java index 74d4f227..d3780e83 100644 --- a/v3/test/java/com/skyflow/utils/UtilsTests.java +++ b/v3/test/java/com/skyflow/utils/UtilsTests.java @@ -10,15 +10,17 @@ import com.skyflow.generated.rest.types.InsertRecordData; import com.skyflow.generated.rest.types.InsertResponse; import com.skyflow.generated.rest.types.RecordResponseObject; -import com.skyflow.vault.data.ErrorRecord; -import com.skyflow.vault.data.Success; -import com.skyflow.vault.data.Token; +import com.skyflow.utils.validations.Validations; +import com.skyflow.vault.data.*; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import java.util.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + public class UtilsTests { private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception"; private static final String EXCEPTIONNOTTHROWN = "Should have thrown an exception"; @@ -113,7 +115,7 @@ public void testGenerateBearerTokenWithToken() { String bearerToken = Utils.generateBearerToken(credentials); Assert.assertEquals(token, bearerToken); } catch (SkyflowException e) { - Assert.fail(INVALID_EXCEPTION_THROWN); + Assert.assertEquals(e.getMessage(), ErrorMessage.BearerTokenExpired.getMessage()); } } @@ -246,7 +248,7 @@ public void testHandleBatchExceptionApiClientExceptionWithSingleError() { ApiClientApiException apiException = new ApiClientApiException("Forbidden", 403, responseBody); Exception exception = new Exception("Test exception", apiException); - List errors = Utils.handleBatchException(exception, batch, 0, batches); + List errors = Utils.handleBatchException(exception, batch, 0); Assert.assertEquals("Should have errors for all records", 2, errors.size()); Assert.assertEquals("Error message should be same", "Test exception", errors.get(0).getError()); @@ -262,7 +264,7 @@ public void testHandleBatchExceptionWithNonApiClientException() { RuntimeException exception = new RuntimeException("Unexpected error"); - List errors = Utils.handleBatchException(exception, batch, 0, batches); + List errors = Utils.handleBatchException(exception, batch, 0); Assert.assertEquals("Should have errors for all records", 2, errors.size()); Assert.assertEquals("Error message should match", "Unexpected error", errors.get(0).getError()); @@ -278,7 +280,7 @@ public void testHandleBatchExceptionWithNonZeroBatchNumber() { RuntimeException exception = new RuntimeException("Batch error"); - List errors = Utils.handleBatchException(exception, batch, 1, batches); + List errors = Utils.handleBatchException(exception, batch, 1); Assert.assertEquals("Should have errors for all records", 2, errors.size()); Assert.assertEquals("First error index should be offset", 2, errors.get(0).getIndex()); @@ -293,7 +295,7 @@ public void testHandleBatchExceptionWithNullResponseBody() { ApiClientApiException apiException = new ApiClientApiException("Bad Request", 400, null); Exception exception = new Exception("Test exception", apiException); - List errors = Utils.handleBatchException(exception, batch, 0, batches); + List errors = Utils.handleBatchException(exception, batch, 0); Assert.assertEquals("Should return empty list for null response body", 2, errors.size()); } @@ -507,7 +509,7 @@ public void testHandleBatchExceptionWithRecordsInResponseBody() { Exception exception = new Exception("Test exception", apiException); // Test the method - List errors = Utils.handleBatchException(exception, batch, 0, batches); + List errors = Utils.handleBatchException(exception, batch, 0); // Assertions Assert.assertNotNull("Errors list should not be null", errors); @@ -523,4 +525,304 @@ public void testHandleBatchExceptionWithRecordsInResponseBody() { Assert.assertEquals("Second error code should match", 500, errors.get(1).getCode()); Assert.assertEquals("Second error index should be 1", 1, errors.get(1).getIndex()); } + + @Test + public void testValidateDetokenizeRequestValidInput() throws SkyflowException { + ArrayList tokens = new ArrayList<>(); + tokens.add("token1"); + tokens.add("token2"); + + ArrayList groupRedactions = new ArrayList<>(); + groupRedactions.add(TokenGroupRedactions.builder() + .tokenGroupName("group1") + .redaction("MASK") + .build()); + + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(tokens) + .tokenGroupRedactions(groupRedactions) + .build(); + + Validations.validateDetokenizeRequest(request); // Should not throw an exception + } + + @Test + public void testValidateDetokenizeRequestNullRequest() { + try{ + Validations.validateDetokenizeRequest(null); + Assert.fail(EXCEPTIONNOTTHROWN); + } catch (SkyflowException e){ + assertEquals(e.getMessage(), ErrorMessage.DetokenizeRequestNull.getMessage()); + } + + } + + @Test + public void testValidateDetokenizeRequestEmptyTokens() { + try { + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(new ArrayList<>()) + .tokenGroupRedactions(new ArrayList<>()) + .build(); + + Validations.validateDetokenizeRequest(request); + + } catch (SkyflowException e){ + assertEquals(e.getMessage(), ErrorMessage.EmptyDetokenizeData.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequestNullTokenInList() { + ArrayList tokens = new ArrayList<>(); + tokens.add(null); + + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(tokens) + .tokenGroupRedactions(new ArrayList<>()) + .build(); + } + + @Test + public void testValidateDetokenizeRequestNullGroupRedactions() { + ArrayList tokens = new ArrayList<>(); + tokens.add("token1"); + + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(tokens) + .tokenGroupRedactions(null) + .build(); + try{ + Validations.validateDetokenizeRequest(request); + } catch (SkyflowException e){ + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateDetokenizeRequestNullTokenGroupRedaction() { + ArrayList tokens = new ArrayList<>(); + tokens.add("token1"); + + ArrayList groupRedactions = new ArrayList<>(); + groupRedactions.add(null); + + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(tokens) + .tokenGroupRedactions(groupRedactions) + .build(); + try{ + Validations.validateDetokenizeRequest(request); + } catch (SkyflowException e){ + Assert.assertEquals(ErrorMessage.NullTokenGroupRedactions.getMessage(), e.getMessage());// + } + } + + @Test + public void testValidateDetokenizeRequestEmptyTokenGroupName() { + ArrayList tokens = new ArrayList<>(); + tokens.add("token1"); + + ArrayList groupRedactions = new ArrayList<>(); + groupRedactions.add(TokenGroupRedactions.builder() + .tokenGroupName("") + .redaction("MASK") + .build()); + + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(tokens) + .tokenGroupRedactions(groupRedactions) + .build(); + + try{ + Validations.validateDetokenizeRequest(request); + } catch (SkyflowException e){ + assertEquals(ErrorMessage.NullTokenGroupNameInTokenGroup.getMessage(), e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequestEmptyRedaction() { + ArrayList tokens = new ArrayList<>(); + tokens.add("token1"); + + ArrayList groupRedactions = new ArrayList<>(); + groupRedactions.add(TokenGroupRedactions.builder() + .tokenGroupName("group1") + .redaction("") + .build()); + + DetokenizeRequest request = DetokenizeRequest.builder() + .tokens(tokens) + .tokenGroupRedactions(groupRedactions) + .build(); + + try { + Validations.validateDetokenizeRequest(request); + } catch (SkyflowException e){ + assertEquals(ErrorMessage.NullRedactionInTokenGroup.getMessage(), e.getMessage()); + } + } + + @Test + public void testValidateInsertRequestNullTable() { + ArrayList> values = new ArrayList<>(); + HashMap valueMap = new HashMap<>(); + valueMap.put("key1", "value1"); + values.add(valueMap); + + InsertRequest request = InsertRequest.builder() + .table(null) + .values(values) + .build(); + + try { + Validations.validateInsertRequest(request); + } catch (SkyflowException e) { + assertEquals(ErrorMessage.TableKeyError.getMessage(), e.getMessage()); // Replace with the actual error message + } + } + + @Test + public void testValidateInsertRequestEmptyTable() { + ArrayList> values = new ArrayList<>(); + HashMap valueMap = new HashMap<>(); + valueMap.put("key1", "value1"); + values.add(valueMap); + + InsertRequest request = InsertRequest.builder() + .table("") + .values(values) + .build(); + + try { + Validations.validateInsertRequest(request); + } catch (SkyflowException e) { + assertEquals(ErrorMessage.EmptyTable.getMessage(), e.getMessage()); // Replace with the actual error message + } + } + + @Test + public void testValidateInsertRequestNullValues() { + InsertRequest request = InsertRequest.builder() + .table("testTable") + .values(null) + .build(); + + try { + Validations.validateInsertRequest(request); + } catch (SkyflowException e) { + assertEquals(ErrorMessage.ValuesKeyError.getMessage(), e.getMessage()); // Replace with the actual error message + } + } + + @Test + public void testValidateInsertRequestEmptyValues() { + InsertRequest request = InsertRequest.builder() + .table("testTable") + .values(new ArrayList<>()) + .build(); + + try { + Validations.validateInsertRequest(request); + } catch (SkyflowException e) { + assertEquals(ErrorMessage.EmptyValues.getMessage(), e.getMessage()); // Replace with the actual error message + } + } + + + @Test + public void testFormatDetokenizeResponseValidResponse() { + // Arrange + List responseObjectsGen = new ArrayList<>(); + Map tokens = new HashMap<>(); + tokens.put("token1", "value1"); + tokens.put("token2", "value2"); + com.skyflow.generated.rest.types.DetokenizeResponseObject object = com.skyflow.generated.rest.types.DetokenizeResponseObject.builder().token("token1").value("value1").tokenGroupName("demo").build(); + responseObjectsGen.add(object); + responseObjectsGen.add(object); + + com.skyflow.generated.rest.types.DetokenizeResponse response = com.skyflow.generated.rest.types.DetokenizeResponse.builder().response(Optional.of(responseObjectsGen)).build(); + + int batch = 0; + int batchSize = 2; + + // Act + DetokenizeResponse result = Utils.formatDetokenizeResponse(response, batch, batchSize); + + // Assert + Assert.assertNotNull(result); + Assert.assertEquals(2, result.getSuccess().size()); + Assert.assertEquals(0, result.getErrors().size()); + } + + @Test + public void testFormatDetokenizeResponseResponseWithErrors() { + + + List responseObjectsGen = new ArrayList<>(); + com.skyflow.generated.rest.types.DetokenizeResponseObject object = com.skyflow.generated.rest.types.DetokenizeResponseObject.builder().error("Error occurred").httpCode(400).build(); + com.skyflow.generated.rest.types.DetokenizeResponseObject object2 = com.skyflow.generated.rest.types.DetokenizeResponseObject.builder().token("token1").tokenGroupName("demo").value("hello").build(); + + responseObjectsGen.add(object); + responseObjectsGen.add(object2); + + com.skyflow.generated.rest.types.DetokenizeResponse response = com.skyflow.generated.rest.types.DetokenizeResponse.builder().response(Optional.of(responseObjectsGen)).build(); + + int batch = 1; + int batchSize = 2; + + // Act + DetokenizeResponse result = Utils.formatDetokenizeResponse(response, batch, batchSize); + + // Assert + assertEquals(1, result.getSuccess().size()); + assertEquals(1, result.getErrors().size()); + assertEquals("Error occurred", result.getErrors().get(0).getError()); + } + + @Test + public void testFormatDetokenizeResponse_NullResponse() { + // Act + DetokenizeResponse result = Utils.formatDetokenizeResponse(null, 0, 2); + + // Assert + Assert.assertNull(result); + } + + public static List createDetokenizeBatches(com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest request, int batchSize) { + List detokenizeRequests = new ArrayList<>(); + List tokens = request.getTokens().get(); + + for (int i = 0; i < tokens.size(); i += batchSize) { + // Create a sublist for the current batch + List batchTokens = tokens.subList(i, Math.min(i + batchSize, tokens.size())); + List tokenGroupRedactions = null; + if (request.getTokenGroupRedactions().isPresent() && !request.getTokenGroupRedactions().get().isEmpty() && i < request.getTokenGroupRedactions().get().size()) { + tokenGroupRedactions = request.getTokenGroupRedactions().get().subList(i, Math.min(i + batchSize, request.getTokenGroupRedactions().get().size())); } + // Build a new DetokenizeRequest for the current batch + com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest batchRequest = com.skyflow.generated.rest.resources.recordservice.requests.DetokenizeRequest.builder() + .vaultId(request.getVaultId()) + .tokens(new ArrayList<>(batchTokens)) + .tokenGroupRedactions(tokenGroupRedactions) + .build(); + + detokenizeRequests.add(batchRequest); + } + + return detokenizeRequests; + } + + + private DetokenizeResponseObject createResponseObject(String token, String value, String groupName, String error, Integer httpCode) { + DetokenizeResponseObject responseObject = new DetokenizeResponseObject( + 0, + String.valueOf(Optional.ofNullable(token)), + Optional.ofNullable(value), + String.valueOf(Optional.ofNullable(groupName)), + String.valueOf(Optional.ofNullable(error)), + null + );return responseObject; + } + } \ No newline at end of file diff --git a/v3/test/java/com/skyflow/vault/data/InsertTests.java b/v3/test/java/com/skyflow/vault/data/InsertTests.java index af8b050f..7ea17d89 100644 --- a/v3/test/java/com/skyflow/vault/data/InsertTests.java +++ b/v3/test/java/com/skyflow/vault/data/InsertTests.java @@ -255,11 +255,9 @@ public void testInsertErrorResponse() { errorList.add(error); InsertResponse response1 = new InsertResponse(successList, errorList); - System.out.println("response: " + response1.getErrors()); String responseString = "{\"success\":[{\"index\":0,\"skyflow_id\":\"id\",\"data\":{\"test_column_1\":\"test_value_1\"}}],\"errors\":[{\"index\":1,\"error\":\"Bad Request\",\"code\":400}]}"; Assert.assertEquals(1, response1.getSuccess().size()); Assert.assertEquals(1, response1.getErrors().size()); - System.out.println("response: " + response1.toString()); Assert.assertEquals(responseString, response1.toString()); } catch (Exception e) { Assert.fail(INVALID_EXCEPTION_THROWN);