Skip to content

Commit 1f49c81

Browse files
SK-2646: add support for delete and tokenize schemaless api support in java v3 sdk
1 parent 029cb68 commit 1f49c81

23 files changed

Lines changed: 2873 additions & 0 deletions

common/src/main/java/com/skyflow/errors/ErrorMessage.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ public enum ErrorMessage {
9292
EmptyTokenInDetokenizeData("%s0 Validation error. Invalid data tokens. Specify a valid data token."),
9393
TokensSizeExceedError("%s0 Maximum number of tokens exceeded. The limit is 10000."),
9494

95+
// Delete Tokens
96+
DeleteTokensRequestNull("%s0 Validation error. DeleteTokensRequest object is null. Specify a valid DeleteTokensRequest object."),
97+
EmptyDeleteTokensData("%s0 Validation error. Tokens list is empty. Specify at least one token to delete."),
98+
EmptyTokenInDeleteTokensData("%s0 Validation error. Invalid token in delete tokens request. Specify a valid token."),
99+
DeleteTokensSizeExceedError("%s0 Maximum number of tokens exceeded. The limit is 10000."),
100+
95101
// Get
96102
IdsKeyError("%s0 Validation error. 'ids' key is missing from the payload. Specify an 'ids' key."),
97103
EmptyIds("%s0 Validation error. 'ids' can't be empty. Specify at least one id."),
@@ -128,6 +134,13 @@ public enum ErrorMessage {
128134
// Tokenize
129135
ColumnValuesKeyErrorTokenize("%s0 Validation error. 'columnValues' key is missing from the payload. Specify a 'columnValues' key."),
130136
EmptyColumnGroupInColumnValue("%s0 Validation error. Invalid column group in column value. Specify a valid column group."),
137+
TokenizeRequestNull("%s0 Validation error. TokenizeRequest object is null. Specify a valid TokenizeRequest object."),
138+
EmptyTokenizeData("%s0 Validation error. Tokenize data is empty. Specify at least one tokenize record."),
139+
TokenizeRecordNull("%s0 Validation error. TokenizeRecord in the list is null. Specify a valid TokenizeRecord object."),
140+
EmptyValueInTokenizeRecord("%s0 Validation error. Value in TokenizeRecord is null or empty. Specify a valid value."),
141+
EmptyTokenGroupNamesInTokenizeRecord("%s0 Validation error. TokenGroupNames in TokenizeRecord is null or empty. Specify at least one token group name."),
142+
EmptyTokenGroupNameInTokenizeRecord("%s0 Validation error. Token group name in TokenizeRecord is null or empty. Specify a valid token group name."),
143+
TokenizeDataSizeExceedError("%s0 Maximum number of tokenize records exceeded. The limit is 10000."),
131144

132145
// Connection
133146
InvalidRequestHeaders("%s0 Validation error. Request headers aren't valid. Specify valid request headers."),

common/src/main/java/com/skyflow/logs/ErrorLogs.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,18 @@ public enum ErrorLogs {
113113
COLUMN_VALUES_IS_REQUIRED_TOKENIZE("Invalid %s1 request. ColumnValues are required."),
114114
EMPTY_OR_NULL_COLUMN_GROUP_IN_COLUMN_VALUES("Invalid %s1 request. Column group can not be null or empty in column values at index %s2."),
115115
TOKENIZE_REQUEST_REJECTED("Tokenize request resulted in failure."),
116+
TOKENIZE_REQUEST_NULL("Invalid %s1 request. Tokenize request can not be null."),
117+
EMPTY_TOKENIZE_DATA("Invalid %s1 request. Tokenize data can not be empty."),
118+
TOKENIZE_RECORD_NULL("Invalid %s1 request. TokenizeRecord in list can not be null."),
119+
EMPTY_VALUE_IN_TOKENIZE_RECORD("Invalid %s1 request. Value in TokenizeRecord can not be null or empty."),
120+
EMPTY_TOKEN_GROUP_NAMES_IN_TOKENIZE_RECORD("Invalid %s1 request. TokenGroupNames in TokenizeRecord can not be null or empty."),
121+
EMPTY_TOKEN_GROUP_NAME_IN_TOKENIZE_RECORD("Invalid %s1 request. Token group name in TokenizeRecord can not be null or empty at index %s2."),
122+
TOKENIZE_DATA_SIZE_EXCEED("Maximum number of tokenize records exceeded. The limit is 10000."),
116123
DELETE_REQUEST_REJECTED("Delete request resulted in failure."),
124+
DELETE_TOKENS_REQUEST_NULL("Invalid %s1 request. DeleteTokens request can not be null."),
125+
EMPTY_DELETE_TOKENS_DATA("Invalid %s1 request. Delete tokens data can not be empty."),
126+
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."),
127+
DELETE_TOKENS_SIZE_EXCEED("Maximum number of tokens exceeded. The limit is 10000."),
117128

118129
// invoke connection interface
119130
INVOKE_CONNECTION_INVALID_CONNECTION_URL("Invalid %s1 request. Connection URL is not a valid URL."),

common/src/main/java/com/skyflow/logs/InfoLogs.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public enum InfoLogs {
5858
DELETE_REQUEST_RESOLVED("Delete request resolved."),
5959
DELETE_SUCCESS("Data deleted."),
6060

61+
// Delete Tokens interface
62+
DELETE_TOKENS_TRIGGERED("DeleteTokens method triggered."),
63+
VALIDATE_DELETE_TOKENS_REQUEST("Validating delete tokens request."),
64+
DELETE_TOKENS_REQUEST_RESOLVED("DeleteTokens request resolved."),
65+
DELETE_TOKENS_SUCCESS("Tokens deleted."),
66+
6167
// Query interface
6268
QUERY_TRIGGERED("Query method triggered."),
6369
VALIDATING_QUERY_REQUEST("Validating query request."),

v3/src/main/java/com/skyflow/VaultClient.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import com.skyflow.utils.validations.Validations;
2222
import com.skyflow.vault.data.DetokenizeRequest;
2323
import com.skyflow.vault.data.InsertRecord;
24+
import com.skyflow.vault.data.DeleteTokensRequest;
25+
import com.skyflow.vault.data.TokenizeRequest;
26+
import com.skyflow.vault.data.TokenizeRecord;
2427
import io.github.cdimascio.dotenv.Dotenv;
2528
import io.github.cdimascio.dotenv.DotenvException;
2629
import okhttp3.OkHttpClient;
@@ -216,4 +219,27 @@ protected com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDetoke
216219
}
217220
return builder.build();
218221
}
222+
223+
protected com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest getDeleteTokensRequestBody(DeleteTokensRequest request) {
224+
return com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest.builder()
225+
.vaultId(this.vaultConfig.getVaultId())
226+
.tokens(request.getTokens())
227+
.build();
228+
}
229+
230+
protected com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest getTokenizeRequestBody(TokenizeRequest request) {
231+
List<com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject> dataList = new ArrayList<>();
232+
for (TokenizeRecord record : request.getData()) {
233+
com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject obj =
234+
com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject.builder()
235+
.value(record.getValue())
236+
.tokenGroupNames(record.getTokenGroupNames())
237+
.build();
238+
dataList.add(obj);
239+
}
240+
return com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest.builder()
241+
.vaultId(this.vaultConfig.getVaultId())
242+
.data(dataList)
243+
.build();
244+
}
219245
}

v3/src/main/java/com/skyflow/utils/Constants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ public final class Constants extends BaseConstants {
1717
public static final Integer DETOKENIZE_CONCURRENCY_LIMIT = 1;
1818
public static final Integer MAX_DETOKENIZE_BATCH_SIZE = 1000;
1919
public static final Integer MAX_DETOKENIZE_CONCURRENCY_LIMIT = 10;
20+
public static final Integer DELETE_TOKENS_BATCH_SIZE = 50;
21+
public static final Integer DELETE_TOKENS_CONCURRENCY_LIMIT = 1;
22+
public static final Integer MAX_DELETE_TOKENS_BATCH_SIZE = 1000;
23+
public static final Integer MAX_DELETE_TOKENS_CONCURRENCY_LIMIT = 10;
24+
public static final Integer TOKENIZE_BATCH_SIZE = 50;
25+
public static final Integer TOKENIZE_CONCURRENCY_LIMIT = 1;
26+
public static final Integer MAX_TOKENIZE_BATCH_SIZE = 1000;
27+
public static final Integer MAX_TOKENIZE_CONCURRENCY_LIMIT = 10;
2028
public static final String DEFAULT_SDK_VERSION = "v3";
2129

2230
static {

v3/src/main/java/com/skyflow/utils/Utils.java

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
import com.skyflow.vault.data.ErrorRecord;
1818
import com.skyflow.vault.data.Success;
1919
import com.skyflow.vault.data.Token;
20+
import com.skyflow.vault.data.DeleteTokensResponse;
21+
import com.skyflow.vault.data.DeleteTokensSuccess;
22+
import com.skyflow.vault.data.TokenizeResponse;
23+
import com.skyflow.vault.data.TokenizeSuccess;
2024
import io.github.cdimascio.dotenv.Dotenv;
2125
import io.github.cdimascio.dotenv.DotenvException;
2226

@@ -282,4 +286,215 @@ public static boolean isValidURL(String url) {
282286
return parsedUrl.getHost() != null && !parsedUrl.getHost().isEmpty();
283287
}
284288
}
289+
290+
public static List<com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest> createDeleteTokensBatches(
291+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest request, int batchSize) {
292+
List<com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest> batches = new ArrayList<>();
293+
List<String> tokens = request.getTokens().get();
294+
for (int i = 0; i < tokens.size(); i += batchSize) {
295+
List<String> batchTokens = tokens.subList(i, Math.min(i + batchSize, tokens.size()));
296+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest batchRequest =
297+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest.builder()
298+
.vaultId(request.getVaultId())
299+
.tokens(new ArrayList<>(batchTokens))
300+
.build();
301+
batches.add(batchRequest);
302+
}
303+
return batches;
304+
}
305+
306+
public static List<ErrorRecord> handleDeleteTokensBatchException(
307+
Throwable ex,
308+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowDeleteTokenRequest batch,
309+
int batchNumber, int batchSize
310+
) {
311+
List<ErrorRecord> errorRecords = new ArrayList<>();
312+
Throwable cause = ex.getCause();
313+
if (cause instanceof ApiClientApiException) {
314+
ApiClientApiException apiException = (ApiClientApiException) cause;
315+
Map<String, Object> responseBody = (Map<String, Object>) apiException.body();
316+
int indexNumber = batchNumber * batchSize;
317+
if (responseBody != null) {
318+
if (responseBody.containsKey("tokens")) {
319+
Object tokensList = responseBody.get("tokens");
320+
if (tokensList instanceof List) {
321+
List<?> recordsList = (List<?>) tokensList;
322+
for (Object record : recordsList) {
323+
if (record instanceof Map) {
324+
Map<String, Object> recordMap = (Map<String, Object>) record;
325+
ErrorRecord err = Utils.createErrorRecord(recordMap, indexNumber);
326+
errorRecords.add(err);
327+
indexNumber++;
328+
}
329+
}
330+
}
331+
} else if (responseBody.containsKey("error")) {
332+
Map<String, Object> recordMap = (Map<String, Object>) responseBody.get("error");
333+
for (int j = 0; j < batch.getTokens().get().size(); j++) {
334+
ErrorRecord err = Utils.createErrorRecord(recordMap, indexNumber);
335+
errorRecords.add(err);
336+
indexNumber++;
337+
}
338+
}
339+
}
340+
} else {
341+
int indexNumber = batchNumber * batchSize;
342+
for (int j = 0; j < batch.getTokens().get().size(); j++) {
343+
ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500);
344+
errorRecords.add(err);
345+
indexNumber++;
346+
}
347+
}
348+
return errorRecords;
349+
}
350+
351+
public static DeleteTokensResponse formatDeleteTokensResponse(
352+
com.skyflow.generated.rest.types.V1FlowDeleteTokenResponse response, int batch, int batchSize) {
353+
if (response != null && response.getTokens().isPresent()) {
354+
List<com.skyflow.generated.rest.types.V1DeleteTokenResponseObject> records = response.getTokens().get();
355+
List<ErrorRecord> errorRecords = new ArrayList<>();
356+
List<DeleteTokensSuccess> successRecords = new ArrayList<>();
357+
int indexNumber = batch * batchSize;
358+
for (com.skyflow.generated.rest.types.V1DeleteTokenResponseObject record : records) {
359+
// The API returns the token string in "value" field regardless of success or error
360+
String tokenValue = record.getValue().orElse(null);
361+
if (record.getError().isPresent()
362+
&& record.getError().get() != null
363+
&& !record.getError().get().isEmpty()
364+
&& record.getHttpCode().orElse(200) != 200) {
365+
ErrorRecord errorRecord = new ErrorRecord(indexNumber, record.getError().get(),
366+
record.getHttpCode().orElse(500));
367+
errorRecords.add(errorRecord);
368+
} else {
369+
DeleteTokensSuccess success = new DeleteTokensSuccess(indexNumber, tokenValue);
370+
successRecords.add(success);
371+
}
372+
indexNumber++;
373+
}
374+
return new DeleteTokensResponse(successRecords, errorRecords);
375+
}
376+
return null;
377+
}
378+
379+
public static List<com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest> createTokenizeBatches(
380+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest request, int batchSize) {
381+
List<com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest> batches = new ArrayList<>();
382+
List<com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject> data = request.getData().get();
383+
for (int i = 0; i < data.size(); i += batchSize) {
384+
List<com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject> batchData =
385+
data.subList(i, Math.min(i + batchSize, data.size()));
386+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest batchRequest =
387+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest.builder()
388+
.vaultId(request.getVaultId())
389+
.data(new ArrayList<>(batchData))
390+
.build();
391+
batches.add(batchRequest);
392+
}
393+
return batches;
394+
}
395+
396+
public static List<ErrorRecord> handleTokenizeBatchException(
397+
Throwable ex,
398+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest batch,
399+
int batchNumber, int batchSize
400+
) {
401+
List<ErrorRecord> errorRecords = new ArrayList<>();
402+
Throwable cause = ex.getCause();
403+
if (cause instanceof ApiClientApiException) {
404+
ApiClientApiException apiException = (ApiClientApiException) cause;
405+
Map<String, Object> responseBody = (Map<String, Object>) apiException.body();
406+
int indexNumber = batchNumber * batchSize;
407+
if (responseBody != null) {
408+
if (responseBody.containsKey("response")) {
409+
Object responseList = responseBody.get("response");
410+
if (responseList instanceof List) {
411+
List<?> recordsList = (List<?>) responseList;
412+
for (Object record : recordsList) {
413+
if (record instanceof Map) {
414+
Map<String, Object> recordMap = (Map<String, Object>) record;
415+
ErrorRecord err = Utils.createErrorRecord(recordMap, indexNumber);
416+
errorRecords.add(err);
417+
indexNumber++;
418+
}
419+
}
420+
}
421+
} else if (responseBody.containsKey("error")) {
422+
Map<String, Object> recordMap = (Map<String, Object>) responseBody.get("error");
423+
int batchDataSize = batch.getData().isPresent() ? batch.getData().get().size() : 0;
424+
for (int j = 0; j < batchDataSize; j++) {
425+
ErrorRecord err = Utils.createErrorRecord(recordMap, indexNumber);
426+
errorRecords.add(err);
427+
indexNumber++;
428+
}
429+
}
430+
}
431+
} else {
432+
int indexNumber = batchNumber * batchSize;
433+
int batchDataSize = batch.getData().isPresent() ? batch.getData().get().size() : 0;
434+
for (int j = 0; j < batchDataSize; j++) {
435+
ErrorRecord err = new ErrorRecord(indexNumber, ex.getMessage(), 500);
436+
errorRecords.add(err);
437+
indexNumber++;
438+
}
439+
}
440+
return errorRecords;
441+
}
442+
443+
public static TokenizeResponse formatTokenizeResponse(
444+
com.skyflow.generated.rest.types.V1FlowTokenizeResponse response,
445+
com.skyflow.generated.rest.resources.flowservice.requests.V1FlowTokenizeRequest batchRequest,
446+
int batchNumber, int batchSize) {
447+
if (response != null && response.getResponse().isPresent()) {
448+
// The API returns a flat list — one entry per (value x tokenGroupName).
449+
// We group them by input record position using the request's tokenGroupNames count
450+
// so that records with identical values are still treated as separate entries.
451+
List<com.skyflow.generated.rest.types.V1FlowTokenizeResponseObject> flatList =
452+
response.getResponse().get();
453+
List<com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject> requestData =
454+
batchRequest.getData().isPresent() ? batchRequest.getData().get() : new ArrayList<>();
455+
456+
List<TokenizeSuccess> successRecords = new ArrayList<>();
457+
List<ErrorRecord> errorRecords = new ArrayList<>();
458+
459+
int flatIndex = 0;
460+
for (int i = 0; i < requestData.size(); i++) {
461+
int inputRecordIndex = batchNumber * batchSize + i;
462+
com.skyflow.generated.rest.types.V1FlowTokenizeRequestObject reqObj = requestData.get(i);
463+
List<String> groupNames = reqObj.getTokenGroupNames().isPresent()
464+
? reqObj.getTokenGroupNames().get() : new ArrayList<>();
465+
int groupCount = groupNames.size();
466+
467+
// Consume exactly groupCount entries from the flat response for this record
468+
TokenizeSuccess successEntry = null;
469+
for (int g = 0; g < groupCount && flatIndex < flatList.size(); g++, flatIndex++) {
470+
com.skyflow.generated.rest.types.V1FlowTokenizeResponseObject obj = flatList.get(flatIndex);
471+
Map<String, Object> props = obj.getAdditionalProperties();
472+
473+
Object value = obj.getValue().isPresent() ? obj.getValue().get()
474+
: (props != null ? props.get("value") : null);
475+
String tokenGroupName = props != null ? (String) props.get("tokenGroupName") : null;
476+
String token = props != null ? (String) props.get("token") : null;
477+
String errorMsg = (props != null && props.containsKey("error") && props.get("error") != null)
478+
? String.valueOf(props.get("error")) : null;
479+
int httpCode = (props != null && props.containsKey("httpCode") && props.get("httpCode") instanceof Number)
480+
? ((Number) props.get("httpCode")).intValue() : 200;
481+
482+
if (errorMsg != null) {
483+
errorRecords.add(new ErrorRecord(inputRecordIndex, errorMsg, httpCode));
484+
} else {
485+
if (successEntry == null) {
486+
successEntry = new TokenizeSuccess(inputRecordIndex, value);
487+
}
488+
successEntry.addToken(tokenGroupName, token);
489+
}
490+
}
491+
if (successEntry != null) {
492+
successRecords.add(successEntry);
493+
}
494+
}
495+
496+
return new TokenizeResponse(successRecords, errorRecords);
497+
}
498+
return null;
499+
}
285500
}

0 commit comments

Comments
 (0)