Refactor code structure for improved readability and maintainability
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 53s
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 53s
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package de.tsschulz.tt_tagebuch.shared.api.models
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DiaryDrawingSerializationTest {
|
||||
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
@Test
|
||||
fun predefinedActivity_deserializesDrawingDataAndImageLink() {
|
||||
val raw = """{"id":7,"name":"Aufschlag","imageLink":"/images/7.png","drawingData":{"targetPosition":"5"}}"""
|
||||
|
||||
val activity = json.decodeFromString(PredefinedActivityDto.serializer(), raw)
|
||||
|
||||
assertEquals("/images/7.png", activity.imageLink)
|
||||
assertEquals("""{"targetPosition":"5"}""", activity.drawingData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun planActivity_deserializesNestedDrawingAndGroupScope() {
|
||||
val raw = """{"id":1,"drawingData":{"selectedStartPosition":"AS1"},"groupActivities":[{"id":2,"groupId":4,"drawingData":{"targetPosition":"4"},"groupPredefinedActivity":{"id":9,"drawingData":{"strokeType":"RH"}}}]}"""
|
||||
|
||||
val activity = json.decodeFromString(DiaryDateActivityItem.serializer(), raw)
|
||||
val nested = activity.groupActivities.single()
|
||||
|
||||
assertEquals("""{"selectedStartPosition":"AS1"}""", activity.drawingData)
|
||||
assertEquals(4, nested.groupId)
|
||||
assertEquals("""{"targetPosition":"4"}""", nested.drawingData)
|
||||
assertEquals("""{"strokeType":"RH"}""", nested.groupPredefinedActivity?.drawingData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun planActivity_findsDrawingStoredOnActivityImage() {
|
||||
val raw = """{"id":3,"predefinedActivity":{"id":10,"images":[{"id":12,"drawingData":{"strokeType":"VH","targetPosition":"5"}}]}}"""
|
||||
|
||||
val activity = json.decodeFromString(DiaryDateActivityItem.serializer(), raw)
|
||||
|
||||
assertEquals("""{"strokeType":"VH","targetPosition":"5"}""", activity.predefinedActivity.drawingDataForDisplay())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun predefinedActivity_keepsStringDrawingDataCompatible() {
|
||||
val raw = """{"id":8,"drawingData":"{\"strokeType\":\"VH\"}"}"""
|
||||
|
||||
val activity = json.decodeFromString(PredefinedActivityDto.serializer(), raw)
|
||||
|
||||
assertEquals("""{"strokeType":"VH"}""", activity.drawingData)
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ data class DiaryDateActivityItem(
|
||||
val groupId: Int? = null,
|
||||
val planGroup: DiaryPlanGroupSummary? = null,
|
||||
val predefinedActivity: PredefinedActivitySummary? = null,
|
||||
@Serializable(with = FlexibleNullableDrawingDataSerializer::class)
|
||||
val drawingData: String? = null,
|
||||
val groupActivities: List<GroupActivitySummary> = emptyList(),
|
||||
)
|
||||
|
||||
@@ -30,15 +32,29 @@ data class PredefinedActivitySummary(
|
||||
/** Wie Backend: z. B. `/api/predefined-activities/…/image/…` */
|
||||
val imageLink: String? = null,
|
||||
val imageUrl: String? = null,
|
||||
@Serializable(with = FlexibleNullableDrawingDataSerializer::class)
|
||||
val drawingData: String? = null,
|
||||
val images: List<PredefinedActivityImageSummary> = emptyList(),
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class PredefinedActivityImageSummary(
|
||||
val id: Int? = null,
|
||||
val imagePath: String? = null,
|
||||
@Serializable(with = FlexibleNullableDrawingDataSerializer::class)
|
||||
val drawingData: String? = null,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GroupActivitySummary(
|
||||
val id: Int? = null,
|
||||
val orderId: Int? = null,
|
||||
val groupId: Int? = null,
|
||||
val duration: Int? = null,
|
||||
val durationText: String? = null,
|
||||
val groupPredefinedActivity: PredefinedActivitySummary? = null,
|
||||
@Serializable(with = FlexibleNullableDrawingDataSerializer::class)
|
||||
val drawingData: String? = null,
|
||||
)
|
||||
|
||||
fun PredefinedActivitySummary?.displayLabel(): String {
|
||||
@@ -55,3 +71,9 @@ fun DiaryDateActivityItem.displayTitle(fallbackTimeblock: String): String {
|
||||
if (label.isNotEmpty()) return label
|
||||
return if (isTimeblock) fallbackTimeblock else ""
|
||||
}
|
||||
|
||||
fun PredefinedActivitySummary?.drawingDataForDisplay(): String? {
|
||||
if (this == null) return null
|
||||
return drawingData?.takeIf { it.isNotBlank() }
|
||||
?: images.firstNotNullOfOrNull { image -> image.drawingData?.takeIf { it.isNotBlank() } }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package de.tsschulz.tt_tagebuch.shared.api.models
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.JsonDecoder
|
||||
import kotlinx.serialization.json.JsonEncoder
|
||||
import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.contentOrNull
|
||||
|
||||
/**
|
||||
* The API may expose drawing data as stored JSON text or as an expanded JSON object.
|
||||
* The app keeps one canonical text representation for the existing drawing parser.
|
||||
*/
|
||||
object FlexibleNullableDrawingDataSerializer : KSerializer<String?> {
|
||||
override val descriptor: SerialDescriptor =
|
||||
PrimitiveSerialDescriptor("FlexibleNullableDrawingData", PrimitiveKind.STRING)
|
||||
|
||||
override fun deserialize(decoder: Decoder): String? {
|
||||
val input = decoder as? JsonDecoder
|
||||
?: error("FlexibleNullableDrawingDataSerializer requires JsonDecoder")
|
||||
return when (val element = input.decodeJsonElement()) {
|
||||
is JsonNull -> null
|
||||
is JsonPrimitive -> element.contentOrNull
|
||||
else -> element.toString()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
override fun serialize(encoder: Encoder, value: String?) {
|
||||
when (encoder) {
|
||||
is JsonEncoder -> encoder.encodeJsonElement(
|
||||
if (value == null) JsonNull else JsonPrimitive(value),
|
||||
)
|
||||
else -> if (value == null) encoder.encodeNull() else encoder.encodeString(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,9 @@ data class PredefinedActivityDto(
|
||||
val description: String? = null,
|
||||
val duration: Int? = null,
|
||||
val durationText: String? = null,
|
||||
val imageLink: String? = null,
|
||||
@Serializable(with = FlexibleNullableDrawingDataSerializer::class)
|
||||
val drawingData: String? = null,
|
||||
val excludeFromStats: Boolean? = null,
|
||||
)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberTagLinkDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryMemberTagMutationBody
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.DiaryTag
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.PredefinedActivityDto
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.PredefinedActivityUpsertBody
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.AddDiaryPlanGroupActivityRequest
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.CreateDiaryPlanActivityRequest
|
||||
import de.tsschulz.tt_tagebuch.shared.api.models.CreateTrainingGroupBody
|
||||
@@ -121,6 +122,14 @@ class DiaryManager(
|
||||
return predefinedActivitiesApi.getById(id)
|
||||
}
|
||||
|
||||
suspend fun createPredefinedActivity(body: PredefinedActivityUpsertBody): PredefinedActivityDto {
|
||||
return predefinedActivitiesApi.create(body)
|
||||
}
|
||||
|
||||
suspend fun updatePredefinedActivity(id: Int, body: PredefinedActivityUpsertBody): PredefinedActivityDto {
|
||||
return predefinedActivitiesApi.update(id, body)
|
||||
}
|
||||
|
||||
suspend fun listAccidents(clubId: Int, diaryDateId: Int): List<AccidentReportDto> {
|
||||
return accidentApi.list(clubId, diaryDateId)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user