Skip to content

Commit

Permalink
Restructure data layer. Custom models in :data-common, GraphQL sche…
Browse files Browse the repository at this point in the history
…ma and queries in `:data-runtime-cloud` with mappers.
  • Loading branch information
ychescale9 committed Nov 10, 2023
1 parent 7d465c9 commit 7b7fa95
Show file tree
Hide file tree
Showing 19 changed files with 400 additions and 129 deletions.
23 changes: 0 additions & 23 deletions kmm/apollo-models/build.gradle.kts

This file was deleted.

11 changes: 0 additions & 11 deletions kmm/data-common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
plugins {
id("kstreamlined.kmm.jvm-and-ios")
}

kotlin {
sourceSets {
commonMain {
dependencies {
api(project(":kmm:apollo-models"))
implementation(libs.kotlinx.coroutines.core)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package io.github.reactivecircus.kstreamlined.kmm.data.feed

import io.github.reactivecircus.kstreamlined.kmm.apollo.FeedEntriesQuery
import io.github.reactivecircus.kstreamlined.kmm.apollo.FeedSourcesQuery
import io.github.reactivecircus.kstreamlined.kmm.apollo.type.FeedSourceKey
import io.github.reactivecircus.kstreamlined.kmm.data.feed.model.FeedEntry
import io.github.reactivecircus.kstreamlined.kmm.data.feed.model.FeedSource

interface FeedRepo {

suspend fun loadFeedSources(
refresh: Boolean = false
): List<FeedSourcesQuery.FeedSource>
): List<FeedSource>

suspend fun loadFeedEntries(
filters: List<FeedSourceKey>? = null,
filters: List<FeedSource.Key>? = null,
refresh: Boolean = false,
): List<FeedEntriesQuery.FeedEntry>
): List<FeedEntry>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.github.reactivecircus.kstreamlined.kmm.data.feed.model

sealed interface FeedEntry {
val id: String
val title: String
val publishTimestamp: String
val contentUrl: String

data class KotlinBlog(
override val id: String,
override val title: String,
override val publishTimestamp: String,
override val contentUrl: String,
val featuredImageUrl: String,
val description: String,
) : FeedEntry

data class KotlinYouTube(
override val id: String,
override val title: String,
override val publishTimestamp: String,
override val contentUrl: String,
val thumbnailUrl: String,
val description: String,
) : FeedEntry

data class TalkingKotlin(
override val id: String,
override val title: String,
override val publishTimestamp: String,
override val contentUrl: String,
val podcastLogoUrl: String,
val tags: List<String>,
) : FeedEntry

data class KotlinWeekly(
override val id: String,
override val title: String,
override val publishTimestamp: String,
override val contentUrl: String,
val newsletterLogoUrl: String,
) : FeedEntry
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.github.reactivecircus.kstreamlined.kmm.data.feed.model

data class FeedSource(
val key: Key,
val title: String,
val description: String,
) {
enum class Key {
KotlinBlog,
KotlinYouTubeChannel,
TalkingKotlinPodcast,
KotlinWeekly,
}
}
10 changes: 10 additions & 0 deletions kmm/data-runtime-cloud/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
plugins {
id("kstreamlined.kmm.jvm-and-ios")
id("kstreamlined.kmm.test")
id("com.apollographql.apollo3")
}

apollo {
service("kstreamlined") {
packageName.set("io.github.reactivecircus.kstreamlined.graphql")
codegenModels.set("responseBased")
flattenModels.set(true)
generateDataBuilders.set(true)
}
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,42 @@ package io.github.reactivecircus.kstreamlined.kmm.data.feed
import co.touchlab.kermit.Logger
import com.apollographql.apollo3.ApolloClient
import com.apollographql.apollo3.api.Optional
import io.github.reactivecircus.kstreamlined.kmm.apollo.FeedEntriesQuery
import io.github.reactivecircus.kstreamlined.kmm.apollo.FeedSourcesQuery
import io.github.reactivecircus.kstreamlined.kmm.apollo.type.FeedSourceKey
import io.github.reactivecircus.kstreamlined.graphql.FeedEntriesQuery
import io.github.reactivecircus.kstreamlined.graphql.FeedSourcesQuery
import io.github.reactivecircus.kstreamlined.kmm.data.feed.mapper.toApollo
import io.github.reactivecircus.kstreamlined.kmm.data.feed.mapper.toModel
import io.github.reactivecircus.kstreamlined.kmm.data.feed.model.FeedEntry
import io.github.reactivecircus.kstreamlined.kmm.data.feed.model.FeedSource
import io.github.reactivecircus.kstreamlined.kmm.data.networking.defaultFetchPolicy

class CloudFeedRepo(private val apolloClient: ApolloClient) : FeedRepo {

override suspend fun loadFeedSources(refresh: Boolean): List<FeedSourcesQuery.FeedSource> {
override suspend fun loadFeedSources(refresh: Boolean): List<FeedSource> {
return runCatching {
apolloClient.query(FeedSourcesQuery())
.defaultFetchPolicy(refresh)
.execute()
.dataAssertNoErrors.feedSources
}.onFailure {
Logger.w("Query failed", it)
}.getOrThrow()
}.getOrThrow().map { it.toModel() }
}

override suspend fun loadFeedEntries(
filters: List<FeedSourceKey>?,
filters: List<FeedSource.Key>?,
refresh: Boolean,
): List<FeedEntriesQuery.FeedEntry> {
): List<FeedEntry> {
return runCatching {
apolloClient.query(FeedEntriesQuery(filters = Optional.presentIfNotNull(filters)))
apolloClient.query(
FeedEntriesQuery(
filters = Optional.presentIfNotNull(filters?.map { it.toApollo() })
)
)
.defaultFetchPolicy(refresh)
.execute()
.dataAssertNoErrors.feedEntries
}.onFailure {
Logger.w("Query failed", it)
}.getOrThrow()
}.getOrThrow().map { it.toModel() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package io.github.reactivecircus.kstreamlined.kmm.data.feed.mapper

import io.github.reactivecircus.kstreamlined.graphql.FeedEntriesQuery
import io.github.reactivecircus.kstreamlined.kmm.data.feed.model.FeedEntry

internal fun FeedEntriesQuery.FeedEntry.toModel(): FeedEntry {
return when (this) {
is FeedEntriesQuery.KotlinBlogFeedEntry -> this.toModel()
is FeedEntriesQuery.KotlinYouTubeFeedEntry -> this.toModel()
is FeedEntriesQuery.TalkingKotlinFeedEntry -> this.toModel()
is FeedEntriesQuery.KotlinWeeklyFeedEntry -> this.toModel()
else -> error("Unknown FeedEntry subtype")
}
}

internal fun FeedEntriesQuery.KotlinBlogFeedEntry.toModel(): FeedEntry.KotlinBlog {
return FeedEntry.KotlinBlog(
id = this.id,
title = this.title,
publishTimestamp = this.publishTimestamp,
contentUrl = this.contentUrl,
featuredImageUrl = this.featuredImageUrl,
description = this.description,
)
}

internal fun FeedEntriesQuery.KotlinYouTubeFeedEntry.toModel(): FeedEntry.KotlinYouTube {
return FeedEntry.KotlinYouTube(
id = this.id,
title = this.title,
publishTimestamp = this.publishTimestamp,
contentUrl = this.contentUrl,
thumbnailUrl = this.thumbnailUrl,
description = this.description,
)
}

internal fun FeedEntriesQuery.TalkingKotlinFeedEntry.toModel(): FeedEntry.TalkingKotlin {
return FeedEntry.TalkingKotlin(
id = this.id,
title = this.title,
publishTimestamp = this.publishTimestamp,
contentUrl = this.contentUrl,
podcastLogoUrl = this.podcastLogoUrl,
tags = this.tags,
)
}

internal fun FeedEntriesQuery.KotlinWeeklyFeedEntry.toModel(): FeedEntry.KotlinWeekly {
return FeedEntry.KotlinWeekly(
id = this.id,
title = this.title,
publishTimestamp = this.publishTimestamp,
contentUrl = this.contentUrl,
newsletterLogoUrl = this.newsletterLogoUrl,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.reactivecircus.kstreamlined.kmm.data.feed.mapper

import io.github.reactivecircus.kstreamlined.graphql.FeedSourcesQuery
import io.github.reactivecircus.kstreamlined.graphql.type.FeedSourceKey
import io.github.reactivecircus.kstreamlined.kmm.data.feed.model.FeedSource

internal fun FeedSource.Key.toApollo(): FeedSourceKey {
return when (this) {
FeedSource.Key.KotlinBlog -> FeedSourceKey.KOTLIN_BLOG
FeedSource.Key.KotlinYouTubeChannel -> FeedSourceKey.KOTLIN_YOUTUBE_CHANNEL
FeedSource.Key.TalkingKotlinPodcast -> FeedSourceKey.TALKING_KOTLIN_PODCAST
FeedSource.Key.KotlinWeekly -> FeedSourceKey.KOTLIN_WEEKLY
}
}

internal fun FeedSourcesQuery.FeedSource.toModel(): FeedSource {
return FeedSource(
key = when (this.key) {
FeedSourceKey.KOTLIN_BLOG -> FeedSource.Key.KotlinBlog
FeedSourceKey.KOTLIN_YOUTUBE_CHANNEL -> FeedSource.Key.KotlinYouTubeChannel
FeedSourceKey.TALKING_KOTLIN_PODCAST -> FeedSource.Key.TalkingKotlinPodcast
FeedSourceKey.KOTLIN_WEEKLY -> FeedSource.Key.KotlinWeekly
else -> error("Unknown FeedSourceKey subtype")
},
title = this.title,
description = this.description,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import com.apollographql.apollo3.exception.CacheMissException
import com.apollographql.apollo3.mockserver.MockResponse
import com.apollographql.apollo3.mockserver.MockServer
import com.apollographql.apollo3.testing.enqueue
import io.github.reactivecircus.kstreamlined.kmm.apollo.FeedEntriesQuery
import io.github.reactivecircus.kstreamlined.kmm.apollo.FeedSourcesQuery
import io.github.reactivecircus.kstreamlined.kmm.apollo.type.buildFeedSource
import io.github.reactivecircus.kstreamlined.kmm.apollo.type.buildKotlinBlog
import io.github.reactivecircus.kstreamlined.kmm.apollo.type.buildKotlinYouTube
import io.github.reactivecircus.kstreamlined.graphql.FeedEntriesQuery
import io.github.reactivecircus.kstreamlined.graphql.FeedSourcesQuery
import io.github.reactivecircus.kstreamlined.graphql.type.buildFeedSource
import io.github.reactivecircus.kstreamlined.graphql.type.buildKotlinBlog
import io.github.reactivecircus.kstreamlined.graphql.type.buildKotlinYouTube
import io.github.reactivecircus.kstreamlined.kmm.data.feed.mapper.toModel
import io.github.reactivecircus.kstreamlined.kmm.test.utils.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -73,7 +74,7 @@ class CloudFeedRepoTest {
FeedSourcesQuery.Data(feedSources = dummyFeedSources)
)
val actual = cloudFeedRepo.loadFeedSources(refresh = true)
assertEquals(dummyFeedSources, actual)
assertEquals(dummyFeedSources.map { it.toModel() }, actual)
}

@Test
Expand All @@ -98,7 +99,7 @@ class CloudFeedRepoTest {
FeedSourcesQuery.Data(feedSources = dummyFeedSources)
)
val actual = cloudFeedRepo.loadFeedSources(refresh = false)
assertEquals(dummyFeedSources, actual)
assertEquals(dummyFeedSources.map { it.toModel() }, actual)
}

@Test
Expand All @@ -118,7 +119,7 @@ class CloudFeedRepoTest {
.build()
)
val actual = cloudFeedRepo.loadFeedSources(refresh = false)
assertEquals(dummyFeedSources, actual)
assertEquals(dummyFeedSources.map { it.toModel() }, actual)
}

@Test
Expand Down Expand Up @@ -147,7 +148,7 @@ class CloudFeedRepoTest {
filters = null,
refresh = true,
)
assertEquals(dummyFeedEntries, actual)
assertEquals(dummyFeedEntries.map { it.toModel() }, actual)
}

@Test
Expand Down Expand Up @@ -178,7 +179,7 @@ class CloudFeedRepoTest {
filters = null,
refresh = false,
)
assertEquals(dummyFeedEntries, actual)
assertEquals(dummyFeedEntries.map { it.toModel() }, actual)
}

@Test
Expand All @@ -204,7 +205,7 @@ class CloudFeedRepoTest {
filters = null,
refresh = false,
)
assertEquals(dummyFeedEntries, actual)
assertEquals(dummyFeedEntries.map { it.toModel() }, actual)
}

@Test
Expand Down
Loading

0 comments on commit 7b7fa95

Please sign in to comment.