Skip to content

Commit

Permalink
Fix date serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
johnoliver committed Oct 11, 2024
1 parent 2ca50f1 commit cdf4948
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ data class UpdatedInfo(
val time: ZonedDateTime,
val checksum: String,
val hashCode: Int,
val hexChecksum: String = BigInteger(1, Base64.getDecoder().decode(checksum)).toString(16),
val lastModified: Date = Date.from(time.toInstant()),
val lastModifiedFormatted: String = lastModified
.toInstant()
.atZone(ZoneId.of("GMT"))
.format(DateTimeFormatter.RFC_1123_DATE_TIME)) {
val hexChecksum: String? = BigInteger(1, Base64.getDecoder().decode(checksum)).toString(16),
val lastModified: Date? = Date.from(time.toInstant()),
val lastModifiedFormatted: String? = lastModified
?.toInstant()
?.atZone(ZoneId.of("GMT"))
?.format(DateTimeFormatter.RFC_1123_DATE_TIME)) {

override fun toString(): String {
return "$time $checksum $hashCode"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package net.adoptium.api.v3.dataSources.persitence.mongo.codecs

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.node.LongNode
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.databind.node.TextNode
import net.adoptium.api.v3.TimeSource
import java.time.Instant
import java.time.format.DateTimeFormatter
import java.util.*

object DateCodecs {
class DateSerializer : JsonSerializer<Date>() {
override fun serialize(p0: Date?, p1: JsonGenerator?, p2: SerializerProvider?) {
p1?.writeStartObject();
p1?.writeStringField(
"\$date",
p0?.toInstant()?.atZone(TimeSource.ZONE)?.format(DateTimeFormatter.ISO_INSTANT)
);
p1?.writeEndObject();
}
}

class DateDeserializer : JsonDeserializer<Date>() {
override fun deserialize(jsonParser: JsonParser?, context: DeserializationContext?): Date {
when (val value = jsonParser?.readValueAsTree<JsonNode>()) {
is ObjectNode -> {
val datetime = DateTimeFormatter.ISO_INSTANT.parse(value.get("\$date").asText())
return Date(Instant.from(datetime).toEpochMilli())
}

is TextNode -> {
val datetime = DateTimeFormatter.ISO_INSTANT.parse(value.asText())
return Date(Instant.from(datetime).toEpochMilli())
}

is LongNode -> {
return Date(value.asLong())
}
}

throw IllegalArgumentException("Could not parse ZonedDateTime")
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.bson.codecs.configuration.CodecRegistry
import java.io.IOException
import java.io.UncheckedIOException
import java.time.ZonedDateTime
import java.util.*

class JacksonCodecProvider : CodecProvider {
companion object {
Expand All @@ -25,7 +26,10 @@ class JacksonCodecProvider : CodecProvider {
.registerModule(JavaTimeModule())
.registerModule(object : SimpleModule() {
init {
addDeserializer(ZonedDateTime::class.java, ZonedDateTimeDeserializer())
addDeserializer(ZonedDateTime::class.java, ZonedDateTimeCodecs.ZonedDateTimeDeserializer())
addSerializer(ZonedDateTime::class.java, ZonedDateTimeCodecs.ZonedDateTimeSerializer())
addDeserializer(Date::class.java, DateCodecs.DateDeserializer())
addSerializer(Date::class.java, DateCodecs.DateSerializer())
}
})
}
Expand All @@ -38,7 +42,8 @@ class JacksonCodecProvider : CodecProvider {
}
}

class JacksonCodec<T>(private val objectMapper: ObjectMapper, private val registry: CodecRegistry, val type: Class<T>) : Codec<T> {
class JacksonCodec<T>(private val objectMapper: ObjectMapper, private val registry: CodecRegistry, val type: Class<T>) :
Codec<T> {

private var rawBsonDocumentCodec: Codec<RawBsonDocument> = registry.get(RawBsonDocument::class.java)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package net.adoptium.api.v3.dataSources.persitence.mongo.codecs

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.node.LongNode
import com.fasterxml.jackson.databind.node.ObjectNode
import com.fasterxml.jackson.databind.node.TextNode
import net.adoptium.api.v3.TimeSource
import java.time.Instant
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

object ZonedDateTimeCodecs {
class ZonedDateTimeDeserializer : JsonDeserializer<ZonedDateTime>() {
override fun deserialize(jsonParser: JsonParser?, context: DeserializationContext?): ZonedDateTime {
when (val value = jsonParser?.readValueAsTree<JsonNode>()) {
is ObjectNode -> {
val datetime = DateTimeFormatter.ISO_INSTANT.parse(value.get("\$date").asText())
return Instant.from(datetime).atZone(TimeSource.ZONE)
}

is TextNode -> {
val datetime = DateTimeFormatter.ISO_INSTANT.parse(value.asText())
return Instant.from(datetime).atZone(TimeSource.ZONE)
}

is LongNode -> {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(value.asLong()), TimeSource.ZONE)
}
}

throw IllegalArgumentException("Could not parse ZonedDateTime")
}

}

class ZonedDateTimeSerializer : JsonSerializer<ZonedDateTime>() {
override fun serialize(p0: ZonedDateTime?, p1: JsonGenerator?, p2: SerializerProvider?) {
p1?.writeStartObject();
p1?.writeStringField("\$date", p0?.format(DateTimeFormatter.ISO_INSTANT));
p1?.writeEndObject();
}

}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class CacheControlService @Inject constructor(private var apiDataStore: APIDataS
val etag = apiDataStore.getUpdateInfo().hexChecksum
val lastModified = apiDataStore.getUpdateInfo().lastModified

if (lastModified == null || etag == null) {
return
}

val builder =
requestContext
.request
Expand All @@ -43,6 +47,11 @@ class CacheControlService @Inject constructor(private var apiDataStore: APIDataS
val ecc = ExtendedCacheControl();
ecc.isPublic = true

if (apiDataStore.getUpdateInfo().hexChecksum == null ||
apiDataStore.getUpdateInfo().lastModifiedFormatted == null) {
return
}

responseContext?.headers?.add("ETag", apiDataStore.getUpdateInfo().hexChecksum)
responseContext?.headers?.add("Last-Modified", apiDataStore.getUpdateInfo().lastModifiedFormatted)
responseContext?.headers?.add("Cache-Control", CacheControlDelegate.INSTANCE.toString(ecc))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import net.adoptium.api.v3.JsonMapper
import net.adoptium.api.v3.TimeSource
import net.adoptium.api.v3.dataSources.persitence.mongo.MongoClient
import net.adoptium.api.v3.models.DateTime
import net.adoptium.api.v3.models.GitHubDownloadStatsDbEntry
import org.bson.Document
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
Expand Down Expand Up @@ -59,6 +60,28 @@ class DateTimeMigrationTest : MongoTest() {
}
}

@Test
fun `github stats`(mongoClient: MongoClient) {
runBlocking {
val collectionName = UUID.randomUUID().toString()
val client1 = mongoClient.getDatabase().getCollection<GitHubDownloadStatsDbEntry>(collectionName)

client1.insertOne(
GitHubDownloadStatsDbEntry(
TimeSource.now(),
1,
mapOf(),
1
)
)

client1.find().firstOrNull()?.let {
assertEquals(1, it.downloads)
}
}

}

@Test
fun `writes datetime as string`() {
val hzdt = HasDateTime(DateTime(ZonedDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneOffset.UTC)))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.adoptium.api

import de.flapdoodle.embed.mongo.config.ImmutableNet
import de.flapdoodle.embed.mongo.config.Net
import de.flapdoodle.embed.mongo.distribution.Version
import de.flapdoodle.embed.mongo.transitions.Mongod
Expand Down Expand Up @@ -34,9 +35,9 @@ abstract class MongoTest {
fun startFongo() {
val bindIp = "localhost"

val port = de.flapdoodle.net.Net.freeServerPort()
val net = ImmutableNet.defaults()

val net = Net.builder().bindIp(bindIp).port(port).isIpv6(false).build()
val port = net.port

val mongodbTestConnectionString = "mongodb://$bindIp:$port"

Expand Down

0 comments on commit cdf4948

Please sign in to comment.