diff --git a/CHANGELOG.md b/CHANGELOG.md index 65a9082..93d6782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Engelsystem changelog +## NEXT + +* Not published yet. + +### Changes + +* **Breaking change:** Parse new mandatory `start_date`, `end_date`, `event_timezone` properties into `Shift`. + * Deprecate `start`, `end`, `timezone` properties in favor of the new ones. + * Related: https://github.com/engelsystem/engelsystem/issues/695 + + ## [v.5.3.0](https://github.com/johnjohndoe/engelsystem/releases/tag/v.5.3.0) * Published: 2021-11-14 diff --git a/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/ApiModule.kt b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/ApiModule.kt index 11676f0..1103327 100644 --- a/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/ApiModule.kt +++ b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/ApiModule.kt @@ -3,9 +3,11 @@ package info.metadude.kotlin.library.engelsystem import com.squareup.moshi.Moshi import info.metadude.kotlin.library.engelsystem.adapters.InstantJsonAdapter import info.metadude.kotlin.library.engelsystem.adapters.ZoneOffsetJsonAdapter +import info.metadude.kotlin.library.engelsystem.adapters.ZonedDateTimeJsonAdapter import okhttp3.OkHttpClient import org.threeten.bp.Instant import org.threeten.bp.ZoneOffset +import org.threeten.bp.ZonedDateTime import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory @@ -22,6 +24,7 @@ object ApiModule { private fun provideMoshiBuilder(): Moshi { return Moshi.Builder() .add(Instant::class.java, InstantJsonAdapter()) + .add(ZonedDateTime::class.java, ZonedDateTimeJsonAdapter()) .add(ZoneOffset::class.java, ZoneOffsetJsonAdapter()) .build() } diff --git a/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeAdapter.kt b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeAdapter.kt new file mode 100644 index 0000000..45f93ad --- /dev/null +++ b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeAdapter.kt @@ -0,0 +1,19 @@ +package info.metadude.kotlin.library.engelsystem.adapters + +import com.squareup.moshi.FromJson +import org.threeten.bp.DateTimeException +import org.threeten.bp.ZonedDateTime + +class ZonedDateTimeAdapter { + + @FromJson + fun fromJson(jsonValue: String?) = jsonValue?.let { + try { + ZonedDateTime.parse(jsonValue) + } catch (e: DateTimeException) { + println(e.message) + null + } + } + +} diff --git a/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeJsonAdapter.kt b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeJsonAdapter.kt new file mode 100644 index 0000000..4c200a3 --- /dev/null +++ b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeJsonAdapter.kt @@ -0,0 +1,21 @@ +package info.metadude.kotlin.library.engelsystem.adapters + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import org.threeten.bp.ZonedDateTime + +class ZonedDateTimeJsonAdapter : JsonAdapter() { + + private val delegate = ZonedDateTimeAdapter() + + override fun fromJson(reader: JsonReader): ZonedDateTime? { + val jsonValue = reader.nextString() + return delegate.fromJson(jsonValue) + } + + override fun toJson(writer: JsonWriter, value: ZonedDateTime?) { + throw NotImplementedError() + } + +} diff --git a/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt index 5fd623b..7c7707c 100644 --- a/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt +++ b/engelsystem-base/src/main/kotlin/info/metadude/kotlin/library/engelsystem/models/Shift.kt @@ -22,9 +22,20 @@ data class Shift internal constructor( /** * Unix timestamp of when the shift ends. */ + @Deprecated( + message = "Use endsAtDate instead." + + " See https://github.com/engelsystem/engelsystem/issues/695", + ReplaceWith("endsAtDate") + ) @Json(name = "end") internal val endsAtInstant: Instant = DEFAULT_INSTANT, + /** + * Date and time with time zone offset of when the shift ends, RFC3339 compliant (Y-m-d\TH:i:sP). + */ + @Json(name = "end_date") + val endsAtDate: ZonedDateTime, + /** * Description of the shift location. */ @@ -58,9 +69,20 @@ data class Shift internal constructor( /** * Unix timestamp of when the shift starts. */ + @Deprecated( + message = "Use startsAtDate instead." + + " See https://github.com/engelsystem/engelsystem/issues/695", + ReplaceWith("startsAtDate") + ) @Json(name = "start") internal val startsAtInstant: Instant = DEFAULT_INSTANT, + /** + * Date and time with time zone offset of when the shift starts, RFC3339 compliant (Y-m-d\TH:i:sP). + */ + @Json(name = "start_date") + val startsAtDate: ZonedDateTime, + /** * Title of the associated talk in case the shift happens at a talk. */ @@ -76,9 +98,19 @@ data class Shift internal constructor( /** * Time zone offset associated with the time stamps in this class. Example: "+01:00" */ + @Deprecated( + message = "Retrieve the time zone offset from either startsAtDate or endsAtDate. " + + "See https://github.com/engelsystem/engelsystem/issues/695" + ) @Json(name = "timezone") val timeZoneOffset: ZoneOffset = DEFAULT_ZONE_OFFSET, + /** + * Time zone name associated with the physical location of the event, e.g. "Europe/Berlin". + */ + @Json(name = "event_timezone") + val timeZoneName: String, + /** * Shift types ids are not fixed. They can be assigned whenever an instance of the Engelsystem is launched. */ @@ -90,27 +122,33 @@ data class Shift internal constructor( constructor( userComment: String = "", endsAt: ZonedDateTime = DEFAULT_ZONED_DATE_TIME, + endsAtDate: ZonedDateTime = DEFAULT_ZONED_DATE_TIME, locationDescription: String = "", locationName: String = "", locationUrl: String = "", name: String = "", sID: Int = 0, startsAt: ZonedDateTime = DEFAULT_ZONED_DATE_TIME, + startsAtDate: ZonedDateTime = DEFAULT_ZONED_DATE_TIME, talkTitle: String = "", talkUrl: String = "", + timeZoneName: String = "", timeZoneOffset: ZoneOffset = DEFAULT_ZONE_OFFSET, typeId: Int = 0 ) : this( userComment = userComment, + endsAtDate = endsAtDate, endsAtInstant = endsAt.toInstant(), locationDescriptionString = locationDescription, locationName = locationName, locationUrlString = locationUrl, name = name, sID = sID, + startsAtDate = startsAtDate, startsAtInstant = startsAt.toInstant(), talkTitle = talkTitle, talkUrlString = talkUrl, + timeZoneName = timeZoneName, timeZoneOffset = timeZoneOffset, typeId = typeId ) @@ -125,6 +163,11 @@ data class Shift internal constructor( /** * Date and time with time zone offset of when the shift ends. */ + @Deprecated( + message = "Use endsAtDate instead." + + " See https://github.com/engelsystem/engelsystem/issues/695", + ReplaceWith("endsAtDate") + ) val endsAt: ZonedDateTime get() = ZonedDateTime.ofInstant(endsAtInstant, timeZoneOffset) @@ -143,6 +186,11 @@ data class Shift internal constructor( /** * Date and time with time zone offset of when the shift starts. */ + @Deprecated( + message = "Use startsAtDate instead." + + " See https://github.com/engelsystem/engelsystem/issues/695", + ReplaceWith("startsAtDate") + ) val startsAt: ZonedDateTime get() = ZonedDateTime.ofInstant(startsAtInstant, timeZoneOffset) diff --git a/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/ProductionApiTest.kt b/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/ProductionApiTest.kt index bf77b3e..0494020 100644 --- a/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/ProductionApiTest.kt +++ b/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/ProductionApiTest.kt @@ -51,7 +51,10 @@ class ProductionApiTest { assertThat(shift.startsAtInstant).isNotEqualTo(Shift.DEFAULT_INSTANT) assertThat(shift.endsAtInstant).isNotEqualTo(Shift.DEFAULT_INSTANT) assertThat(shift.startsAt).isNotEqualTo(DEFAULT_DATE_TIME) + assertThat(shift.startsAtDate).isNotEqualTo(DEFAULT_DATE_TIME) assertThat(shift.endsAt).isNotEqualTo(DEFAULT_DATE_TIME) + assertThat(shift.endsAtDate).isNotEqualTo(DEFAULT_DATE_TIME) + assertThat(shift.timeZoneName).isNotEmpty() assertThat(shift.timeZoneOffset).isNotNull() } diff --git a/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeAdapterTest.kt b/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeAdapterTest.kt new file mode 100644 index 0000000..ee8a534 --- /dev/null +++ b/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/adapters/ZonedDateTimeAdapterTest.kt @@ -0,0 +1,37 @@ +package info.metadude.kotlin.library.engelsystem.adapters + +import com.google.common.truth.Truth.assertThat +import org.junit.jupiter.api.Test +import org.threeten.bp.ZoneOffset +import org.threeten.bp.ZonedDateTime + +class ZonedDateTimeAdapterTest { + + private val adapter = ZonedDateTimeAdapter() + + @Test + fun `Converts RFC3339 date to its ZonedDateTime representation`() { + val actual = adapter.fromJson("2019-08-21T00:00:00+02:00") + val expected = ZonedDateTime.of(2019, 8, 21, 0, 0, 0, 0, ZoneOffset.ofHours(2)) + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `Converts date without zone offset to null`() { + val actual = adapter.fromJson("2019-08-21T00:00:00") + assertThat(actual).isEqualTo(null) + } + + @Test + fun `Converts 0 to null`() { + val actual = adapter.fromJson("") + assertThat(actual).isEqualTo(null) + } + + @Test + fun `Converts null to null`() { + val actual = adapter.fromJson(null) + assertThat(actual).isEqualTo(null) + } + +} diff --git a/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/models/ShiftTest.kt b/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/models/ShiftTest.kt index c607f23..f843f86 100644 --- a/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/models/ShiftTest.kt +++ b/engelsystem-base/src/test/kotlin/info/metadude/kotlin/library/engelsystem/models/ShiftTest.kt @@ -8,14 +8,18 @@ import org.threeten.bp.ZonedDateTime class ShiftTest { @Test - fun `Assert a shift can be constructed`() { + fun `Assert a shift with minimal parameters can be constructed`() { + val startsAt = ZonedDateTime.now() + val endsAt = ZonedDateTime.now().plusHours(1) assertThat( Shift( userComment = "comment", - endsAt = ZonedDateTime.now(), + endsAtDate = endsAt, locationDescription = "", locationUrl = "https://example1.com", + startsAtDate = startsAt, talkUrl = "https://example2.com", + timeZoneName = "Europe/Berlin", timeZoneOffset = ZoneOffset.UTC ) ).isNotNull()