From 53799d7260146963746f07cae97285999345804c Mon Sep 17 00:00:00 2001 From: Yang Date: Fri, 24 Nov 2023 01:31:57 +1100 Subject: [PATCH] Update schema, enable explicit API mode for KMP modules. --- .../kstreamlined/buildlogic/KMPBuildLogic.kt | 14 ++++- .../convention/KMPCommonConventionPlugin.kt | 2 + .../convention/KMPIosOnlyConventionPlugin.kt | 2 + .../convention/KMPTestConventionPlugin.kt | 4 +- .../kstreamlined/kmp/data/utils/ResultExt.kt | 2 +- .../kmp/data/feed/FeedRepository.kt | 23 ++++++--- .../kmp/data/feed/FeedSyncState.kt | 2 +- .../kmp/data/feed/model/FeedItem.kt | 29 +++++------ .../kmp/data/feed/model/FeedOriginItem.kt | 4 +- .../kstreamlined/graphql/FeedEntries.graphql | 4 +- .../kstreamlined/graphql/schema.graphqls | 27 ++++------ .../feed/datasource/CloudFeedDataSource.kt | 2 +- .../datasource/mapper/FeedEntryMappers.kt | 9 ++-- .../networking/ApolloClientConfigs.kt | 4 +- .../datasource/util/ApolloApiErrorChecker.kt | 2 +- .../datasource/mapper/FeedEntryMappersTest.kt | 18 +++---- .../kmp/feed/datasource/FeedDataSource.kt | 6 +-- .../kmp/feed/datasource/model/FeedEntry.kt | 27 +++++----- .../kmp/feed/datasource/model/FeedOrigin.kt | 4 +- .../feed/datasource/util/ApiErrorChecker.kt | 4 +- .../kmp/feed/datasource/EdgeFeedDataSource.kt | 2 +- .../datasource/util/KtorApiErrorChecker.kt | 2 +- .../kmp/feed/datasource/FakeFeedData.kt | 51 ++++++++++--------- .../kmp/feed/datasource/FakeFeedDataSource.kt | 6 +-- .../datasource/util/NoOpApiErrorChecker.kt | 2 +- .../persistence/database/FeedItemEntity.sq | 2 +- .../kstreamlined/kmp/test/utils/runTest.kt | 2 +- 27 files changed, 135 insertions(+), 121 deletions(-) diff --git a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/KMPBuildLogic.kt b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/KMPBuildLogic.kt index a381bd31..a743d937 100644 --- a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/KMPBuildLogic.kt +++ b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/KMPBuildLogic.kt @@ -4,6 +4,7 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.dsl.KotlinCommonCompile import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile /** * Apply common configs to KMP project. @@ -33,7 +34,7 @@ internal fun KotlinMultiplatformExtension.configureKMPCommon( /** * Apply test configs to KMP project. */ -internal fun KotlinMultiplatformExtension.configureKMPCommon() { +internal fun KotlinMultiplatformExtension.configureKMPTest() { with(sourceSets) { commonTest { dependencies { @@ -60,3 +61,14 @@ internal fun Project.configureKotlinCommonCompileOptions() { } } } + +/** + * Enable explicit API mode for non-test Kotlin compilations + */ +internal fun Project.enableExplicitApi() { + tasks.withType().configureEach { + if (!name.contains("TestKotlin")) { + kotlinOptions.freeCompilerArgs += "-Xexplicit-api=strict" + } + } +} diff --git a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPCommonConventionPlugin.kt b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPCommonConventionPlugin.kt index 3ce7b493..4f0497a5 100644 --- a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPCommonConventionPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPCommonConventionPlugin.kt @@ -1,6 +1,7 @@ package io.github.reactivecircus.kstreamlined.buildlogic.convention import io.github.reactivecircus.kstreamlined.buildlogic.configureDetekt +import io.github.reactivecircus.kstreamlined.buildlogic.enableExplicitApi import io.github.reactivecircus.kstreamlined.buildlogic.configureKMPCommon import io.github.reactivecircus.kstreamlined.buildlogic.configureKotlinCommonCompileOptions import io.github.reactivecircus.kstreamlined.buildlogic.configureTest @@ -16,6 +17,7 @@ internal class KMPCommonConventionPlugin : Plugin { extensions.configure { configureKMPCommon(target) + enableExplicitApi() } configureKotlinCommonCompileOptions() diff --git a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPIosOnlyConventionPlugin.kt b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPIosOnlyConventionPlugin.kt index dbd7303e..3b0df214 100644 --- a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPIosOnlyConventionPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPIosOnlyConventionPlugin.kt @@ -4,6 +4,7 @@ import io.github.reactivecircus.kstreamlined.buildlogic.configureDetekt import io.github.reactivecircus.kstreamlined.buildlogic.configureKMPCommon import io.github.reactivecircus.kstreamlined.buildlogic.configureKotlinCommonCompileOptions import io.github.reactivecircus.kstreamlined.buildlogic.configureTest +import io.github.reactivecircus.kstreamlined.buildlogic.enableExplicitApi import io.github.reactivecircus.kstreamlined.buildlogic.markNonCompatibleConfigurationCacheTasks import org.gradle.api.Plugin import org.gradle.api.Project @@ -16,6 +17,7 @@ internal class KMPIosOnlyConventionPlugin : Plugin { extensions.configure { configureKMPCommon(target, enableJvmTarget = false) + enableExplicitApi() } configureKotlinCommonCompileOptions() diff --git a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPTestConventionPlugin.kt b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPTestConventionPlugin.kt index 2be1b866..0071428e 100644 --- a/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPTestConventionPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/reactivecircus/kstreamlined/buildlogic/convention/KMPTestConventionPlugin.kt @@ -1,6 +1,6 @@ package io.github.reactivecircus.kstreamlined.buildlogic.convention -import io.github.reactivecircus.kstreamlined.buildlogic.configureKMPCommon +import io.github.reactivecircus.kstreamlined.buildlogic.configureKMPTest import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -11,7 +11,7 @@ internal class KMPTestConventionPlugin : Plugin { pluginManager.apply("org.jetbrains.kotlin.multiplatform") extensions.configure { - configureKMPCommon() + configureKMPTest() } } } diff --git a/kmp/core-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/utils/ResultExt.kt b/kmp/core-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/utils/ResultExt.kt index 21034ca6..2360d95c 100644 --- a/kmp/core-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/utils/ResultExt.kt +++ b/kmp/core-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/utils/ResultExt.kt @@ -8,7 +8,7 @@ import kotlin.coroutines.cancellation.CancellationException * [block] function execution and encapsulating it as a failure. */ @Suppress("TooGenericExceptionCaught", "InstanceOfCheckForException") -inline fun T.runCatchingNonCancellationException(block: () -> R): Result { +public inline fun T.runCatchingNonCancellationException(block: () -> R): Result { return try { Result.success(block()) } catch (e: Throwable) { diff --git a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedRepository.kt b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedRepository.kt index 9f3202b9..1dc22274 100644 --- a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedRepository.kt +++ b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedRepository.kt @@ -1,31 +1,38 @@ package io.github.reactivecircus.kstreamlined.kmp.data.feed +import co.touchlab.kermit.Logger +import io.github.reactivecircus.kstreamlined.kmp.data.feed.model.FeedItem import io.github.reactivecircus.kstreamlined.kmp.data.feed.model.FeedOriginItem import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.FeedDataSource import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow -class FeedRepository( +public class FeedRepository( private val feedDataSource: FeedDataSource ) { - val feedSyncState: Flow = TODO() + public val feedSyncState: Flow = emptyFlow() - suspend fun syncNow() { - feedDataSource.loadFeedEntries(null, true) - TODO() + public suspend fun syncNow() { + feedDataSource.loadFeedEntries(null, true).also { + Logger.i("<>") + it.forEach { entry -> + Logger.i("${entry::class.simpleName}: ${entry.title}, ${entry.publishTime}}") + } + } } - suspend fun selectFeedSource(feedOrigin: FeedOriginItem) { + public suspend fun selectFeedSource(feedOrigin: FeedOriginItem) { feedOrigin.selected TODO() } - suspend fun unselectFeedSource(feedOrigin: FeedOriginItem) { + public suspend fun unselectFeedSource(feedOrigin: FeedOriginItem) { feedOrigin.selected TODO() } - suspend fun getSavedFeedItems() { + public suspend fun loadSavedFeedItems(): List { TODO() } } diff --git a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedSyncState.kt b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedSyncState.kt index 2c0c898c..3fbefbf9 100644 --- a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedSyncState.kt +++ b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/FeedSyncState.kt @@ -3,7 +3,7 @@ package io.github.reactivecircus.kstreamlined.kmp.data.feed import io.github.reactivecircus.kstreamlined.kmp.data.feed.model.FeedItem import io.github.reactivecircus.kstreamlined.kmp.data.feed.model.FeedOriginItem -data class FeedSyncState( +public data class FeedSyncState( val feedOrigins: List, val feedItems: List, ) diff --git a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedItem.kt b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedItem.kt index d5e4d52a..e4fd9042 100644 --- a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedItem.kt +++ b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedItem.kt @@ -1,48 +1,47 @@ package io.github.reactivecircus.kstreamlined.kmp.data.feed.model -sealed interface FeedItem { - val id: String - val title: String - val publishTimestamp: String - val contentUrl: String - val savedForLater: Boolean +public sealed interface FeedItem { + public val id: String + public val title: String + public val publishTime: String + public val contentUrl: String + public val savedForLater: Boolean - data class KotlinBlog( + public data class KotlinBlog( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, override val savedForLater: Boolean, val featuredImageUrl: String?, val description: String, ) : FeedItem - data class KotlinYouTube( + public data class KotlinYouTube( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, override val savedForLater: Boolean, val thumbnailUrl: String, val description: String, ) : FeedItem - data class TalkingKotlin( + public data class TalkingKotlin( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, override val savedForLater: Boolean, val podcastLogoUrl: String, val tags: List, ) : FeedItem - data class KotlinWeekly( + public data class KotlinWeekly( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, override val savedForLater: Boolean, - val newsletterLogoUrl: String, ) : FeedItem } diff --git a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedOriginItem.kt b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedOriginItem.kt index 07b17356..d0667cd2 100644 --- a/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedOriginItem.kt +++ b/kmp/data/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/data/feed/model/FeedOriginItem.kt @@ -1,12 +1,12 @@ package io.github.reactivecircus.kstreamlined.kmp.data.feed.model -data class FeedOriginItem( +public data class FeedOriginItem( val key: Key, val title: String, val description: String, val selected: Boolean, ) { - enum class Key { + public enum class Key { KotlinBlog, KotlinYouTubeChannel, TalkingKotlinPodcast, diff --git a/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/FeedEntries.graphql b/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/FeedEntries.graphql index 02325f27..e56d5fb5 100644 --- a/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/FeedEntries.graphql +++ b/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/FeedEntries.graphql @@ -2,7 +2,7 @@ query FeedEntriesQuery($filters: [FeedSourceKey!]) { feedEntries(filters: $filters) { id title - publishTimestamp + publishTime contentUrl ... on KotlinBlog { featuredImageUrl @@ -17,7 +17,7 @@ query FeedEntriesQuery($filters: [FeedSourceKey!]) { tags } ... on KotlinWeekly { - newsletterLogoUrl + contentUrl } } } diff --git a/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/schema.graphqls b/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/schema.graphqls index 0f711b33..7bd8d572 100644 --- a/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/schema.graphqls +++ b/kmp/feed-datasource/cloud/src/commonMain/graphql/io/github/reactivecircus/kstreamlined/graphql/schema.graphqls @@ -267,9 +267,9 @@ interface FeedEntry { title: String! """ - Publish date of the feed entry as UTC timestamp. + Publish time of the feed entry in ISO 8601. """ - publishTimestamp: String! + publishTime: String! """ Url of the content. @@ -328,9 +328,9 @@ type KotlinBlog implements FeedEntry { title: String! """ - Publish date of the feed entry as UTC timestamp. + Publish time of the feed entry in ISO 8601. """ - publishTimestamp: String! + publishTime: String! """ Url of the content. @@ -340,7 +340,7 @@ type KotlinBlog implements FeedEntry { """ Url of the feature image. """ - featuredImageUrl: String + featuredImageUrl: String! """ Description of the blog post. @@ -360,19 +360,14 @@ type KotlinWeekly implements FeedEntry { title: String! """ - Publish date of the feed entry as UTC timestamp. + Publish time of the feed entry in ISO 8601. """ - publishTimestamp: String! + publishTime: String! """ Url of the content. """ contentUrl: String! - - """ - Url of the newsletter logo. - """ - newsletterLogoUrl: String! } type KotlinYouTube implements FeedEntry { @@ -387,9 +382,9 @@ type KotlinYouTube implements FeedEntry { title: String! """ - Publish date of the feed entry as UTC timestamp. + Publish time of the feed entry in ISO 8601. """ - publishTimestamp: String! + publishTime: String! """ Url of the content. @@ -433,9 +428,9 @@ type TalkingKotlin implements FeedEntry { title: String! """ - Publish date of the feed entry as UTC timestamp. + Publish time of the feed entry in ISO 8601. """ - publishTimestamp: String! + publishTime: String! """ Url of the content. diff --git a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/CloudFeedDataSource.kt b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/CloudFeedDataSource.kt index 8b014cce..dfa2d96c 100644 --- a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/CloudFeedDataSource.kt +++ b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/CloudFeedDataSource.kt @@ -11,7 +11,7 @@ import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedEntry import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedOrigin import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.networking.defaultFetchPolicy -class CloudFeedDataSource(private val apolloClient: ApolloClient) : FeedDataSource { +public class CloudFeedDataSource(private val apolloClient: ApolloClient) : FeedDataSource { override suspend fun loadFeedOrigins(refresh: Boolean): List { return runCatching { diff --git a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappers.kt b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappers.kt index 81eebc7d..920b2912 100644 --- a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappers.kt +++ b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappers.kt @@ -17,7 +17,7 @@ internal fun FeedEntriesQuery.KotlinBlogFeedEntry.asExternalModel(): FeedEntry.K return FeedEntry.KotlinBlog( id = this.id, title = this.title, - publishTimestamp = this.publishTimestamp, + publishTime = this.publishTime, contentUrl = this.contentUrl, featuredImageUrl = this.onKotlinBlog.featuredImageUrl, description = this.onKotlinBlog.description, @@ -28,7 +28,7 @@ internal fun FeedEntriesQuery.KotlinYouTubeFeedEntry.asExternalModel(): FeedEntr return FeedEntry.KotlinYouTube( id = this.id, title = this.title, - publishTimestamp = this.publishTimestamp, + publishTime = this.publishTime, contentUrl = this.contentUrl, thumbnailUrl = this.onKotlinYouTube.thumbnailUrl, description = this.onKotlinYouTube.description, @@ -39,7 +39,7 @@ internal fun FeedEntriesQuery.TalkingKotlinFeedEntry.asExternalModel(): FeedEntr return FeedEntry.TalkingKotlin( id = this.id, title = this.title, - publishTimestamp = this.publishTimestamp, + publishTime = this.publishTime, contentUrl = this.contentUrl, podcastLogoUrl = this.onTalkingKotlin.podcastLogoUrl, tags = this.onTalkingKotlin.tags, @@ -50,8 +50,7 @@ internal fun FeedEntriesQuery.KotlinWeeklyFeedEntry.asExternalModel(): FeedEntry return FeedEntry.KotlinWeekly( id = this.id, title = this.title, - publishTimestamp = this.publishTimestamp, + publishTime = this.publishTime, contentUrl = this.contentUrl, - newsletterLogoUrl = this.onKotlinWeekly.newsletterLogoUrl, ) } diff --git a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/networking/ApolloClientConfigs.kt b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/networking/ApolloClientConfigs.kt index c989ac59..df51e07f 100644 --- a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/networking/ApolloClientConfigs.kt +++ b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/networking/ApolloClientConfigs.kt @@ -5,8 +5,8 @@ import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory import kotlin.time.DurationUnit import kotlin.time.toDuration -object ApolloClientConfigs { - val apolloStore = ApolloStore( +public object ApolloClientConfigs { + public val apolloStore: ApolloStore = ApolloStore( normalizedCacheFactory = MemoryCacheFactory( maxSizeBytes = MaxSizeBytes, expireAfterMillis = CacheExpiry.toLong(DurationUnit.MILLISECONDS) diff --git a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApolloApiErrorChecker.kt b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApolloApiErrorChecker.kt index be5c4a3f..8e9ac1d5 100644 --- a/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApolloApiErrorChecker.kt +++ b/kmp/feed-datasource/cloud/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApolloApiErrorChecker.kt @@ -3,7 +3,7 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource.util import com.apollographql.apollo3.exception.ApolloNetworkException import com.apollographql.apollo3.exception.NoDataException -object ApolloApiErrorChecker : ApiErrorChecker { +public object ApolloApiErrorChecker : ApiErrorChecker { override fun isNetworkError(throwable: Throwable?): Boolean { return when (throwable) { // when using dataOrThrow() exceptions are wrapped in NoDataException diff --git a/kmp/feed-datasource/cloud/src/commonTest/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappersTest.kt b/kmp/feed-datasource/cloud/src/commonTest/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappersTest.kt index bb66bb86..a1f7dfa5 100644 --- a/kmp/feed-datasource/cloud/src/commonTest/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappersTest.kt +++ b/kmp/feed-datasource/cloud/src/commonTest/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/mapper/FeedEntryMappersTest.kt @@ -18,7 +18,7 @@ class FeedEntryMappersTest { buildKotlinBlog { id = "1" title = "Kotlin Blog Post" - publishTimestamp = "2022-01-01T00:00:00Z" + publishTime = "2022-01-01T00:00:00Z" contentUrl = "https://blog.kotlinlang.org/post" featuredImageUrl = "https://blog.kotlinlang.org/image" description = "A blog post about Kotlin" @@ -29,7 +29,7 @@ class FeedEntryMappersTest { val expectedFeedEntry = FeedEntry.KotlinBlog( id = "1", title = "Kotlin Blog Post", - publishTimestamp = "2022-01-01T00:00:00Z", + publishTime = "2022-01-01T00:00:00Z", contentUrl = "https://blog.kotlinlang.org/post", featuredImageUrl = "https://blog.kotlinlang.org/image", description = "A blog post about Kotlin", @@ -45,7 +45,7 @@ class FeedEntryMappersTest { buildKotlinYouTube { id = "2" title = "Kotlin YouTube Video" - publishTimestamp = "2022-01-02T00:00:00Z" + publishTime = "2022-01-02T00:00:00Z" contentUrl = "https://youtube.com/kotlinvideo" thumbnailUrl = "https://youtube.com/kotlinvideo/thumbnail" description = "A YouTube video about Kotlin" @@ -56,7 +56,7 @@ class FeedEntryMappersTest { val expectedFeedEntry = FeedEntry.KotlinYouTube( id = "2", title = "Kotlin YouTube Video", - publishTimestamp = "2022-01-02T00:00:00Z", + publishTime = "2022-01-02T00:00:00Z", contentUrl = "https://youtube.com/kotlinvideo", thumbnailUrl = "https://youtube.com/kotlinvideo/thumbnail", description = "A YouTube video about Kotlin", @@ -72,7 +72,7 @@ class FeedEntryMappersTest { buildTalkingKotlin { id = "3" title = "Talking Kotlin Podcast" - publishTimestamp = "2022-01-03T00:00:00Z" + publishTime = "2022-01-03T00:00:00Z" contentUrl = "https://talkingkotlin.com/podcast" podcastLogoUrl = "https://talkingkotlin.com/podcast/logo" tags = listOf("Kotlin", "Podcast") @@ -83,7 +83,7 @@ class FeedEntryMappersTest { val expectedFeedEntry = FeedEntry.TalkingKotlin( id = "3", title = "Talking Kotlin Podcast", - publishTimestamp = "2022-01-03T00:00:00Z", + publishTime = "2022-01-03T00:00:00Z", contentUrl = "https://talkingkotlin.com/podcast", podcastLogoUrl = "https://talkingkotlin.com/podcast/logo", tags = listOf("Kotlin", "Podcast"), @@ -99,9 +99,8 @@ class FeedEntryMappersTest { buildKotlinWeekly { id = "4" title = "Kotlin Weekly Newsletter" - publishTimestamp = "2022-01-04T00:00:00Z" + publishTime = "2022-01-04T00:00:00Z" contentUrl = "https://kotlinweekly.net/newsletter" - newsletterLogoUrl = "https://kotlinweekly.net/newsletter/logo" }, ) }.feedEntries.first() @@ -109,9 +108,8 @@ class FeedEntryMappersTest { val expectedFeedEntry = FeedEntry.KotlinWeekly( id = "4", title = "Kotlin Weekly Newsletter", - publishTimestamp = "2022-01-04T00:00:00Z", + publishTime = "2022-01-04T00:00:00Z", contentUrl = "https://kotlinweekly.net/newsletter", - newsletterLogoUrl = "https://kotlinweekly.net/newsletter/logo", ) assertEquals(expectedFeedEntry, apolloFeedEntry.asExternalModel()) diff --git a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FeedDataSource.kt b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FeedDataSource.kt index e8c2b81f..44840abf 100644 --- a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FeedDataSource.kt +++ b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FeedDataSource.kt @@ -3,13 +3,13 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedEntry import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedOrigin -interface FeedDataSource { +public interface FeedDataSource { - suspend fun loadFeedOrigins( + public suspend fun loadFeedOrigins( refresh: Boolean = false ): List - suspend fun loadFeedEntries( + public suspend fun loadFeedEntries( filters: List? = null, refresh: Boolean = false, ): List diff --git a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedEntry.kt b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedEntry.kt index c955721f..b6533d6a 100644 --- a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedEntry.kt +++ b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedEntry.kt @@ -1,43 +1,42 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model -sealed interface FeedEntry { - val id: String - val title: String - val publishTimestamp: String - val contentUrl: String +public sealed interface FeedEntry { + public val id: String + public val title: String + public val publishTime: String + public val contentUrl: String - data class KotlinBlog( + public data class KotlinBlog( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, val featuredImageUrl: String?, val description: String, ) : FeedEntry - data class KotlinYouTube( + public data class KotlinYouTube( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, val thumbnailUrl: String, val description: String, ) : FeedEntry - data class TalkingKotlin( + public data class TalkingKotlin( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, val podcastLogoUrl: String, val tags: List, ) : FeedEntry - data class KotlinWeekly( + public data class KotlinWeekly( override val id: String, override val title: String, - override val publishTimestamp: String, + override val publishTime: String, override val contentUrl: String, - val newsletterLogoUrl: String, ) : FeedEntry } diff --git a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedOrigin.kt b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedOrigin.kt index f1198755..c2ae1866 100644 --- a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedOrigin.kt +++ b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/model/FeedOrigin.kt @@ -1,11 +1,11 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model -data class FeedOrigin( +public data class FeedOrigin( val key: Key, val title: String, val description: String, ) { - enum class Key { + public enum class Key { KotlinBlog, KotlinYouTubeChannel, TalkingKotlinPodcast, diff --git a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApiErrorChecker.kt b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApiErrorChecker.kt index f3ffe434..4861e4ca 100644 --- a/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApiErrorChecker.kt +++ b/kmp/feed-datasource/common/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/ApiErrorChecker.kt @@ -1,10 +1,10 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource.util -interface ApiErrorChecker { +public interface ApiErrorChecker { /** * Returns whether the [throwable] is a network error * e.g. socket closed, DNS issue, TLS problem. */ - fun isNetworkError(throwable: Throwable?): Boolean + public fun isNetworkError(throwable: Throwable?): Boolean } diff --git a/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/EdgeFeedDataSource.kt b/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/EdgeFeedDataSource.kt index f2ffa621..34fb172d 100644 --- a/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/EdgeFeedDataSource.kt +++ b/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/EdgeFeedDataSource.kt @@ -3,7 +3,7 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedEntry import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedOrigin -class EdgeFeedDataSource : FeedDataSource { +public class EdgeFeedDataSource : FeedDataSource { override suspend fun loadFeedOrigins(refresh: Boolean): List { TODO() diff --git a/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/KtorApiErrorChecker.kt b/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/KtorApiErrorChecker.kt index a72bfac9..f3ba6fd3 100644 --- a/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/KtorApiErrorChecker.kt +++ b/kmp/feed-datasource/edge/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/KtorApiErrorChecker.kt @@ -1,6 +1,6 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource.util -object KtorApiErrorChecker : ApiErrorChecker { +public object KtorApiErrorChecker : ApiErrorChecker { override fun isNetworkError(throwable: Throwable?): Boolean { TODO() } diff --git a/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedData.kt b/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedData.kt index c02a9e49..7a63af6d 100644 --- a/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedData.kt +++ b/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedData.kt @@ -5,7 +5,7 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedEntry import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedOrigin -val FakeFeedOrigins = listOf( +public val FakeFeedOrigins: List = listOf( FeedOrigin( key = FeedOrigin.Key.KotlinBlog, title = "Kotlin Blog", @@ -28,39 +28,40 @@ val FakeFeedOrigins = listOf( ), ) -val FakeFeedEntries = listOf( +public val FakeFeedEntries: List = listOf( FeedEntry.KotlinBlog( - id = "https://blog.jetbrains.com/?post_type=kotlin&p=264203", - title = "A New Approach to Incremental Compilation in Kotlin", - publishTimestamp = "1657882573", - contentUrl = "https://blog.jetbrains.com/kotlin/2022/07/a-new-approach-to-incremental-compilation-in-kotlin/", - featuredImageUrl = "https://blog.jetbrains.com/wp-content/uploads/2022/07/A-New-Approach-to-Incremental-Compilation-in-Kotlin-EN-2_Twitter-Blog.png", - description = "In Kotlin 1.7.0, we’ve reworked incremental compilation for project changes in cross-module dependencies. The new approach lifts previous limitations on incremental compilation. It’s now supported when changes are made inside dependent non-Kotlin modules, and it is compatible with the Gradle build cache. Support for compilation avoidance has also been improved. All of these advancements decrease […]", + id = "https://blog.jetbrains.com/?post_type=kotlin&p=405553", + title = "Kotlin Multiplatform Development Roadmap for 2024", + publishTime = "2023-11-16T11:59:46Z", + contentUrl = "https://blog.jetbrains.com/kotlin/2023/11/kotlin-multiplatform-development-roadmap-for-2024/", + featuredImageUrl = "https://blog.jetbrains.com/wp-content/uploads/2023/11/kmp_roadmap_720.png", + description = "With the recently achieved stability of Kotlin Multiplatform, development teams worldwide can now seamlessly and confidently adopt it in production. However, this is just the beginning for KMP and its ecosystem. To equip you with the best cross-platform development experience, JetBrains aims to deliver a host of further improvements to the core Kotlin Multiplatform technology, […]", ), FeedEntry.KotlinYouTube( - id = "yt:video:ihMhu3hvCCE", - title = "Kotlin and Java Interoperability in Spring Projects", - publishTimestamp = "1657114786", - contentUrl = "https://www.youtube.com/watch?v=ihMhu3hvCCE", - thumbnailUrl = "https://i2.ytimg.com/vi/ihMhu3hvCCE/hqdefault.jpg", - description = "We have configured the Kotlin compiler in a Java/Spring project - now what? Let's talk about important details you need to know about calling Java from Kotlin code and vice versa. Links: Adding Kotlin to Spring/Maven project: https://youtu.be/4-qOxvjjF8g Calling Java from Kotlin: https://kotlinlang.org/docs/java-interop.html Calling Kotlin from Java: https://kotlinlang.org/docs/java-to-kotlin-interop.html Kotlin Spring compiler plugin: https://kotlinlang.org/docs/all-open-plugin.html#spring-support Just starting with Kotlin? Learn Kotlin by creating real-world applications with JetBrains Academy Build simple games, a chat bot, a coffee machine simulator, and other interactive projects step by step in a hands-on learning environment. Get started: https://hyperskill.org/join/fromyoutubetoJetSalesStat?redirect=true&next=/tracks/18 #springboot #springframework #kotlin #interoperability", + id = "yt:video:bz4cQeaXmsI", + title = "The State of Kotlin Multiplatform", + publishTime = "2023-11-21T18:47:47Z", + contentUrl = "https://www.youtube.com/watch?v=bz4cQeaXmsI", + thumbnailUrl = "https://i3.ytimg.com/vi/bz4cQeaXmsI/hqdefault.jpg", + description = "JetBrains Kotlin Multiplatform (KMP) is an open-source technology designed for flexible cross-platform development. It allows you to develop apps for Android, iOS, desktop, web, and server-side and efficiently reuse code across them, all while retaining the benefits of native programming. After 8 years of development, KMP has been refined into a production-ready technology and is going Stable, which means now is a great time to start using it in your project.", ), FeedEntry.TalkingKotlin( - id = "https://talkingkotlin.com/turbocharging-kotlin-arrow-analysis-optics-meta", - title = "Turbocharging Kotlin: Arrow Analysis, Optics & Meta", - publishTimestamp = "1656374400", - contentUrl = "https://talkingkotlin.com/turbocharging-kotlin-arrow-analysis-optics-meta/", + id = "https://talkingkotlin.com/making-multiplatform-better", + title = "Making Multiplatform Better", + publishTime = "Making Multiplatform Better", + contentUrl = "https://talkingkotlin.com/making-multiplatform-better/", podcastLogoUrl = "https://talkingkotlin.com/images/kotlin_talking_logo.png", tags = listOf( - "Arrow", - "Code Quality", + "Kotlin", + "KMP", + "Kotlin Multiplatform", + "Coroutines", ), ), FeedEntry.KotlinWeekly( - id = "21a2c7f9e24fae1631468c5507e4ff7c", - title = "Kotlin Weekly #312 has just been published!", - publishTimestamp = "1658675020", - contentUrl = "https://t.co/7JzvarYb05", - newsletterLogoUrl = "https://pbs.twimg.com/profile_images/883969154667204608/26qTz9AE_400x400.jpg", + id = "https://mailchi.mp/kotlinweekly/kotlin-weekly-381", + title = "Kotlin Weekly #381", + publishTime = "2023-11-19T09:13:00Z", + contentUrl = "https://mailchi.mp/kotlinweekly/kotlin-weekly-381", ), ) diff --git a/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedDataSource.kt b/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedDataSource.kt index d1cfaa65..e0f3e555 100644 --- a/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedDataSource.kt +++ b/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/FakeFeedDataSource.kt @@ -3,13 +3,13 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedEntry import io.github.reactivecircus.kstreamlined.kmp.feed.datasource.model.FeedOrigin -class FakeFeedDataSource : FeedDataSource { +public class FakeFeedDataSource : FeedDataSource { - var nextFeedSourcesResponse: suspend () -> List = { + public var nextFeedSourcesResponse: suspend () -> List = { FakeFeedOrigins } - var nextFeedEntriesResponse: suspend ( + public var nextFeedEntriesResponse: suspend ( filters: List? ) -> List = { FakeFeedEntries diff --git a/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/NoOpApiErrorChecker.kt b/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/NoOpApiErrorChecker.kt index 1bb3cfb8..89a03722 100644 --- a/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/NoOpApiErrorChecker.kt +++ b/kmp/feed-datasource/testing/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/feed/datasource/util/NoOpApiErrorChecker.kt @@ -1,5 +1,5 @@ package io.github.reactivecircus.kstreamlined.kmp.feed.datasource.util -object NoOpApiErrorChecker : ApiErrorChecker { +public object NoOpApiErrorChecker : ApiErrorChecker { override fun isNetworkError(throwable: Throwable?): Boolean = false } diff --git a/kmp/persistence/src/commonMain/sqldelight/io/github/reactivecircus/kstreamlined/kmp/persistence/database/FeedItemEntity.sq b/kmp/persistence/src/commonMain/sqldelight/io/github/reactivecircus/kstreamlined/kmp/persistence/database/FeedItemEntity.sq index d00f4b44..34a7fbe8 100644 --- a/kmp/persistence/src/commonMain/sqldelight/io/github/reactivecircus/kstreamlined/kmp/persistence/database/FeedItemEntity.sq +++ b/kmp/persistence/src/commonMain/sqldelight/io/github/reactivecircus/kstreamlined/kmp/persistence/database/FeedItemEntity.sq @@ -3,7 +3,7 @@ import kotlin.Boolean; CREATE TABLE feedItemEntity ( id TEXT NOT NULL PRIMARY KEY, title TEXT NOT NULL, - publishTimestamp TEXT NOT NULL, + publishTime TEXT NOT NULL, savedForLater INTEGER AS Boolean NOT NULL ); diff --git a/kmp/test-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/test/utils/runTest.kt b/kmp/test-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/test/utils/runTest.kt index 870159fc..837d3cc3 100644 --- a/kmp/test-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/test/utils/runTest.kt +++ b/kmp/test-utils/src/commonMain/kotlin/io/github/reactivecircus/kstreamlined/kmp/test/utils/runTest.kt @@ -8,7 +8,7 @@ import kotlin.coroutines.CoroutineContext * Test util that executes [block] with [kotlinx.coroutines.test.runTest] with optional [before] and [after] blocks. * Adapted from https://github.com/apollographql/apollo-kotlin/blob/main/apollo-testing-support/src/commonMain/kotlin/com/apollographql/apollo3/testing/internal/runTest.kt. */ -fun runTest( +public fun runTest( context: CoroutineContext = EmptyCoroutineContext, before: suspend CoroutineScope.() -> Unit = {}, after: suspend CoroutineScope.() -> Unit = {},