diff --git a/src/main/kotlin/dk/cachet/carp/webservices/common/audit/Auditable.kt b/src/main/kotlin/dk/cachet/carp/webservices/common/audit/Auditable.kt index 67a81ff6..e273d0ba 100644 --- a/src/main/kotlin/dk/cachet/carp/webservices/common/audit/Auditable.kt +++ b/src/main/kotlin/dk/cachet/carp/webservices/common/audit/Auditable.kt @@ -1,13 +1,15 @@ package dk.cachet.carp.webservices.common.audit +import dk.cachet.carp.webservices.common.converter.InstantConverter +import jakarta.persistence.Convert import jakarta.persistence.EntityListeners import jakarta.persistence.MappedSuperclass +import kotlinx.datetime.Instant import org.springframework.data.annotation.CreatedBy import org.springframework.data.annotation.CreatedDate import org.springframework.data.annotation.LastModifiedBy import org.springframework.data.annotation.LastModifiedDate import org.springframework.data.jpa.domain.support.AuditingEntityListener -import java.time.Instant /** * The Class [Auditable]. @@ -22,6 +24,7 @@ abstract class Auditable { /** The [createdAt] time of creation. */ @CreatedDate + @Convert(converter = InstantConverter::class) var createdAt: Instant? = null /** The [updatedBy] the ID of the user the entity was updated by. */ @@ -30,5 +33,6 @@ abstract class Auditable { /** The [updatedAt] last time the entity was updated. */ @LastModifiedDate + @Convert(converter = InstantConverter::class) var updatedAt: Instant? = null } diff --git a/src/main/kotlin/dk/cachet/carp/webservices/common/audit/EntityDateTimeProvider.kt b/src/main/kotlin/dk/cachet/carp/webservices/common/audit/EntityDateTimeProvider.kt new file mode 100644 index 00000000..a05cd5cf --- /dev/null +++ b/src/main/kotlin/dk/cachet/carp/webservices/common/audit/EntityDateTimeProvider.kt @@ -0,0 +1,16 @@ +package dk.cachet.carp.webservices.common.audit + +import org.springframework.data.auditing.DateTimeProvider +import java.time.temporal.TemporalAccessor +import java.util.* +import java.time.Instant as JavaInstant + +/** + * Overrides the default behaviour: java.time.LocalDateTime -> java.time.Instant + */ + +class EntityDateTimeProvider : DateTimeProvider { + override fun getNow(): Optional { + return Optional.of(JavaInstant.now()) + } +} diff --git a/src/main/kotlin/dk/cachet/carp/webservices/common/audit/JpaAuditConfiguration.kt b/src/main/kotlin/dk/cachet/carp/webservices/common/audit/JpaAuditConfiguration.kt index 46e6a826..587ad6ab 100644 --- a/src/main/kotlin/dk/cachet/carp/webservices/common/audit/JpaAuditConfiguration.kt +++ b/src/main/kotlin/dk/cachet/carp/webservices/common/audit/JpaAuditConfiguration.kt @@ -11,11 +11,17 @@ import org.springframework.data.jpa.repository.config.EnableJpaAuditing * The [JpaAuditConfiguration] implements the configuration logic for the [EntityAuditorAware]. */ @Configuration -@EnableJpaAuditing(auditorAwareRef = "auditorAware") +@EnableJpaAuditing(auditorAwareRef = "auditorAware", dateTimeProviderRef = "dateTimeProvider") class JpaAuditConfiguration(private val authenticationService: AuthenticationService) { /** * The function [auditorAware] returns the current auditor of the application. */ @Bean("auditorAware") fun auditorAware(): AuditorAware = EntityAuditorAware(authenticationService) + + /** + * The function [dateTimeProvider] returns the current date and time. + */ + @Bean("dateTimeProvider") + fun dateTimeProvider(): EntityDateTimeProvider = EntityDateTimeProvider() } diff --git a/src/main/kotlin/dk/cachet/carp/webservices/common/converter/InstantConverter.kt b/src/main/kotlin/dk/cachet/carp/webservices/common/converter/InstantConverter.kt new file mode 100644 index 00000000..5fcad97e --- /dev/null +++ b/src/main/kotlin/dk/cachet/carp/webservices/common/converter/InstantConverter.kt @@ -0,0 +1,24 @@ +package dk.cachet.carp.webservices.common.converter + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter +import kotlinx.datetime.toJavaInstant +import kotlinx.datetime.toKotlinInstant +import java.time.Instant +import java.time.Instant as JavaInstant +import kotlinx.datetime.Instant as KotlinInstant + +@Converter +class InstantConverter : AttributeConverter { + override fun convertToDatabaseColumn(p0: kotlinx.datetime.Instant?): Instant? { + if (p0 == null) return null + + return p0.toJavaInstant() + } + + override fun convertToEntityAttribute(p0: Instant?): kotlinx.datetime.Instant? { + if (p0 == null) return null + + return p0.toKotlinInstant() + } +} diff --git a/src/main/kotlin/dk/cachet/carp/webservices/protocol/repository/CoreProtocolRepository.kt b/src/main/kotlin/dk/cachet/carp/webservices/protocol/repository/CoreProtocolRepository.kt index 415ac22b..d7f5ae9d 100644 --- a/src/main/kotlin/dk/cachet/carp/webservices/protocol/repository/CoreProtocolRepository.kt +++ b/src/main/kotlin/dk/cachet/carp/webservices/protocol/repository/CoreProtocolRepository.kt @@ -12,7 +12,6 @@ import dk.cachet.carp.webservices.common.input.WS_JSON import dk.cachet.carp.webservices.protocol.domain.Protocol import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import kotlinx.datetime.Instant import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.Logger import org.springframework.stereotype.Service @@ -129,7 +128,7 @@ class CoreProtocolRepository( validationMessages.get("protocol.version_history.not_found", id.stringRepresentation) } protocols.map { - ProtocolVersion(it.versionTag, Instant.fromEpochMilliseconds(it.createdAt!!.toEpochMilli())) + ProtocolVersion(it.versionTag, it.createdAt!!) } } diff --git a/src/main/kotlin/dk/cachet/carp/webservices/protocol/service/impl/ProtocolServiceWrapper.kt b/src/main/kotlin/dk/cachet/carp/webservices/protocol/service/impl/ProtocolServiceWrapper.kt index 0bdaf56a..509174b8 100644 --- a/src/main/kotlin/dk/cachet/carp/webservices/protocol/service/impl/ProtocolServiceWrapper.kt +++ b/src/main/kotlin/dk/cachet/carp/webservices/protocol/service/impl/ProtocolServiceWrapper.kt @@ -12,7 +12,6 @@ import dk.cachet.carp.webservices.protocol.service.ProtocolService import dk.cachet.carp.webservices.security.authentication.domain.Account import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import kotlinx.datetime.toKotlinInstant import org.springframework.stereotype.Service @Service @@ -61,8 +60,8 @@ class ProtocolServiceWrapper( return ProtocolOverview( owner?.fullName, - versions.first().createdAt?.toKotlinInstant(), - versions.last().createdAt?.toKotlinInstant(), + versions.first().createdAt, + versions.last().createdAt, versions.last().versionTag, snapshot, ) diff --git a/src/test/kotlin/dk/cachet/carp/webservices/protocol/service/ProtocolServiceTest.kt b/src/test/kotlin/dk/cachet/carp/webservices/protocol/service/ProtocolServiceTest.kt index d4cd0910..aa286e2b 100644 --- a/src/test/kotlin/dk/cachet/carp/webservices/protocol/service/ProtocolServiceTest.kt +++ b/src/test/kotlin/dk/cachet/carp/webservices/protocol/service/ProtocolServiceTest.kt @@ -16,7 +16,6 @@ import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest import kotlinx.datetime.Clock -import kotlinx.datetime.toJavaInstant import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import kotlin.test.Test @@ -62,12 +61,12 @@ class ProtocolServiceTest { mockk { every { versionTag } returns "version 1" every { snapshot } returns mockk() - every { createdAt } returns yesterday.toJavaInstant() + every { createdAt } returns yesterday }, mockk { every { versionTag } returns "version 2" every { snapshot } returns mockk() - every { createdAt } returns now.toJavaInstant() + every { createdAt } returns now }, )