fix(glue): write timestamps the AWS SDK for JavaScript can read
Glue times went out as nanosecond epoch numbers (1792964720.708701378). The AWS SDK for JavaScript v3 keeps any number whose text is not its shortest form as a BigDecimal and then refuses it as a timestamp, so GetTables still failed. They are now written as AWS writes them: epoch seconds to the millisecond, in the shortest form. CreateDatabase also records the database's CreateTime.
This commit is contained in:
@@ -243,6 +243,9 @@ public class GlueService {
|
|||||||
}
|
}
|
||||||
database.setName(databaseName);
|
database.setName(databaseName);
|
||||||
database.setCatalogId(regionResolver.getAccountId());
|
database.setCatalogId(regionResolver.getAccountId());
|
||||||
|
if (database.getCreateTime() == null) {
|
||||||
|
database.setCreateTime(Instant.now());
|
||||||
|
}
|
||||||
databaseStore.put(databaseName, database);
|
databaseStore.put(databaseName, database);
|
||||||
if (tags != null && !tags.isEmpty()) {
|
if (tags != null && !tags.isEmpty()) {
|
||||||
resourceGroupsTaggingService.tagResources(List.of(databaseArn(region, databaseName)), tags, region);
|
resourceGroupsTaggingService.tagResources(List.of(databaseArn(region, databaseName)), tags, region);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -47,10 +48,12 @@ public class Connection {
|
|||||||
|
|
||||||
@JsonProperty("CreationTime")
|
@JsonProperty("CreationTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant creationTime;
|
private Instant creationTime;
|
||||||
|
|
||||||
@JsonProperty("LastUpdatedTime")
|
@JsonProperty("LastUpdatedTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant lastUpdatedTime;
|
private Instant lastUpdatedTime;
|
||||||
|
|
||||||
@JsonProperty("LastUpdatedBy")
|
@JsonProperty("LastUpdatedBy")
|
||||||
@@ -64,6 +67,7 @@ public class Connection {
|
|||||||
|
|
||||||
@JsonProperty("LastConnectionValidationTime")
|
@JsonProperty("LastConnectionValidationTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant lastConnectionValidationTime;
|
private Instant lastConnectionValidationTime;
|
||||||
|
|
||||||
@JsonProperty("AuthenticationConfiguration")
|
@JsonProperty("AuthenticationConfiguration")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.github.hectorvent.floci.services.glue.model;
|
package io.github.hectorvent.floci.services.glue.model;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
@@ -24,6 +25,7 @@ public class Crawler {
|
|||||||
|
|
||||||
@JsonProperty("CreationTime")
|
@JsonProperty("CreationTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant creationTime;
|
private Instant creationTime;
|
||||||
|
|
||||||
@JsonProperty("DatabaseName")
|
@JsonProperty("DatabaseName")
|
||||||
@@ -40,6 +42,7 @@ public class Crawler {
|
|||||||
|
|
||||||
@JsonProperty("LastUpdated")
|
@JsonProperty("LastUpdated")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant lastUpdated;
|
private Instant lastUpdated;
|
||||||
|
|
||||||
@JsonProperty("LineageConfiguration")
|
@JsonProperty("LineageConfiguration")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -20,6 +21,7 @@ public class Database {
|
|||||||
private Map<String, String> parameters;
|
private Map<String, String> parameters;
|
||||||
@JsonProperty("CreateTime")
|
@JsonProperty("CreateTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant createTime;
|
private Instant createTime;
|
||||||
@JsonProperty("CatalogId")
|
@JsonProperty("CatalogId")
|
||||||
private String catalogId;
|
private String catalogId;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -22,10 +23,12 @@ public class GluePolicy {
|
|||||||
|
|
||||||
@JsonProperty("CreateTime")
|
@JsonProperty("CreateTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant createTime;
|
private Instant createTime;
|
||||||
|
|
||||||
@JsonProperty("UpdateTime")
|
@JsonProperty("UpdateTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant updateTime;
|
private Instant updateTime;
|
||||||
|
|
||||||
public String getPolicyInJson() { return policyInJson; }
|
public String getPolicyInJson() { return policyInJson; }
|
||||||
|
|||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package io.github.hectorvent.floci.services.glue.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||||
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a Glue timestamp as AWS does: epoch seconds to the millisecond, in the shortest
|
||||||
|
* form ({@code 1792964720.27}, not {@code 1792964720.270000000}). The AWS SDK for
|
||||||
|
* JavaScript v3 keeps any number whose text differs from its shortest form as a
|
||||||
|
* BigDecimal, and then refuses it as a timestamp.
|
||||||
|
*/
|
||||||
|
@RegisterForReflection
|
||||||
|
public class GlueTimestampSerializer extends JsonSerializer<Instant> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
|
gen.writeNumber(epochSeconds(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String epochSeconds(Instant value) {
|
||||||
|
return BigDecimal.valueOf(value.toEpochMilli(), 3).stripTrailingZeros().toPlainString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ public class Job {
|
|||||||
|
|
||||||
@JsonProperty("CreatedOn")
|
@JsonProperty("CreatedOn")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant createdOn;
|
private Instant createdOn;
|
||||||
|
|
||||||
@JsonProperty("DefaultArguments")
|
@JsonProperty("DefaultArguments")
|
||||||
@@ -51,6 +53,7 @@ public class Job {
|
|||||||
|
|
||||||
@JsonProperty("LastModifiedOn")
|
@JsonProperty("LastModifiedOn")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant lastModifiedOn;
|
private Instant lastModifiedOn;
|
||||||
|
|
||||||
@JsonProperty("LogUri")
|
@JsonProperty("LogUri")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.github.hectorvent.floci.services.glue.model;
|
package io.github.hectorvent.floci.services.glue.model;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
@@ -23,6 +24,7 @@ public class LastCrawlInfo {
|
|||||||
|
|
||||||
@JsonProperty("StartTime")
|
@JsonProperty("StartTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant startTime;
|
private Instant startTime;
|
||||||
|
|
||||||
@JsonProperty("Status")
|
@JsonProperty("Status")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -19,9 +20,11 @@ public class Partition {
|
|||||||
private String tableName;
|
private String tableName;
|
||||||
@JsonProperty("CreationTime")
|
@JsonProperty("CreationTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant creationTime;
|
private Instant creationTime;
|
||||||
@JsonProperty("LastAccessTime")
|
@JsonProperty("LastAccessTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant lastAccessTime;
|
private Instant lastAccessTime;
|
||||||
@JsonProperty("StorageDescriptor")
|
@JsonProperty("StorageDescriptor")
|
||||||
private StorageDescriptor storageDescriptor;
|
private StorageDescriptor storageDescriptor;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -21,12 +22,15 @@ public class Table {
|
|||||||
private String owner;
|
private String owner;
|
||||||
@JsonProperty("CreateTime")
|
@JsonProperty("CreateTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant createTime;
|
private Instant createTime;
|
||||||
@JsonProperty("UpdateTime")
|
@JsonProperty("UpdateTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant updateTime;
|
private Instant updateTime;
|
||||||
@JsonProperty("LastAccessTime")
|
@JsonProperty("LastAccessTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant lastAccessTime;
|
private Instant lastAccessTime;
|
||||||
@JsonProperty("PartitionKeys")
|
@JsonProperty("PartitionKeys")
|
||||||
private List<Column> partitionKeys;
|
private List<Column> partitionKeys;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.github.hectorvent.floci.services.glue.model;
|
|||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.quarkus.runtime.annotations.RegisterForReflection;
|
import io.quarkus.runtime.annotations.RegisterForReflection;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -25,6 +26,7 @@ public class UserDefinedFunction {
|
|||||||
private String ownerType;
|
private String ownerType;
|
||||||
@JsonProperty("CreateTime")
|
@JsonProperty("CreateTime")
|
||||||
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
@JsonFormat(shape = JsonFormat.Shape.NUMBER)
|
||||||
|
@JsonSerialize(using = GlueTimestampSerializer.class)
|
||||||
private Instant createTime;
|
private Instant createTime;
|
||||||
@JsonProperty("ResourceUris")
|
@JsonProperty("ResourceUris")
|
||||||
private List<ResourceUri> resourceUris;
|
private List<ResourceUri> resourceUris;
|
||||||
|
|||||||
+13
-6
@@ -12,10 +12,12 @@ import static org.hamcrest.Matchers.equalTo;
|
|||||||
import static org.hamcrest.Matchers.hasKey;
|
import static org.hamcrest.Matchers.hasKey;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AWS leaves unset fields out of Glue responses. A null timestamp instead breaks
|
* AWS leaves unset fields out of Glue responses and reports times in milliseconds.
|
||||||
* the AWS SDK for JavaScript v3, which rejects anything but a number there.
|
* A null or nanosecond timestamp instead breaks the AWS SDK for JavaScript v3,
|
||||||
|
* which refuses to read either.
|
||||||
*/
|
*/
|
||||||
@QuarkusTest
|
@QuarkusTest
|
||||||
class GlueOmitsNullFieldsIntegrationTest {
|
class GlueOmitsNullFieldsIntegrationTest {
|
||||||
@@ -37,7 +39,7 @@ class GlueOmitsNullFieldsIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void aNewTableHasNoUpdateOrLastAccessTime() {
|
void aNewTableHasOnlyAMillisecondCreateTime() {
|
||||||
glue("CreateDatabase", "{\"DatabaseInput\": {\"Name\": \"" + DATABASE + "\"}}");
|
glue("CreateDatabase", "{\"DatabaseInput\": {\"Name\": \"" + DATABASE + "\"}}");
|
||||||
glue("CreateTable", """
|
glue("CreateTable", """
|
||||||
{
|
{
|
||||||
@@ -49,12 +51,17 @@ class GlueOmitsNullFieldsIntegrationTest {
|
|||||||
}
|
}
|
||||||
""".formatted(DATABASE));
|
""".formatted(DATABASE));
|
||||||
|
|
||||||
glue("GetTables", "{\"DatabaseName\": \"" + DATABASE + "\"}")
|
String tables = glue("GetTables", "{\"DatabaseName\": \"" + DATABASE + "\"}")
|
||||||
.body("TableList[0].Name", equalTo("orders"))
|
.body("TableList[0].Name", equalTo("orders"))
|
||||||
.body("TableList[0].CreateTime", notNullValue())
|
.body("TableList[0].CreateTime", notNullValue())
|
||||||
.body("TableList[0]", not(hasKey("UpdateTime")))
|
.body("TableList[0]", not(hasKey("UpdateTime")))
|
||||||
.body("TableList[0]", not(hasKey("LastAccessTime")));
|
.body("TableList[0]", not(hasKey("LastAccessTime")))
|
||||||
|
.extract().asString();
|
||||||
|
// Milliseconds in their shortest form, as AWS sends them: no trailing zeros.
|
||||||
|
assertTrue(tables.matches("(?s).*\"CreateTime\":\\d+(\\.\\d{0,2}[1-9])?[,}].*"), tables);
|
||||||
|
|
||||||
glue("GetDatabase", "{\"Name\": \"" + DATABASE + "\"}")
|
glue("GetDatabase", "{\"Name\": \"" + DATABASE + "\"}")
|
||||||
.body("Database", not(hasKey("Description")));
|
.body("Database", not(hasKey("Description")))
|
||||||
|
.body("Database.CreateTime", notNullValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package io.github.hectorvent.floci.services.glue.model;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
class GlueTimestampSerializerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void writesMillisecondsInTheirShortestForm() {
|
||||||
|
assertEquals("1792964720.273", GlueTimestampSerializer.epochSeconds(Instant.ofEpochSecond(1792964720, 273_456_789)));
|
||||||
|
assertEquals("1792964720.27", GlueTimestampSerializer.epochSeconds(Instant.ofEpochSecond(1792964720, 270_000_000)));
|
||||||
|
assertEquals("1792964720", GlueTimestampSerializer.epochSeconds(Instant.ofEpochSecond(1792964720)));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user