diff --git a/src/main/java/com/skyflow/VaultClient.java b/src/main/java/com/skyflow/VaultClient.java index 76de35e8..b9cdfd98 100644 --- a/src/main/java/com/skyflow/VaultClient.java +++ b/src/main/java/com/skyflow/VaultClient.java @@ -144,7 +144,7 @@ protected RecordServiceInsertRecordBody getBulkInsertRequestBody(InsertRequest r .tokenization(request.getReturnTokens()) .homogeneous(request.getHomogeneous()) .upsert(request.getUpsert()) - .byot(request.getTokenMode().getBYOT()) + .byot(request.getTokenMode().getByot()) .records(records) .build(); } @@ -171,14 +171,14 @@ protected RecordServiceBatchOperationBody getBatchInsertRequestBody(InsertReques return RecordServiceBatchOperationBody.builder() .continueOnError(true) - .byot(request.getTokenMode().getBYOT()) + .byot(request.getTokenMode().getByot()) .records(records) .build(); } protected RecordServiceUpdateRecordBody getUpdateRequestBody(UpdateRequest request) { RecordServiceUpdateRecordBody.Builder updateRequestBodyBuilder = RecordServiceUpdateRecordBody.builder(); - updateRequestBodyBuilder.byot(request.getTokenMode().getBYOT()); + updateRequestBodyBuilder.byot(request.getTokenMode().getByot()); updateRequestBodyBuilder.tokenization(request.getReturnTokens()); V1FieldRecords.Builder recordBuilder = V1FieldRecords.builder(); HashMap values = request.getData(); diff --git a/src/main/java/com/skyflow/enums/TokenMode.java b/src/main/java/com/skyflow/enums/TokenMode.java index 63e9b8a9..f855d7cc 100644 --- a/src/main/java/com/skyflow/enums/TokenMode.java +++ b/src/main/java/com/skyflow/enums/TokenMode.java @@ -1,6 +1,8 @@ package com.skyflow.enums; import com.skyflow.generated.rest.types.V1Byot; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.logger.LogUtil; public enum TokenMode { DISABLE(V1Byot.DISABLE), @@ -13,10 +15,17 @@ public enum TokenMode { this.byot = byot; } - public V1Byot getBYOT() { + public V1Byot getByot() { return byot; } + /** @deprecated Use {@link #getByot()} instead. */ + @Deprecated(since = "2.1", forRemoval = true) + public V1Byot getBYOT() { + LogUtil.printWarningLog(InfoLogs.DEPRECATED_GET_BYOT.getLog()); + return getByot(); + } + @Override public String toString() { return byot.toString(); diff --git a/src/main/java/com/skyflow/logs/InfoLogs.java b/src/main/java/com/skyflow/logs/InfoLogs.java index 4391f1b1..de93a924 100644 --- a/src/main/java/com/skyflow/logs/InfoLogs.java +++ b/src/main/java/com/skyflow/logs/InfoLogs.java @@ -98,7 +98,9 @@ public enum InfoLogs { // Deprecation warnings — v2 backward compat DEPRECATED_SKYFLOW_ID_KEY("[DEPRECATED] Response key 'skyflow_id' is deprecated and will be removed in an upcoming release. Use 'skyflowId' instead."), - DEPRECATED_DOWNLOAD_URL("[DEPRECATED] Method 'downloadURL()' is deprecated and will be removed in an upcoming release. Use 'downloadUrl()' instead."); + DEPRECATED_SKYFLOW_ID_REQUEST_KEY("[DEPRECATED] Request data key 'skyflow_id' is deprecated and will be removed in an upcoming release. Use 'skyflowId' instead."), + DEPRECATED_DOWNLOAD_URL("[DEPRECATED] Method 'downloadURL()' is deprecated and will be removed in an upcoming release. Use 'downloadUrl()' instead."), + DEPRECATED_GET_BYOT("[DEPRECATED] Method 'getBYOT()' is deprecated and will be removed in an upcoming release. Use 'getByot()' instead."); diff --git a/src/main/java/com/skyflow/utils/logger/LogUtil.java b/src/main/java/com/skyflow/utils/logger/LogUtil.java index a7523e6d..85655506 100644 --- a/src/main/java/com/skyflow/utils/logger/LogUtil.java +++ b/src/main/java/com/skyflow/utils/logger/LogUtil.java @@ -4,16 +4,12 @@ import com.skyflow.logs.InfoLogs; import com.skyflow.utils.Constants; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.*; public final class LogUtil { private static final Logger LOGGER = Logger.getLogger(LogUtil.class.getName()); private static final String SDK_LOG_PREFIX = "[" + Constants.SDK_PREFIX + "] "; private static boolean isLoggerSetupDone = false; - private static final Set WARNED_ONCE = Collections.newSetFromMap(new ConcurrentHashMap<>()); synchronized public static void setupLogger(LogLevel logLevel) { isLoggerSetupDone = true; @@ -60,11 +56,6 @@ public static void printWarningLog(String message) { LOGGER.warning(SDK_LOG_PREFIX + message); } - public static void printWarningLogOnce(String message) { - if (isLoggerSetupDone && WARNED_ONCE.add(message)) - LOGGER.warning(SDK_LOG_PREFIX + message); - } - public static void printInfoLog(String message) { if (isLoggerSetupDone) LOGGER.info(SDK_LOG_PREFIX + message); diff --git a/src/main/java/com/skyflow/utils/validations/Validations.java b/src/main/java/com/skyflow/utils/validations/Validations.java index 0a4f847c..3bd75626 100644 --- a/src/main/java/com/skyflow/utils/validations/Validations.java +++ b/src/main/java/com/skyflow/utils/validations/Validations.java @@ -548,17 +548,17 @@ public static void validateUpdateRequest(UpdateRequest updateRequest) throws Sky ErrorLogs.EMPTY_DATA.getLog(), InterfaceName.UPDATE.getName() )); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyData.getMessage()); - } else if (!data.containsKey("skyflow_id")) { + } else if (!data.containsKey("skyflowId") && !data.containsKey("skyflow_id")) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.SKYFLOW_ID_IS_REQUIRED.getLog(), InterfaceName.UPDATE.getName() )); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.SkyflowIdKeyError.getMessage()); - } else if (!(data.get("skyflow_id") instanceof String)) { + } else if (!(resolveUpdateId(data) instanceof String)) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.INVALID_SKYFLOW_ID_TYPE.getLog(), InterfaceName.UPDATE.getName() )); throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.InvalidSkyflowIdType.getMessage()); - } else if (data.get("skyflow_id").toString().trim().isEmpty()) { + } else if (resolveUpdateId(data).toString().trim().isEmpty()) { LogUtil.printErrorLog(Utils.parameterizedString( ErrorLogs.EMPTY_SKYFLOW_ID.getLog(), InterfaceName.UPDATE.getName() )); @@ -973,6 +973,13 @@ public static void validateDeidentifyFileRequest(DeidentifyFileRequest request) } } + static Object resolveUpdateId(HashMap data) { + if (data.containsKey("skyflowId")) { + return data.get("skyflowId"); + } + return data.get("skyflow_id"); + } + public static void validateGetDetectRunRequest(GetDetectRunRequest request) throws SkyflowException { if (request == null) { throw new SkyflowException(ErrorCode.INVALID_INPUT.getCode(), ErrorMessage.EmptyRequestBody.getMessage()); diff --git a/src/main/java/com/skyflow/vault/controller/VaultController.java b/src/main/java/com/skyflow/vault/controller/VaultController.java index f4e7c7cf..434afeae 100644 --- a/src/main/java/com/skyflow/vault/controller/VaultController.java +++ b/src/main/java/com/skyflow/vault/controller/VaultController.java @@ -81,6 +81,18 @@ public VaultController(VaultConfig vaultConfig, Credentials credentials) { super(vaultConfig, credentials); } + private static String extractUpdateSkyflowId(HashMap data) { + if (data.containsKey("skyflowId")) { + if (data.containsKey("skyflow_id")) { + data.remove("skyflow_id"); + LogUtil.printWarningLog(InfoLogs.DEPRECATED_SKYFLOW_ID_REQUEST_KEY.getLog()); + } + return data.remove("skyflowId").toString(); + } + LogUtil.printWarningLog(InfoLogs.DEPRECATED_SKYFLOW_ID_REQUEST_KEY.getLog()); + return data.remove("skyflow_id").toString(); + } + private static synchronized HashMap getFormattedBatchInsertRecord(Object record, Integer requestIndex) { HashMap insertRecord = new HashMap<>(); String jsonString = GSON.toJson(record); @@ -133,7 +145,7 @@ private static synchronized HashMap getFormattedGetRecord(V1Fiel if (getRecord.containsKey("skyflow_id")) { getRecord.put("skyflowId", getRecord.get("skyflow_id")); - LogUtil.printWarningLogOnce(InfoLogs.DEPRECATED_SKYFLOW_ID_KEY.getLog()); + LogUtil.printWarningLog(InfoLogs.DEPRECATED_SKYFLOW_ID_KEY.getLog()); } return getRecord; @@ -158,7 +170,7 @@ private static synchronized HashMap getFormattedQueryRecord(V1Fi if (queryRecord.containsKey("skyflow_id")) { queryRecord.put("skyflowId", queryRecord.get("skyflow_id")); - LogUtil.printWarningLogOnce(InfoLogs.DEPRECATED_SKYFLOW_ID_KEY.getLog()); + LogUtil.printWarningLog(InfoLogs.DEPRECATED_SKYFLOW_ID_KEY.getLog()); } return queryRecord; @@ -336,7 +348,7 @@ public UpdateResponse update(UpdateRequest updateRequest) throws SkyflowExceptio result = super.getRecordsApi().recordServiceUpdateRecord( super.getVaultConfig().getVaultId(), updateRequest.getTable(), - updateRequest.getData().remove("skyflow_id").toString(), + extractUpdateSkyflowId(updateRequest.getData()), updateBody, requestOptions ); diff --git a/src/test/java/com/skyflow/enums/TokenModeTest.java b/src/test/java/com/skyflow/enums/TokenModeTest.java new file mode 100644 index 00000000..72236a16 --- /dev/null +++ b/src/test/java/com/skyflow/enums/TokenModeTest.java @@ -0,0 +1,113 @@ +package com.skyflow.enums; + +import com.skyflow.enums.LogLevel; +import com.skyflow.generated.rest.types.V1Byot; +import com.skyflow.logs.InfoLogs; +import com.skyflow.utils.logger.LogUtil; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +public class TokenModeTest { + + private static class CapturingHandler extends Handler { + final List records = new ArrayList<>(); + @Override public void publish(LogRecord r) { records.add(r); } + @Override public void flush() {} + @Override public void close() {} + } + + private CapturingHandler attachCapture() { + CapturingHandler handler = new CapturingHandler(); + handler.setLevel(Level.ALL); + Logger.getLogger(LogUtil.class.getName()).addHandler(handler); + return handler; + } + + // --- getByot() --- + + @Test + public void testGetByotDisable() { + Assert.assertEquals(V1Byot.DISABLE, TokenMode.DISABLE.getByot()); + } + + @Test + public void testGetByotEnable() { + Assert.assertEquals(V1Byot.ENABLE, TokenMode.ENABLE.getByot()); + } + + @Test + public void testGetByotEnableStrict() { + Assert.assertEquals(V1Byot.ENABLE_STRICT, TokenMode.ENABLE_STRICT.getByot()); + } + + // --- getBYOT() delegates and emits a runtime warning --- + + @Test + @SuppressWarnings("deprecation") + public void testGetBYOTDelegatesToGetByotDisable() { + Assert.assertEquals(V1Byot.DISABLE, TokenMode.DISABLE.getBYOT()); + } + + @Test + @SuppressWarnings("deprecation") + public void testGetBYOTDelegatesToGetByotEnable() { + Assert.assertEquals(V1Byot.ENABLE, TokenMode.ENABLE.getBYOT()); + } + + @Test + @SuppressWarnings("deprecation") + public void testGetBYOTDelegatesToGetByotEnableStrict() { + Assert.assertEquals(V1Byot.ENABLE_STRICT, TokenMode.ENABLE_STRICT.getBYOT()); + } + + @Test + @SuppressWarnings("deprecation") + public void testGetBYOTEmitsDeprecationWarning() { + LogUtil.setupLogger(LogLevel.INFO); + CapturingHandler handler = attachCapture(); + + TokenMode.ENABLE.getBYOT(); + + boolean warnFired = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.WARNING) + && r.getMessage().contains(InfoLogs.DEPRECATED_GET_BYOT.getLog())); + Assert.assertTrue("getBYOT() should emit a deprecation warning log", warnFired); + } + + @Test + @SuppressWarnings("deprecation") + public void testGetBYOTWarningIsSuppressedAtErrorLevel() { + LogUtil.setupLogger(LogLevel.ERROR); + CapturingHandler handler = attachCapture(); + + TokenMode.ENABLE.getBYOT(); + + boolean warnFired = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.WARNING)); + Assert.assertFalse("getBYOT() warning should be suppressed at ERROR log level", warnFired); + } + + // --- toString() --- + + @Test + public void testToStringDisable() { + Assert.assertEquals("DISABLE", TokenMode.DISABLE.toString()); + } + + @Test + public void testToStringEnable() { + Assert.assertEquals("ENABLE", TokenMode.ENABLE.toString()); + } + + @Test + public void testToStringEnableStrict() { + Assert.assertEquals("ENABLE_STRICT", TokenMode.ENABLE_STRICT.toString()); + } +} diff --git a/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java b/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java new file mode 100644 index 00000000..9754687a --- /dev/null +++ b/src/test/java/com/skyflow/utils/logger/LogUtilLevelTests.java @@ -0,0 +1,99 @@ +package com.skyflow.utils.logger; + +import com.skyflow.enums.LogLevel; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +public class LogUtilLevelTests { + + private static class CapturingHandler extends Handler { + final List records = new ArrayList<>(); + + @Override + public void publish(LogRecord record) { + records.add(record); + } + + @Override public void flush() {} + @Override public void close() {} + } + + // setupLogger calls LogManager.reset() which clears all handlers, + // so the capturing handler must be attached after setupLogger runs. + private CapturingHandler attachCapture() { + CapturingHandler handler = new CapturingHandler(); + handler.setLevel(Level.ALL); + Logger.getLogger(LogUtil.class.getName()).addHandler(handler); + return handler; + } + + @Test + public void testWarnLogAppearsWhenLogLevelIsInfo() { + LogUtil.setupLogger(LogLevel.INFO); + CapturingHandler handler = attachCapture(); + + LogUtil.printWarningLog("deprecation warning"); + + boolean warnCaptured = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.WARNING) + && r.getMessage().contains("deprecation warning")); + Assert.assertTrue("WARN log should appear when LogLevel is INFO", warnCaptured); + } + + @Test + public void testWarnLogAppearsWhenLogLevelIsWarn() { + LogUtil.setupLogger(LogLevel.WARN); + CapturingHandler handler = attachCapture(); + + LogUtil.printWarningLog("warn level warning"); + + boolean warnCaptured = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.WARNING) + && r.getMessage().contains("warn level warning")); + Assert.assertTrue("WARN log should appear when LogLevel is WARN", warnCaptured); + } + + @Test + public void testWarnLogAppearsWhenLogLevelIsDebug() { + LogUtil.setupLogger(LogLevel.DEBUG); + CapturingHandler handler = attachCapture(); + + LogUtil.printWarningLog("debug level warning"); + + boolean warnCaptured = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.WARNING) + && r.getMessage().contains("debug level warning")); + Assert.assertTrue("WARN log should appear when LogLevel is DEBUG", warnCaptured); + } + + @Test + public void testWarnLogSuppressedWhenLogLevelIsError() { + LogUtil.setupLogger(LogLevel.ERROR); + CapturingHandler handler = attachCapture(); + + LogUtil.printWarningLog("suppressed warning"); + + boolean warnCaptured = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.WARNING)); + Assert.assertFalse("WARN log should NOT appear when LogLevel is ERROR", warnCaptured); + } + + @Test + public void testInfoLogSuppressedWhenLogLevelIsWarn() { + LogUtil.setupLogger(LogLevel.WARN); + CapturingHandler handler = attachCapture(); + + LogUtil.printInfoLog("info message"); + + boolean infoCaptured = handler.records.stream() + .anyMatch(r -> r.getLevel().equals(Level.INFO)); + Assert.assertFalse("INFO log should NOT appear when LogLevel is WARN", infoCaptured); + } +} diff --git a/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java b/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java index 5dc02f37..77a8ed95 100644 --- a/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java +++ b/src/test/java/com/skyflow/vault/controller/VaultControllerTests.java @@ -291,4 +291,66 @@ public void testDetokenizeRequestDownloadUrlDefaultIsFalse() { Assert.assertFalse("downloadUrl should be false by default", request.getDownloadUrl()); } + // extractUpdateSkyflowId — all cases + + @Test + public void testExtractUpdateSkyflowId_onlyCamelCase() throws Exception { + HashMap data = new HashMap<>(); + data.put("skyflowId", "id-camel-only"); + data.put("card_number", "4111111111111111"); + + Method method = VaultController.class.getDeclaredMethod("extractUpdateSkyflowId", HashMap.class); + method.setAccessible(true); + String result = (String) method.invoke(null, data); + + Assert.assertEquals("should return the skyflowId value", "id-camel-only", result); + Assert.assertFalse("skyflowId should be removed from data map", data.containsKey("skyflowId")); + Assert.assertTrue("other fields should be preserved", data.containsKey("card_number")); + } + + @Test + public void testExtractUpdateSkyflowId_onlySnakeCase() throws Exception { + HashMap data = new HashMap<>(); + data.put("skyflow_id", "id-snake-only"); + data.put("card_number", "4111111111111111"); + + Method method = VaultController.class.getDeclaredMethod("extractUpdateSkyflowId", HashMap.class); + method.setAccessible(true); + String result = (String) method.invoke(null, data); + + Assert.assertEquals("should return the skyflow_id value", "id-snake-only", result); + Assert.assertFalse("skyflow_id should be removed from data map", data.containsKey("skyflow_id")); + Assert.assertTrue("other fields should be preserved", data.containsKey("card_number")); + } + + @Test + public void testExtractUpdateSkyflowId_bothKeys_prefersSkyflowId() throws Exception { + HashMap data = new HashMap<>(); + data.put("skyflowId", "id-camel"); + data.put("skyflow_id", "id-snake"); + data.put("card_number", "4111111111111111"); + + Method method = VaultController.class.getDeclaredMethod("extractUpdateSkyflowId", HashMap.class); + method.setAccessible(true); + String result = (String) method.invoke(null, data); + + Assert.assertEquals("skyflowId should be preferred when both keys are present", "id-camel", result); + } + + @Test + public void testExtractUpdateSkyflowId_bothKeys_removesBothFromMap() throws Exception { + HashMap data = new HashMap<>(); + data.put("skyflowId", "id-camel"); + data.put("skyflow_id", "id-snake"); + data.put("card_number", "4111111111111111"); + + Method method = VaultController.class.getDeclaredMethod("extractUpdateSkyflowId", HashMap.class); + method.setAccessible(true); + method.invoke(null, data); + + Assert.assertFalse("skyflowId should be removed from data map", data.containsKey("skyflowId")); + Assert.assertFalse("skyflow_id should be removed from data map", data.containsKey("skyflow_id")); + Assert.assertTrue("other fields should be preserved", data.containsKey("card_number")); + } + } diff --git a/src/test/java/com/skyflow/vault/data/UpdateTests.java b/src/test/java/com/skyflow/vault/data/UpdateTests.java index 55e5b811..8ae1db8d 100644 --- a/src/test/java/com/skyflow/vault/data/UpdateTests.java +++ b/src/test/java/com/skyflow/vault/data/UpdateTests.java @@ -381,6 +381,101 @@ public void testNullKeyInTokensInUpdateRequestValidations() { } } + // --- camelCase skyflowId key tests --- + + @Test + public void testValidInputWithCamelCaseSkyflowIdInUpdateRequest() { + try { + dataMap.put("skyflowId", skyflowID); + dataMap.put("test_column_1", "test_value_1"); + dataMap.put("test_column_2", "test_value_2"); + tokenMap.put("test_column_1", "test_token_1"); + UpdateRequest request = UpdateRequest.builder() + .table(table) + .data(dataMap) + .tokens(tokenMap) + .returnTokens(true) + .tokenMode(TokenMode.ENABLE) + .build(); + Validations.validateUpdateRequest(request); + Assert.assertEquals(table, request.getTable()); + Assert.assertEquals(3, request.getData().size()); + Assert.assertEquals(1, request.getTokens().size()); + Assert.assertTrue(request.getReturnTokens()); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testValidInputWithCamelCaseSkyflowIdTokenModeDisableInUpdateRequest() { + try { + dataMap.put("skyflowId", skyflowID); + dataMap.put("test_column_1", "test_value_1"); + UpdateRequest request = UpdateRequest.builder() + .table(table) + .data(dataMap) + .returnTokens(null) + .tokenMode(null) + .build(); + Validations.validateUpdateRequest(request); + Assert.assertEquals(table, request.getTable()); + Assert.assertEquals(2, request.getData().size()); + Assert.assertFalse(request.getReturnTokens()); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + + @Test + public void testInvalidSkyflowIdTypeWithCamelCaseKeyInUpdateRequest() { + dataMap.put("skyflowId", 123); + UpdateRequest request = UpdateRequest.builder().table(table).data(dataMap).build(); + try { + Validations.validateUpdateRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals( + Utils.parameterizedString(ErrorMessage.InvalidSkyflowIdType.getMessage(), Constants.SDK_PREFIX), + e.getMessage() + ); + } + } + + @Test + public void testEmptySkyflowIdWithCamelCaseKeyInUpdateRequest() { + dataMap.put("skyflowId", ""); + UpdateRequest request = UpdateRequest.builder().table(table).data(dataMap).build(); + try { + Validations.validateUpdateRequest(request); + Assert.fail(EXCEPTION_NOT_THROWN); + } catch (SkyflowException e) { + Assert.assertEquals(ErrorCode.INVALID_INPUT.getCode(), e.getHttpCode()); + Assert.assertEquals( + Utils.parameterizedString(ErrorMessage.EmptySkyflowId.getMessage(), Constants.SDK_PREFIX), + e.getMessage() + ); + } + } + + @Test + public void testCamelCaseSkyflowIdTakesPrecedenceOverSnakeCaseInUpdateRequest() { + try { + dataMap.put("skyflowId", skyflowID); + dataMap.put("skyflow_id", "should-be-ignored"); + dataMap.put("test_column_1", "test_value_1"); + UpdateRequest request = UpdateRequest.builder() + .table(table) + .data(dataMap) + .build(); + Validations.validateUpdateRequest(request); + Assert.assertEquals(3, request.getData().size()); + } catch (SkyflowException e) { + Assert.fail(INVALID_EXCEPTION_THROWN); + } + } + @Test public void testUpdateResponse() { try {