diff --git a/.github/workflows/flowvault-internal-release.yml b/.github/workflows/flowvault-internal-release.yml deleted file mode 100644 index b1e65c12..00000000 --- a/.github/workflows/flowvault-internal-release.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Publish v3 module to the JFROG Artifactory -on: - push: - tags-ignore: - - '*.*' - paths-ignore: - - "*.md" - branches: - - v3-release/* - -jobs: - build-and-deploy-v3: - uses: ./.github/workflows/shared-build-and-deploy.yml - with: - ref: ${{ github.ref_name }} - server-id: central - profile: jfrog - tag: 'internal' - module: 'flowvault' - secrets: - server-username: ${{ secrets.ARTIFACTORY_USERNAME }} - server-password: ${{ secrets.ARTIFACTORY_PASSWORD }} - gpg-key: ${{ secrets.JFROG_GPG_KEY }} - gpg-passphrase: ${{ secrets.JFROG_GPG_PASSPHRASE }} - skyflow-credentials: ${{ secrets.SKYFLOW_CREDENTIALS }} - test-expired-token: ${{ secrets.TEST_EXPIRED_TOKEN }} - test-reusable-token: ${{ secrets.TEST_REUSABLE_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/internal-release.yml b/.github/workflows/internal-release.yml index 70a33281..a246a6e3 100644 --- a/.github/workflows/internal-release.yml +++ b/.github/workflows/internal-release.yml @@ -1,4 +1,4 @@ -name: Publish v2 module to the JFROG Artifactory +name: Publish module to the JFROG Artifactory on: push: tags-ignore: @@ -7,21 +7,37 @@ on: - "*.md" branches: - release/* + - flowvault-release/* jobs: - build-and-deploy-v2: + resolve-module: + runs-on: ubuntu-latest + outputs: + module: ${{ steps.set-module.outputs.module }} + steps: + - name: Resolve module from branch name + id: set-module + run: | + if [[ "${{ github.ref_name }}" == flowvault-release/* ]]; then + echo "module=flowvault" >> "$GITHUB_OUTPUT" + else + echo "module=v2" >> "$GITHUB_OUTPUT" + fi + + build-and-deploy: + needs: resolve-module uses: ./.github/workflows/shared-build-and-deploy.yml with: ref: ${{ github.ref_name }} server-id: central profile: jfrog tag: 'internal' - module: 'v2' + module: ${{ needs.resolve-module.outputs.module }} secrets: server-username: ${{ secrets.ARTIFACTORY_USERNAME }} server-password: ${{ secrets.ARTIFACTORY_PASSWORD }} gpg-key: ${{ secrets.JFROG_GPG_KEY }} gpg-passphrase: ${{ secrets.JFROG_GPG_PASSPHRASE }} - skyflow-credentials: ${{ secrets.SKYFLOW_CREDENTIALS }} >> .env - test-expired-token: ${{ secrets.TEST_EXPIRED_TOKEN }} >> .env - test-reusable-token: ${{ secrets.TEST_REUSABLE_TOKEN }} >> .env + skyflow-credentials: ${{ secrets.SKYFLOW_CREDENTIALS }} + test-expired-token: ${{ secrets.TEST_EXPIRED_TOKEN }} + test-reusable-token: ${{ secrets.TEST_REUSABLE_TOKEN }} diff --git a/common/src/main/java/com/skyflow/BaseSkyflow.java b/common/src/main/java/com/skyflow/BaseSkyflow.java index 2daff37b..7e7554ff 100644 --- a/common/src/main/java/com/skyflow/BaseSkyflow.java +++ b/common/src/main/java/com/skyflow/BaseSkyflow.java @@ -16,7 +16,7 @@ import java.util.Map; -abstract class BaseSkyflow implements ISkyflow { +abstract class BaseSkyflow implements ISkyflow { protected final BaseSkyflowClientBuilder builder; protected BaseSkyflow(BaseSkyflowClientBuilder builder) { diff --git a/common/src/main/java/com/skyflow/logs/ErrorLogs.java b/common/src/main/java/com/skyflow/logs/ErrorLogs.java index 0028a5c6..b77eee2c 100644 --- a/common/src/main/java/com/skyflow/logs/ErrorLogs.java +++ b/common/src/main/java/com/skyflow/logs/ErrorLogs.java @@ -127,6 +127,7 @@ public enum ErrorLogs { EMPTY_DELETE_TOKENS_DATA("Invalid %s1 request. Delete tokens data can not be empty."), EMPTY_OR_NULL_TOKEN_IN_DELETE_TOKENS_DATA("Invalid %s1 request. Token can not be null or empty in delete tokens data at index %s2."), DELETE_TOKENS_SIZE_EXCEED("Maximum number of tokens exceeded. The limit is 10000."), + DELETE_TOKENS_REQUEST_REJECTED("DeleteTokens request resulted in failure."), // invoke connection interface INVOKE_CONNECTION_INVALID_CONNECTION_URL("Invalid %s1 request. Connection URL is not a valid URL."), diff --git a/common/src/main/java/com/skyflow/logs/WarningLogs.java b/common/src/main/java/com/skyflow/logs/WarningLogs.java index 5f80a728..1eb1bcd8 100644 --- a/common/src/main/java/com/skyflow/logs/WarningLogs.java +++ b/common/src/main/java/com/skyflow/logs/WarningLogs.java @@ -4,7 +4,11 @@ public enum WarningLogs { INVALID_BATCH_SIZE_PROVIDED("Invalid value for batch size provided, switching to default value."), INVALID_CONCURRENCY_LIMIT_PROVIDED("Invalid value for concurrency limit provided, switching to default value."), BATCH_SIZE_EXCEEDS_MAX_LIMIT("Provided batch size exceeds the maximum limit, switching to max limit."), - CONCURRENCY_EXCEEDS_MAX_LIMIT("Provided concurrency limit exceeds the maximum limit, switching to max limit.") + CONCURRENCY_EXCEEDS_MAX_LIMIT("Provided concurrency limit exceeds the maximum limit, switching to max limit."), + EMPTY_DELETE_TOKENS_RESPONSE("DeleteTokens response did not include any token results."), + INCOMPLETE_DELETE_TOKENS_RESPONSE("DeleteTokens response did not account for all requested tokens."), + EMPTY_TOKENIZE_RESPONSE("Tokenize response did not include any record results."), + INCOMPLETE_TOKENIZE_RESPONSE("Tokenize response did not account for all requested records.") ; private final String log; diff --git a/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java b/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java index 317847e5..60c07f6d 100644 --- a/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java +++ b/common/src/main/java/com/skyflow/serviceaccount/util/BearerToken.java @@ -65,7 +65,7 @@ private static V1GetAuthTokenResponse generateBearerTokenFromCredentials( } finally { try { reader.close(); } catch (IOException ignored) {} } - } catch (JsonSyntaxException e) { + } catch (JsonSyntaxException | IllegalStateException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_FILE_FORMAT.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), BaseUtils.parameterizedString( ErrorMessage.FileInvalidJson.getMessage(), credentialsFile.getPath())); @@ -87,7 +87,7 @@ private static V1GetAuthTokenResponse generateBearerTokenFromCredentialString( } JsonObject serviceAccountCredentials = JsonParser.parseString(credentials).getAsJsonObject(); return getBearerTokenFromCredentials(serviceAccountCredentials, context, roles); - } catch (JsonSyntaxException e) { + } catch (JsonSyntaxException | IllegalStateException e) { LogUtil.printErrorLog(ErrorLogs.INVALID_CREDENTIALS_STRING_FORMAT.getLog()); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.CredentialsStringInvalidJson.getMessage()); diff --git a/flowvault/src/main/java/com/skyflow/utils/Utils.java b/flowvault/src/main/java/com/skyflow/utils/Utils.java index 670aa05b..cb69453f 100644 --- a/flowvault/src/main/java/com/skyflow/utils/Utils.java +++ b/flowvault/src/main/java/com/skyflow/utils/Utils.java @@ -14,6 +14,7 @@ import com.skyflow.generated.rest.types.*; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; +import com.skyflow.logs.WarningLogs; import com.skyflow.serviceaccount.util.BearerToken; import com.skyflow.serviceaccount.util.Token; import com.skyflow.utils.logger.LogUtil; @@ -165,7 +166,7 @@ public static InsertResponse buildInsertResponse(V1InsertResponse res) { ArrayList> insertedFields = new ArrayList<>(); ArrayList> errors = new ArrayList<>(); - if (res.getRecords().isPresent()) { + if (res != null && res.getRecords().isPresent()) { for (V1RecordResponseObject record : res.getRecords().get()) { if (record.getError().isPresent()) { HashMap errorRecord = new HashMap<>(); @@ -215,7 +216,7 @@ public static DetokenizeResponse buildDetokenizeResponse(V1FlowDetokenizeRespons ArrayList detokenizedFields = new ArrayList<>(); ArrayList errors = new ArrayList<>(); - if (res.getResponse().isPresent()) { + if (res != null && res.getResponse().isPresent()) { for (V1FlowDetokenizeResponseObject record : res.getResponse().get()) { String token = record.getToken().orElse(null); String tokenGroupName = record.getTokenGroupName().orElse(null); @@ -230,4 +231,99 @@ public static DetokenizeResponse buildDetokenizeResponse(V1FlowDetokenizeRespons } return new DetokenizeResponse(detokenizedFields, errors); } + + public static com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest getDeleteTokensRequestBody(DeleteTokensRequest request, String vaultid) { + return com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest.builder() + .vaultId(vaultid) + .tokens(request.getTokens()) + .build(); + } + private static String extractRequestId(Map> headers) { + if (headers == null) return null; + List ids = headers.get(BaseConstants.REQUEST_ID_HEADER_KEY); + return (ids == null || ids.isEmpty()) ? null : ids.get(0); + } + + public static DeleteTokensResponse buildDeleteTokensResponse(V1FlowDeleteTokenResponse res, Map> headers, int requestedTokenCount) { + ArrayList deletedTokens = new ArrayList<>(); + ArrayList> errors = new ArrayList<>(); + String requestId = extractRequestId(headers); + if (res != null && res.getTokens().isPresent()) { + for (V1DeleteTokenResponseObject record : res.getTokens().get()) { + if (record.getError().isPresent()) { + HashMap errorRecord = new HashMap<>(); + errorRecord.put("error", record.getError().get()); + record.getHttpCode().ifPresent(httpCode -> errorRecord.put("httpCode", httpCode)); + errorRecord.put("requestId", requestId); + errors.add(errorRecord); + } else { + record.getValue().ifPresent(deletedTokens::add); + } + } + if (deletedTokens.size() + errors.size() != requestedTokenCount) { + LogUtil.printWarningLog(WarningLogs.INCOMPLETE_DELETE_TOKENS_RESPONSE.getLog()); + } + } else { + LogUtil.printWarningLog(WarningLogs.EMPTY_DELETE_TOKENS_RESPONSE.getLog()); + } + return new DeleteTokensResponse(deletedTokens, errors); + } + + public static com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest getTokenizeRequestBody(TokenizeRequest request, String vaultid) { + List dataList = new ArrayList<>(); + for (TokenizeRecord record : request.getData()) { + V1FlowTokenizeRequestObject obj = V1FlowTokenizeRequestObject.builder() + .value(record.getValue()) + .tokenGroupNames(record.getTokenGroupNames()) + .build(); + dataList.add(obj); + } + return com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest.builder() + .vaultId(vaultid) + .data(dataList) + .build(); + } + + public static TokenizeResponse buildTokenizeResponse(V1FlowTokenizeResponse res, Map> headers, int requestedRecordCount) { + List tokenizedData = new ArrayList<>(); + ArrayList> errors = new ArrayList<>(); + String requestId = extractRequestId(headers); + if (res != null && res.getResponse().isPresent()) { + List records = res.getResponse().get(); + int indexNumber = 0; + for (V1FlowTokenizeResponseObject record : records) { + Object value = record.getValue().orElse(null); + TokenizeData tokenizeData = new TokenizeData(value, indexNumber); + boolean hasAnySuccess = false; + if (record.getTokens().isPresent()) { + for (FlowTokenizeResponseObjectToken tokenObj : record.getTokens().get()) { + if (tokenObj.getError().isPresent()) { + HashMap errorRecord = new HashMap<>(); + errorRecord.put("error", tokenObj.getError().get()); + tokenObj.getHttpCode().ifPresent(httpCode -> errorRecord.put("httpCode", httpCode)); + tokenObj.getTokenGroupName().ifPresent(name -> errorRecord.put("tokenGroupName", name)); + errorRecord.put("index", indexNumber); + errorRecord.put("requestId", requestId); + errors.add(errorRecord); + } else if (tokenObj.getTokenGroupName().isPresent() && tokenObj.getToken().isPresent()) { + tokenizeData.addToken(tokenObj.getTokenGroupName().get(), tokenObj.getToken().get()); + hasAnySuccess = true; + } + } + } + if (hasAnySuccess) { + tokenizedData.add(tokenizeData); + } + indexNumber++; + } + if (indexNumber != requestedRecordCount) { + LogUtil.printWarningLog(WarningLogs.INCOMPLETE_TOKENIZE_RESPONSE.getLog()); + } + } else { + LogUtil.printWarningLog(WarningLogs.EMPTY_TOKENIZE_RESPONSE.getLog()); + } + TokenizeResponse tokenizeResponse = new TokenizeResponse(errors); + tokenizeResponse.setTokenizedData(tokenizedData); + return tokenizeResponse; + } } diff --git a/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java b/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java index c94fe05b..98dfdf64 100644 --- a/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java +++ b/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java @@ -165,10 +165,7 @@ public static void validateInsertRequest(InsertRequest insertRequest) throws Sky )); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRecords.getMessage()); } -// else if (records.size() > 10000) { -// LogUtil.printErrorLog(ErrorLogs.RECORD_SIZE_EXCEED.getLog()); -// throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.RecordSizeExceedError.getMessage()); -// } + for (InsertRecord record : records) { if(record == null){ LogUtil.printErrorLog(Utils.parameterizedString( @@ -273,13 +270,10 @@ public static void validateDetokenizeRequest(DetokenizeRequest request) throws S )); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyDetokenizeData.getMessage()); } -// if (tokens.size() > 10000) { -// LogUtil.printErrorLog(ErrorLogs.TOKENS_SIZE_EXCEED.getLog()); -// throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.TokensSizeExceedError.getMessage()); -// } + for (int index = 0; index < tokens.size(); index++) { DetokenizeData token = tokens.get(index); - if (token != null && token.getToken().isEmpty() && token.getToken().trim().isEmpty()) { + if (token == null || token.getToken() == null || token.getToken().trim().isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_OR_NULL_TOKEN_IN_DETOKENIZE_DATA.getLog(), InterfaceName.DETOKENIZE.getName(), @@ -307,7 +301,84 @@ public static void validateDetokenizeRequest(DetokenizeRequest request) throws S } } } + } + public static void validateDeleteTokensRequest(DeleteTokensRequest request) throws SkyflowException { + if (request == null) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.DELETE_TOKENS_REQUEST_NULL.getLog(), InterfaceName.DELETE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.DeleteTokensRequestNull.getMessage()); + } + List tokens = request.getTokens(); + if (tokens == null || tokens.isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_DELETE_TOKENS_DATA.getLog(), InterfaceName.DELETE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyDeleteTokensData.getMessage()); + } + + for (int index = 0; index < tokens.size(); index++) { + String token = tokens.get(index); + if (token == null || token.trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_OR_NULL_TOKEN_IN_DELETE_TOKENS_DATA.getLog(), + InterfaceName.DELETE.getName(), + String.valueOf(index))); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokenInDeleteTokensData.getMessage()); + } + } + } + + public static void validateTokenizeRequest(TokenizeRequest request) throws SkyflowException { + if (request == null) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.TOKENIZE_REQUEST_NULL.getLog(), InterfaceName.TOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.TokenizeRequestNull.getMessage()); + } + ArrayList data = request.getData(); + if (data == null || data.isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_TOKENIZE_DATA.getLog(), InterfaceName.TOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokenizeData.getMessage()); + } + for (int index = 0; index < data.size(); index++) { + TokenizeRecord record = data.get(index); + if (record == null) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.TOKENIZE_RECORD_NULL.getLog(), InterfaceName.TOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.TokenizeRecordNull.getMessage()); + } + Object value = record.getValue(); + boolean isInvalidValue = value == null || (value instanceof String && ((String) value).trim().isEmpty()); + if (isInvalidValue) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_VALUE_IN_TOKENIZE_RECORD.getLog(), InterfaceName.TOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyValueInTokenizeRecord.getMessage()); + } + List tokenGroupNames = record.getTokenGroupNames(); + if (tokenGroupNames == null || tokenGroupNames.isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_TOKEN_GROUP_NAMES_IN_TOKENIZE_RECORD.getLog(), InterfaceName.TOKENIZE.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokenGroupNamesInTokenizeRecord.getMessage()); + } + for (int groupIndex = 0; groupIndex < tokenGroupNames.size(); groupIndex++) { + String groupName = tokenGroupNames.get(groupIndex); + if (groupName == null || groupName.trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_TOKEN_GROUP_NAME_IN_TOKENIZE_RECORD.getLog(), + InterfaceName.TOKENIZE.getName(), + String.valueOf(groupIndex))); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokenGroupNameInTokenizeRecord.getMessage()); + } + } + } + } } diff --git a/flowvault/src/main/java/com/skyflow/vault/controller/VaultController.java b/flowvault/src/main/java/com/skyflow/vault/controller/VaultController.java index 24ba2b3f..28c29839 100644 --- a/flowvault/src/main/java/com/skyflow/vault/controller/VaultController.java +++ b/flowvault/src/main/java/com/skyflow/vault/controller/VaultController.java @@ -8,8 +8,12 @@ import com.skyflow.config.VaultConfig; import com.skyflow.errors.SkyflowException; 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.RequestOptions; import com.skyflow.generated.rest.resources.flowservice.requests.V1InsertRequest; +import com.skyflow.generated.rest.types.V1FlowDeleteTokenResponse; +import com.skyflow.generated.rest.types.V1FlowTokenizeResponse; import com.skyflow.generated.rest.types.V1InsertResponse; import com.skyflow.logs.ErrorLogs; import com.skyflow.logs.InfoLogs; @@ -49,6 +53,9 @@ public InsertResponse insert(InsertRequest request) throws SkyflowException { String bodyString = gson.toJson(e.body()); LogUtil.printErrorLog(ErrorLogs.INSERT_RECORDS_REJECTED.getLog()); throw new SkyflowException(e.statusCode(), e, e.headers(), bodyString); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.INSERT_RECORDS_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); } } @@ -68,17 +75,58 @@ public DetokenizeResponse detokenize(DetokenizeRequest request) throws SkyflowEx String bodyString = gson.toJson(e.body()); LogUtil.printErrorLog(ErrorLogs.DETOKENIZE_REQUEST_REJECTED.getLog()); throw new SkyflowException(e.statusCode(), e, e.headers(), bodyString); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.DETOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); } } - public TokenizeResponse tokenize(TokenizeRequest request){ - LogUtil.printInfoLog(InfoLogs.TOKENIZE_TRIGGERED.getLog()); + public TokenizeResponse tokenize(TokenizeRequest request) throws SkyflowException { try { + LogUtil.printInfoLog(InfoLogs.TOKENIZE_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATING_TOKENIZE_REQUEST.getLog()); + Validations.validateTokenizeRequest(request); + setBearerToken(); - } catch (Exception e) { - throw new RuntimeException(e); + com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest requestTokenize = getTokenizeRequestBody(request, this.getVaultConfig().getVaultId()); + ApiClientHttpResponse response = this.getRecordsApi().withRawResponse().tokenize(requestTokenize, buildRequestOptions()); + LogUtil.printInfoLog(InfoLogs.TOKENIZE_REQUEST_RESOLVED.getLog()); + + TokenizeResponse tokenizeResponse = buildTokenizeResponse(response.body(), response.headers(), request.getData().size()); + LogUtil.printInfoLog(InfoLogs.TOKENIZE_SUCCESS.getLog()); + return tokenizeResponse; + } catch (ApiClientApiException e) { + String bodyString = gson.toJson(e.body()); + LogUtil.printErrorLog(ErrorLogs.TOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.statusCode(), e, e.headers(), bodyString); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.TOKENIZE_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); + } + } + + public DeleteTokensResponse deleteTokens(DeleteTokensRequest request) throws SkyflowException { + try { + LogUtil.printInfoLog(InfoLogs.DELETE_TOKENS_TRIGGERED.getLog()); + LogUtil.printInfoLog(InfoLogs.VALIDATE_DELETE_TOKENS_REQUEST.getLog()); + Validations.validateDeleteTokensRequest(request); + setBearerToken(); + + com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest requestDeleteTokens = getDeleteTokensRequestBody(request, this.getVaultConfig().getVaultId()); + ApiClientHttpResponse response = this.getRecordsApi().withRawResponse().deletetoken(requestDeleteTokens, buildRequestOptions()); + LogUtil.printInfoLog(InfoLogs.DELETE_TOKENS_REQUEST_RESOLVED.getLog()); + + DeleteTokensResponse deleteTokensResponse = buildDeleteTokensResponse(response.body(), response.headers(), request.getTokens().size()); + LogUtil.printInfoLog(InfoLogs.DELETE_TOKENS_SUCCESS.getLog()); + return deleteTokensResponse; + } catch (ApiClientApiException e) { + String bodyString = gson.toJson(e.body()); + LogUtil.printErrorLog(ErrorLogs.DELETE_TOKENS_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.statusCode(), e, e.headers(), bodyString); + } catch (ApiClientException e) { + LogUtil.printErrorLog(ErrorLogs.DELETE_TOKENS_REQUEST_REJECTED.getLog()); + throw new SkyflowException(e.getMessage()); } - return null; } diff --git a/flowvault/src/main/java/com/skyflow/vault/data/DeleteTokensRequest.java b/flowvault/src/main/java/com/skyflow/vault/data/DeleteTokensRequest.java new file mode 100644 index 00000000..ea8e9a7a --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/vault/data/DeleteTokensRequest.java @@ -0,0 +1,34 @@ +package com.skyflow.vault.data; + +import java.util.List; + +public class DeleteTokensRequest { + private final DeleteTokensRequestBuilder builder; + + private DeleteTokensRequest(DeleteTokensRequestBuilder builder) { + this.builder = builder; + } + + public static DeleteTokensRequestBuilder builder() { + return new DeleteTokensRequestBuilder(); + } + + public List getTokens() { + return this.builder.tokens; + } + + public static final class DeleteTokensRequestBuilder { + private List tokens; + + private DeleteTokensRequestBuilder() {} + + public DeleteTokensRequestBuilder tokens(List tokens) { + this.tokens = tokens; + return this; + } + + public DeleteTokensRequest build() { + return new DeleteTokensRequest(this); + } + } +} diff --git a/flowvault/src/main/java/com/skyflow/vault/data/DeleteTokensResponse.java b/flowvault/src/main/java/com/skyflow/vault/data/DeleteTokensResponse.java new file mode 100644 index 00000000..7f621d8d --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/vault/data/DeleteTokensResponse.java @@ -0,0 +1,22 @@ +package com.skyflow.vault.data; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DeleteTokensResponse { + private final List tokens; + private final ArrayList> errors; + + public DeleteTokensResponse(List tokens, ArrayList> errors) { + this.tokens = tokens; + this.errors = errors; + } + + public List getTokens() { + return tokens; + } + public ArrayList> getErrors(){ + return this.errors; + } +} diff --git a/flowvault/src/main/java/com/skyflow/vault/data/TokenizeData.java b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeData.java new file mode 100644 index 00000000..6f04ed8d --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeData.java @@ -0,0 +1,36 @@ +package com.skyflow.vault.data; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class TokenizeData { + private Object value; + private Map tokens; + private int index; + + public TokenizeData(Object value, int index){ + this.value = value; + this.tokens = new LinkedHashMap<>(); + this.index = index; + } + public void addToken(String tokenGroupName, String token) { + this.tokens.put(tokenGroupName, token); + } + + public Object getValue() { + return value; + } + + public Map getTokens() { + return tokens; + } + + @Override + public String toString() { + Gson gson = new GsonBuilder().create(); + return gson.toJson(this); + } +} diff --git a/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRecord.java b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRecord.java new file mode 100644 index 00000000..faa676df --- /dev/null +++ b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRecord.java @@ -0,0 +1,44 @@ +package com.skyflow.vault.data; + +import java.util.List; + +public class TokenizeRecord { + private final TokenizeRecordBuilder builder; + + private TokenizeRecord(TokenizeRecordBuilder builder) { + this.builder = builder; + } + + public static TokenizeRecordBuilder builder() { + return new TokenizeRecordBuilder(); + } + + public Object getValue() { + return this.builder.value; + } + + public List getTokenGroupNames() { + return this.builder.tokenGroupNames; + } + + public static final class TokenizeRecordBuilder { + private Object value; + private List tokenGroupNames; + + private TokenizeRecordBuilder() {} + + public TokenizeRecordBuilder value(Object value) { + this.value = value; + return this; + } + + public TokenizeRecordBuilder tokenGroupNames(List tokenGroupNames) { + this.tokenGroupNames = tokenGroupNames; + return this; + } + + public TokenizeRecord build() { + return new TokenizeRecord(this); + } + } +} diff --git a/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRequest.java b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRequest.java index f6b6cf99..a6ef8072 100644 --- a/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRequest.java +++ b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeRequest.java @@ -1,4 +1,34 @@ package com.skyflow.vault.data; +import java.util.ArrayList; + public class TokenizeRequest { + private final TokenizeRequestBuilder builder; + + private TokenizeRequest(TokenizeRequestBuilder builder) { + this.builder = builder; + } + + public static TokenizeRequestBuilder builder() { + return new TokenizeRequestBuilder(); + } + + public ArrayList getData() { + return this.builder.data; + } + + public static final class TokenizeRequestBuilder { + private ArrayList data; + + private TokenizeRequestBuilder() {} + + public TokenizeRequestBuilder data(ArrayList data) { + this.data = data; + return this; + } + + public TokenizeRequest build() { + return new TokenizeRequest(this); + } + } } diff --git a/flowvault/src/main/java/com/skyflow/vault/data/TokenizeResponse.java b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeResponse.java index 403ad53c..a4b47351 100644 --- a/flowvault/src/main/java/com/skyflow/vault/data/TokenizeResponse.java +++ b/flowvault/src/main/java/com/skyflow/vault/data/TokenizeResponse.java @@ -1,4 +1,26 @@ package com.skyflow.vault.data; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + public class TokenizeResponse { + private List tokenizedData; + private final ArrayList> errors; + + public TokenizeResponse(ArrayList> errors) { + this.errors = errors; + } + public TokenizeResponse(){ + this.errors = new ArrayList<>(); + } + public List getTokenizedData(){ + return this.tokenizedData; + } + public void setTokenizedData(List tokenizedData) { + this.tokenizedData = tokenizedData; + } + public ArrayList> getErrors(){ + return this.errors; + } } diff --git a/flowvault/src/test/java/com/skyflow/SkyflowTests.java b/flowvault/src/test/java/com/skyflow/SkyflowTests.java new file mode 100644 index 00000000..fc215e2b --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/SkyflowTests.java @@ -0,0 +1,168 @@ +package com.skyflow; + +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.enums.Env; +import com.skyflow.enums.LogLevel; +import com.skyflow.errors.SkyflowException; +import com.skyflow.vault.controller.VaultController; +import org.junit.Assert; +import org.junit.Test; + +public class SkyflowTests { + private static final String EXCEPTION_NOT_THROWN = "Should have thrown an exception"; + private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception"; + + private static VaultConfig buildConfig(String vaultId, String clusterId) { + VaultConfig config = new VaultConfig(); + config.setVaultId(vaultId); + config.setClusterId(clusterId); + config.setEnv(Env.DEV); + return config; + } + + // ── addVaultConfig ──────────────────────────────────────────────────────── + + @Test + public void testAddVaultConfig_success() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")).build(); + Assert.assertEquals("vault1", client.getVaultConfig("vault1").getVaultId()); + } + + @Test + public void testAddVaultConfig_duplicateVaultIdThrows() throws SkyflowException { + Skyflow.SkyflowClientBuilder builder = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")); + try { + builder.addVaultConfig(buildConfig("vault1", "cluster2")); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testAddVaultConfig_invalidConfigThrows() { + VaultConfig config = new VaultConfig(); + // no vaultId set + try { + Skyflow.builder().addVaultConfig(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── updateVaultConfig ───────────────────────────────────────────────────── + + @Test + public void testUpdateVaultConfig_success() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")).build(); + + VaultConfig update = new VaultConfig(); + update.setVaultId("vault1"); + update.setClusterId("cluster2"); + update.setEnv(Env.PROD); + client.updateVaultConfig(update); + + Assert.assertEquals("cluster2", client.getVaultConfig("vault1").getClusterId()); + Assert.assertEquals(Env.PROD, client.getVaultConfig("vault1").getEnv()); + } + + @Test + public void testUpdateVaultConfig_nonExistentVaultIdThrows() { + try { + Skyflow.builder().updateVaultConfig(buildConfig("vault-unknown", "cluster1")); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── removeVaultConfig ───────────────────────────────────────────────────── + + @Test + public void testRemoveVaultConfig_success() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")).build(); + client.removeVaultConfig("vault1"); + Assert.assertNull(client.getVaultConfig("vault1")); + } + + @Test + public void testRemoveVaultConfig_nonExistentVaultIdThrows() { + try { + Skyflow.builder().removeVaultConfig("vault-unknown"); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── addSkyflowCredentials ───────────────────────────────────────────────── + + @Test + public void testAddSkyflowCredentials_success() throws SkyflowException { + Credentials credentials = new Credentials(); + credentials.setToken("some-token"); + Skyflow client = Skyflow.builder() + .addVaultConfig(buildConfig("vault1", "cluster1")) + .addSkyflowCredentials(credentials) + .build(); + Assert.assertNotNull(client); + } + + @Test + public void testAddSkyflowCredentials_invalidCredentialsThrows() { + Credentials credentials = new Credentials(); + credentials.setApiKey("not-a-valid-api-key"); + try { + Skyflow.builder().addSkyflowCredentials(credentials); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── setLogLevel / getLogLevel ───────────────────────────────────────────── + + @Test + public void testSetLogLevel_updatesLogLevel() throws SkyflowException { + Skyflow client = Skyflow.builder() + .addVaultConfig(buildConfig("vault1", "cluster1")) + .setLogLevel(LogLevel.DEBUG) + .build(); + Assert.assertEquals(LogLevel.DEBUG, client.getLogLevel()); + } + + @Test + public void testGetLogLevel_defaultsToError() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")).build(); + Assert.assertEquals(LogLevel.ERROR, client.getLogLevel()); + } + + // ── vault() ─────────────────────────────────────────────────────────────── + + @Test + public void testVault_returnsVaultControllerWhenConfigured() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")).build(); + VaultController controller = client.vault(); + Assert.assertNotNull(controller); + } + + @Test + public void testVault_throwsWhenNoConfigExists() { + try { + Skyflow.builder().build().vault(); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── getVaultConfig ──────────────────────────────────────────────────────── + + @Test + public void testGetVaultConfig_returnsNullForUnknownVaultId() throws SkyflowException { + Skyflow client = Skyflow.builder().addVaultConfig(buildConfig("vault1", "cluster1")).build(); + Assert.assertNull(client.getVaultConfig("vault-unknown")); + } +} diff --git a/flowvault/src/test/java/com/skyflow/VaultClientTests.java b/flowvault/src/test/java/com/skyflow/VaultClientTests.java new file mode 100644 index 00000000..24e741b5 --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/VaultClientTests.java @@ -0,0 +1,130 @@ +package com.skyflow; + +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.enums.Env; +import com.skyflow.errors.SkyflowException; +import org.junit.Assert; +import org.junit.Test; + +public class VaultClientTests { + + private static VaultConfig buildConfig(String vaultId, String clusterId, Credentials credentials) { + VaultConfig config = new VaultConfig(); + config.setVaultId(vaultId); + config.setClusterId(clusterId); + config.setEnv(Env.DEV); + if (credentials != null) { + config.setCredentials(credentials); + } + return config; + } + + // ── updateVaultURL priority order ──────────────────────────────────────── + + @Test + public void testUpdateVaultURL_usesExplicitVaultURLOverClusterId() throws SkyflowException { + VaultConfig config = buildConfig("vault1", "cluster1", null); + config.setVaultURL("https://custom.example.com"); + + VaultClient client = new VaultClient(config, null); + + Assert.assertEquals("https://custom.example.com", client.currentVaultURL); + } + + @Test + public void testUpdateVaultURL_constructsFromClusterIdWhenNoVaultURL() throws SkyflowException { + VaultConfig config = buildConfig("vault1", "cluster1", null); + + VaultClient client = new VaultClient(config, null); + + Assert.assertEquals("https://cluster1.skyvault.skyflowapis.dev", client.currentVaultURL); + } + + // ── setBearerToken ──────────────────────────────────────────────────────── + + @Test + public void testSetBearerToken_usesApiKeyDirectly() throws SkyflowException { + Credentials creds = new Credentials(); + creds.setApiKey("sky-ab123-abcd1234cdef1234abcd4321cdef4321"); + VaultConfig config = buildConfig("vault1", "cluster1", creds); + + VaultClient client = new VaultClient(config, null); + client.setBearerToken(); + + Assert.assertEquals("sky-ab123-abcd1234cdef1234abcd4321cdef4321", client.token); + } + + @Test + public void testSetBearerToken_reusesNonExpiredToken() throws SkyflowException { + Credentials creds = new Credentials(); + creds.setToken("x.eyJleHAiOjk5OTk5OTk5OTl9.y"); + VaultConfig config = buildConfig("vault1", "cluster1", creds); + + VaultClient client = new VaultClient(config, null); + client.setBearerToken(); + String firstToken = client.token; + client.setBearerToken(); + + Assert.assertEquals(firstToken, client.token); + } + + @Test + public void testSetBearerToken_invalidCredentialsThrows() { + Credentials creds = new Credentials(); + creds.setApiKey("not-a-valid-api-key"); + VaultConfig config = buildConfig("vault1", "cluster1", creds); + + try { + VaultClient client = new VaultClient(config, null); + client.setBearerToken(); + Assert.fail("Should have thrown an exception"); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── getRecordsApi ───────────────────────────────────────────────────────── + + @Test + public void testGetRecordsApi_availableAfterSetBearerToken() throws SkyflowException { + Credentials creds = new Credentials(); + creds.setApiKey("sky-ab123-abcd1234cdef1234abcd4321cdef4321"); + VaultConfig config = buildConfig("vault1", "cluster1", creds); + + VaultClient client = new VaultClient(config, null); + client.setBearerToken(); + + Assert.assertNotNull(client.getRecordsApi()); + } + + // ── setCommonCredentials ────────────────────────────────────────────────── + + @Test + public void testSetCommonCredentials_vaultSpecificCredentialsTakePriority() throws SkyflowException { + Credentials vaultCreds = new Credentials(); + vaultCreds.setToken("vault-specific-token"); + VaultConfig config = buildConfig("vault1", "cluster1", vaultCreds); + + VaultClient client = new VaultClient(config, null); + Credentials commonCreds = new Credentials(); + commonCreds.setToken("common-token"); + client.setCommonCredentials(commonCreds); + client.setBearerToken(); + + Assert.assertEquals("vault-specific-token", client.token); + } + + @Test + public void testSetCommonCredentials_usedWhenNoVaultSpecificCredentials() throws SkyflowException { + VaultConfig config = buildConfig("vault1", "cluster1", null); + + VaultClient client = new VaultClient(config, null); + Credentials commonCreds = new Credentials(); + commonCreds.setToken("common-token"); + client.setCommonCredentials(commonCreds); + client.setBearerToken(); + + Assert.assertEquals("common-token", client.token); + } +} diff --git a/flowvault/src/test/java/com/skyflow/config/VaultConfigTests.java b/flowvault/src/test/java/com/skyflow/config/VaultConfigTests.java new file mode 100644 index 00000000..a1c82af5 --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/config/VaultConfigTests.java @@ -0,0 +1,94 @@ +package com.skyflow.config; + +import com.skyflow.enums.Env; +import org.junit.Assert; +import org.junit.Test; + +public class VaultConfigTests { + + @Test + public void testDefaultEnvIsProd() { + VaultConfig config = new VaultConfig(); + Assert.assertEquals(Env.PROD, config.getEnv()); + } + + @Test + public void testSettingNullEnvFallsBackToProd() { + VaultConfig config = new VaultConfig(); + config.setEnv(null); + Assert.assertEquals(Env.PROD, config.getEnv()); + } + + @Test + public void testVaultIdGetterSetter() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + Assert.assertEquals("vault123", config.getVaultId()); + } + + @Test + public void testClusterIdGetterSetter() { + VaultConfig config = new VaultConfig(); + config.setClusterId("cluster123"); + Assert.assertEquals("cluster123", config.getClusterId()); + } + + @Test + public void testEnvGetterSetter() { + VaultConfig config = new VaultConfig(); + config.setEnv(Env.SANDBOX); + Assert.assertEquals(Env.SANDBOX, config.getEnv()); + } + + @Test + public void testCredentialsGetterSetter() { + VaultConfig config = new VaultConfig(); + Credentials credentials = new Credentials(); + credentials.setToken("token1"); + config.setCredentials(credentials); + Assert.assertEquals(credentials, config.getCredentials()); + } + + @Test + public void testVaultURLDefaultsToNull() { + VaultConfig config = new VaultConfig(); + Assert.assertNull(config.getVaultURL()); + } + + @Test + public void testVaultURLGetterSetter() { + VaultConfig config = new VaultConfig(); + config.setVaultURL("https://myvault.example.com"); + Assert.assertEquals("https://myvault.example.com", config.getVaultURL()); + } + + @Test + public void testClone_copiesFieldsAndDeepCopiesCredentials() throws CloneNotSupportedException { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId("cluster123"); + config.setEnv(Env.DEV); + Credentials credentials = new Credentials(); + credentials.setToken("token1"); + config.setCredentials(credentials); + + VaultConfig cloned = (VaultConfig) config.clone(); + + Assert.assertEquals(config.getVaultId(), cloned.getVaultId()); + Assert.assertEquals(config.getClusterId(), cloned.getClusterId()); + Assert.assertEquals(config.getEnv(), cloned.getEnv()); + Assert.assertNotSame(config.getCredentials(), cloned.getCredentials()); + Assert.assertEquals(config.getCredentials().getToken(), cloned.getCredentials().getToken()); + } + + @Test + public void testClone_withNullCredentials() throws CloneNotSupportedException { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId("cluster123"); + + VaultConfig cloned = (VaultConfig) config.clone(); + + Assert.assertNull(cloned.getCredentials()); + } +} diff --git a/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java b/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java new file mode 100644 index 00000000..425f461a --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/utils/UtilsTests.java @@ -0,0 +1,568 @@ +package com.skyflow.utils; + +import com.google.gson.JsonObject; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.enums.Env; +import com.skyflow.enums.UpsertType; +import com.skyflow.errors.SkyflowException; +import com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest; +import com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDetokenizeRequest; +import com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest; +import com.skyflow.generated.rest.resources.flowservice.requests.V1InsertRequest; +import com.skyflow.generated.rest.types.FlowEnumUpdateType; +import com.skyflow.generated.rest.types.FlowTokenizeResponseObjectToken; +import com.skyflow.generated.rest.types.V1DeleteTokenResponseObject; +import com.skyflow.generated.rest.types.V1FlowDeleteTokenResponse; +import com.skyflow.generated.rest.types.V1FlowDetokenizeResponse; +import com.skyflow.generated.rest.types.V1FlowDetokenizeResponseObject; +import com.skyflow.generated.rest.types.V1FlowTokenizeResponse; +import com.skyflow.generated.rest.types.V1FlowTokenizeResponseObject; +import com.skyflow.generated.rest.types.V1InsertResponse; +import com.skyflow.generated.rest.types.V1RecordResponseObject; +import com.skyflow.vault.data.DeleteTokensRequest; +import com.skyflow.vault.data.DeleteTokensResponse; +import com.skyflow.vault.data.DetokenizeData; +import com.skyflow.vault.data.DetokenizeRecordResponse; +import com.skyflow.vault.data.DetokenizeRequest; +import com.skyflow.vault.data.DetokenizeResponse; +import com.skyflow.vault.data.InsertRecord; +import com.skyflow.vault.data.InsertRequest; +import com.skyflow.vault.data.InsertResponse; +import com.skyflow.vault.data.TokenGroupRedactions; +import com.skyflow.vault.data.TokenizeData; +import com.skyflow.vault.data.TokenizeRecord; +import com.skyflow.vault.data.TokenizeRequest; +import com.skyflow.vault.data.TokenizeResponse; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class UtilsTests { + + // ── getVaultURL ─────────────────────────────────────────────────────────── + + @Test + public void testGetVaultURL_prodEnv() { + String url = Utils.getVaultURL("cluster1", Env.PROD); + Assert.assertEquals("https://cluster1.skyvault.skyflowapis.com", url); + } + + @Test + public void testGetVaultURL_devEnv() { + String url = Utils.getVaultURL("cluster1", Env.DEV); + Assert.assertEquals("https://cluster1.skyvault.skyflowapis.dev", url); + } + + @Test + public void testGetVaultURL_stageEnv() { + String url = Utils.getVaultURL("cluster1", Env.STAGE); + Assert.assertEquals("https://cluster1.skyvault.skyflowapis.tech", url); + } + + @Test + public void testGetVaultURL_sandboxEnv() { + String url = Utils.getVaultURL("cluster1", Env.SANDBOX); + Assert.assertEquals("https://cluster1.skyvault.skyflowapis-preview.com", url); + } + + // ── getMetrics ──────────────────────────────────────────────────────────── + + @Test + public void testGetMetrics_containsSdkVersion() { + JsonObject metrics = Utils.getMetrics(); + Assert.assertTrue(metrics.has(BaseConstants.SDK_METRIC_NAME_VERSION)); + String sdkVersionMetric = metrics.get(BaseConstants.SDK_METRIC_NAME_VERSION).getAsString(); + Assert.assertTrue(sdkVersionMetric.startsWith(Constants.SDK_METRIC_NAME_VERSION_PREFIX)); + } + + // ── getEnvVaultURL ──────────────────────────────────────────────────────── + + @Test + public void testGetEnvVaultURL_doesNotThrowUnexpectedException() { + try { + Utils.getEnvVaultURL(); + } catch (SkyflowException e) { + // acceptable if this environment happens to have an invalid VAULT_URL set + Assert.assertNotNull(e.getMessage()); + } + } + + // ── isValidURL ──────────────────────────────────────────────────────────── + + @Test + public void testIsValidURL_validHttpsUrl() { + Assert.assertTrue(Utils.isValidURL("https://example.com")); + } + + @Test + public void testIsValidURL_httpUrlIsInvalid() { + Assert.assertFalse(Utils.isValidURL("http://example.com")); + } + + @Test + public void testIsValidURL_malformedUrl() { + Assert.assertFalse(Utils.isValidURL("not a url")); + } + + // ── generateBearerToken ─────────────────────────────────────────────────── + + @Test + public void testGenerateBearerToken_withDirectToken() throws SkyflowException { + Credentials credentials = new Credentials(); + credentials.setToken("direct-token-value"); + + String token = Utils.generateBearerToken(credentials); + + Assert.assertEquals("direct-token-value", token); + } + + @Test + public void testGenerateBearerToken_withInvalidCredentialsStringThrows() { + Credentials credentials = new Credentials(); + // A string that fails JSON syntax parsing (as opposed to e.g. a bare word, + // which Gson's lenient parser accepts as a JSON primitive rather than rejecting outright). + credentials.setCredentialsString("./src/test/credentials.json"); + try { + Utils.generateBearerToken(credentials); + Assert.fail("Should have thrown an exception"); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testGenerateBearerToken_withNonExistentPathThrows() { + Credentials credentials = new Credentials(); + credentials.setPath("/nonexistent/path/credentials.json"); + try { + Utils.generateBearerToken(credentials); + Assert.fail("Should have thrown an exception"); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── getBulkInsertRequestBody ────────────────────────────────────────────── + + @Test + public void testGetBulkInsertRequestBody_buildsCorrectRequest() { + Map data = new HashMap<>(); + data.put("name", "john"); + InsertRecord record = InsertRecord.builder().data(data).build(); + ArrayList records = new ArrayList<>(); + records.add(record); + InsertRequest request = InsertRequest.builder().table("table1").records(records).build(); + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + + V1InsertRequest body = Utils.getBulkInsertRequestBody(request, config); + + Assert.assertEquals("vault123", body.getVaultId().get()); + Assert.assertEquals("table1", body.getTableName().get()); + Assert.assertEquals(1, body.getRecords().get().size()); + Assert.assertEquals(data, body.getRecords().get().get(0).getData().get()); + } + + @Test + public void testGetBulkInsertRequestBody_withUpsertAtRequestLevel() { + Map data = new HashMap<>(); + data.put("name", "john"); + InsertRecord record = InsertRecord.builder().data(data).build(); + ArrayList records = new ArrayList<>(); + records.add(record); + InsertRequest request = InsertRequest.builder() + .table("table1") + .records(records) + .upsert(Collections.singletonList("email")) + .upsertType(UpsertType.UPDATE) + .build(); + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + + V1InsertRequest body = Utils.getBulkInsertRequestBody(request, config); + + Assert.assertTrue(body.getUpsert().isPresent()); + Assert.assertEquals(Collections.singletonList("email"), body.getUpsert().get().getUniqueColumns().get()); + Assert.assertEquals(FlowEnumUpdateType.UPDATE, body.getUpsert().get().getUpdateType().get()); + } + + @Test + public void testGetBulkInsertRequestBody_withUpsertAtRecordLevel() { + Map data = new HashMap<>(); + data.put("name", "john"); + InsertRecord record = InsertRecord.builder() + .table("table1") + .data(data) + .upsert(Collections.singletonList("email")) + .upsertType(UpsertType.REPLACE) + .build(); + ArrayList records = new ArrayList<>(); + records.add(record); + InsertRequest request = InsertRequest.builder().records(records).build(); + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + + V1InsertRequest body = Utils.getBulkInsertRequestBody(request, config); + + Assert.assertEquals("table1", body.getRecords().get().get(0).getTableName().get()); + Assert.assertTrue(body.getRecords().get().get(0).getUpsert().isPresent()); + Assert.assertEquals(FlowEnumUpdateType.REPLACE, body.getRecords().get().get(0).getUpsert().get().getUpdateType().get()); + } + + // ── buildInsertResponse ─────────────────────────────────────────────────── + + @Test + public void testBuildInsertResponse_success() { + Map tokens = new HashMap<>(); + tokens.put("name", "tok-abc"); + V1RecordResponseObject record = V1RecordResponseObject.builder() + .skyflowId("sky-id-1") + .tokens(tokens) + .build(); + V1InsertResponse res = V1InsertResponse.builder().records(Collections.singletonList(record)).build(); + + InsertResponse response = Utils.buildInsertResponse(res); + + Assert.assertEquals(1, response.getInsertedFields().size()); + Assert.assertEquals("sky-id-1", response.getInsertedFields().get(0).get("skyflowId")); + Assert.assertEquals("tok-abc", response.getInsertedFields().get(0).get("name")); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildInsertResponse_error() { + V1RecordResponseObject record = V1RecordResponseObject.builder() + .skyflowId("sky-id-1") + .tableName("table1") + .error("insert failed") + .httpCode(400) + .build(); + V1InsertResponse res = V1InsertResponse.builder().records(Collections.singletonList(record)).build(); + + InsertResponse response = Utils.buildInsertResponse(res); + + Assert.assertTrue(response.getInsertedFields().isEmpty()); + Assert.assertEquals(1, response.getErrors().size()); + Assert.assertEquals("insert failed", response.getErrors().get(0).get("error")); + Assert.assertEquals("table1", response.getErrors().get(0).get("tableName")); + Assert.assertEquals(400, response.getErrors().get(0).get("httpCode")); + } + + @Test + public void testBuildInsertResponse_nullResponse() { + InsertResponse response = Utils.buildInsertResponse(null); + Assert.assertTrue(response.getInsertedFields().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildInsertResponse_emptyResponse() { + V1InsertResponse res = V1InsertResponse.builder().build(); + InsertResponse response = Utils.buildInsertResponse(res); + Assert.assertTrue(response.getInsertedFields().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + // ── getDetokenizeRequestBody ────────────────────────────────────────────── + + @Test + public void testGetDetokenizeRequestBody_buildsCorrectRequest() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + + V1FlowDetokenizeRequest body = Utils.getDetokenizeRequestBody(request, "vault123"); + + Assert.assertEquals("vault123", body.getVaultId().get()); + Assert.assertEquals(Collections.singletonList("token1"), body.getTokens().get()); + Assert.assertFalse(body.getTokenGroupRedactions().isPresent()); + } + + @Test + public void testGetDetokenizeRequestBody_withTokenGroupRedactions() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + List groupRedactions = Collections.singletonList( + TokenGroupRedactions.builder().tokenGroupName("group1").redaction("MASKED").build()); + DetokenizeRequest request = DetokenizeRequest.builder() + .detokenizeData(data) + .tokenGroupRedactions(groupRedactions) + .build(); + + V1FlowDetokenizeRequest body = Utils.getDetokenizeRequestBody(request, "vault123"); + + Assert.assertTrue(body.getTokenGroupRedactions().isPresent()); + Assert.assertEquals("group1", body.getTokenGroupRedactions().get().get(0).getTokenGroupName().get()); + Assert.assertEquals("MASKED", body.getTokenGroupRedactions().get().get(0).getRedaction().get()); + } + + // ── buildDetokenizeResponse ─────────────────────────────────────────────── + + @Test + public void testBuildDetokenizeResponse_success() { + V1FlowDetokenizeResponseObject record = V1FlowDetokenizeResponseObject.builder() + .token("token1") + .value("secret-value") + .tokenGroupName("group1") + .build(); + V1FlowDetokenizeResponse res = V1FlowDetokenizeResponse.builder() + .response(Collections.singletonList(record)) + .build(); + + DetokenizeResponse response = Utils.buildDetokenizeResponse(res); + + Assert.assertEquals(1, response.getDetokenizedFields().size()); + DetokenizeRecordResponse detokenizedField = response.getDetokenizedFields().get(0); + Assert.assertEquals("token1", detokenizedField.getToken()); + Assert.assertEquals("secret-value", detokenizedField.getValue()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildDetokenizeResponse_error() { + V1FlowDetokenizeResponseObject record = V1FlowDetokenizeResponseObject.builder() + .token("token1") + .error("detokenize failed") + .build(); + V1FlowDetokenizeResponse res = V1FlowDetokenizeResponse.builder() + .response(Collections.singletonList(record)) + .build(); + + DetokenizeResponse response = Utils.buildDetokenizeResponse(res); + + Assert.assertTrue(response.getDetokenizedFields().isEmpty()); + Assert.assertEquals(1, response.getErrors().size()); + Assert.assertEquals("detokenize failed", response.getErrors().get(0).getError()); + } + + @Test + public void testBuildDetokenizeResponse_nullResponse() { + DetokenizeResponse response = Utils.buildDetokenizeResponse(null); + Assert.assertTrue(response.getDetokenizedFields().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + // ── getTokenizeRequestBody ──────────────────────────────────────────────── + + @Test + public void testGetTokenizeRequestBody_buildsCorrectRequest() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder() + .value("value1") + .tokenGroupNames(Collections.singletonList("group1")) + .build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + + V1FlowTokenizeRequest body = Utils.getTokenizeRequestBody(request, "vault123"); + + Assert.assertEquals("vault123", body.getVaultId().get()); + Assert.assertTrue(body.getData().isPresent()); + Assert.assertEquals(1, body.getData().get().size()); + Assert.assertEquals("value1", body.getData().get().get(0).getValue().get()); + Assert.assertEquals(Collections.singletonList("group1"), body.getData().get().get(0).getTokenGroupNames().get()); + } + + // ── buildTokenizeResponse ───────────────────────────────────────────────── + + @Test + public void testBuildTokenizeResponse_success() { + FlowTokenizeResponseObjectToken token = FlowTokenizeResponseObjectToken.builder() + .tokenGroupName("group1") + .token("tok-abc") + .build(); + V1FlowTokenizeResponseObject responseObject = V1FlowTokenizeResponseObject.builder() + .value("value1") + .tokens(Collections.singletonList(token)) + .build(); + V1FlowTokenizeResponse res = V1FlowTokenizeResponse.builder() + .response(Collections.singletonList(responseObject)) + .build(); + + TokenizeResponse response = Utils.buildTokenizeResponse(res, new HashMap<>(), 1); + + Assert.assertEquals(1, response.getTokenizedData().size()); + TokenizeData data = response.getTokenizedData().get(0); + Assert.assertEquals("value1", data.getValue()); + Assert.assertEquals("tok-abc", data.getTokens().get("group1")); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildTokenizeResponse_error() { + FlowTokenizeResponseObjectToken token = FlowTokenizeResponseObjectToken.builder() + .tokenGroupName("group1") + .error("tokenization failed") + .httpCode(400) + .build(); + V1FlowTokenizeResponseObject responseObject = V1FlowTokenizeResponseObject.builder() + .value("value1") + .tokens(Collections.singletonList(token)) + .build(); + V1FlowTokenizeResponse res = V1FlowTokenizeResponse.builder() + .response(Collections.singletonList(responseObject)) + .build(); + + TokenizeResponse response = Utils.buildTokenizeResponse(res, new HashMap<>(), 1); + + Assert.assertTrue(response.getTokenizedData().isEmpty()); + Assert.assertEquals(1, response.getErrors().size()); + Map errorRecord = response.getErrors().get(0); + Assert.assertEquals("tokenization failed", errorRecord.get("error")); + Assert.assertEquals(400, errorRecord.get("httpCode")); + Assert.assertEquals("group1", errorRecord.get("tokenGroupName")); + Assert.assertEquals(0, errorRecord.get("index")); + } + + @Test + public void testBuildTokenizeResponse_mixedSuccessAndError() { + FlowTokenizeResponseObjectToken successToken = FlowTokenizeResponseObjectToken.builder() + .tokenGroupName("group1") + .token("tok-abc") + .build(); + FlowTokenizeResponseObjectToken errorToken = FlowTokenizeResponseObjectToken.builder() + .tokenGroupName("group2") + .error("group2 failed") + .httpCode(400) + .build(); + V1FlowTokenizeResponseObject responseObject = V1FlowTokenizeResponseObject.builder() + .value("value1") + .tokens(java.util.Arrays.asList(successToken, errorToken)) + .build(); + V1FlowTokenizeResponse res = V1FlowTokenizeResponse.builder() + .response(Collections.singletonList(responseObject)) + .build(); + + TokenizeResponse response = Utils.buildTokenizeResponse(res, new HashMap<>(), 1); + + Assert.assertEquals(1, response.getTokenizedData().size()); + Assert.assertEquals("tok-abc", response.getTokenizedData().get(0).getTokens().get("group1")); + Assert.assertEquals(1, response.getErrors().size()); + Assert.assertEquals("group2 failed", response.getErrors().get(0).get("error")); + } + + @Test + public void testBuildTokenizeResponse_emptyResponse() { + V1FlowTokenizeResponse res = V1FlowTokenizeResponse.builder().build(); + + TokenizeResponse response = Utils.buildTokenizeResponse(res, new HashMap<>(), 1); + + Assert.assertTrue(response.getTokenizedData().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildTokenizeResponse_nullResponse() { + TokenizeResponse response = Utils.buildTokenizeResponse(null, new HashMap<>(), 1); + + Assert.assertTrue(response.getTokenizedData().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildTokenizeResponse_incompleteResponseDoesNotThrow() { + FlowTokenizeResponseObjectToken token = FlowTokenizeResponseObjectToken.builder() + .tokenGroupName("group1") + .token("tok-abc") + .build(); + V1FlowTokenizeResponseObject responseObject = V1FlowTokenizeResponseObject.builder() + .value("value1") + .tokens(Collections.singletonList(token)) + .build(); + V1FlowTokenizeResponse res = V1FlowTokenizeResponse.builder() + .response(Collections.singletonList(responseObject)) + .build(); + + // requestedRecordCount=2 but only 1 record came back — should just warn, not throw + TokenizeResponse response = Utils.buildTokenizeResponse(res, new HashMap<>(), 2); + + Assert.assertEquals(1, response.getTokenizedData().size()); + } + + // ── getDeleteTokensRequestBody ──────────────────────────────────────────── + + @Test + public void testGetDeleteTokensRequestBody_buildsCorrectRequest() { + DeleteTokensRequest request = DeleteTokensRequest.builder() + .tokens(Collections.singletonList("token1")) + .build(); + + V1FlowDeleteTokenRequest body = Utils.getDeleteTokensRequestBody(request, "vault123"); + + Assert.assertEquals("vault123", body.getVaultId().get()); + Assert.assertEquals(Collections.singletonList("token1"), body.getTokens().get()); + } + + // ── buildDeleteTokensResponse ───────────────────────────────────────────── + + @Test + public void testBuildDeleteTokensResponse_success() { + V1DeleteTokenResponseObject record = V1DeleteTokenResponseObject.builder() + .value("token1") + .build(); + V1FlowDeleteTokenResponse res = V1FlowDeleteTokenResponse.builder() + .tokens(Collections.singletonList(record)) + .build(); + + DeleteTokensResponse response = Utils.buildDeleteTokensResponse(res, new HashMap<>(), 1); + + Assert.assertEquals(Collections.singletonList("token1"), response.getTokens()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildDeleteTokensResponse_error() { + V1DeleteTokenResponseObject record = V1DeleteTokenResponseObject.builder() + .error("token not found") + .httpCode(404) + .build(); + V1FlowDeleteTokenResponse res = V1FlowDeleteTokenResponse.builder() + .tokens(Collections.singletonList(record)) + .build(); + + DeleteTokensResponse response = Utils.buildDeleteTokensResponse(res, new HashMap<>(), 1); + + Assert.assertTrue(response.getTokens().isEmpty()); + Assert.assertEquals(1, response.getErrors().size()); + Assert.assertEquals("token not found", response.getErrors().get(0).get("error")); + Assert.assertEquals(404, response.getErrors().get(0).get("httpCode")); + } + + @Test + public void testBuildDeleteTokensResponse_emptyResponse() { + V1FlowDeleteTokenResponse res = V1FlowDeleteTokenResponse.builder().build(); + + DeleteTokensResponse response = Utils.buildDeleteTokensResponse(res, new HashMap<>(), 1); + + Assert.assertTrue(response.getTokens().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildDeleteTokensResponse_nullResponse() { + DeleteTokensResponse response = Utils.buildDeleteTokensResponse(null, new HashMap<>(), 1); + + Assert.assertTrue(response.getTokens().isEmpty()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testBuildDeleteTokensResponse_incompleteResponseDoesNotThrow() { + V1DeleteTokenResponseObject record = V1DeleteTokenResponseObject.builder() + .value("token1") + .build(); + V1FlowDeleteTokenResponse res = V1FlowDeleteTokenResponse.builder() + .tokens(Collections.singletonList(record)) + .build(); + + // requestedTokenCount=2 but only 1 record came back — should just warn, not throw + DeleteTokensResponse response = Utils.buildDeleteTokensResponse(res, new HashMap<>(), 2); + + Assert.assertEquals(1, response.getTokens().size()); + } +} diff --git a/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java b/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java new file mode 100644 index 00000000..2167fd30 --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java @@ -0,0 +1,732 @@ +package com.skyflow.utils.validations; + +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.enums.Env; +import com.skyflow.errors.SkyflowException; +import com.skyflow.vault.data.DeleteTokensRequest; +import com.skyflow.vault.data.DetokenizeData; +import com.skyflow.vault.data.DetokenizeRequest; +import com.skyflow.vault.data.InsertRecord; +import com.skyflow.vault.data.InsertRequest; +import com.skyflow.vault.data.TokenGroupRedactions; +import com.skyflow.vault.data.TokenizeRecord; +import com.skyflow.vault.data.TokenizeRequest; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ValidationsTests { + private static final String EXCEPTION_NOT_THROWN = "Should have thrown an exception"; + private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception"; + + // ── validateTokenizeRequest ────────────────────────────────────────────── + + @Test + public void testValidateTokenizeRequest_nullRequest() { + try { + Validations.validateTokenizeRequest(null); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_nullData() { + TokenizeRequest request = TokenizeRequest.builder().data(null).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_emptyData() { + TokenizeRequest request = TokenizeRequest.builder().data(new ArrayList<>()).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_largeValidListDoesNotThrow() { + ArrayList records = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + records.add(TokenizeRecord.builder() + .value("value" + i) + .tokenGroupNames(Collections.singletonList("group1")) + .build()); + } + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateTokenizeRequest_nullRecordInList() { + ArrayList records = new ArrayList<>(); + records.add(null); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_nullValue() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder().value(null).tokenGroupNames(Collections.singletonList("group1")).build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_blankStringValue() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder().value(" ").tokenGroupNames(Collections.singletonList("group1")).build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_nullTokenGroupNames() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder().value("value1").tokenGroupNames(null).build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_emptyTokenGroupNames() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder().value("value1").tokenGroupNames(new ArrayList<>()).build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_blankTokenGroupNameAtIndex() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder() + .value("value1") + .tokenGroupNames(Arrays.asList("group1", " ")) + .build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateTokenizeRequest_validRequest() { + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder() + .value("value1") + .tokenGroupNames(Collections.singletonList("group1")) + .build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + try { + Validations.validateTokenizeRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + // ── validateDeleteTokensRequest ────────────────────────────────────────── + + @Test + public void testValidateDeleteTokensRequest_nullRequest() { + try { + Validations.validateDeleteTokensRequest(null); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDeleteTokensRequest_nullTokens() { + DeleteTokensRequest request = DeleteTokensRequest.builder().tokens(null).build(); + try { + Validations.validateDeleteTokensRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDeleteTokensRequest_emptyTokens() { + DeleteTokensRequest request = DeleteTokensRequest.builder().tokens(new ArrayList<>()).build(); + try { + Validations.validateDeleteTokensRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDeleteTokensRequest_largeValidListDoesNotThrow() { + List tokens = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + tokens.add("token" + i); + } + DeleteTokensRequest request = DeleteTokensRequest.builder().tokens(tokens).build(); + try { + Validations.validateDeleteTokensRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateDeleteTokensRequest_blankTokenAtIndex() { + DeleteTokensRequest request = DeleteTokensRequest.builder() + .tokens(Arrays.asList("token1", " ")) + .build(); + try { + Validations.validateDeleteTokensRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDeleteTokensRequest_validRequest() { + DeleteTokensRequest request = DeleteTokensRequest.builder() + .tokens(Collections.singletonList("token1")) + .build(); + try { + Validations.validateDeleteTokensRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + // ── validateCredentials ─────────────────────────────────────────────────── + + @Test + public void testValidateCredentials_validToken() { + Credentials credentials = new Credentials(); + credentials.setToken("some-token"); + try { + Validations.validateCredentials(credentials); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateCredentials_validApiKey() { + Credentials credentials = new Credentials(); + credentials.setApiKey("sky-ab123-abcd1234cdef1234abcd4321cdef4321"); + try { + Validations.validateCredentials(credentials); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateCredentials_invalidApiKeyFormat() { + Credentials credentials = new Credentials(); + credentials.setApiKey("not-a-valid-api-key"); + try { + Validations.validateCredentials(credentials); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateCredentials_noAuthMeansPassed() { + Credentials credentials = new Credentials(); + try { + Validations.validateCredentials(credentials); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateCredentials_multipleAuthMeansPassed() { + Credentials credentials = new Credentials(); + credentials.setToken("some-token"); + credentials.setApiKey("sky-ab123-abcd1234cdef1234abcd4321cdef4321"); + try { + Validations.validateCredentials(credentials); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateCredentials_emptyRoles() { + Credentials credentials = new Credentials(); + credentials.setToken("some-token"); + credentials.setRoles(new ArrayList<>()); + try { + Validations.validateCredentials(credentials); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── validateVaultConfiguration ──────────────────────────────────────────── + + @Test + public void testValidateVaultConfiguration_nullVaultId() { + VaultConfig config = new VaultConfig(); + config.setClusterId("cluster1"); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateVaultConfiguration_emptyVaultId() { + VaultConfig config = new VaultConfig(); + config.setVaultId(" "); + config.setClusterId("cluster1"); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateVaultConfiguration_neitherVaultUrlNorClusterId() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateVaultConfiguration_emptyClusterId() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId(" "); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateVaultConfiguration_emptyVaultUrl() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId("cluster1"); + config.setVaultURL(" "); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateVaultConfiguration_invalidVaultUrlFormat() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setVaultURL("http://not-https.example.com"); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateVaultConfiguration_validWithClusterId() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId("cluster1"); + config.setEnv(Env.DEV); + try { + Validations.validateVaultConfiguration(config); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateVaultConfiguration_validWithVaultUrl() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setVaultURL("https://myvault.example.com"); + try { + Validations.validateVaultConfiguration(config); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateVaultConfiguration_delegatesToCredentialValidation() { + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId("cluster1"); + Credentials credentials = new Credentials(); + credentials.setApiKey("not-a-valid-api-key"); + config.setCredentials(credentials); + try { + Validations.validateVaultConfiguration(config); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── validateInsertRequest ───────────────────────────────────────────────── + + @Test + public void testValidateInsertRequest_nullRecords() { + InsertRequest request = InsertRequest.builder().table("table1").records(null).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_emptyRecords() { + InsertRequest request = InsertRequest.builder().table("table1").records(new ArrayList<>()).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_nullRecordInList() { + ArrayList records = new ArrayList<>(); + records.add(null); + InsertRequest request = InsertRequest.builder().table("table1").records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_tableSpecifiedAtBothPlaces() { + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("recordTable").data(data).build()); + InsertRequest request = InsertRequest.builder().table("requestTable").records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_tableMissingAtBothPlaces() { + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_upsertAtRecordLevelWhenTableAtRequestLevel() { + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().data(data).upsert(Collections.singletonList("email")).build()); + InsertRequest request = InsertRequest.builder() + .table("table1") + .records(records) + .build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_emptyUpsertAtRequestLevel() { + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().data(data).build()); + InsertRequest request = InsertRequest.builder() + .table("table1") + .records(records) + .upsert(new ArrayList<>()) + .build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_emptyOrNullKeyInData() { + Map data = new HashMap<>(); + data.put("", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("table1").data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_emptyOrNullValueInData() { + Map data = new HashMap<>(); + data.put("name", ""); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("table1").data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_validRequest() { + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("table1").data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + // ── validateDetokenizeRequest ───────────────────────────────────────────── + + @Test + public void testValidateDetokenizeRequest_nullRequest() { + try { + Validations.validateDetokenizeRequest(null); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_nullDetokenizeData() { + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(null).build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_emptyDetokenizeData() { + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(new ArrayList<>()).build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_nullEntryInDetokenizeData() { + ArrayList data = new ArrayList<>(); + data.add(null); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_blankTokenInDetokenizeData() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData(" ")); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_nullTokenGroupRedactionInList() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + List groupRedactions = new ArrayList<>(); + groupRedactions.add(null); + DetokenizeRequest request = DetokenizeRequest.builder() + .detokenizeData(data) + .tokenGroupRedactions(groupRedactions) + .build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_blankTokenGroupNameInRedaction() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + List groupRedactions = Collections.singletonList( + TokenGroupRedactions.builder().tokenGroupName(" ").redaction("MASKED").build()); + DetokenizeRequest request = DetokenizeRequest.builder() + .detokenizeData(data) + .tokenGroupRedactions(groupRedactions) + .build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_blankRedactionInGroup() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + List groupRedactions = Collections.singletonList( + TokenGroupRedactions.builder().tokenGroupName("group1").redaction(" ").build()); + DetokenizeRequest request = DetokenizeRequest.builder() + .detokenizeData(data) + .tokenGroupRedactions(groupRedactions) + .build(); + try { + Validations.validateDetokenizeRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testValidateDetokenizeRequest_validRequest() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + try { + Validations.validateDetokenizeRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidateDetokenizeRequest_validRequestWithTokenGroupRedactions() { + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + List groupRedactions = Collections.singletonList( + TokenGroupRedactions.builder().tokenGroupName("group1").redaction("MASKED").build()); + DetokenizeRequest request = DetokenizeRequest.builder() + .detokenizeData(data) + .tokenGroupRedactions(groupRedactions) + .build(); + try { + Validations.validateDetokenizeRequest(request); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } +} diff --git a/flowvault/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java b/flowvault/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java new file mode 100644 index 00000000..78d6aadd --- /dev/null +++ b/flowvault/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java @@ -0,0 +1,445 @@ +package com.skyflow.vault.controller; + +import com.skyflow.VaultClient; +import com.skyflow.config.Credentials; +import com.skyflow.config.VaultConfig; +import com.skyflow.enums.Env; +import com.skyflow.errors.SkyflowException; +import com.skyflow.generated.rest.ApiClient; +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.resources.flowservice.FlowserviceClient; +import com.skyflow.generated.rest.resources.flowservice.RawFlowserviceClient; +import com.skyflow.generated.rest.types.FlowTokenizeResponseObjectToken; +import com.skyflow.generated.rest.types.V1DeleteTokenResponseObject; +import com.skyflow.generated.rest.types.V1FlowDeleteTokenResponse; +import com.skyflow.generated.rest.types.V1FlowDetokenizeResponse; +import com.skyflow.generated.rest.types.V1FlowDetokenizeResponseObject; +import com.skyflow.generated.rest.types.V1FlowTokenizeResponse; +import com.skyflow.generated.rest.types.V1FlowTokenizeResponseObject; +import com.skyflow.generated.rest.types.V1InsertResponse; +import com.skyflow.generated.rest.types.V1RecordResponseObject; +import com.skyflow.utils.Constants; +import com.skyflow.vault.data.DeleteTokensRequest; +import com.skyflow.vault.data.DeleteTokensResponse; +import com.skyflow.vault.data.DetokenizeData; +import com.skyflow.vault.data.DetokenizeRequest; +import com.skyflow.vault.data.DetokenizeResponse; +import com.skyflow.vault.data.InsertRecord; +import com.skyflow.vault.data.InsertRequest; +import com.skyflow.vault.data.InsertResponse; +import com.skyflow.vault.data.TokenizeRecord; +import com.skyflow.vault.data.TokenizeRequest; +import com.skyflow.vault.data.TokenizeResponse; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +public class VaultControllerTests { + private static final String EXCEPTION_NOT_THROWN = "Should have thrown an exception"; + private static final String INVALID_EXCEPTION_THROWN = "Should not have thrown any exception"; + + private static VaultController createControllerWithMock(ApiClient mockApiClient) throws Exception { + Credentials creds = new Credentials(); + creds.setApiKey("sky-ab123-abcd1234cdef1234abcd4321cdef4321"); + + VaultConfig config = new VaultConfig(); + config.setVaultId("vault123"); + config.setClusterId("cluster123"); + config.setEnv(Env.DEV); + + VaultController controller = new VaultController(config, creds); + Field field = VaultClient.class.getDeclaredField("apiClient"); + field.setAccessible(true); + field.set(controller, mockApiClient); + return controller; + } + + private static Response buildOkHttpResponse() { + return new Response.Builder() + .request(new Request.Builder().url("https://dummy.example.com").build()) + .protocol(Protocol.HTTP_1_1) + .code(200) + .message("OK") + .header(Constants.REQUEST_ID_HEADER_KEY, "req-test-123") + .build(); + } + + private static RawFlowserviceClient mockRawFlowservice(ApiClient mockApi) { + FlowserviceClient mockFlowservice = Mockito.mock(FlowserviceClient.class); + RawFlowserviceClient mockRaw = Mockito.mock(RawFlowserviceClient.class); + when(mockApi.flowservice()).thenReturn(mockFlowservice); + when(mockFlowservice.withRawResponse()).thenReturn(mockRaw); + return mockRaw; + } + + // ── insert ──────────────────────────────────────────────────────────────── + + @Test + public void testInsert_success() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + + Map tokens = new HashMap<>(); + tokens.put("name", "tok-abc"); + V1RecordResponseObject record = V1RecordResponseObject.builder() + .skyflowId("sky-id-1") + .tokens(tokens) + .build(); + V1InsertResponse body = V1InsertResponse.builder().records(Collections.singletonList(record)).build(); + ApiClientHttpResponse httpResp = new ApiClientHttpResponse<>(body, buildOkHttpResponse()); + when(mockRaw.insert(any(), any())).thenReturn(httpResp); + + VaultController controller = createControllerWithMock(mockApi); + + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("table1").data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + + InsertResponse response = controller.insert(request); + Assert.assertNotNull(INVALID_EXCEPTION_THROWN, response); + Assert.assertEquals(1, response.getInsertedFields().size()); + Assert.assertEquals("sky-id-1", response.getInsertedFields().get(0).get("skyflowId")); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testInsert_apiErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.insert(any(), any())) + .thenThrow(new ApiClientApiException("insert failed", 401, "unauthorized")); + + VaultController controller = createControllerWithMock(mockApi); + + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("table1").data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + + try { + controller.insert(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(401, e.getHttpCode()); + } + } + + @Test + public void testInsert_networkErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.insert(any(), any())) + .thenThrow(new ApiClientException("Network error executing HTTP request")); + + VaultController controller = createControllerWithMock(mockApi); + + Map data = new HashMap<>(); + data.put("name", "john"); + ArrayList records = new ArrayList<>(); + records.add(InsertRecord.builder().table("table1").data(data).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + + try { + controller.insert(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testInsert_invalidRequestThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + VaultController controller = createControllerWithMock(mockApi); + InsertRequest request = InsertRequest.builder().records(new ArrayList<>()).build(); + try { + controller.insert(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── detokenize ──────────────────────────────────────────────────────────── + + @Test + public void testDetokenize_success() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + + V1FlowDetokenizeResponseObject record = V1FlowDetokenizeResponseObject.builder() + .token("token1") + .value("secret-value") + .build(); + V1FlowDetokenizeResponse body = V1FlowDetokenizeResponse.builder() + .response(Collections.singletonList(record)) + .build(); + ApiClientHttpResponse httpResp = + new ApiClientHttpResponse<>(body, buildOkHttpResponse()); + when(mockRaw.detokenize(any(), any())).thenReturn(httpResp); + + VaultController controller = createControllerWithMock(mockApi); + + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + + DetokenizeResponse response = controller.detokenize(request); + Assert.assertNotNull(INVALID_EXCEPTION_THROWN, response); + Assert.assertEquals(1, response.getDetokenizedFields().size()); + Assert.assertEquals("secret-value", response.getDetokenizedFields().get(0).getValue()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testDetokenize_apiErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.detokenize(any(), any())) + .thenThrow(new ApiClientApiException("detokenize failed", 401, "unauthorized")); + + VaultController controller = createControllerWithMock(mockApi); + + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + + try { + controller.detokenize(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(401, e.getHttpCode()); + } + } + + @Test + public void testDetokenize_networkErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.detokenize(any(), any())) + .thenThrow(new ApiClientException("Network error executing HTTP request")); + + VaultController controller = createControllerWithMock(mockApi); + + ArrayList data = new ArrayList<>(); + data.add(new DetokenizeData("token1")); + DetokenizeRequest request = DetokenizeRequest.builder().detokenizeData(data).build(); + + try { + controller.detokenize(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testDetokenize_nullRequestThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + VaultController controller = createControllerWithMock(mockApi); + try { + controller.detokenize(null); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── tokenize ────────────────────────────────────────────────────────────── + + @Test + public void testTokenize_success() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + + FlowTokenizeResponseObjectToken token = FlowTokenizeResponseObjectToken.builder() + .tokenGroupName("group1") + .token("tok-abc") + .build(); + V1FlowTokenizeResponseObject responseObject = V1FlowTokenizeResponseObject.builder() + .value("value1") + .tokens(Collections.singletonList(token)) + .build(); + V1FlowTokenizeResponse body = V1FlowTokenizeResponse.builder() + .response(Collections.singletonList(responseObject)) + .build(); + ApiClientHttpResponse httpResp = + new ApiClientHttpResponse<>(body, buildOkHttpResponse()); + when(mockRaw.tokenize(any(), any())).thenReturn(httpResp); + + VaultController controller = createControllerWithMock(mockApi); + + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder() + .value("value1") + .tokenGroupNames(Collections.singletonList("group1")) + .build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + + TokenizeResponse response = controller.tokenize(request); + Assert.assertNotNull(INVALID_EXCEPTION_THROWN, response); + Assert.assertEquals(1, response.getTokenizedData().size()); + Assert.assertEquals("tok-abc", response.getTokenizedData().get(0).getTokens().get("group1")); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testTokenize_apiErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.tokenize(any(), any())) + .thenThrow(new ApiClientApiException("tokenize failed", 401, "unauthorized")); + + VaultController controller = createControllerWithMock(mockApi); + + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder() + .value("value1") + .tokenGroupNames(Collections.singletonList("group1")) + .build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + + try { + controller.tokenize(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(401, e.getHttpCode()); + } + } + + @Test + public void testTokenize_networkErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.tokenize(any(), any())) + .thenThrow(new ApiClientException("Network error executing HTTP request")); + + VaultController controller = createControllerWithMock(mockApi); + + ArrayList records = new ArrayList<>(); + records.add(TokenizeRecord.builder() + .value("value1") + .tokenGroupNames(Collections.singletonList("group1")) + .build()); + TokenizeRequest request = TokenizeRequest.builder().data(records).build(); + + try { + controller.tokenize(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testTokenize_nullRequestThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + VaultController controller = createControllerWithMock(mockApi); + try { + controller.tokenize(null); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + // ── deleteTokens ────────────────────────────────────────────────────────── + + @Test + public void testDeleteTokens_success() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + + V1DeleteTokenResponseObject record = V1DeleteTokenResponseObject.builder() + .value("token1") + .build(); + V1FlowDeleteTokenResponse body = V1FlowDeleteTokenResponse.builder() + .tokens(Collections.singletonList(record)) + .build(); + ApiClientHttpResponse httpResp = + new ApiClientHttpResponse<>(body, buildOkHttpResponse()); + when(mockRaw.deletetoken(any(), any())).thenReturn(httpResp); + + VaultController controller = createControllerWithMock(mockApi); + + DeleteTokensRequest request = DeleteTokensRequest.builder() + .tokens(Collections.singletonList("token1")) + .build(); + + DeleteTokensResponse response = controller.deleteTokens(request); + Assert.assertNotNull(INVALID_EXCEPTION_THROWN, response); + Assert.assertEquals(Collections.singletonList("token1"), response.getTokens()); + Assert.assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void testDeleteTokens_apiErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.deletetoken(any(), any())) + .thenThrow(new ApiClientApiException("delete failed", 404, "not found")); + + VaultController controller = createControllerWithMock(mockApi); + + DeleteTokensRequest request = DeleteTokensRequest.builder() + .tokens(Collections.singletonList("token1")) + .build(); + + try { + controller.deleteTokens(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(404, e.getHttpCode()); + } + } + + @Test + public void testDeleteTokens_networkErrorThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + RawFlowserviceClient mockRaw = mockRawFlowservice(mockApi); + when(mockRaw.deletetoken(any(), any())) + .thenThrow(new ApiClientException("Network error executing HTTP request")); + + VaultController controller = createControllerWithMock(mockApi); + + DeleteTokensRequest request = DeleteTokensRequest.builder() + .tokens(Collections.singletonList("token1")) + .build(); + + try { + controller.deleteTokens(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + public void testDeleteTokens_nullRequestThrowsSkyflowException() throws Exception { + ApiClient mockApi = Mockito.mock(ApiClient.class); + VaultController controller = createControllerWithMock(mockApi); + try { + controller.deleteTokens(null); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertNotNull(e.getMessage()); + } + } +} diff --git a/v2/src/main/java/com/skyflow/vault/tokens/TokenizeResponse.java b/v2/src/main/java/com/skyflow/vault/tokens/TokenizeResponse.java index d8d8072b..c77787cf 100644 --- a/v2/src/main/java/com/skyflow/vault/tokens/TokenizeResponse.java +++ b/v2/src/main/java/com/skyflow/vault/tokens/TokenizeResponse.java @@ -1,9 +1,13 @@ package com.skyflow.vault.tokens; -import com.google.gson.*; - import java.util.List; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + public class TokenizeResponse { private final List tokens;