diff --git a/databricks-sdk-java/pom.xml b/databricks-sdk-java/pom.xml index cdcea322f..0eed8fa0c 100644 --- a/databricks-sdk-java/pom.xml +++ b/databricks-sdk-java/pom.xml @@ -103,5 +103,16 @@ jackson-datatype-jsr310 ${jackson.version} + + + com.google.protobuf + protobuf-java + 3.25.1 + + + com.google.protobuf + protobuf-java-util + 3.25.1 + diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/DurationDeserializer.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/DurationDeserializer.java new file mode 100644 index 000000000..765dccd66 --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/DurationDeserializer.java @@ -0,0 +1,23 @@ +package com.databricks.sdk.core.serialization; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.google.protobuf.Duration; +import com.google.protobuf.util.Durations; +import java.io.IOException; + +public class DurationDeserializer extends JsonDeserializer { + @Override + public Duration deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String durationStr = p.getValueAsString(); + if (durationStr == null || durationStr.isEmpty()) { + return null; + } + try { + return Durations.parse(durationStr); // Parses duration format like "3.000s" + } catch (Exception e) { + throw new IOException("Failed to parse duration: " + durationStr, e); + } + } +} diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/DurationSerializer.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/DurationSerializer.java new file mode 100644 index 000000000..27fde56b6 --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/DurationSerializer.java @@ -0,0 +1,21 @@ +package com.databricks.sdk.core.serialization; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.google.protobuf.Duration; +import com.google.protobuf.util.Durations; +import java.io.IOException; + +public class DurationSerializer extends JsonSerializer { + @Override + public void serialize(Duration value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (value != null) { + String durationStr = Durations.toString(value); // Converts to "3.000s" + gen.writeString(durationStr); + } else { + gen.writeNull(); + } + } +} diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/FieldMaskDeserializer.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/FieldMaskDeserializer.java new file mode 100644 index 000000000..5061f4b3e --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/FieldMaskDeserializer.java @@ -0,0 +1,23 @@ +package com.databricks.sdk.core.serialization; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.google.protobuf.FieldMask; +import com.google.protobuf.util.FieldMaskUtil; +import java.io.IOException; + +public class FieldMaskDeserializer extends JsonDeserializer { + @Override + public FieldMask deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String fieldMaskStr = p.getValueAsString(); + if (fieldMaskStr == null || fieldMaskStr.isEmpty()) { + return null; + } + try { + return FieldMaskUtil.fromJsonString(fieldMaskStr); // Parses JSON string format + } catch (Exception e) { + throw new IOException("Failed to parse field mask: " + fieldMaskStr, e); + } + } +} diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/FieldMaskSerializer.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/FieldMaskSerializer.java new file mode 100644 index 000000000..301f0b5bb --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/FieldMaskSerializer.java @@ -0,0 +1,21 @@ +package com.databricks.sdk.core.serialization; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.google.protobuf.FieldMask; +import com.google.protobuf.util.FieldMaskUtil; +import java.io.IOException; + +public class FieldMaskSerializer extends JsonSerializer { + @Override + public void serialize(FieldMask value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (value != null) { + String fieldMaskStr = FieldMaskUtil.toJsonString(value); // Converts to JSON string format + gen.writeString(fieldMaskStr); + } else { + gen.writeNull(); + } + } +} diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/TimestampDeserializer.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/TimestampDeserializer.java new file mode 100644 index 000000000..b5474bdc6 --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/TimestampDeserializer.java @@ -0,0 +1,23 @@ +package com.databricks.sdk.core.serialization; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Timestamps; +import java.io.IOException; + +public class TimestampDeserializer extends JsonDeserializer { + @Override + public Timestamp deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String timestampStr = p.getValueAsString(); + if (timestampStr == null || timestampStr.isEmpty()) { + return null; + } + try { + return Timestamps.parse(timestampStr); // Parses RFC 3339 format + } catch (Exception e) { + throw new IOException("Failed to parse timestamp: " + timestampStr, e); + } + } +} diff --git a/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/TimestampSerializer.java b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/TimestampSerializer.java new file mode 100644 index 000000000..a760b4749 --- /dev/null +++ b/databricks-sdk-java/src/main/java/com/databricks/sdk/core/serialization/TimestampSerializer.java @@ -0,0 +1,21 @@ +package com.databricks.sdk.core.serialization; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.google.protobuf.Timestamp; +import com.google.protobuf.util.Timestamps; +import java.io.IOException; + +public class TimestampSerializer extends JsonSerializer { + @Override + public void serialize(Timestamp value, JsonGenerator gen, SerializerProvider serializers) + throws IOException { + if (value != null) { + String timestampStr = Timestamps.toString(value); // Converts to RFC 3339 format + gen.writeString(timestampStr); + } else { + gen.writeNull(); + } + } +} diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/DurationDeserializerTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/DurationDeserializerTest.java new file mode 100644 index 000000000..19a33ff18 --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/DurationDeserializerTest.java @@ -0,0 +1,46 @@ +package com.databricks.sdk.core.serialization; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.protobuf.Duration; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class DurationDeserializerTest { + private static class TestClass { + @JsonDeserialize(using = DurationDeserializer.class) + private Duration duration; + + public Duration getDuration() { + return duration; + } + } + + @ParameterizedTest + @MethodSource("durationDeserializationTestCases") + public void testDurationDeserialization(String inputJson, Duration expectedDuration) + throws Exception { + ObjectMapper mapper = new ObjectMapper(); + TestClass obj = mapper.readValue(inputJson, TestClass.class); + assertEquals(expectedDuration, obj.getDuration()); + } + + static Stream durationDeserializationTestCases() { + return Stream.of( + // Duration with seconds and nanos + Arguments.of( + "{\"duration\":\"3.500s\"}", + Duration.newBuilder().setSeconds(3).setNanos(500000000).build()), + // Duration with only seconds + Arguments.of("{\"duration\":\"5s\"}", Duration.newBuilder().setSeconds(5).build()), + // Duration with only nanos + Arguments.of( + "{\"duration\":\"0.123456789s\"}", Duration.newBuilder().setNanos(123456789).build()), + // Null duration + Arguments.of("{\"duration\":null}", null)); + } +} diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/DurationSerializerTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/DurationSerializerTest.java new file mode 100644 index 000000000..f703972a1 --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/DurationSerializerTest.java @@ -0,0 +1,49 @@ +package com.databricks.sdk.core.serialization; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.protobuf.Duration; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class DurationSerializerTest { + + private static class TestClass { + @JsonSerialize(using = DurationSerializer.class) + private Duration duration; + + public TestClass(Duration duration) { + this.duration = duration; + } + } + + @ParameterizedTest + @MethodSource("durationSerializationTestCases") + public void testDurationSerialization(Duration duration, String expectedJson) throws Exception { + TestClass testObject = new TestClass(duration); + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(testObject); + assertEquals(expectedJson, json); + } + + static Stream durationSerializationTestCases() { + return Stream.of( + // Duration of 3 seconds + Arguments.of(Duration.newBuilder().setSeconds(3).build(), "{\"duration\":\"3s\"}"), + // Duration of 3.5 seconds (3 seconds + 500000000 nanoseconds) + Arguments.of( + Duration.newBuilder().setSeconds(3).setNanos(500000000).build(), + "{\"duration\":\"3.500s\"}"), + // Duration of 0 seconds + Arguments.of(Duration.newBuilder().setSeconds(0).build(), "{\"duration\":\"0s\"}"), + // Duration with only nanos + Arguments.of( + Duration.newBuilder().setNanos(123456789).build(), "{\"duration\":\"0.123456789s\"}"), + // Null duration + Arguments.of(null, "{\"duration\":null}")); + } +} diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/FieldMaskDeserializerTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/FieldMaskDeserializerTest.java new file mode 100644 index 000000000..0fe41e2a6 --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/FieldMaskDeserializerTest.java @@ -0,0 +1,54 @@ +package com.databricks.sdk.core.serialization; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.protobuf.FieldMask; +import com.google.protobuf.util.FieldMaskUtil; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FieldMaskDeserializerTest { + private static class TestClass { + @JsonDeserialize(using = FieldMaskDeserializer.class) + private FieldMask fieldMask; + + public FieldMask getFieldMask() { + return fieldMask; + } + } + + @ParameterizedTest + @MethodSource("fieldMaskDeserializationTestCases") + public void testFieldMaskDeserialization(String inputJson, List expectedFieldPaths) + throws Exception { + ObjectMapper mapper = new ObjectMapper(); + TestClass obj = mapper.readValue(inputJson, TestClass.class); + + if (expectedFieldPaths == null) { + assertNull(obj.getFieldMask()); + } else { + FieldMask expected = FieldMaskUtil.fromStringList(expectedFieldPaths); + assertEquals(expected, obj.getFieldMask()); + } + } + + static Stream fieldMaskDeserializationTestCases() { + return Stream.of( + // Simple field mask + Arguments.of("{\"fieldMask\":\"foo,bar.baz\"}", Arrays.asList("foo", "bar.baz")), + // Single field + Arguments.of("{\"fieldMask\":\"name\"}", Arrays.asList("name")), + // Nested fields + Arguments.of( + "{\"fieldMask\":\"user.profile.email,user.profile.name\"}", + Arrays.asList("user.profile.email", "user.profile.name")), + // Null field mask + Arguments.of("{\"fieldMask\":null}", null)); + } +} diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/FieldMaskSerializerTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/FieldMaskSerializerTest.java new file mode 100644 index 000000000..83baf0936 --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/FieldMaskSerializerTest.java @@ -0,0 +1,54 @@ +package com.databricks.sdk.core.serialization; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.protobuf.FieldMask; +import com.google.protobuf.util.FieldMaskUtil; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class FieldMaskSerializerTest { + private static class TestClass { + @JsonSerialize(using = FieldMaskSerializer.class) + private FieldMask fieldMask; + + public TestClass(FieldMask fieldMask) { + this.fieldMask = fieldMask; + } + } + + @ParameterizedTest + @MethodSource("fieldMaskSerializationTestCases") + public void testFieldMaskSerialization(List fieldPaths, String expectedJson) + throws Exception { + FieldMask fieldMask = null; + if (fieldPaths != null) { + fieldMask = FieldMaskUtil.fromStringList(fieldPaths); + } + + TestClass testObject = new TestClass(fieldMask); + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(testObject); + assertEquals(expectedJson, json); + } + + static Stream fieldMaskSerializationTestCases() { + return Stream.of( + // Simple field mask + Arguments.of(Arrays.asList("foo", "bar.baz"), "{\"fieldMask\":\"foo,bar.baz\"}"), + // Single field + Arguments.of(Arrays.asList("name"), "{\"fieldMask\":\"name\"}"), + // Nested fields + Arguments.of( + Arrays.asList("user.profile.email", "user.profile.name"), + "{\"fieldMask\":\"user.profile.email,user.profile.name\"}"), + // Null field mask + Arguments.of(null, "{\"fieldMask\":null}")); + } +} diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/TimestampDeserializerTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/TimestampDeserializerTest.java new file mode 100644 index 000000000..270bd56bd --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/TimestampDeserializerTest.java @@ -0,0 +1,45 @@ +package com.databricks.sdk.core.serialization; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.google.protobuf.Timestamp; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class TimestampDeserializerTest { + private static class TestClass { + @JsonDeserialize(using = TimestampDeserializer.class) + private Timestamp timestamp; + + public Timestamp getTimestamp() { + return timestamp; + } + } + + @ParameterizedTest + @MethodSource("timestampDeserializationTestCases") + public void testTimestampDeserialization(String inputJson, Timestamp expectedTimestamp) + throws Exception { + ObjectMapper mapper = new ObjectMapper(); + TestClass obj = mapper.readValue(inputJson, TestClass.class); + assertEquals(expectedTimestamp, obj.getTimestamp()); + } + + static Stream timestampDeserializationTestCases() { + return Stream.of( + // Timestamp without nanos + Arguments.of( + "{\"timestamp\":\"2024-06-20T12:34:56Z\"}", + Timestamp.newBuilder().setSeconds(1718886896L).build()), + // Timestamp with nanos + Arguments.of( + "{\"timestamp\":\"2024-06-20T12:34:56.123456789Z\"}", + Timestamp.newBuilder().setSeconds(1718886896L).setNanos(123456789).build()), + // Null timestamp + Arguments.of("{\"timestamp\":null}", null)); + } +} diff --git a/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/TimestampSerializerTest.java b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/TimestampSerializerTest.java new file mode 100644 index 000000000..05b9c567d --- /dev/null +++ b/databricks-sdk-java/src/test/java/com/databricks/sdk/core/serialization/TimestampSerializerTest.java @@ -0,0 +1,46 @@ +package com.databricks.sdk.core.serialization; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.google.protobuf.Timestamp; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +public class TimestampSerializerTest { + private static class TestClass { + @JsonSerialize(using = TimestampSerializer.class) + private Timestamp timestamp; + + public TestClass(Timestamp timestamp) { + this.timestamp = timestamp; + } + } + + @ParameterizedTest + @MethodSource("timestampSerializationTestCases") + public void testTimestampSerialization(Timestamp timestamp, String expectedJson) + throws Exception { + TestClass testObject = new TestClass(timestamp); + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(testObject); + assertEquals(expectedJson, json); + } + + static Stream timestampSerializationTestCases() { + return Stream.of( + // Basic timestamp without nanos + Arguments.of( + Timestamp.newBuilder().setSeconds(1718886896L).build(), + "{\"timestamp\":\"2024-06-20T12:34:56Z\"}"), + // Timestamp with nanos + Arguments.of( + Timestamp.newBuilder().setSeconds(1718886896L).setNanos(123456789).build(), + "{\"timestamp\":\"2024-06-20T12:34:56.123456789Z\"}"), + // Null timestamp + Arguments.of(null, "{\"timestamp\":null}")); + } +}