From 6e8648c53010ac2b8bede16824590bcd6004ad07 Mon Sep 17 00:00:00 2001 From: Petr David Date: Thu, 5 Feb 2026 11:54:56 +0100 Subject: [PATCH 1/7] [NEMO-256] fix logging by introducing Kermit, cleanup --- data/build.gradle.kts | 1 + .../org/tidepool/sdk/repository/DataRepositoryImpl.kt | 3 ++- domain/build.gradle.kts | 1 + .../kotlin/org/tidepool/sdk/service/DataService.kt | 11 ++++++----- lib/build.gradle.kts | 2 ++ 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 2374e52..dcb287c 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -44,6 +44,7 @@ kotlin { implementation("io.ktor:ktor-client-content-negotiation:3.3.1") implementation("io.ktor:ktor-serialization-kotlinx-json:3.3.1") implementation("io.ktor:ktor-client-logging:3.3.1") + implementation("co.touchlab:kermit:2.0.4") // Serialization implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1") diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt index 66b7163..23053de 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt @@ -1,5 +1,6 @@ package org.tidepool.sdk.repository +import co.touchlab.kermit.Logger import io.ktor.client.HttpClient import org.tidepool.sdk.api.DataApi import org.tidepool.sdk.di.provideDataApi @@ -402,7 +403,7 @@ class DataRepositoryImpl( is CgmSettingsDataDto -> cgmSettingsDataDao.insert(dto.toEntity()) is ControllerSettingsDataDto -> controllerSettingsDataDao.insert(dto.toEntity()) is PumpSettingsDataDto -> pumpSettingsDataDao.insert(dto.toEntity()) - else -> println("DataRepository: Unknown data type: ${dto::class.simpleName}") + else -> Logger.w(toUpload.javaClass.simpleName) { "Unknown data type: ${dto::class.simpleName}" } } } Result.failure(ex) diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 432b7f4..7202b42 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -23,6 +23,7 @@ kotlin { implementation("io.insert-koin:koin-core:4.1.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.0") implementation("com.benasher44:uuid:0.8.4") + implementation("co.touchlab:kermit:2.0.4") } } diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt index 0aa7e94..d59da24 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt @@ -1,5 +1,6 @@ package org.tidepool.sdk.service +import co.touchlab.kermit.Logger import kotlinx.coroutines.CoroutineScope import kotlinx.datetime.Clock import org.tidepool.sdk.AppLifecycleProvider @@ -135,9 +136,9 @@ class DataService internal constructor( suspend fun createDataSet(newDataSet: NewDataSet): Result = tokenProvider.getToken().flatMap { token -> - println("DataService: Creating data set with token: ${token.take(5)}...") + Logger.d(javaClass.simpleName) { "Creating data set with token: ${token.take(5)}..." } userRepository.getCurrentUser(token).flatMap { user -> - println("DataService: Creating data set for user: ${user.userId}") + Logger.d(javaClass.simpleName) { "Creating data set for user: ${user.userId}" } dataRepository.createDataSet( userId = user.userId, newDataSet = newDataSet, @@ -176,7 +177,7 @@ class DataService internal constructor( data: List, ): Result> = tokenProvider.getToken().flatMap { - println("DataService: Uploading data to data set $dataSetId") + Logger.d(javaClass.simpleName) { "Uploading data to data set $dataSetId" } dataRepository.uploadDataToDataSet( dataSetId = dataSetId, data = data, @@ -351,7 +352,7 @@ class DataService internal constructor( .filter { it.uploadId != null } .minByOrNull { it.uploadId!! } ?.let { - println("DataService: Found existing data set with id: ${it.id}") + Logger.d(javaClass.simpleName) { "Found existing data set with id: ${it.id}" } Result.success(it) } ?: createDataSet( @@ -381,7 +382,7 @@ class DataService internal constructor( ), ) }.flatMap { - println("DataService: Created data set: ${it.id}") + Logger.d(javaClass.simpleName) { "Created data set: ${it.id}" } it.id?.let { Result.success(it) } ?: Result.failure(IllegalStateException("No id")) } diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index 385fa9a..0507f6c 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -66,6 +66,8 @@ kotlin { // Koin dependency injection implementation("io.insert-koin:koin-core:4.1.0") implementation("io.insert-koin:koin-annotations:2.1.0") + + implementation("co.touchlab:kermit:2.0.4") } } From 483f8fed468dd71c2d665f1ed9056059edc309fc Mon Sep 17 00:00:00 2001 From: Petr David Date: Tue, 10 Feb 2026 14:21:42 +0100 Subject: [PATCH 2/7] [NEMO-256] fix data upload, cache data and upload every 5 minutes to improve debuggability --- .../org/tidepool/sdk/di/DataModule.android.kt | 20 +++ .../entity/data/BasalAutomatedDataEntity.kt | 14 ++- .../database/entity/data/BolusDataEntity.kt | 4 + .../org/tidepool/sdk/dto/AssociationDto.kt | 4 +- .../sdk/dto/data/BasalAutomatedDataDto.kt | 114 +++++++++++++++--- .../org/tidepool/sdk/dto/data/BolusDataDto.kt | 4 + .../sdk/repository/DataRepositoryImpl.kt | 62 +++++----- .../sdk/model/data/BasalAutomatedData.kt | 32 +++-- .../org/tidepool/sdk/model/data/BolusData.kt | 1 + .../tidepool/sdk/repository/DataRepository.kt | 4 +- .../org/tidepool/sdk/service/DataService.kt | 47 +++++--- .../LifecycleAwareDataUploadManager.kt | 6 +- 12 files changed, 228 insertions(+), 84 deletions(-) diff --git a/data/src/androidMain/kotlin/org/tidepool/sdk/di/DataModule.android.kt b/data/src/androidMain/kotlin/org/tidepool/sdk/di/DataModule.android.kt index 998608d..671885d 100644 --- a/data/src/androidMain/kotlin/org/tidepool/sdk/di/DataModule.android.kt +++ b/data/src/androidMain/kotlin/org/tidepool/sdk/di/DataModule.android.kt @@ -1,6 +1,9 @@ package org.tidepool.sdk.di import androidx.room.Room +import androidx.room.migration.Migration +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.driver.bundled.BundledSQLiteDriver import org.koin.core.module.Module import org.koin.dsl.module @@ -35,6 +38,7 @@ actual val platformDataModule: Module name = "loop-kit-database", ) .setDriver(BundledSQLiteDriver()) + .addMigrations(MIGRATION_1_2) .build() } single { @@ -48,6 +52,22 @@ actual val platformDataModule: Module } } +private val MIGRATION_1_2 = object : Migration(1, 2) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL("ALTER TABLE bolus_data ADD COLUMN normal REAL") + } + + override fun migrate(connection: SQLiteConnection) { + val statement = connection.prepare("ALTER TABLE bolus_data ADD COLUMN normal REAL") + try { + statement.step() + } finally { + statement.close() + } + } +} + + actual fun provideAlertApi(ktorfit: Ktorfit) = ktorfit.createAlertApi() actual fun provideAuthorizationApi(ktorfit: Ktorfit) = ktorfit.createAuthorizationApi() actual fun provideBlobApi(ktorfit: Ktorfit) = ktorfit.createBlobApi() diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt index f87bc22..009b7b0 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt @@ -7,7 +7,9 @@ import kotlinx.serialization.json.Json import org.tidepool.sdk.dto.data.BasalAutomatedDataDto import kotlinx.datetime.Instant import java.util.TimeZone +import kotlin.math.roundToLong import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.minutes @Entity(tableName = "basal_automated_data") data class BasalAutomatedDataEntity( @@ -41,9 +43,9 @@ data class BasalAutomatedDataEntity( @ColumnInfo(name = "delivery_type") var deliveryType: String, @ColumnInfo(name = "duration") - var duration: Int, + var duration: Double, @ColumnInfo(name = "expected_duration") - var expectedDuration: Int? = null, + var expectedDuration: Double? = null, @ColumnInfo(name = "rate") var rate: Double = -1.0, @ColumnInfo(name = "schedule_name") @@ -77,8 +79,8 @@ fun BasalAutomatedDataDto.toEntity() = BasalAutomatedDataEntity( timeZone = timeZone?.id, timeZoneOffset = timeZoneOffset, deliveryType = Json.encodeToString(deliveryType), - duration = duration, - expectedDuration = expectedDuration, + duration = duration.toDouble().div(1.minutes.inWholeMilliseconds), + expectedDuration = expectedDuration?.toDouble()?.div(1.minutes.inWholeMilliseconds), rate = rate, scheduleName = scheduleName, ) @@ -97,8 +99,8 @@ fun BasalAutomatedDataEntity.toDto() = BasalAutomatedDataDto( timeZone = timeZone?.let { TimeZone.getTimeZone(it) }, timeZoneOffset = timeZoneOffset, deliveryType = Json.decodeFromString(deliveryType), - duration = duration, - expectedDuration = expectedDuration, + duration = duration.times(1.minutes.inWholeMilliseconds).roundToLong(), + expectedDuration = expectedDuration?.times(1.minutes.inWholeMilliseconds)?.roundToLong(), rate = rate, scheduleName = scheduleName, ) \ No newline at end of file diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt index f51bc3c..9559ebd 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt @@ -42,6 +42,8 @@ data class BolusDataEntity( var subType: String = "normal", @ColumnInfo(name = "delivery_context") var deliveryContext: String = "", + @ColumnInfo(name = "normal") + var normal: Double? = null, ) : BaseDataEntity( id = id, type = type, @@ -72,6 +74,7 @@ fun BolusDataDto.toEntity() = BolusDataEntity( timeZoneOffset = timeZoneOffset, subType = Json.encodeToString(subType), deliveryContext = Json.encodeToString(deliveryContext), + normal = normal, ) fun BolusDataEntity.toDto() = BolusDataDto( @@ -95,4 +98,5 @@ fun BolusDataEntity.toDto() = BolusDataDto( timeZoneOffset = timeZoneOffset, subType = Json.decodeFromString(subType), deliveryContext = Json.decodeFromString(deliveryContext), + normal = normal, ) diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/AssociationDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/AssociationDto.kt index 8d329ab..5202037 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/AssociationDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/AssociationDto.kt @@ -11,9 +11,9 @@ data class AssociationDto( val id: String?, @SerialName("url") - val url: String?, + val url: String? = null, @SerialName("reason") - val reason: String?, + val reason: String? = null, ) { @Serializable enum class AssociationTypeDto { diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt index a8b4f53..a488d75 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt @@ -10,8 +10,9 @@ import org.tidepool.sdk.dto.toDto import kotlinx.datetime.Instant import java.util.TimeZone import kotlin.time.Duration +import kotlin.math.roundToLong +import kotlin.time.Duration.Companion.minutes -// TODO: Finish implementing automated.v1 @Serializable data class BasalAutomatedDataDto( override val id: String = "", @@ -35,19 +36,52 @@ data class BasalAutomatedDataDto( @SerialName("deliveryType") val deliveryType: DeliveryTypeDto, @SerialName("duration") - val duration: Int, + val duration: Long, @SerialName("expectedDuration") - val expectedDuration: Int? = null, + val expectedDuration: Long? = null, @SerialName("rate") val rate: Double = -1.0, @SerialName("scheduleName") val scheduleName: String? = null, + @SerialName("origin") + val origin: OriginDto? = null, + @SerialName("payload") + val payload: PayloadDto? = null, + @SerialName("suppressed") + val suppressed: SuppressedDto? = null, ) : BaseDataDto() { - val insulinFormulation: Nothing - get() = TODO("schema \"formulation.v1\" not implemented") - val suppressed: Nothing - get() = TODO("schema \"scheduled.v1\" not implemented") - + @Serializable + data class OriginDto( + @SerialName("id") + val id: String, + @SerialName("name") + val name: String, + @SerialName("type") + val type: String, + @SerialName("version") + val version: String, + ) + + @Serializable + data class PayloadDto( + @SerialName("deliveredUnits") + val deliveredUnits: Double, + @SerialName("syncIdentifier") + val syncIdentifier: String, + ) + + @Serializable + data class SuppressedDto( + @SerialName("type") + val type: DataTypeDto = DataTypeDto.Basal, + @SerialName("deliveryType") + val deliveryType: DeliveryTypeDto, + @SerialName("rate") + val rate: Double, + @SerialName("scheduleName") + val scheduleName: String? = null, + ) + @Serializable enum class DeliveryTypeDto { @@ -79,10 +113,13 @@ fun BasalAutomatedDataDto.toDomain(): BasalAutomatedData = BasalAutomatedData( timeZone = timeZone, timeZoneOffset = timeZoneOffset, deliveryType = this.deliveryType.toDomain(), - duration = duration, - expectedDuration = expectedDuration, + duration = duration.toDouble().div(1.minutes.inWholeMilliseconds), + expectedDuration = expectedDuration?.toDouble()?.div(1.minutes.inWholeMilliseconds), rate = rate, - scheduleName = scheduleName + scheduleName = scheduleName, + origin = origin?.toDomain(), + payload = payload?.toDomain(), + suppressed = suppressed?.toDomain(), ) fun BasalAutomatedDataDto.DeliveryTypeDto.toDomain(): BasalAutomatedData.DeliveryType = when (this) { @@ -106,10 +143,13 @@ fun BasalAutomatedData.toDto(): BasalAutomatedDataDto = BasalAutomatedDataDto( timeZone = timeZone, timeZoneOffset = timeZoneOffset, deliveryType = deliveryType.toDto(), - duration = duration, - expectedDuration = expectedDuration, + duration = duration.times(1.minutes.inWholeMilliseconds).roundToLong(), + expectedDuration = expectedDuration?.times(1.minutes.inWholeMilliseconds)?.roundToLong(), rate = rate, - scheduleName = scheduleName + scheduleName = scheduleName, + origin = origin?.toDto(), + payload = payload?.toDto(), + suppressed = suppressed?.toDto(), ) fun BasalAutomatedData.DeliveryType.toDto(): BasalAutomatedDataDto.DeliveryTypeDto = when (this) { @@ -117,4 +157,48 @@ fun BasalAutomatedData.DeliveryType.toDto(): BasalAutomatedDataDto.DeliveryTypeD BasalAutomatedData.DeliveryType.Scheduled -> BasalAutomatedDataDto.DeliveryTypeDto.Scheduled BasalAutomatedData.DeliveryType.Suspend -> BasalAutomatedDataDto.DeliveryTypeDto.Suspend BasalAutomatedData.DeliveryType.Temp -> BasalAutomatedDataDto.DeliveryTypeDto.Temp -} \ No newline at end of file +} + +private fun BasalAutomatedDataDto.OriginDto.toDomain(): BasalAutomatedData.Origin = + BasalAutomatedData.Origin( + id = id, + name = name, + type = type, + version = version, + ) + +private fun BasalAutomatedDataDto.PayloadDto.toDomain(): BasalAutomatedData.Payload = + BasalAutomatedData.Payload( + deliveredUnits = deliveredUnits, + syncIdentifier = syncIdentifier, + ) + +private fun BasalAutomatedDataDto.SuppressedDto.toDomain(): BasalAutomatedData.Suppressed = + BasalAutomatedData.Suppressed( + type = type.toDomain(), + deliveryType = deliveryType.toDomain(), + rate = rate, + scheduleName = scheduleName, + ) + +private fun BasalAutomatedData.Origin.toDto(): BasalAutomatedDataDto.OriginDto = + BasalAutomatedDataDto.OriginDto( + id = id, + name = name, + type = type, + version = version, + ) + +private fun BasalAutomatedData.Payload.toDto(): BasalAutomatedDataDto.PayloadDto = + BasalAutomatedDataDto.PayloadDto( + deliveredUnits = deliveredUnits, + syncIdentifier = syncIdentifier, + ) + +private fun BasalAutomatedData.Suppressed.toDto(): BasalAutomatedDataDto.SuppressedDto = + BasalAutomatedDataDto.SuppressedDto( + type = type.toDto(), + deliveryType = deliveryType.toDto(), + rate = rate, + scheduleName = scheduleName, + ) diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt index 12cf7e8..a54d7bf 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt @@ -39,6 +39,8 @@ data class BolusDataDto( val subType: BolusSubtypeDto = BolusSubtypeDto.Normal, @SerialName("deliveryContext") val deliveryContext: DeliveryContextDto, + @SerialName("normal") + val normal: Double? = null, ) : BaseDataDto() { val insulinFormulation: Nothing @@ -90,6 +92,7 @@ fun BolusDataDto.toDomain(): BolusData = BolusData( timeZoneOffset = timeZoneOffset, subType = subType.toDomain(), deliveryContext = deliveryContext.toDomain(), + normal = normal, ) fun DeliveryContextDto.toDomain(): DeliveryContext = when (this) { @@ -121,6 +124,7 @@ fun BolusData.toDto(): BolusDataDto = BolusDataDto( timeZoneOffset = timeZoneOffset, subType = subType.toDto(), deliveryContext = deliveryContext.toDto(), + normal = normal, ) fun DeliveryContext.toDto(): DeliveryContextDto = when (this) { diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt index 23053de..06ab963 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt @@ -51,7 +51,7 @@ import kotlin.collections.toTypedArray import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import org.tidepool.sdk.repository.EnvironmentRepository +import org.tidepool.sdk.database.entity.data.toDto class DataRepositoryImpl( private val environmentRepository: EnvironmentRepository, @@ -208,21 +208,15 @@ class DataRepositoryImpl( } override suspend fun uploadDataToDataSet( - dataSetId: String, data: List, sessionToken: String, ): Result> { val dtos = data.map { it.toDto() } - return runCatchingNetworkExceptions { - dataApi.uploadDataToDataSet( - sessionToken = sessionToken, - dataSetId = dataSetId, - data = dtos, - ) + if (data.isNotEmpty() && dtos.isEmpty()) { + return Result.failure(Throwable("Mapping data to DTO failed")) } - .map { it.data } - .cacheOnFailure(dtos) - .mapList { it.toDomain() } + dtos.cache() + return Result.success(data) } override suspend fun deleteDataSetData( @@ -352,8 +346,9 @@ class DataRepositoryImpl( } override suspend fun uploadCachedData( - sessionToken: String, userId: String, + sessionToken: String, + dataSetId: String, ): Result = listOf( basalAutomatedDataDao.getAll(), bolusDataDao.getAll(), @@ -362,11 +357,12 @@ class DataRepositoryImpl( foodDataDao.getAll(), insulinDataDao.getAll(), ).flatten().let { entities -> + Logger.v(javaClass.simpleName) { "Uploading ${entities.size} entities to data set $dataSetId" } runCatchingNetworkExceptions { - dataApi.uploadDataForUser( + dataApi.uploadDataToDataSet( sessionToken = sessionToken, - userId = userId, - data = emptyList(), // entities.map { it.toDto() }, + dataSetId = dataSetId, + data = entities.map { it.toDto() }, ) }.map { entities.forEach { entity -> @@ -386,27 +382,33 @@ class DataRepositoryImpl( } } + override fun clearCachedDataSetId() { + cachedDataSetId = null + } + private suspend fun Result>.cacheOnFailure( toUpload: List, ) = fold( onSuccess = { Result.success(it) }, onFailure = { ex -> - toUpload.forEach { dto -> - when (dto) { - is BasalAutomatedDataDto -> basalAutomatedDataDao.insert(dto.toEntity()) - is BolusDataDto -> bolusDataDao.insert(dto.toEntity()) - is ContinuousGlucoseDataDto -> continuousGlucoseDataDao.insert(dto.toEntity()) - is DosingDecisionDataDto -> dosingDecisionDataDao.insert(dto.toEntity()) - is FoodDataDto -> foodDataDao.insert(dto.toEntity()) - is InsulinDataDto -> insulinDataDao.insert(dto.toEntity()) - is DeviceEventDataDto -> deviceEventDataDao.insert(dto.toEntity()) - is CgmSettingsDataDto -> cgmSettingsDataDao.insert(dto.toEntity()) - is ControllerSettingsDataDto -> controllerSettingsDataDao.insert(dto.toEntity()) - is PumpSettingsDataDto -> pumpSettingsDataDao.insert(dto.toEntity()) - else -> Logger.w(toUpload.javaClass.simpleName) { "Unknown data type: ${dto::class.simpleName}" } - } - } + toUpload.cache() Result.failure(ex) }, ) + + private suspend fun List.cache() = forEach { dto -> + when (dto) { + is BasalAutomatedDataDto -> basalAutomatedDataDao.insert(dto.toEntity()) + is BolusDataDto -> bolusDataDao.insert(dto.toEntity()) + is ContinuousGlucoseDataDto -> continuousGlucoseDataDao.insert(dto.toEntity()) + is DosingDecisionDataDto -> dosingDecisionDataDao.insert(dto.toEntity()) + is FoodDataDto -> foodDataDao.insert(dto.toEntity()) + is InsulinDataDto -> insulinDataDao.insert(dto.toEntity()) + is DeviceEventDataDto -> deviceEventDataDao.insert(dto.toEntity()) + is CgmSettingsDataDto -> cgmSettingsDataDao.insert(dto.toEntity()) + is ControllerSettingsDataDto -> controllerSettingsDataDao.insert(dto.toEntity()) + is PumpSettingsDataDto -> pumpSettingsDataDao.insert(dto.toEntity()) + else -> Logger.w(javaClass.simpleName) { "Unknown data type: ${dto::class.simpleName}" } + } + } } \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt index 9c6cbc5..c3fcd2c 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt @@ -19,10 +19,13 @@ data class BasalAutomatedData( override val timeZone: TimeZone? = null, override val timeZoneOffset: Int? = null, val deliveryType: DeliveryType, - val duration: Int, - val expectedDuration: Int? = null, + val duration: Double, + val expectedDuration: Double? = null, val rate: Double = -1.0, val scheduleName: String? = null, + val origin: Origin? = null, + val payload: Payload? = null, + val suppressed: Suppressed? = null, ) : BaseData( id = id, type = type, @@ -37,16 +40,29 @@ data class BasalAutomatedData( timeZone = timeZone, timeZoneOffset = timeZoneOffset, ) { - - val insulinFormulation: Nothing - get() = TODO("schema \"formulation.v1\" not implemented") - val suppressed: Nothing - get() = TODO("schema \"scheduled.v1\" not implemented") - enum class DeliveryType { Automated, Scheduled, Suspend, Temp, } + + data class Origin( + val id: String, + val name: String, + val type: String, + val version: String, + ) + + data class Payload( + val deliveredUnits: Double, + val syncIdentifier: String, + ) + + data class Suppressed( + val type: DataType = DataType.Basal, + val deliveryType: DeliveryType, + val rate: Double, + val scheduleName: String? = null, + ) } \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt index 3d6ee08..68432c8 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt @@ -22,6 +22,7 @@ data class BolusData( override val timeZoneOffset: Int? = null, val subType: BolusSubtype = BolusSubtype.Normal, val deliveryContext: DeliveryContext, + val normal: Double? = null, ) : BaseData( id = id, type = type, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepository.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepository.kt index 73f7e23..3aa0fda 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepository.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepository.kt @@ -64,7 +64,6 @@ interface DataRepository { ): Result suspend fun uploadDataToDataSet( - dataSetId: String, data: List, sessionToken: String ): Result> @@ -150,5 +149,8 @@ interface DataRepository { suspend fun uploadCachedData( userId: String, sessionToken: String, + dataSetId: String, ): Result + + fun clearCachedDataSetId() } \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt index d59da24..579bc83 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt @@ -31,6 +31,7 @@ class DataService internal constructor( private val tokenProvider: TokenProvider, ) { + private val TAG = javaClass.simpleName fun startLifecycleAwareRecurrentUpload( lifecycleProvider: AppLifecycleProvider, scope: CoroutineScope, @@ -44,11 +45,18 @@ class DataService internal constructor( period = period, delay = delay, action = { - tokenProvider.getToken().flatMap { - dataRepository.uploadCachedData( - userId = userRepository.getCurrentUser(it).getOrThrow().userId, - sessionToken = it, - ) + Logger.d(TAG) { "Uploading cached data" } + tokenProvider.getToken().flatMap { sessionToken -> + getDataSetId().flatMap { dataSetId -> + dataRepository.uploadCachedData( + userId = userRepository.getCurrentUser(sessionToken) + .getOrThrow().userId, + sessionToken = sessionToken, + dataSetId = dataSetId, + ) + } + .onSuccess { Logger.d(TAG) { "Cached data uploaded successfully" } } + .onFailure { Logger.e(TAG, it) { "Failed to upload cached data: " } } } }, ) @@ -136,9 +144,9 @@ class DataService internal constructor( suspend fun createDataSet(newDataSet: NewDataSet): Result = tokenProvider.getToken().flatMap { token -> - Logger.d(javaClass.simpleName) { "Creating data set with token: ${token.take(5)}..." } + Logger.d(TAG) { "Creating data set with token: ${token.take(5)}..." } userRepository.getCurrentUser(token).flatMap { user -> - Logger.d(javaClass.simpleName) { "Creating data set for user: ${user.userId}" } + Logger.d(TAG) { "Creating data set for user: ${user.userId}" } dataRepository.createDataSet( userId = user.userId, newDataSet = newDataSet, @@ -173,13 +181,10 @@ class DataService internal constructor( } suspend fun uploadDataToDataSet( - dataSetId: String, data: List, ): Result> = tokenProvider.getToken().flatMap { - Logger.d(javaClass.simpleName) { "Uploading data to data set $dataSetId" } dataRepository.uploadDataToDataSet( - dataSetId = dataSetId, data = data, sessionToken = it, ) @@ -335,13 +340,13 @@ class DataService internal constructor( ) } - suspend fun uploadData(data: List): Result> = - getDataSetId().flatMap { dataSetId -> - uploadDataToDataSet( - dataSetId = dataSetId, - data = data, - ) - } + suspend fun uploadData(data: List): Result> = if (data.isEmpty()) { + Result.failure(IllegalArgumentException("Data list is empty")) + } else { + uploadDataToDataSet(data = data) + .onSuccess { Logger.d(TAG) { "${data.map { it.javaClass.simpleName }} saved for upload" } } + .onFailure { Logger.e(TAG, it) { "Saving ${data.map { it.javaClass.simpleName }} failed: " } } + } suspend fun uploadData(data: BaseData): Result> = uploadData(listOf(data)) @@ -352,7 +357,7 @@ class DataService internal constructor( .filter { it.uploadId != null } .minByOrNull { it.uploadId!! } ?.let { - Logger.d(javaClass.simpleName) { "Found existing data set with id: ${it.id}" } + Logger.d(TAG) { "Found existing data set with id: ${it.id}" } Result.success(it) } ?: createDataSet( @@ -382,9 +387,13 @@ class DataService internal constructor( ), ) }.flatMap { - Logger.d(javaClass.simpleName) { "Created data set: ${it.id}" } + Logger.d(TAG) { "Created data set: ${it.id}" } it.id?.let { Result.success(it) } ?: Result.failure(IllegalStateException("No id")) } } + + fun clearUserDataSetId() { + dataRepository.clearCachedDataSetId() + } } \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt index cbd8d41..b5abf84 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt @@ -1,5 +1,6 @@ package org.tidepool.sdk.service +import co.touchlab.kermit.Logger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -91,10 +92,9 @@ class LifecycleAwareDataUploadManager( while (scope.isActive && lifecycleProvider.isInForeground()) { try { action() + Logger.v(javaClass.simpleName) { "Upload successful" } } catch (e: Exception) { - // Log error but continue uploads - // In production, you might want to implement exponential backoff - // or provide error handling callbacks + Logger.e(javaClass.simpleName, e) { "Error during upload" } } // Break early if we went to background during the delay From 4974d0db205bfe849b76306fe3db0a4019e2824d Mon Sep 17 00:00:00 2001 From: Petr David Date: Tue, 10 Feb 2026 14:21:42 +0100 Subject: [PATCH 3/7] [NEMO-256] production environment --- .../src/commonMain/kotlin/org/tidepool/sdk/Environment.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/Environment.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/Environment.kt index eab52c7..ae0965c 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/Environment.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/Environment.kt @@ -1,3 +1,7 @@ package org.tidepool.sdk -data class Environment(val url: String) +data class Environment(val url: String) { + companion object { + val Producion = Environment("app.tidepool.org") + } +} From 3670a7cd1d50ed03b5b4fb81c7bdec7fe7e7efda Mon Sep 17 00:00:00 2001 From: Petr David Date: Mon, 16 Feb 2026 13:03:20 +0100 Subject: [PATCH 4/7] [NEMO-256] fix upload models --- .../kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt | 4 ++-- .../kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt index a488d75..feb824c 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt @@ -113,8 +113,8 @@ fun BasalAutomatedDataDto.toDomain(): BasalAutomatedData = BasalAutomatedData( timeZone = timeZone, timeZoneOffset = timeZoneOffset, deliveryType = this.deliveryType.toDomain(), - duration = duration.toDouble().div(1.minutes.inWholeMilliseconds), - expectedDuration = expectedDuration?.toDouble()?.div(1.minutes.inWholeMilliseconds), + duration = duration.toDouble().div(1.minutes.inWholeNanoseconds), + expectedDuration = expectedDuration?.toDouble()?.div(1.minutes.inWholeNanoseconds), rate = rate, scheduleName = scheduleName, origin = origin?.toDomain(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt index 06ab963..ba1cab2 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt @@ -357,6 +357,9 @@ class DataRepositoryImpl( foodDataDao.getAll(), insulinDataDao.getAll(), ).flatten().let { entities -> + if (entities.isEmpty()) { + return@let Result.success(Unit) + } Logger.v(javaClass.simpleName) { "Uploading ${entities.size} entities to data set $dataSetId" } runCatchingNetworkExceptions { dataApi.uploadDataToDataSet( From b40a740fbc7c2428396fe9da04379b7d86a58252 Mon Sep 17 00:00:00 2001 From: Petr David Date: Mon, 16 Feb 2026 14:54:16 +0100 Subject: [PATCH 5/7] [NEMO-256] username --- .../org/tidepool/sdk/dto/metadata/users/TrustUserDto.kt | 4 ++-- .../commonMain/kotlin/org/tidepool/sdk/dto/user/UserDto.kt | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/metadata/users/TrustUserDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/metadata/users/TrustUserDto.kt index 42cd25b..8cf411d 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/metadata/users/TrustUserDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/metadata/users/TrustUserDto.kt @@ -47,7 +47,7 @@ fun TrustUserDto.toDomain(): TrustUser = when { emails = emails, termsAccepted = termsAccepted, userId = userId, - username = userName, + username = username, roles = roles, createdTime = createdTime, createdUserId = createdUserId, @@ -66,7 +66,7 @@ fun TrustUserDto.toDomain(): TrustUser = when { emails = emails, termsAccepted = termsAccepted, userId = userId, - username = userName, + username = username, roles = roles, createdTime = createdTime, createdUserId = createdUserId, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/user/UserDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/user/UserDto.kt index f70b449..81bc665 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/user/UserDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/user/UserDto.kt @@ -20,7 +20,7 @@ open class UserDto( @SerialName("userid") val userId: String = "", @SerialName("username") - val userName: String? = null, + val username: String? = null, @SerialName("roles") val roles: List? = null, @Contextual @@ -47,6 +47,7 @@ fun UserDto.toDomain(): User = User( emails = emails, termsAccepted = termsAccepted, userId = userId, + username = username, roles = roles, createdTime = createdTime, createdUserId = createdUserId, From 1fff61f2b121f8a37ae4bf8228d7137c39a5e3bc Mon Sep 17 00:00:00 2001 From: Petr David Date: Wed, 18 Feb 2026 19:38:15 +0100 Subject: [PATCH 6/7] [NEMO-256] fix annotations --- .../entity/data/BasalAutomatedDataEntity.kt | 4 ++- .../database/entity/data/BaseDataEntity.kt | 26 +++++++++++-------- .../database/entity/data/BolusDataEntity.kt | 4 +-- .../entity/data/CgmSettingsDataEntity.kt | 6 ++--- .../data/ContinuousGlucoseDataEntity.kt | 6 ++--- .../data/ControllerSettingsDataEntity.kt | 6 ++--- .../entity/data/DeviceEventDataEntity.kt | 6 ++--- .../entity/data/DosingDecisionDataEntity.kt | 4 +-- .../database/entity/data/FoodDataEntity.kt | 6 ++--- .../database/entity/data/InsulinDataEntity.kt | 6 ++--- .../entity/data/PumpSettingsDataEntity.kt | 6 ++--- .../sdk/dto/data/BasalAutomatedDataDto.kt | 4 +-- .../org/tidepool/sdk/dto/data/BaseDataDto.kt | 2 +- .../org/tidepool/sdk/dto/data/BolusDataDto.kt | 4 +-- .../sdk/dto/data/CgmSettingsDataDto.kt | 4 +-- .../sdk/dto/data/ContinuousGlucoseDataDto.kt | 4 +-- .../sdk/dto/data/ControllerSettingsDataDto.kt | 4 +-- .../sdk/dto/data/DeviceEventDataDto.kt | 4 +-- .../sdk/dto/data/DosingDecisionDataDto.kt | 4 +-- .../org/tidepool/sdk/dto/data/FoodDataDto.kt | 4 +-- .../tidepool/sdk/dto/data/InsulinDataDto.kt | 4 +-- .../sdk/dto/data/PumpSettingsDataDto.kt | 4 +-- .../sdk/repository/DataRepositoryImpl.kt | 5 +++- .../sdk/model/data/BasalAutomatedData.kt | 2 +- .../org/tidepool/sdk/model/data/BaseData.kt | 2 +- .../org/tidepool/sdk/model/data/BolusData.kt | 2 +- .../sdk/model/data/CgmSettingsData.kt | 2 +- .../sdk/model/data/ContinuousGlucoseData.kt | 2 +- .../sdk/model/data/ControllerSettingsData.kt | 2 +- .../sdk/model/data/DeviceEventData.kt | 2 +- .../sdk/model/data/DosingDecisionData.kt | 2 +- .../org/tidepool/sdk/model/data/FoodData.kt | 2 +- .../tidepool/sdk/model/data/InsulinData.kt | 2 +- .../sdk/model/data/PumpSettingsData.kt | 2 +- .../org/tidepool/sdk/service/DataService.kt | 22 +++++++++++++--- .../LifecycleAwareDataUploadManager.kt | 1 - 36 files changed, 97 insertions(+), 75 deletions(-) diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt index 009b7b0..1f3bfd4 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BasalAutomatedDataEntity.kt @@ -89,7 +89,9 @@ fun BasalAutomatedDataEntity.toDto() = BasalAutomatedDataDto( id = id, type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, - annotations = if (annotations.isNullOrBlank()) emptyList() else Json.decodeFromString(annotations!!), + annotations = annotations + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = if (associations.isNullOrBlank()) emptyList() else Json.decodeFromString(associations!!), clockDriftOffset = clockDriftOffset?.milliseconds, conversionOffset = conversionOffset?.milliseconds, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BaseDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BaseDataEntity.kt index 2638645..e3e888a 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BaseDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BaseDataEntity.kt @@ -2,6 +2,7 @@ package org.tidepool.sdk.database.entity.data import androidx.room.Entity import androidx.room.PrimaryKey +import co.touchlab.kermit.Logger import org.tidepool.sdk.dto.data.BasalAutomatedDataDto import org.tidepool.sdk.dto.data.BaseDataDto import org.tidepool.sdk.dto.data.BolusDataDto @@ -47,17 +48,20 @@ internal fun BaseDataEntity.toDomain(): BaseData = when (this) { is PumpSettingsDataEntity -> toDomain() } -internal fun BaseDataEntity.toDto(): BaseDataDto = when (this) { - is BasalAutomatedDataEntity -> toDto() - is BolusDataEntity -> toDto() - is ContinuousGlucoseDataEntity -> toDto() - is DosingDecisionDataEntity -> toDto() - is CgmSettingsDataEntity -> toDto() - is ControllerSettingsDataEntity -> toDto() - is FoodDataEntity -> toDto() - is InsulinDataEntity -> toDto() - is DeviceEventDataEntity -> toDto() - is PumpSettingsDataEntity -> toDto() +internal fun BaseDataEntity.toDto(): BaseDataDto { + Logger.d("BaseDataEntity") { "toDto(): ${javaClass.simpleName}, ${annotations?.let { "\"$it\"" }}"} + return when (this) { + is BasalAutomatedDataEntity -> toDto() + is BolusDataEntity -> toDto() + is ContinuousGlucoseDataEntity -> toDto() + is DosingDecisionDataEntity -> toDto() + is CgmSettingsDataEntity -> toDto() + is ControllerSettingsDataEntity -> toDto() + is FoodDataEntity -> toDto() + is InsulinDataEntity -> toDto() + is DeviceEventDataEntity -> toDto() + is PumpSettingsDataEntity -> toDto() + } } fun BaseDataDto.toEntity(): BaseDataEntity = when (this) { diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt index 9559ebd..5056e7a 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/BolusDataEntity.kt @@ -82,8 +82,8 @@ fun BolusDataEntity.toDto() = BolusDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/CgmSettingsDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/CgmSettingsDataEntity.kt index e8f2a31..caec830 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/CgmSettingsDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/CgmSettingsDataEntity.kt @@ -56,7 +56,7 @@ fun CgmSettingsDataDto.toEntity() = CgmSettingsDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -72,8 +72,8 @@ fun CgmSettingsDataEntity.toDto() = CgmSettingsDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ContinuousGlucoseDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ContinuousGlucoseDataEntity.kt index 3fb785d..c2d6fe5 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ContinuousGlucoseDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ContinuousGlucoseDataEntity.kt @@ -65,7 +65,7 @@ fun ContinuousGlucoseDataDto.toEntity() = ContinuousGlucoseDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -85,8 +85,8 @@ fun ContinuousGlucoseDataEntity.toDto() = ContinuousGlucoseDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ControllerSettingsDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ControllerSettingsDataEntity.kt index ab090d4..254398c 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ControllerSettingsDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/ControllerSettingsDataEntity.kt @@ -56,7 +56,7 @@ fun ControllerSettingsDataDto.toEntity() = ControllerSettingsDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -72,8 +72,8 @@ fun ControllerSettingsDataEntity.toDto() = ControllerSettingsDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DeviceEventDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DeviceEventDataEntity.kt index 9e73a2c..f1578e0 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DeviceEventDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DeviceEventDataEntity.kt @@ -67,7 +67,7 @@ fun DeviceEventDataDto.toEntity() = DeviceEventDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -88,8 +88,8 @@ fun DeviceEventDataEntity.toDto() = DeviceEventDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DosingDecisionDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DosingDecisionDataEntity.kt index d57d83e..7985589 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DosingDecisionDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/DosingDecisionDataEntity.kt @@ -116,8 +116,8 @@ fun DosingDecisionDataEntity.toDto() = DosingDecisionDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/FoodDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/FoodDataEntity.kt index 4130d35..fdae310 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/FoodDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/FoodDataEntity.kt @@ -67,7 +67,7 @@ fun FoodDataDto.toEntity() = FoodDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -96,8 +96,8 @@ fun FoodDataEntity.toDto() = FoodDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/InsulinDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/InsulinDataEntity.kt index 809d4ea..880f8b7 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/InsulinDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/InsulinDataEntity.kt @@ -62,7 +62,7 @@ fun InsulinDataDto.toEntity() = InsulinDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -80,8 +80,8 @@ fun InsulinDataEntity.toDto() = InsulinDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/PumpSettingsDataEntity.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/PumpSettingsDataEntity.kt index bbd1f5e..2a46183 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/PumpSettingsDataEntity.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/database/entity/data/PumpSettingsDataEntity.kt @@ -57,7 +57,7 @@ fun PumpSettingsDataDto.toEntity() = PumpSettingsDataEntity( id = id, type = Json.encodeToString(type), time = time?.epochSeconds, - annotations = annotations.let { Json.encodeToString(it) }, + annotations = annotations?.let { Json.encodeToString(it) }, associations = associations.let { Json.encodeToString(it) }, clockDriftOffset = clockDriftOffset?.inWholeMilliseconds, conversionOffset = conversionOffset?.inWholeMilliseconds, @@ -73,8 +73,8 @@ fun PumpSettingsDataEntity.toDto() = PumpSettingsDataDto( type = Json.decodeFromString(type), time = time?.let { Instant.fromEpochSeconds(it) }, annotations = annotations - ?.let { Json.decodeFromString>>(it) } - .orEmpty(), + ?.takeUnless { it == "null" } + ?.let { Json.decodeFromString>>(it) }, associations = associations ?.let { Json.decodeFromString>(it) } .orEmpty(), diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt index feb824c..3906021 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BasalAutomatedDataDto.kt @@ -19,7 +19,7 @@ data class BasalAutomatedDataDto( override val type: DataTypeDto = DataTypeDto.Alert, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -133,7 +133,7 @@ fun BasalAutomatedData.toDto(): BasalAutomatedDataDto = BasalAutomatedDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BaseDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BaseDataDto.kt index d87ceed..8242c7d 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BaseDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BaseDataDto.kt @@ -32,7 +32,7 @@ abstract class BaseDataDto { @SerialName("time") abstract val time: Instant? @SerialName("annotations") - abstract val annotations: List> + abstract val annotations: List>? @SerialName("associations") abstract val associations: List @SerialName("clockDriftOffset") diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt index a54d7bf..fee73b3 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/BolusDataDto.kt @@ -21,7 +21,7 @@ data class BolusDataDto( override val type: DataTypeDto = DataTypeDto.Alert, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -113,7 +113,7 @@ fun BolusData.toDto(): BolusDataDto = BolusDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/CgmSettingsDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/CgmSettingsDataDto.kt index 4e853f0..da0addb 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/CgmSettingsDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/CgmSettingsDataDto.kt @@ -16,7 +16,7 @@ data class CgmSettingsDataDto( override val type: BaseDataDto.DataTypeDto = BaseDataDto.DataTypeDto.CgmSettings, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -50,7 +50,7 @@ fun CgmSettingsData.toDto(): CgmSettingsDataDto = CgmSettingsDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ContinuousGlucoseDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ContinuousGlucoseDataDto.kt index 7ac5c45..22eb32a 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ContinuousGlucoseDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ContinuousGlucoseDataDto.kt @@ -19,7 +19,7 @@ data class ContinuousGlucoseDataDto( override val type: DataTypeDto = DataTypeDto.Alert, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -75,7 +75,7 @@ fun ContinuousGlucoseData.toDto() = ContinuousGlucoseDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ControllerSettingsDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ControllerSettingsDataDto.kt index 365fc2b..2e9515c 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ControllerSettingsDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/ControllerSettingsDataDto.kt @@ -16,7 +16,7 @@ data class ControllerSettingsDataDto( override val type: BaseDataDto.DataTypeDto = BaseDataDto.DataTypeDto.ControllerSettings, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -50,7 +50,7 @@ fun ControllerSettingsData.toDto(): ControllerSettingsDataDto = ControllerSettin id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DeviceEventDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DeviceEventDataDto.kt index 775444a..a9bd2ea 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DeviceEventDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DeviceEventDataDto.kt @@ -17,7 +17,7 @@ data class DeviceEventDataDto( override val type: DataTypeDto = DataTypeDto.DeviceEvent, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -97,7 +97,7 @@ fun DeviceEventData.toDto(): DeviceEventDataDto = DeviceEventDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DosingDecisionDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DosingDecisionDataDto.kt index baf0138..1971dbb 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DosingDecisionDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/DosingDecisionDataDto.kt @@ -21,7 +21,7 @@ data class DosingDecisionDataDto( override val type: DataTypeDto = DataTypeDto.DosingDecision, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -204,7 +204,7 @@ fun DosingDecisionData.toDto(): DosingDecisionDataDto = DosingDecisionDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/FoodDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/FoodDataDto.kt index 7e72060..edacd86 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/FoodDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/FoodDataDto.kt @@ -18,7 +18,7 @@ data class FoodDataDto( override val type: DataTypeDto = DataTypeDto.Alert, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -95,7 +95,7 @@ fun FoodData.toDto(): FoodDataDto = FoodDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/InsulinDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/InsulinDataDto.kt index 0a39e1a..7d28a81 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/InsulinDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/InsulinDataDto.kt @@ -18,7 +18,7 @@ data class InsulinDataDto( override val type: DataTypeDto = DataTypeDto.Alert, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -63,7 +63,7 @@ fun InsulinData.toDto(): InsulinDataDto = InsulinDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/PumpSettingsDataDto.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/PumpSettingsDataDto.kt index 63c5acf..6bf7e03 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/PumpSettingsDataDto.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/dto/data/PumpSettingsDataDto.kt @@ -16,7 +16,7 @@ data class PumpSettingsDataDto( override val type: BaseDataDto.DataTypeDto = BaseDataDto.DataTypeDto.PumpSettings, @Contextual override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), @Contextual override val clockDriftOffset: Duration? = null, @@ -50,7 +50,7 @@ fun PumpSettingsData.toDto(): PumpSettingsDataDto = PumpSettingsDataDto( id = id, type = type.toDto(), time = time, - annotations = annotations, + annotations = annotations?.takeUnless { it.isEmpty() }, associations = associations.map { it.toDto() }, clockDriftOffset = clockDriftOffset, conversionOffset = conversionOffset, diff --git a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt index ba1cab2..15bfa05 100644 --- a/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/org/tidepool/sdk/repository/DataRepositoryImpl.kt @@ -211,6 +211,9 @@ class DataRepositoryImpl( data: List, sessionToken: String, ): Result> { + Logger.d(javaClass.simpleName) { + "uploadDataToDataSet(): ${data.map { it.javaClass.simpleName }}" + } val dtos = data.map { it.toDto() } if (data.isNotEmpty() && dtos.isEmpty()) { return Result.failure(Throwable("Mapping data to DTO failed")) @@ -357,10 +360,10 @@ class DataRepositoryImpl( foodDataDao.getAll(), insulinDataDao.getAll(), ).flatten().let { entities -> + Logger.v(javaClass.simpleName) { "Uploading ${entities.size} entities" } if (entities.isEmpty()) { return@let Result.success(Unit) } - Logger.v(javaClass.simpleName) { "Uploading ${entities.size} entities to data set $dataSetId" } runCatchingNetworkExceptions { dataApi.uploadDataToDataSet( sessionToken = sessionToken, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt index c3fcd2c..2ff7dc5 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BasalAutomatedData.kt @@ -9,7 +9,7 @@ data class BasalAutomatedData( override val id: String, override val type: DataType = DataType.Basal, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BaseData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BaseData.kt index 7b9ddaf..45f56a7 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BaseData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BaseData.kt @@ -10,7 +10,7 @@ sealed class BaseData( open val id: String, open val type: DataType = DataType.Alert, open val time: Instant? = null, - open val annotations: List> = emptyList(), + open val annotations: List>? = null, open val associations: List = emptyList(), open val clockDriftOffset: Duration? = null, open val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt index 68432c8..8d7755e 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/BolusData.kt @@ -11,7 +11,7 @@ data class BolusData( override val id: String, override val type: DataType = DataType.Bolus, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/CgmSettingsData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/CgmSettingsData.kt index 3858daa..b13205d 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/CgmSettingsData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/CgmSettingsData.kt @@ -9,7 +9,7 @@ data class CgmSettingsData( override val id: String, override val type: DataType = DataType.CgmSettings, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ContinuousGlucoseData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ContinuousGlucoseData.kt index e39c583..54a3e7a 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ContinuousGlucoseData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ContinuousGlucoseData.kt @@ -11,7 +11,7 @@ data class ContinuousGlucoseData( override val id: String, override val type: DataType = DataType.Cbg, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ControllerSettingsData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ControllerSettingsData.kt index 3c50747..27be729 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ControllerSettingsData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/ControllerSettingsData.kt @@ -9,7 +9,7 @@ data class ControllerSettingsData( override val id: String, override val type: DataType = DataType.ControllerSettings, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DeviceEventData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DeviceEventData.kt index a1f16cc..8e85cd1 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DeviceEventData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DeviceEventData.kt @@ -12,7 +12,7 @@ data class DeviceEventData( override val id: String, override val type: DataType = DataType.DeviceEvent, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DosingDecisionData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DosingDecisionData.kt index 5f9e9b4..ac192b3 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DosingDecisionData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/DosingDecisionData.kt @@ -11,7 +11,7 @@ data class DosingDecisionData( override val id: String, override val type: DataType = DataType.DosingDecision, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/FoodData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/FoodData.kt index 3d6616a..bc674fd 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/FoodData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/FoodData.kt @@ -10,7 +10,7 @@ data class FoodData( override val id: String, override val type: DataType = DataType.Food, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/InsulinData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/InsulinData.kt index 5689e44..6dd7f7b 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/InsulinData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/InsulinData.kt @@ -10,7 +10,7 @@ data class InsulinData( override val id: String, override val type: DataType = DataType.Insulin, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/PumpSettingsData.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/PumpSettingsData.kt index 2220c8f..599042c 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/PumpSettingsData.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/model/data/PumpSettingsData.kt @@ -9,7 +9,7 @@ data class PumpSettingsData( override val id: String, override val type: DataType = DataType.PumpSettings, override val time: Instant? = null, - override val annotations: List> = emptyList(), + override val annotations: List>? = null, override val associations: List = emptyList(), override val clockDriftOffset: Duration? = null, override val conversionOffset: Duration? = null, diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt index 579bc83..814d913 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/DataService.kt @@ -45,9 +45,11 @@ class DataService internal constructor( period = period, delay = delay, action = { - Logger.d(TAG) { "Uploading cached data" } + Logger.d("DataService") { "Uploading cached data" } tokenProvider.getToken().flatMap { sessionToken -> + Logger.v("DataService") { "Have token" } getDataSetId().flatMap { dataSetId -> + Logger.i("DataService") { "Have data set id" } dataRepository.uploadCachedData( userId = userRepository.getCurrentUser(sessionToken) .getOrThrow().userId, @@ -343,9 +345,21 @@ class DataService internal constructor( suspend fun uploadData(data: List): Result> = if (data.isEmpty()) { Result.failure(IllegalArgumentException("Data list is empty")) } else { - uploadDataToDataSet(data = data) - .onSuccess { Logger.d(TAG) { "${data.map { it.javaClass.simpleName }} saved for upload" } } - .onFailure { Logger.e(TAG, it) { "Saving ${data.map { it.javaClass.simpleName }} failed: " } } + tokenProvider.getToken().flatMap { + uploadDataToDataSet(data = data) + .onSuccess { + Logger.d(TAG) { "${data.map { it.javaClass.simpleName }} saved for upload" } + Logger.v(TAG) { + "${data.map { "${it.javaClass.simpleName}: ${it.annotations}" }} saved for upload" + } + } + .onFailure { + Logger.e( + TAG, + it + ) { "Saving ${data.map { it.javaClass.simpleName }} failed: " } + } + } } suspend fun uploadData(data: BaseData): Result> = uploadData(listOf(data)) diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt index b5abf84..9e37d97 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/service/LifecycleAwareDataUploadManager.kt @@ -92,7 +92,6 @@ class LifecycleAwareDataUploadManager( while (scope.isActive && lifecycleProvider.isInForeground()) { try { action() - Logger.v(javaClass.simpleName) { "Upload successful" } } catch (e: Exception) { Logger.e(javaClass.simpleName, e) { "Error during upload" } } From 7e45dbd9f9ad1456236f31e8db6ca3d8627a9992 Mon Sep 17 00:00:00 2001 From: Petr David Date: Wed, 18 Feb 2026 19:55:17 +0100 Subject: [PATCH 7/7] [NEMO-256] revoke token functionality --- domain/src/commonMain/kotlin/org/tidepool/sdk/TokenProvider.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/domain/src/commonMain/kotlin/org/tidepool/sdk/TokenProvider.kt b/domain/src/commonMain/kotlin/org/tidepool/sdk/TokenProvider.kt index 5ed3c00..cb94e58 100644 --- a/domain/src/commonMain/kotlin/org/tidepool/sdk/TokenProvider.kt +++ b/domain/src/commonMain/kotlin/org/tidepool/sdk/TokenProvider.kt @@ -2,4 +2,5 @@ package org.tidepool.sdk interface TokenProvider { suspend fun getToken(): Result + fun clearToken() } \ No newline at end of file