diff --git a/flowvault/pom.xml b/flowvault/pom.xml index 14e6e75a..66c6b7c1 100644 --- a/flowvault/pom.xml +++ b/flowvault/pom.xml @@ -11,7 +11,7 @@ skyflow-flowvault-java - 3.0.0-beta.13-dev.962614b1 + 3.0.0-beta.13-dev.7c0fb7be jar ${project.groupId}:${project.artifactId} Skyflow V3 SDK for the Java programming language 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 39565674..667028ce 100644 --- a/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java +++ b/flowvault/src/main/java/com/skyflow/utils/validations/Validations.java @@ -205,6 +205,37 @@ public static void validateInsertRequest(InsertRequest insertRequest) throws Sky } } } + validateInsertRecordTokens(record.getTokens()); + } + } + + // Tokens are optional on an insert record, but when supplied the map must not be empty and + // every entry must have a non-blank key and value — mirroring the checks on data above. + private static void validateInsertRecordTokens(Map tokens) throws SkyflowException { + if (tokens == null) { + return; + } + if (tokens.isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_TOKENS.getLog(), InterfaceName.INSERT.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyTokens.getMessage()); + } + for (String key : tokens.keySet()) { + if (key == null || key.trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_OR_NULL_KEY_IN_TOKENS.getLog(), InterfaceName.INSERT.getName() + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyKeyInTokens.getMessage()); + } + Object value = tokens.get(key); + if (value == null || value.toString().trim().isEmpty()) { + LogUtil.printErrorLog(Utils.parameterizedString( + ErrorLogs.EMPTY_OR_NULL_VALUE_IN_TOKENS.getLog(), + InterfaceName.INSERT.getName(), key + )); + throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyValueInTokens.getMessage()); + } } } diff --git a/flowvault/src/test/java/com/skyflow/utils/RequestFidelityTests.java b/flowvault/src/test/java/com/skyflow/utils/RequestFidelityTests.java index 13e2506e..42b3dac9 100644 --- a/flowvault/src/test/java/com/skyflow/utils/RequestFidelityTests.java +++ b/flowvault/src/test/java/com/skyflow/utils/RequestFidelityTests.java @@ -528,9 +528,9 @@ private static com.skyflow.generated.rest.types.V1Upsert upsertWire(String updat // ── insert: tokens map ─────────────────────────────────────────────────── @Test - public void testBulkInsert_emptyTokensMapIsOmittedFromWire_knownGap() { - // KNOWN GAP: an explicitly-set-but-empty tokens map is dropped rather than sent as {}. - // Pinning current behavior — a caller cannot distinguish "no tokens" from "empty tokens". + public void testBulkInsert_emptyTokensMapIsOmittedFromWire() { + // Validations.validateInsertRequest rejects an explicitly-set-but-empty tokens map before + // the body builder runs; this pins the builder's own behavior when called directly. Map data = new HashMap<>(); data.put("name", "john"); BulkInsertRequestRecord record = BulkInsertRequestRecord.builder() diff --git a/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java b/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java index 44a58686..7c20526a 100644 --- a/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java +++ b/flowvault/src/test/java/com/skyflow/utils/validations/ValidationsTests.java @@ -568,6 +568,79 @@ public void testValidateInsertRequest_validRequestWithTokens() { } } + @Test + public void testValidateInsertRequest_emptyTokensMapThrows() { + ArrayList records = new ArrayList<>(); + records.add(InsertRequestRecord.builder().tableName("table1").tokens(new HashMap<>()).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorMessage.EmptyTokens.getMessage(), e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_nullKeyInTokensThrows() { + Map tokens = new HashMap<>(); + tokens.put(null, "tok-abc"); + ArrayList records = new ArrayList<>(); + records.add(InsertRequestRecord.builder().tableName("table1").tokens(tokens).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_blankKeyInTokensThrows() { + Map tokens = new HashMap<>(); + tokens.put(" ", "tok-abc"); + ArrayList records = new ArrayList<>(); + records.add(InsertRequestRecord.builder().tableName("table1").tokens(tokens).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorMessage.EmptyKeyInTokens.getMessage(), e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_nullValueInTokensThrows() { + Map tokens = new HashMap<>(); + tokens.put("name", null); + ArrayList records = new ArrayList<>(); + records.add(InsertRequestRecord.builder().tableName("table1").tokens(tokens).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorMessage.EmptyValueInTokens.getMessage(), e.getMessage()); + } + } + + @Test + public void testValidateInsertRequest_blankValueInTokensThrows() { + Map tokens = new HashMap<>(); + tokens.put("name", " "); + ArrayList records = new ArrayList<>(); + records.add(InsertRequestRecord.builder().tableName("table1").tokens(tokens).build()); + InsertRequest request = InsertRequest.builder().records(records).build(); + try { + Validations.validateInsertRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorMessage.EmptyValueInTokens.getMessage(), e.getMessage()); + } + } + // ── validateDetokenizeRequest ───────────────────────────────────────────── @Test